HTTP Server upon new mbed Ethernet Interface. Based on original code by Henry Leinen.
Dependencies: EthernetInterface mbed-rtos mbed
Fork of HTTP_server by
Revision 3:27b3a889b327, committed 2013-07-30
- Comitter:
- pabloxid
- Date:
- Tue Jul 30 04:37:09 2013 +0000
- Parent:
- 2:dc9184e97328
- Child:
- 4:2a34139c7246
- Commit message:
- some fixes; post handling added
Changed in this revision
HTTPServer.cpp | Show annotated file Show diff for this revision Revisions of this file |
HTTPServer.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/HTTPServer.cpp Sun Jul 28 07:53:35 2013 +0000 +++ b/HTTPServer.cpp Tue Jul 30 04:37:09 2013 +0000 @@ -21,7 +21,7 @@ } INFO("Connected !"); - // set into blocking operation + // set into blocking operation socketServer.set_blocking (true); path = _path; @@ -32,75 +32,83 @@ int HTTPServer::poll () { + int retvalue; + cliente = new TCPSocketConnection; cliente->set_blocking (false, TIMEOUT); - if (socketServer.accept(*cliente) < 0) { + retvalue = socketServer.accept (*cliente); + + if (retvalue == OK) { + + // a new connection was received + INFO("Client (IP=%s) is connected !", cliente->get_address()); + + msg = new HTTPMsg; // estructura para decodificar y alojar el mensaje + + retvalue = pollConnection (); // esto parsea y llena las cosas contenidas en msg + + if (retvalue == OK) { + // Handle the request + INFO("Handling request !"); + handleRequest (); + } + + delete msg; + + } else { // retvalue == ERROR INFO("No connection\n"); - return ERROR; } - // a new connection was received - INFO("Client (IP=%s) is connected !", cliente->get_address()); - - msg = new HTTPMsg; // estructura para decodificar y alojar el mensaje - - int c = pollConnection (); // esto parsea y llena las cosas contenidas en msg - - if (c == OK) { - // Handle the request - // cliente->set_blocking (true); - INFO("Handling request !"); - handleRequest (); - } - - delete msg; delete cliente; INFO("Leaving polling thread\n"); - return c; + return retvalue; + } int HTTPServer::pollConnection () { int received = 0; INFO("Waiting for new data in connection"); - // Try receiving request line + + // Try receiving request line received = receiveLine (); if (received == ERROR) { - // there was an error, probably the connection was closed, so close this connection as well + // there was an error, probably the connection was closed, so close this connection as well INFO("No more data available. Will close this connection now."); return ERROR; } - // The Request has not yet been received so try it - received = parse (); + // Request has not yet been received, so try it + received = parseRequest (); if (received == ERROR) { - // Invalid content received, so close the connection + // Invalid content received, so close the connection INFO("Invalid message received, so sending negative response and closing connection !"); tcpsend ("HTTP/1.1 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\r\n\r\n\r", 0); return ERROR; } - // The request has been received, try receive the body + // Request has been received, try receive the headers section do { received = receiveLine (); if (received == ERROR) {return ERROR;} - // First check if we received an empty line. This would indicate the end of the message or message body. + // First check if we received an empty line; + // This would indicate the end of headers section. if (received == EMPTY) { - // there was an empty line, so we can start with performing the request + // there was an empty line, so the headers section is complete INFO("Request Header was received completely. Performing request."); received = 0; break; } else { - /* add message body - if (parseHeader () != 0) { + // parse header field + if (parseHeader() != OK) { WARN("Invalid message header received !"); - }*/ + } } - } while (received > 0); // + } while (received > 0); INFO("Leaving poll function!"); return received; @@ -151,7 +159,7 @@ return i; } -int HTTPServer::parse () { +int HTTPServer::parseRequest () { // Check if buffer content is not long enough. if (strlen(buffer) < MIN_LONG) { @@ -191,11 +199,12 @@ } } + // init body section length + msg->body_length = 0; + return OK; } -/* esta rutina no se usa */ - int HTTPServer::parseHeader () { // Check if the buffer content is too short to be meaningful @@ -209,13 +218,19 @@ // touple found buffer[i] = 0; value_start = i+1; - msg->headers[buffer] = &buffer[value_start]; + // headers storage is disabled; uncomment next line to enable + // msg->headers[buffer] = &buffer[value_start]; INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]); + // Look for "Content-Length" header + if (strcmp (buffer, "Content-Length") == 0) { + msg->body_length = atoi(&buffer[value_start]); + INFO ("Body section found. Length: %i", msg->body_length); + } return OK; } } - ERR("Did not recieve a valid header : \"%s\".", buffer); + ERR("Did not receive a valid header : \"%s\".", buffer); return ERROR; } @@ -230,16 +245,16 @@ char* argname = NULL; char* valuename = NULL; for (int i=0; i<buflen; i++) { - if (args_start == -1) { // args section not yet found + if (args_start == -1) { // args section not yet found if (uri_buffer[i] == '?') { // starts with a question mark, so got it uri_buffer[i] = 0; args_start = i; // set the start of the args section INFO("Argument section found !"); } - } else { // search arg-value touples - if (argname == NULL) { // arg-name found ? + } else { // search arg-value touples + if (argname == NULL) { // arg-name found ? if (uri_buffer[i] == '=') { - // yes, separate the arg-name + // yes, separate the arg-name uri_buffer[i] = 0; argname = &uri_buffer[args_start]; value_start = i+1; @@ -250,21 +265,17 @@ buffer[i] = 0; valuename = &uri_buffer[value_start]; INFO("Argument value %s", valuename); - msg->args[argname] = valuename; - // reset all indicators + msg->uri_args[argname] = valuename; + // reset all indicators argname = NULL; valuename = NULL; } } } } - return OK; } -/* verificar qué parte de msg realmente se usa, - eliminar el resto, incluyendo los procesos asociados */ - void HTTPServer::handleRequest () { int err_; @@ -311,17 +322,6 @@ FILE *file = fopen(reqPath.c_str(), "r"); if (file != NULL) { - // asigna toda la memoria dinámica disponible para 'chunk' - char * chunk = NULL; - int chunk_sz = MAX_CHUNK_SIZE; - while(chunk == NULL) { - chunk_sz /= 2; - chunk = (char*) malloc (chunk_sz); - if (chunk_sz < MIN_CHUNK_SIZE) { - error ("OutOfMemory"); - } - } - // File was found and can be returned; first determine the size fseek (file, 0, SEEK_END); int size = ftell (file); @@ -329,16 +329,14 @@ startResponse (200, size); // response: 200 = HTTP_Ok while (!feof(file) && !ferror(file)) { - int count = fread (chunk, 1, chunk_sz , file); + int count = fread (buffer, 1, CHUNK_SIZE, file); INFO("Processing Response (%d bytes)!", count); - if (cliente->send_all (chunk, count) != count) { + if (cliente->send_all (buffer, count) != count) { WARN ("Unsent bytes left !"); // TODO: handle filesystem errors } } - INFO("Ending Response !"); - free (chunk); fclose (file); } else { @@ -352,6 +350,36 @@ int HTTPServer::handlePostRequest() { + // Try receive the body data, if there is any + if (msg->body_length > 0) { + + INFO("Receiving body data."); + if (msg->body_length > BUFFER_SIZE) {error ("OutOfMemory");} + + int bytes_read = 0; + while (bytes_read < msg->body_length) { + int result = cliente->receive_all(buffer+bytes_read, msg->body_length-bytes_read); + if (result == ERROR) { + WARN("Error receiving body data."); + break; + } + bytes_read += result; + } + + INFO("Body data received."); + + // do something + // use the url_decode routine :) + + INFO("Done !\n"); + return handleGetRequest(); + + } else { + + ERR("POST data not found !"); + + } + return 404; } @@ -361,6 +389,9 @@ "Content-Type: text/html\r\n" // TODO: handle file types "Server: mbed embedded\r\n" "Accessible: 1\r\n" + "Pragma: no-cache\r\n" + "Cache-control: no-cache;no-store\r\n" + "Expires: 0\r\n" "\r\n"; void HTTPServer::startResponse (int returnCode, int nLen) { @@ -390,3 +421,33 @@ INFO("Done !"); } + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// UTILS // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include <ctype.h> + +/* Converts a hex character to its integer value */ +char from_hex (char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +/* Returns a url-decoded version of str */ +void url_decode (char *str) { + char *lee = str, *escribe = str; + while (*lee) { + if (*lee == '%') { + if (lee[1] && lee[2]) { + *escribe++ = from_hex(lee[1])<<4 | from_hex(lee[2]); + lee += 2; + } + } else if (*lee == '+') { + *escribe++ = ' '; + } else { + *escribe++ = *lee; + } + lee++; + } + *escribe = 0; +}
--- a/HTTPServer.h Sun Jul 28 07:53:35 2013 +0000 +++ b/HTTPServer.h Tue Jul 30 04:37:09 2013 +0000 @@ -31,17 +31,16 @@ #include <string> #include <map> -#define BUFFER_SIZE 256 +#define BUFFER_SIZE 256 // all-purpose buffer #define TIMEOUT 800 #define OK 0 #define ERROR -1 #define EMPTY -2 #define MIN_LONG 3 -#define MAX_CHUNK_SIZE 512 -#define MIN_CHUNK_SIZE 128 +#define CHUNK_SIZE 256 #define DEBUG 0 -#include "debug.h" +#include "../debug.h" enum RequestType { HTTP_RT_GET, /*!< GET request */ @@ -57,9 +56,11 @@ // Contains the HTTP/1.1 or HTTP/1.0 version requested by client. std::string version; // Map of headers provided by the client in the form <HeaderName>:<HeaderValue> - std::map<std::string, std::string> headers; + std::map<std::string, std::string> headers; // Map of arguments that came with the uri string - std::map<std::string, std::string> args; + std::map<std::string, std::string> uri_args; + // length of the body data section + int body_length; }; struct RequestConfig { @@ -77,7 +78,7 @@ char buffer [BUFFER_SIZE]; int pollConnection (); int receiveLine (); - int parse (); + int parseRequest (); int parseHeader (); int parseUriArgs (char *uri_buffer); void handleRequest (); @@ -120,4 +121,7 @@ }; +void url_decode (char *str); + + #endif //__HTTPSERVER_H__