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 0:fcceff3299be, committed 2013-07-26
- Comitter:
- pabloxid
- Date:
- Fri Jul 26 22:05:19 2013 +0000
- Child:
- 1:f0c641cd9bad
- Commit message:
- HTTP Server ; Copyright (c) 2013 Pablo Gindel (palmer@pablogindel.com); Based on original code by Henry Leinen.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EthernetInterface.lib Fri Jul 26 22:05:19 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/EthernetInterface/#40640efbfcae
--- /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 !");
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.h Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,91 @@
+/*
+Copyright (c) 2013 Pablo Gindel (palmer@pablogindel.com)
+Based on original code by Henry Leinen (henry[dot]leinen [at] online [dot] de)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __HTTPSERVER_H__
+#define __HTTPSERVER_H__
+
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include <vector>
+#include <string>
+#include <map>
+
+enum RequestType {
+ HTTP_RT_GET, /*!< GET request */
+ HTTP_RT_POST, /*!< POST request */
+};
+
+/** HTTPMessage contains all the details of the request received by external HTTP client. */
+ struct HTTPMsg {
+ // Specifies the request type received
+ RequestType request;
+ // The uri associated with the request.
+ std::string uri;
+ // 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;
+ // Map of arguments that came with the uri string
+ std::map<std::string, std::string> args;
+};
+
+struct RequestConfig {
+ const char* request_string;
+ RequestType request_type;
+};
+
+#define BUFFER_SIZE 256
+
+class HTTPServer {
+
+private:
+ TCPSocketServer socketServer;
+ TCPSocketConnection *cliente;
+ HTTPMsg *msg;
+ std::string path;
+ char buffer [BUFFER_SIZE];
+ int pollConnection ();
+ int receiveLine ();
+ int parse ();
+ int parseHeader ();
+ int parseUriArgs (char *uri_buffer);
+ void handleRequest ();
+ int handleGetRequest();
+ int handlePostRequest();
+ void startResponse (int returnCode, long nLen);
+ void processResponse (int nLen, char* body);
+ void handleError (int errorCode);
+
+public:
+ /** Constructor for HTTPServer objects. */
+ HTTPServer (int port, const char* _path);
+
+ /** Destructor for HTTPServer objects. */
+ ~HTTPServer();
+
+ int poll();
+};
+
+
+#endif //__HTTPSERVER_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/debug.h Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,17 @@
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+
+#ifdef DEBUG
+#define INFO(x, ...) std::printf("[INFO: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#define WARN(x, ...) std::printf("[WARN: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#define ERR(x, ...) std::printf("[ERR: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#else
+#define INFO(x, ...)
+#define WARN(x, ...)
+#define ERR(x, ...)
+#endif
+
+
+#endif // __DEBUG_H__#ifndef __DEBUG_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,50 @@
+
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include "HTTPServer.h"
+
+LocalFileSystem local("local");
+
+DigitalOut led1(LED1);
+
+void http_thread (void const* arg); ///////
+
+int main() {
+
+ EthernetInterface eth;
+ eth.init(); //Use DHCP
+ eth.connect();
+ printf("IP Address is %s\n", eth.getIPAddress());
+
+ Timer onesec;
+ onesec.start();
+
+ Thread httpsvr( &http_thread );
+
+ while (true) {
+
+ if (onesec.read() > 1) {
+ onesec.reset();
+ led1 = 1-led1;
+ }
+
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+// HTTP THREAD //
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void http_thread (void const* arg) {
+
+ HTTPServer svr (80, "/local/"); // esto incluye el init
+
+ // osThreadSetPriority( Thread::gettid() , osPriorityBelowNormal );
+
+ while (1) {
+
+ svr.poll();
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-rtos.lib Fri Jul 26 22:05:19 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed-rtos/#58b30ac3f00e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Jul 26 22:05:19 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17 \ No newline at end of file
