HTTP Server upon new mbed Ethernet Interface. Based on original code by Henry Leinen.
Dependencies: EthernetInterface mbed-rtos mbed
Fork of HTTP_server by
HTTPServer.cpp
- Committer:
- pabloxid
- Date:
- 2013-08-05
- Revision:
- 4:2a34139c7246
- Parent:
- 3:27b3a889b327
File content as of revision 4:2a34139c7246:
#include "mbed.h" #include "HTTPServer.h" RequestConfig rq_conf[] = { { "GET", HTTP_RT_GET }, { "POST", HTTP_RT_POST} }; HTTPServer::HTTPServer (int port, const char* _path) { INFO("Binding to port %d...", port); if (socketServer.bind (port) < 0) { ERR("Failed to bind to port !\n"); error("Binding"); } INFO("Listening ..."); if (socketServer.listen(1) < 0) { ERR("Failed to listen !\n"); error("Listening"); } INFO("Connected !"); // set into blocking operation socketServer.set_blocking (true); path = _path; msg = NULL; cliente = NULL; } HTTPServer::~HTTPServer() { }; int HTTPServer::poll () { cliente = new TCPSocketConnection; cliente->set_blocking (false, TIMEOUT); int 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 (); // handling request } delete msg; } else { // retvalue == ERROR ERR("Error accepting client"); } delete cliente; INFO("Leaving polling thread\n"); return retvalue; } int HTTPServer::pollConnection () { INFO("Waiting for new data in connection"); // Try receiving request line int received = receiveLine (); if (received == ERROR) { // 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; } // Request has not yet been received, so try it received = parseRequest (); /*alternative (fast) parse request method: * ret = sscanf(buffer, "%s %s HTTP/%*d.%*d", request, uri); */ if (received == ERROR) { // 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; } // 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 headers section. if (received == EMPTY) { // there was an empty line, so the headers section is complete INFO("Request Header was received completely. Performing request."); received = OK; break; } else { // parse header field if (parseHeader() != OK) { WARN("Invalid message header received !"); } } } while (received > 0); INFO("Leaving poll function!"); return received; } int HTTPServer::receiveLine () { buffer[0] = 0; int i; // Try to receive up to the max number of characters for (i=0; i<BUFFER_SIZE-1; i++) { int c = cliente->receive (buffer+i, 1); // Check that - if no character was currently received - the timeout period is reached. if (c == 0 || c == -1) { // no character was read, so check if operation timed out ERR("Timeout occured in function 'receiveLine'."); return ERROR; } // Check if line terminating character was received if (buffer[i] == '\n') {break;} } // Terminate with \0 buffer[i] = 0; // Trim for '\r' linefeed at the end if (i>0 && buffer[i-1] == '\r') { i--; buffer[i] = 0; } // return number of characters received in the line or return -2 if an empty line was received if (i==0 || (i==1 && buffer[0]=='\r')) { // empty line received, so return -2 return EMPTY; } // retorna número de caracteres leidos return i; } int HTTPServer::parseRequest () { // Check if buffer content is not long enough. if (strlen(buffer) < MIN_LONG) { ERR("Buffer content is invalid or too short."); return ERROR; } std::vector<std::string> args; int argno = 0; // decompose string into a list of arguments int start = 0; // current starting char int nLen = strlen(buffer)+1; for (int i=0; i<nLen; i++) { if ((buffer[i] == ' ') || (buffer[i] == '\n') || (buffer[i] == 0)) { // new arg found buffer[i] = 0; if (argno++ == 1) { // it's the uri // parse the uri args parseUriArgs (&buffer[start]); } INFO("Found argument \"%s\"", &buffer[start]); args.push_back(&buffer[start]); start = i+1; } } // store the uri and the HTTP version msg->uri = args[1]; msg->version = args[2]; // Find matching request type for (int i=0; i<sizeof(rq_conf)/sizeof(RequestConfig) ; i++) { if (args.at(0) == rq_conf[i].request_string) { msg->request = rq_conf[i].request_type; } } // init body section length msg->body_length = 0; return OK; } int HTTPServer::parseHeader () { // Check if the buffer content is too short to be meaningful if (strlen(buffer) < MIN_LONG) {return ERROR;} // decompose string into a touple of <field name> : <field value> int value_start = 0; int buflen = strlen(buffer)+1; for (int i=0; i<buflen; i++) { if (buffer[i] == ':') { // touple found buffer[i] = 0; value_start = i+1; // 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 receive a valid header : \"%s\".", buffer); return ERROR; } int HTTPServer::parseUriArgs (char *uri_buffer) { // Check if the buffer content is too short to be meaningful if (strlen(uri_buffer) < MIN_LONG) {return ERROR;} int args_start = -1; int value_start = -1; int buflen = strlen(uri_buffer) + 1; char* argname = NULL; char* valuename = NULL; for (int i=0; i<buflen; i++) { 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 ? if (uri_buffer[i] == '=') { // yes, separate the arg-name uri_buffer[i] = 0; argname = &uri_buffer[args_start]; value_start = i+1; INFO("Argument name %s", argname); } } else { // search for end of value if ((uri_buffer[i] == '&') || (uri_buffer[i] == 0) || (uri_buffer[i] == '\r') || (uri_buffer[i] == '\n')) { buffer[i] = 0; valuename = &uri_buffer[value_start]; INFO("Argument value %s", valuename); msg->uri_args[argname] = valuename; // reset all indicators argname = NULL; valuename = NULL; } } } } return OK; } void HTTPServer::handleRequest () { int err_; switch (msg->request) { case HTTP_RT_GET: INFO("Dispatching GET Request."); err_ = handleGetRequest(); break; case HTTP_RT_POST: INFO("Dispatching POST request."); err_ = handlePostRequest(); break; default: INFO("Error in handleRequest, unhandled request type."); err_ = 501; // HTTP_NotImplemented break; } // if any of these functions returns a negative number, call the error handler if (err_ > 0) { handleError (err_); } } int HTTPServer::handleGetRequest() { int retval = OK; //success INFO("Handling Get Request."); // maping to root path std::string reqPath = path + msg->uri.substr(1); // Check if we received a directory with the local path if (reqPath.substr(reqPath.length()-1, 1) == "/") { // yes, we shall append the default page name reqPath += "index.htm"; } INFO("Mapping \"%s\" to \"%s\"", msg->uri.c_str(), reqPath.c_str()); FILE *file = fopen(reqPath.c_str(), "r"); if (file != NULL) { // File was found and can be returned; first determine the size fseek (file, 0, SEEK_END); int size = ftell (file); fseek (file, 0, SEEK_SET); startResponse (200, size); // response: 200 = HTTP_Ok while (!feof(file) && !ferror(file)) { // TODO: handle filesystem errors too int count = fread (buffer, 1, CHUNK_SIZE, file); INFO("Processing Response (%d bytes)!", count); tcpsend (buffer, count); } INFO("Ending Response !"); fclose (file); } else { retval = 404; ERR("Requested file was not found !"); } return retval; } int HTTPServer::handlePostRequest() { // Try receive the body data, if there is any if (msg->body_length > 0) { char post_buffer [msg->body_length]; INFO("Receiving body data. (%i bytes)", msg->body_length); int bytes_read = 0; while (bytes_read < msg->body_length) { int result = cliente->receive_all(post_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 function :) INFO("Done !\n"); return handleGetRequest(); } else { ERR("POST data not found !"); } return 404; } static const char hdrStandard[] = "DNT: 1\r\n" "MaxAge: 0\r\n" "Connection: Keep-Alive\r\n" "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) { INFO("Starting response (%d bytes in total)!", nLen); tcpsend ("HTTP/1.1 %d OK\r\n", returnCode); tcpsend ("Content-Length: %d\r\n", nLen); // Add 2 chars for the terminating CR+LF INFO("Sending standard headers !"); tcpsend (hdrStandard); INFO("Done !"); } 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"; void HTTPServer::handleError (int errorCode) { INFO("Handling error !"); tcpsend ("HTTP/1.1 %d Error\r\n", errorCode); tcpsend ("Content-Length: %d\r\n", strlen(errorPage)); tcpsend ("Content-Type: text/html\r\nServer: mbed embedded\r\n\r\n"); tcpsend (errorPage); // TODO: better error page (handle error type) 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; }