HTTP Server upon new mbed Ethernet Interface. Based on original code by Henry Leinen.

Dependencies:   EthernetInterface mbed-rtos mbed

Fork of HTTP_server by pablo gindel

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPServer.cpp Source File

HTTPServer.cpp

00001 #include "mbed.h"
00002 #include "HTTPServer.h"
00003 
00004 RequestConfig rq_conf[] = {
00005     { "GET",    HTTP_RT_GET },
00006     { "POST",   HTTP_RT_POST}
00007 };
00008 
00009 HTTPServer::HTTPServer (int port, const char* _path) {
00010 
00011     INFO("Binding to port %d...", port);
00012     if (socketServer.bind (port) < 0) {
00013        ERR("Failed to bind to port !\n");
00014        error("Binding");
00015     }
00016 
00017     INFO("Listening ...");
00018     if (socketServer.listen(1) < 0) {
00019        ERR("Failed to listen !\n");
00020        error("Listening");
00021     }
00022 
00023     INFO("Connected !");
00024     // set into blocking operation
00025     socketServer.set_blocking (true);
00026 
00027     path = _path;
00028     msg = NULL;
00029     cliente = NULL;
00030 
00031 }
00032 
00033 HTTPServer::~HTTPServer() { };
00034 
00035 int HTTPServer::poll () {
00036 
00037     cliente = new TCPSocketConnection;
00038     cliente->set_blocking (false, TIMEOUT);
00039 
00040     int retvalue = socketServer.accept (*cliente);
00041 
00042     if (retvalue == OK) {
00043         // a new connection was received
00044         INFO("Client (IP=%s) is connected !", cliente->get_address());
00045         msg = new HTTPMsg;     // estructura para decodificar y alojar el mensaje
00046 
00047         retvalue = pollConnection (); // esto parsea y llena las cosas contenidas en msg
00048 
00049         if (retvalue == OK) {
00050             // Handle the request
00051             INFO("Handling request !");
00052 
00053             handleRequest ();    // handling request
00054 
00055         }
00056         delete msg;
00057     } else {  // retvalue == ERROR
00058         ERR("Error accepting client");
00059     }
00060 
00061     delete cliente;
00062 
00063     INFO("Leaving polling thread\n");
00064     return retvalue;
00065 
00066 }
00067 
00068 int HTTPServer::pollConnection () {
00069 
00070     INFO("Waiting for new data in connection");
00071 
00072     // Try receiving request line
00073     int received = receiveLine ();
00074 
00075     if (received == ERROR) {
00076         // there was an error, probably the connection was closed, so close this connection as well
00077         INFO("No more data available. Will close this connection now.");
00078         return ERROR;
00079     }
00080 
00081     // Request has not yet been received, so try it
00082     received = parseRequest ();
00083 
00084     /*alternative (fast) parse request method:
00085      * ret = sscanf(buffer, "%s %s HTTP/%*d.%*d", request, uri);
00086      */
00087 
00088     if (received == ERROR) {
00089         // Invalid content received, so close the connection
00090         INFO("Invalid message received, so sending negative response and closing connection !");
00091         tcpsend ("HTTP/1.1 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\r\n\r\n\r", 0);
00092         return ERROR;
00093     }
00094 
00095     // Request has been received, try receive the headers section
00096     do {
00097         received = receiveLine ();
00098         if (received == ERROR) {return ERROR;}
00099         // First check if we received an empty line;
00100         // This would indicate the end of headers section.
00101         if (received == EMPTY) {
00102             // there was an empty line, so the headers section is complete
00103             INFO("Request Header was received completely. Performing request.");
00104             received = OK;
00105             break;
00106         } else {
00107             // parse header field
00108             if (parseHeader() != OK) {
00109                 WARN("Invalid message header received !");
00110             }
00111         }
00112     } while (received > 0);
00113 
00114     INFO("Leaving poll function!");
00115     return received;
00116 }
00117 
00118 int HTTPServer::receiveLine () {
00119 
00120     buffer[0] = 0;
00121 
00122     int i;
00123 
00124     //  Try to receive up to the max number of characters
00125     for (i=0; i<BUFFER_SIZE-1; i++) {
00126         int c = cliente->receive (buffer+i, 1);
00127         //  Check that - if no character was currently received - the timeout period is reached.
00128         if (c == 0 || c == -1) {
00129             //  no character was read, so check if operation timed out
00130             ERR("Timeout occured in function 'receiveLine'.");
00131             return ERROR;
00132         }
00133         //  Check if line terminating character was received
00134         if (buffer[i] == '\n') {break;}
00135     }
00136     //  Terminate with \0
00137     buffer[i] = 0;
00138 
00139     //  Trim for '\r' linefeed at the end
00140     if (i>0 && buffer[i-1] == '\r') {
00141         i--;
00142         buffer[i] = 0;
00143     }
00144 
00145     //  return number of characters received in the line or return -2 if an empty line was received
00146     if (i==0 || (i==1 && buffer[0]=='\r')) {
00147         //  empty line received, so return -2
00148         return EMPTY;
00149     }
00150     // retorna número de caracteres leidos
00151     return i;
00152 }
00153 
00154 int HTTPServer::parseRequest () {
00155 
00156     //  Check if buffer content is not long enough.
00157     if (strlen(buffer) < MIN_LONG) {
00158         ERR("Buffer content is invalid or too short.");
00159         return ERROR;
00160     }
00161 
00162     std::vector<std::string> args;
00163 
00164     int argno = 0;
00165     //  decompose string into a list of arguments
00166     int start = 0; // current starting char
00167     int nLen = strlen(buffer)+1;
00168     for (int i=0; i<nLen; i++) {
00169         if ((buffer[i] == ' ') || (buffer[i] == '\n') || (buffer[i] == 0)) {
00170             // new arg found
00171             buffer[i] = 0;
00172             if (argno++ == 1) {
00173                 // it's the uri
00174                 // parse the uri args
00175                 parseUriArgs (&buffer[start]);
00176             }
00177             INFO("Found argument \"%s\"", &buffer[start]);
00178             args.push_back(&buffer[start]);
00179             start = i+1;
00180         }
00181     }
00182 
00183     // store the uri and the HTTP version
00184     msg->uri = args[1];
00185     msg->version = args[2];
00186 
00187     //  Find matching request type
00188     for (int i=0; i<sizeof(rq_conf)/sizeof(RequestConfig) ; i++) {
00189         if (args.at(0) == rq_conf[i].request_string) {
00190             msg->request = rq_conf[i].request_type;
00191         }
00192     }
00193 
00194     // init body section length
00195     msg->body_length = 0;
00196 
00197     return OK;
00198 }
00199 
00200 int HTTPServer::parseHeader () {
00201 
00202     //  Check if the buffer content is too short to be meaningful
00203     if (strlen(buffer) < MIN_LONG) {return ERROR;}
00204 
00205     //  decompose string into a touple of <field name> : <field value>
00206     int value_start = 0;
00207     int buflen = strlen(buffer)+1;
00208     for (int i=0; i<buflen; i++) {
00209         if (buffer[i] == ':') {
00210             //  touple found
00211             buffer[i] = 0;
00212             value_start = i+1;
00213             // headers storage is disabled; uncomment next line to enable
00214             // msg->headers[buffer] = &buffer[value_start];
00215             INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]);
00216             // Look for "Content-Length" header
00217             if (strcmp (buffer, "Content-Length") == 0) {
00218                 msg->body_length = atoi(&buffer[value_start]);
00219                 INFO ("Body section found. Length: %i", msg->body_length);
00220             }
00221             return OK;
00222         }
00223     }
00224 
00225     ERR("Did not receive a valid header : \"%s\".", buffer);
00226     return ERROR;
00227 }
00228 
00229 int HTTPServer::parseUriArgs (char *uri_buffer) {
00230 
00231     // Check if the buffer content is too short to be meaningful
00232     if (strlen(uri_buffer) < MIN_LONG) {return ERROR;}
00233 
00234     int args_start = -1;
00235     int value_start = -1;
00236     int buflen = strlen(uri_buffer) + 1;
00237     char* argname = NULL;
00238     char* valuename = NULL;
00239     for (int i=0; i<buflen; i++) {
00240         if (args_start == -1) {  // args section not yet found
00241             if (uri_buffer[i] == '?') {  // starts with a question mark, so got it
00242                 uri_buffer[i] = 0;
00243                 args_start = i;  // set the start of the args section
00244                 INFO("Argument section found !");
00245             }
00246         } else {                    // search arg-value touples
00247             if (argname == NULL) {     // arg-name found ?
00248                 if (uri_buffer[i] == '=') {
00249                     // yes, separate the arg-name
00250                     uri_buffer[i] = 0;
00251                     argname = &uri_buffer[args_start];
00252                     value_start = i+1;
00253                     INFO("Argument name %s", argname);
00254                 }
00255             } else { // search for end of value
00256                 if ((uri_buffer[i] == '&') || (uri_buffer[i] == 0) || (uri_buffer[i] == '\r') || (uri_buffer[i] == '\n')) {
00257                     buffer[i] = 0;
00258                     valuename = &uri_buffer[value_start];
00259                     INFO("Argument value %s", valuename);
00260                     msg->uri_args[argname] = valuename;
00261                     // reset all indicators
00262                     argname = NULL;
00263                     valuename = NULL;
00264                 }
00265             }
00266         }
00267     }
00268     return OK;
00269 }
00270 
00271 void HTTPServer::handleRequest () {
00272 
00273     int err_;
00274 
00275     switch (msg->request) {
00276       case HTTP_RT_GET:
00277         INFO("Dispatching GET Request.");
00278         err_ = handleGetRequest();
00279         break;
00280       case HTTP_RT_POST:
00281         INFO("Dispatching POST request.");
00282         err_ = handlePostRequest();
00283         break;
00284       default:
00285         INFO("Error in handleRequest, unhandled request type.");
00286         err_ = 501;      // HTTP_NotImplemented
00287         break;
00288     }
00289 
00290     //  if any of these functions returns a negative number, call the error handler
00291     if (err_ > 0) {
00292         handleError (err_);
00293     }
00294 
00295 }
00296 
00297 int HTTPServer::handleGetRequest() {
00298 
00299     int retval = OK;     //success
00300 
00301     INFO("Handling Get Request.");
00302 
00303     // maping to root path
00304     std::string reqPath = path + msg->uri.substr(1);
00305 
00306     //  Check if we received a directory with the local path
00307     if (reqPath.substr(reqPath.length()-1, 1) == "/") {
00308         //  yes, we shall append the default page name
00309         reqPath += "index.htm";
00310     }
00311 
00312     INFO("Mapping \"%s\" to \"%s\"", msg->uri.c_str(), reqPath.c_str());
00313 
00314     FILE *file = fopen(reqPath.c_str(), "r");
00315     if (file != NULL) {
00316 
00317         // File was found and can be returned; first determine the size
00318         fseek (file, 0, SEEK_END);
00319         int size = ftell (file);
00320         fseek (file, 0, SEEK_SET);
00321 
00322         startResponse (200, size);                  // response: 200 = HTTP_Ok
00323         while (!feof(file) && !ferror(file)) {
00324             // TODO: handle filesystem errors too
00325             int count = fread (buffer, 1, CHUNK_SIZE, file);
00326             INFO("Processing Response (%d bytes)!", count);
00327             tcpsend (buffer, count);
00328         }
00329         INFO("Ending Response !");
00330 
00331         fclose (file);
00332 
00333     } else {
00334         retval = 404;
00335         ERR("Requested file was not found !");
00336     }
00337 
00338     return retval;
00339 
00340 }
00341 
00342 int HTTPServer::handlePostRequest() {
00343 
00344     // Try receive the body data, if there is any
00345     if (msg->body_length > 0) {
00346 
00347         char post_buffer [msg->body_length];
00348 
00349         INFO("Receiving body data. (%i bytes)", msg->body_length);
00350 
00351         int bytes_read = 0;
00352         while (bytes_read < msg->body_length) {
00353             int result = cliente->receive_all(post_buffer+bytes_read, msg->body_length-bytes_read);
00354             if (result == ERROR) {
00355                 WARN("Error receiving body data.");
00356                 break;
00357             }
00358             bytes_read += result;
00359         }
00360 
00361         INFO("Body data received.");
00362     
00363         // do something
00364         // use the url_decode function :)
00365             
00366         INFO("Done !\n");
00367         
00368         return handleGetRequest();
00369         
00370     } else {
00371         ERR("POST data not found !");
00372     }
00373 
00374     return 404;
00375 }
00376 
00377 static const char hdrStandard[] = "DNT: 1\r\n"
00378                             "MaxAge: 0\r\n"
00379                             "Connection: Keep-Alive\r\n"
00380                             "Content-Type: text/html\r\n"       // TODO: handle file types
00381                             "Server: mbed embedded\r\n"
00382                             "Accessible: 1\r\n"
00383                             "Pragma: no-cache\r\n"
00384                             "Cache-control: no-cache;no-store\r\n"
00385                             "Expires: 0\r\n"
00386                             "\r\n";
00387 
00388 void HTTPServer::startResponse (int returnCode, int nLen) {
00389 
00390     INFO("Starting response (%d bytes in total)!", nLen);
00391 
00392     tcpsend ("HTTP/1.1 %d OK\r\n", returnCode);
00393     tcpsend ("Content-Length: %d\r\n", nLen);    // Add 2 chars for the terminating CR+LF
00394     INFO("Sending standard headers !");
00395     tcpsend (hdrStandard);
00396 
00397     INFO("Done !");
00398 
00399 }
00400 
00401 static const char* errorPage = "<HTML><HEAD><META content=\"text/html\" http-equiv=Content-Type></HEAD><BODY><h1>Error</h1><P>HTTPServer Error<P></BODY></HTML>\r\n\r\n";
00402 
00403 void HTTPServer::handleError (int errorCode) {
00404 
00405     INFO("Handling error !");
00406 
00407     tcpsend ("HTTP/1.1 %d Error\r\n", errorCode);
00408     tcpsend ("Content-Length: %d\r\n", strlen(errorPage));
00409     tcpsend ("Content-Type: text/html\r\nServer: mbed embedded\r\n\r\n");
00410     tcpsend (errorPage);                                                  // TODO: better error page (handle error type)
00411 
00412     INFO("Done !");
00413 
00414 }
00415 
00416 ////////////////////////////////////////////////////////////////////////////////////////////////////
00417 //                                            UTILS                                               //
00418 ////////////////////////////////////////////////////////////////////////////////////////////////////
00419 
00420 #include <ctype.h>
00421 
00422 /* Converts a hex character to its integer value */
00423 char from_hex (char ch) {
00424   return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
00425 }
00426 
00427 /* Returns a url-decoded version of str */
00428 void url_decode (char *str) {
00429   char *lee = str, *escribe = str;
00430   while (*lee) {
00431     if (*lee == '%') {
00432       if (lee[1] && lee[2]) {
00433         *escribe++ = from_hex(lee[1])<<4 | from_hex(lee[2]);
00434         lee += 2;
00435       }
00436     } else if (*lee == '+') {
00437       *escribe++ = ' ';
00438     } else {
00439       *escribe++ = *lee;
00440     }
00441     lee++;
00442   }
00443   *escribe = 0;
00444 }