/* webserver.c */ #include #include #include #include #include #include #include #include #include #include #include #include "tcplib.h" #include "CgiEnv.h" using namespace std; #define SOLARIS #if defined(LINUX) || defined(SOLARIS) #include #endif #define BUFFSIZE 256 #define SERVER_NAME "CSCI 3335 Project Web Server" #define ERROR_400 "

Error 400

Th\ e server couldn't understand your request.\n" #define ERROR_404 "

Error 404

Do\ cument not found.\n" #define HOME_PAGE "

Welcome to the CSCI \ 335 Demo Server

Why not visit:

\n" #define TIME_PAGE "

The current date is\ : %s

\n" int recvln(int, char *, int); void send_head(int, int, int, char*); int send_eof(int); void openAndSendFile(int, char *, char *); void processForm(int, char *); char* toLower(char *); /*----------------------------------------------------------------------- * * Program: webserver * Purpose: serve hard-coded webpages to web clients * Usage: webserver * *----------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int conn; int sock; int port; int n; char buff[BUFFSIZE], cmd[16], path[64], vers[16]; char header[8*BUFFSIZE]; /* assume header is no longer than 2048, not very robust */ char *timestr; #if defined(LINUX) || defined(SOLARIS) struct timeval tv; #elif defined(WIN32) time_t tv; #endif if (argc != 2) { (void) fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } port = atoi(argv[1]); sock = socketServer(port); while(1) { /* wait for contact from a client on specified appnum */ conn = acceptConn(sock); if (conn < 0) exit(1); /* read and parse the request line */ n = recvln(conn, buff, BUFFSIZE); sscanf(buff, "%s %s %s", cmd, path, vers); /* read all headers - read until we get \r\n alone */ strcpy(header,""); while((n = recvln(conn, buff, BUFFSIZE)) > 0) { if (n == 2 && buff[0] == '\r' && buff[1] == '\n') break; strcat(header, buff); } /* check for unexpected end of file */ if (n < 1) { (void) send_eof(conn); continue; } /* check for a request that we cannot understand */ if ((strcmp(cmd, "GET") && strcmp(cmd, "POST")) || (strcmp(vers, "HTTP/1.0") && strcmp(vers, "HTTP/1.1"))) { send_head(conn, 400, strlen(ERROR_400), "text/html"); (void) send(conn, ERROR_400, strlen(ERROR_400),0); (void) send_eof(conn); continue; } /* send the requested web page or a "not found" error */ if (strcmp(path, "/") == 0) { send_head(conn, 200, strlen(HOME_PAGE), "text/html"); (void) send(conn, HOME_PAGE, strlen(HOME_PAGE),0); } else if (strcmp(path, "/sample.html") == 0) { openAndSendFile(conn, "sample.html", "text/html"); } else if (strcmp(path, "/search") == 0) { openAndSendFile(conn, "form.html", "text/html"); } else if (strcmp(path, "/cross.gif") == 0) { openAndSendFile(conn, "cross.gif", "image/gif"); } else if (strcmp(path, "/form") == 0) { processForm(conn, header); } else if (strcmp(path, "/time") == 0) { #if defined(LINUX) || defined(SOLARIS) gettimeofday(&tv, NULL); timestr = ctime(&tv.tv_sec); #elif defined(WIN32) time(&tv); timestr = ctime(&tv); #endif (void) sprintf(buff, TIME_PAGE, timestr); send_head(conn, 200, strlen(buff), "text/html"); (void) send(conn, buff, strlen(buff), 0); } else { /* not found */ send_head(conn, 404, strlen(ERROR_404), "text/html"); (void) send(conn, ERROR_404, strlen(ERROR_404),0); } // if (strcmp(path, "/cross.gif") != 0) (void) send_eof(conn); } } /*----------------------------------------------------------------------- * send_head - send an HTTP 1.0 header with given status and content-len *----------------------------------------------------------------------- */ void send_head(int conn, int stat, int len, char * contentType) { char *statstr, buff[BUFFSIZE]; /* convert the status code to a string */ switch(stat) { case 200: statstr = "OK"; break; case 400: statstr = "Bad Request"; break; case 404: statstr = "Not Found"; break; default: statstr = "Unknown"; break; } /* * send an HTTP/1.0 response with Server, Content-Length, * and Content-Type headers. */ (void) sprintf(buff, "HTTP/1.0 %d %s\r\n", stat, statstr); (void) send(conn, buff, strlen(buff), 0); (void) sprintf(buff, "Server: %s\r\n", SERVER_NAME); (void) send(conn, buff, strlen(buff), 0); (void) sprintf(buff, "Content-Length: %d\r\n", len); (void) send(conn, buff, strlen(buff), 0); /* (void) sprintf(buff, "Content-Type: text/html\r\n"); */ /** (void) sprintf(buff, "Content-Type: "); (void) sprintf(buff, contentType); (void) sprintf(buff, "\r\n"); **/ strcpy(buff, "Content-Type: "); strcat(buff, contentType); strcat(buff, "\r\n"); (void) send(conn, buff, strlen(buff), 0); (void) sprintf(buff, "\r\n"); (void) send(conn, buff, strlen(buff), 0); } void openAndSendFile(int conn, char * fName, char * fType) { const int MAXBUF = 65536; // read buffer length int fId = open(fName, O_RDONLY); // open file for raw read int n; if (fId < 0) /* can't open the file */ { send_head(conn, 404, strlen(ERROR_404), "text/html"); (void) send(conn, ERROR_404, strlen(ERROR_404),0); } else { unsigned char * content; // assuem the file is no bigger than this content = new unsigned char[MAXBUF]; int length = 0; while ((n = read(fId, &(content[length]), MAXBUF)) > 0) length += n; cout << " length of the file : " << length << endl; cout << " name of the file : " << fName << endl; send_head(conn, 200, length, fType); //(void) send(conn, content, length, 0); n = send(conn, content, length, 0); cout << " bytes sent : " << n << endl; } close(fId); } void openAndSendFileB(int conn, char * fName) { ifstream inf(fName); if (!inf) /* can't open the file */ { send_head(conn, 404, strlen(ERROR_404), "text/html"); (void) send(conn, ERROR_404, strlen(ERROR_404),0); } else { /* the method used here is not efficient, nor it scalable */ char * line; line = new char[256]; // assume no line is longer than this char * text; text = new char[65536]; // assuem the file is no bigger than this strcpy(text, ""); int length = 0; while (inf.getline(line, 255)) { strcat(text, line); strcat(text, "\n"); length += strlen(line) + 1; } send_head(conn, 200, length, "text/html"); (void) send(conn, text, length, 0); } } int getContentLength(char * header) { char *lengthPtr; // search where the length value is lengthPtr = strstr(header, "content-length"); if (lengthPtr == NULL) return 0; /* now extract the integer value */ // first skip all non-digit chars while (!isdigit(*lengthPtr)) lengthPtr ++; // keep the begining of the digit char *digitsPtr = lengthPtr; // now skip until the end of the digits while (isdigit(*lengthPtr)) lengthPtr ++; char digits[32]; // assume the integer is less than 32 digits // copy the number strncpy(digits, digitsPtr, (lengthPtr - digitsPtr)); // terminate the string digits[(lengthPtr - digitsPtr)]; return atoi(digits); } void processForm(int sock, char * header) { int n; char buff[BUFFSIZE]; // assume the form is no longer than BUFFSIZE // convert all chars to lower case header = toLower(header); // extract the integer representing the content length int contentLength = getContentLength(header); if (contentLength == 0) { send_head(sock, 404, strlen(ERROR_404), "text/html"); (void) send(sock, ERROR_404, strlen(ERROR_404),0); return; } // read the form from browser n = recv(sock, buff, contentLength, 0); buff[n] = 0; string value, name; // for name=value pair string retContent; // what is returned to the browser CgiEnv myCGI; // use CgiEnv to parse the pairs // extract all pais of name/value and store them myCGI.populate(buff); // now construct the output string // how many pairs? n = myCGI.getSize(); // build HTML code for display retContent = "
\n"; for (int i = 0; i < n; i ++) { if (myCGI.getPair(i, name, value)) // successful getting it { retContent += "
" + name + "
: " + value + ":
\n"; } } retContent += "
\n"; // return the header first n = retContent.size(); send_head(sock, 200, n, "text/html"); // return the contents (void) send(sock, retContent.c_str(), n, 0); } char * toLower(char * str) { int length; char * retStr; length = strlen(str); retStr = new char[length]; for (int i = 0; i < length; i ++) retStr[i] = tolower(str[i]); return retStr; }