HTTP Server upon new mbed Ethernet Interface. Based on original code by Henry Leinen.
Dependencies: EthernetInterface mbed-rtos mbed
Fork of HTTP_server by
Diff: HTTPServer.cpp
- Revision:
- 0:fcceff3299be
- Child:
- 2:dc9184e97328
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.cpp Fri Jul 26 22:05:19 2013 +0000 @@ -0,0 +1,417 @@ +#include "mbed.h" +#include "HTTPServer.h" + +#define DEBUG +#include "debug.h" + +#define TIMEOUT 500 +#define OK 0 +#define ERROR -1 +#define EMPTY -2 +#define MIN_LONG 3 + + +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, TIMEOUT); + + 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 !\n", 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 + INFO("Handling request !"); + //cliente->set_blocking (true, TIMEOUT); + handleRequest (); + } + + delete msg; + delete cliente; + + INFO("Leaving polling thread"); + 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 !"); + sprintf (buffer,"HTTP/1.1 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\r\n\r\n\r",0); + + cliente->send (buffer, strlen (buffer)); + + 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() > 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() { + + INFO("Handling Get Request."); + + int retval = OK; //success + + // 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 *fp = fopen(reqPath.c_str(), "r"); + if (fp != NULL) { + + char * pBuffer = NULL; + int sz = 8192; // fixme harcode + while( pBuffer == NULL) { + sz /= 2; + pBuffer = (char*)malloc(sz); + if (sz < 128) // fixme harcode + error ("OutOfMemory"); + } + + // File was found and can be returned + + // first determine the size + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + startResponse (200, size); // response: 200 + while (!feof(fp) && !ferror(fp)) { + int cnt = fread (pBuffer, 1, sz , fp); + if (cnt < 0) + cnt = 0; + processResponse (cnt, pBuffer); + } + + INFO("Ending Response !"); + + free (pBuffer); + fclose (fp); + + } 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" + "Server: mbed embedded\r\n" + "Accessible: 1\r\n" + "\r\n"; + +void HTTPServer::startResponse (int returnCode, long nLen) { + + INFO("Starting response (%ld bytes in total)!", nLen); + + sprintf (buffer, "HTTP/1.1 %d OK\r\n", returnCode); + cliente->send(buffer, strlen(buffer)); + sprintf (buffer, "Content-Length: %ld\r\n", nLen); // Add 2 chars for the terminating CR+LF + cliente->send(buffer, strlen(buffer)); + INFO("Sending standard headers !"); + cliente->send_all((char*)hdrStandard, strlen(hdrStandard)); + + INFO("Proceeding !"); + // other content must be sent using the 'processResponse' function +} + +void HTTPServer::processResponse (int nLen, char* body) { + + INFO("Processing Response (%d bytes)!\n", nLen); + cliente->send_all (body, nLen); + +} + +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 !"); + + sprintf (buffer,"HTTP/1.1 %d Error\r\n", errorCode); + cliente->send (buffer, strlen(buffer)); + sprintf (buffer, "Content-Length: %ld\r\n", strlen(errorPage)); + cliente->send (buffer, strlen(buffer)); + sprintf(buffer, "Content-Type: text/html\r\nServer: mbed embedded\r\n\r\n"); + cliente->send(buffer, strlen(buffer)); + cliente->send_all((char*)errorPage, strlen(errorPage)); + cliente->send("\r\n", 3); + + INFO("Done !"); + +}