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-07-28
- Revision:
- 2:dc9184e97328
- Parent:
- 0:fcceff3299be
- Child:
- 3:27b3a889b327
File content as of revision 2:dc9184e97328:
#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; } HTTPServer::~HTTPServer() { }; int HTTPServer::poll () { cliente = new TCPSocketConnection; cliente->set_blocking (false, TIMEOUT); if (socketServer.accept(*cliente) < 0) { 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; } int HTTPServer::pollConnection () { int received = 0; INFO("Waiting for new data in connection"); // Try receiving request line 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; } // The Request has not yet been received so try it received = parse (); 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; } // The request has been received, try receive the body 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. if (received == EMPTY) { // there was an empty line, so we can start with performing the request INFO("Request Header was received completely. Performing request."); received = 0; break; } else { /* add message body if (parseHeader () != 0) { WARN("Invalid message header received !"); }*/ } } while (received > 0); // INFO("Leaving poll function!"); return received; } int HTTPServer::receiveLine () { buffer[0] = 0; if (!cliente->is_connected()) { error("NOT Connected anymore"); return ERROR; } Timer tm; 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 if (tm.read_ms() > 2*TIMEOUT) { // Operation timed out INFO("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::parse () { // 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; } } return OK; } /* esta rutina no se usa */ 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; msg->headers[buffer] = &buffer[value_start]; INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]); return OK; } } ERR("Did not recieve 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->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_; 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) { // 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); fseek (file, 0, SEEK_SET); startResponse (200, size); // response: 200 = HTTP_Ok while (!feof(file) && !ferror(file)) { int count = fread (chunk, 1, chunk_sz , file); INFO("Processing Response (%d bytes)!", count); if (cliente->send_all (chunk, count) != count) { WARN ("Unsent bytes left !"); // TODO: handle filesystem errors } } INFO("Ending Response !"); free (chunk); fclose (file); } else { retval = 404; ERR("Requested file was not found !"); } return retval; } int HTTPServer::handlePostRequest() { 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" "\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 !"); }