Single instance HTTP Server using WiFly Interface.

Dependents:   WiFlyHTTPServerSample MultiThreadingHTTPServer

This is my implementation for a HTTP Server using the WiFly Interface. Please note that this is still under development.

It may still contain several bugs. I have tested it using a 1768 on an application board plus RN-XV board.

Currently there is only a FileSystem implemented. Also it is limited to GET request.

I try to extend it further so it will be more useful.

Btw, it does NOT work with RTOS, which seems not to be the Problem of my library.

Do not Forget to Import the WiFly Interface into your Project when using this library.

Change History:

REV5: - added support for basic RPC GET request functionality.

REV4: - added argument parsing from the request uri. - documentation extended and updated.

Committer:
leihen
Date:
Sun Jun 02 00:33:56 2013 +0000
Revision:
9:c2a1462b9b71
Parent:
6:fe661fa9d18a
Child:
11:3943841e1798
Some small simplifications and improvements.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
leihen 0:7a2421e63e74 1 /* HTTPConnection.cpp */
leihen 0:7a2421e63e74 2
leihen 0:7a2421e63e74 3 #include "mbed.h"
leihen 0:7a2421e63e74 4 #include "HTTPConnection.h"
leihen 0:7a2421e63e74 5
leihen 6:fe661fa9d18a 6 #define _DEBUG 0
leihen 6:fe661fa9d18a 7
leihen 0:7a2421e63e74 8 #include <vector>
leihen 0:7a2421e63e74 9 using std::vector;
leihen 0:7a2421e63e74 10
leihen 0:7a2421e63e74 11 using std::string;
leihen 0:7a2421e63e74 12
leihen 6:fe661fa9d18a 13 #if (_DEBUG && !defined(TARGET_LPC11U24))
leihen 0:7a2421e63e74 14 #define INFO(x, ...) std::printf("[HttpConnection : INFO]"x"\r\n", ##__VA_ARGS__);
leihen 0:7a2421e63e74 15 #define WARN(x, ...) std::printf("[HttpConnection : WARN]"x"\r\n", ##__VA_ARGS__);
leihen 0:7a2421e63e74 16 #define ERR(x, ...) std::printf("[HttpConnection : ERR]"x"\r\n", ##__VA_ARGS__);
leihen 0:7a2421e63e74 17 #else
leihen 0:7a2421e63e74 18 #define INFO(x, ...)
leihen 0:7a2421e63e74 19 #define WARN(x, ...)
leihen 0:7a2421e63e74 20 #define ERR(x, ...)
leihen 0:7a2421e63e74 21 #endif
leihen 0:7a2421e63e74 22
leihen 0:7a2421e63e74 23
leihen 0:7a2421e63e74 24
leihen 4:d065642c32cc 25 const struct HTTPRequestConfig {
leihen 4:d065642c32cc 26 const char* request_string;
leihen 4:d065642c32cc 27 HTTPRequestType request_type;
leihen 4:d065642c32cc 28 } g_requestConfig[] = {
leihen 4:d065642c32cc 29 { "GET", HTTP_RT_GET },
leihen 4:d065642c32cc 30 { "POST", HTTP_RT_POST},
leihen 4:d065642c32cc 31 { "PUT", HTTP_RT_PUT},
leihen 4:d065642c32cc 32 { "OPTIONS",HTTP_RT_OPTIONS},
leihen 4:d065642c32cc 33 { "HEAD", HTTP_RT_HEAD},
leihen 4:d065642c32cc 34 { "DELETE", HTTP_RT_DELETE},
leihen 4:d065642c32cc 35 { "TRACE", HTTP_RT_TRACE},
leihen 4:d065642c32cc 36 { "CONNECT",HTTP_RT_CONNECT}
leihen 4:d065642c32cc 37 };
leihen 4:d065642c32cc 38
leihen 0:7a2421e63e74 39
leihen 0:7a2421e63e74 40 HTTPConnection::HTTPConnection()
leihen 0:7a2421e63e74 41 {
leihen 0:7a2421e63e74 42 }
leihen 0:7a2421e63e74 43
leihen 0:7a2421e63e74 44
leihen 0:7a2421e63e74 45 HTTPConnection::~HTTPConnection()
leihen 0:7a2421e63e74 46 {
leihen 0:7a2421e63e74 47 close();
leihen 0:7a2421e63e74 48 }
leihen 0:7a2421e63e74 49
leihen 0:7a2421e63e74 50 void HTTPConnection::close()
leihen 0:7a2421e63e74 51 {
leihen 0:7a2421e63e74 52 m_Msg.headers.clear();
leihen 0:7a2421e63e74 53 }
leihen 0:7a2421e63e74 54
leihen 0:7a2421e63e74 55 int HTTPConnection::poll()
leihen 0:7a2421e63e74 56 {
leihen 0:7a2421e63e74 57 static char buffer[256] = {};
leihen 0:7a2421e63e74 58
leihen 0:7a2421e63e74 59
leihen 0:7a2421e63e74 60 int rcvd= 0;
leihen 9:c2a1462b9b71 61 INFO("Waiting for new data in connection");
leihen 0:7a2421e63e74 62 // Try receiving request line
leihen 0:7a2421e63e74 63 rcvd = receiveLine(buffer, 255, 3000);
leihen 0:7a2421e63e74 64 if (rcvd == -1) {
leihen 0:7a2421e63e74 65 // there was an error, probably the connection was closed, so close this connection as well
leihen 0:7a2421e63e74 66 INFO("No more data available. Will close this connection now.");
leihen 0:7a2421e63e74 67 close();
leihen 0:7a2421e63e74 68 return -1;
leihen 0:7a2421e63e74 69 }
leihen 0:7a2421e63e74 70
leihen 0:7a2421e63e74 71 // The Request has not yet been received so try it
leihen 0:7a2421e63e74 72 rcvd = parse(buffer);
leihen 0:7a2421e63e74 73 if (rcvd == -1) {
leihen 0:7a2421e63e74 74 // Invalid content received, so close the connection
leihen 0:7a2421e63e74 75 INFO("Invalid message received, so sending negative response and closing connection !");
leihen 9:c2a1462b9b71 76 sprintf(buffer,"HTTP/1.0 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\rConnection: Close\n\r\n\r",0);
leihen 0:7a2421e63e74 77 m_Tcp.set_blocking(true, 1500);
leihen 0:7a2421e63e74 78 m_Tcp.send(buffer,strlen(buffer));
leihen 0:7a2421e63e74 79 close();
leihen 0:7a2421e63e74 80 rcvd = -1;
leihen 0:7a2421e63e74 81 return -1;
leihen 0:7a2421e63e74 82 }
leihen 0:7a2421e63e74 83 // The request has been received, try receive the body
leihen 0:7a2421e63e74 84 while(rcvd > 0) {
leihen 0:7a2421e63e74 85 rcvd = receiveLine((char*)buffer, 255, 3000);
leihen 0:7a2421e63e74 86 // First check if we received an empty line. This would indicate the end of the message or message body.
leihen 0:7a2421e63e74 87 if (rcvd < 0) {
leihen 0:7a2421e63e74 88 // there was an empty line, so we can start with performing the request
leihen 0:7a2421e63e74 89 INFO("Request Header was received completely. Performing request.");
leihen 0:7a2421e63e74 90 rcvd = 0;
leihen 0:7a2421e63e74 91 break;
leihen 0:7a2421e63e74 92 }
leihen 0:7a2421e63e74 93 else {
leihen 0:7a2421e63e74 94 // add message body
leihen 0:7a2421e63e74 95 if (parseHeader(buffer) == 0) {
leihen 0:7a2421e63e74 96 }
leihen 0:7a2421e63e74 97 else {
leihen 0:7a2421e63e74 98 WARN("Invalid message header received !");
leihen 0:7a2421e63e74 99 }
leihen 0:7a2421e63e74 100 }
leihen 0:7a2421e63e74 101 }
leihen 0:7a2421e63e74 102 INFO("Leaving poll function!");
leihen 0:7a2421e63e74 103 return rcvd;
leihen 0:7a2421e63e74 104 }
leihen 0:7a2421e63e74 105
leihen 0:7a2421e63e74 106 int HTTPConnection::receiveLine(char* szLine, int nMaxLen, int nTimeout, char cLineTerm)
leihen 0:7a2421e63e74 107 {
leihen 0:7a2421e63e74 108 if ((szLine == NULL) || (nMaxLen == 0))
leihen 0:7a2421e63e74 109 return -1;
leihen 0:7a2421e63e74 110
leihen 0:7a2421e63e74 111 m_Tcp.set_blocking(false);
leihen 0:7a2421e63e74 112
leihen 0:7a2421e63e74 113 Timer tm;
leihen 0:7a2421e63e74 114 int i;
leihen 0:7a2421e63e74 115
leihen 0:7a2421e63e74 116 // Try to receive up to the max number of characters
leihen 0:7a2421e63e74 117 for (i = 0 ; i < nMaxLen-1 ; i++) {
leihen 0:7a2421e63e74 118 int c;
leihen 0:7a2421e63e74 119 c = m_Tcp.receive_all( szLine + i, 1 );
leihen 0:7a2421e63e74 120 // Check that - if no character was currently received - the timeout period is reached.
leihen 0:7a2421e63e74 121 if ((c == 0) || (c==-1)) {
leihen 0:7a2421e63e74 122 // no character was read, so check if operation timed out
leihen 0:7a2421e63e74 123 if (tm.read_ms() > nTimeout) {
leihen 0:7a2421e63e74 124 // Operation timed out
leihen 0:7a2421e63e74 125 INFO("Timeout occured in function 'receiveLine'.");
leihen 0:7a2421e63e74 126 return -1;
leihen 0:7a2421e63e74 127 }
leihen 0:7a2421e63e74 128 }
leihen 0:7a2421e63e74 129
leihen 0:7a2421e63e74 130 // Check if line terminating character was received
leihen 0:7a2421e63e74 131 if (szLine[i] == cLineTerm)
leihen 0:7a2421e63e74 132 break;
leihen 0:7a2421e63e74 133 }
leihen 0:7a2421e63e74 134 // Terminate with \0
leihen 0:7a2421e63e74 135 szLine[i] = 0;
leihen 0:7a2421e63e74 136
leihen 0:7a2421e63e74 137 // Trim for '\r' linefeed at the end
leihen 0:7a2421e63e74 138 if( (i >0) && (szLine[i-1] == '\r')) {
leihen 0:7a2421e63e74 139 i--;
leihen 0:7a2421e63e74 140 szLine[i] = 0;
leihen 0:7a2421e63e74 141 }
leihen 0:7a2421e63e74 142 INFO("receiveLine : \"%s\".", szLine);
leihen 0:7a2421e63e74 143
leihen 0:7a2421e63e74 144 // return number of characters received in the line or return -2 if an empty line was received
leihen 0:7a2421e63e74 145 if ((i == 0) || ((i==1) &&(szLine[0] == '\r')))
leihen 0:7a2421e63e74 146 {
leihen 0:7a2421e63e74 147 // empty line received, so return -2
leihen 0:7a2421e63e74 148 return -2;
leihen 0:7a2421e63e74 149 }
leihen 0:7a2421e63e74 150 return i;
leihen 0:7a2421e63e74 151 }
leihen 0:7a2421e63e74 152
leihen 2:8653bbcf7e58 153 int HTTPConnection::parse(char* buffer)
leihen 0:7a2421e63e74 154 {
leihen 4:d065642c32cc 155 // Check if buffer is invalid or its content not long enough.
leihen 0:7a2421e63e74 156 if ((buffer == NULL) || (strlen(buffer) < 4)) {
leihen 0:7a2421e63e74 157 ERR("Buffer content is invalid or too short.");
leihen 0:7a2421e63e74 158 return -1;
leihen 0:7a2421e63e74 159 }
leihen 0:7a2421e63e74 160
leihen 4:d065642c32cc 161 std::vector<std::string> args;
leihen 0:7a2421e63e74 162 args.clear();
leihen 0:7a2421e63e74 163
leihen 4:d065642c32cc 164 int argno = 0;
leihen 0:7a2421e63e74 165 // decompose string into a list of arguments
leihen 0:7a2421e63e74 166 char s = 0; // current starting char
leihen 2:8653bbcf7e58 167 int nLen = strlen(buffer)+1;
leihen 2:8653bbcf7e58 168 for (int i = 0 ; i < nLen ; i++) {
leihen 0:7a2421e63e74 169 if ((buffer[i] == ' ') || (buffer[i] == '\n') || (buffer[i] == 0)) {
leihen 0:7a2421e63e74 170 // new arg found
leihen 2:8653bbcf7e58 171 buffer[i] = 0;
leihen 4:d065642c32cc 172 if (argno++ == 1) {
leihen 4:d065642c32cc 173 // its the uri
leihen 4:d065642c32cc 174 // parse the uri args
leihen 4:d065642c32cc 175 parseUriArgs(&buffer[s], m_Msg.args);
leihen 4:d065642c32cc 176 }
leihen 2:8653bbcf7e58 177 INFO("Found argument \"%s\"", &buffer[s]);
leihen 2:8653bbcf7e58 178 args.push_back(&buffer[s]);
leihen 0:7a2421e63e74 179 s = i+1;
leihen 0:7a2421e63e74 180 }
leihen 0:7a2421e63e74 181 }
leihen 4:d065642c32cc 182
leihen 4:d065642c32cc 183 // store the uri and the HTTP version
leihen 4:d065642c32cc 184 m_Msg.uri = args[1];
leihen 4:d065642c32cc 185 m_Msg.version = args[2];
leihen 4:d065642c32cc 186
leihen 4:d065642c32cc 187 // Find matching request type
leihen 4:d065642c32cc 188 for (int i = 0 ; i < sizeof(g_requestConfig)/sizeof(struct HTTPRequestConfig) ; i++) {
leihen 4:d065642c32cc 189 if (args.at(0) == g_requestConfig[i].request_string) {
leihen 4:d065642c32cc 190 m_Msg.request = g_requestConfig[i].request_type;
leihen 0:7a2421e63e74 191 }
leihen 0:7a2421e63e74 192 }
leihen 0:7a2421e63e74 193 args.clear();
leihen 0:7a2421e63e74 194
leihen 0:7a2421e63e74 195 return 1;
leihen 0:7a2421e63e74 196 }
leihen 0:7a2421e63e74 197
leihen 0:7a2421e63e74 198
leihen 3:d6224049b3bf 199 int HTTPConnection::parseHeader(char *buffer)
leihen 0:7a2421e63e74 200 {
leihen 4:d065642c32cc 201 // Check if the buffer is invalid or if the content is too short to be meaningful
leihen 0:7a2421e63e74 202 if ((strlen(buffer) <3) || (buffer == NULL))
leihen 0:7a2421e63e74 203 return -1;
leihen 0:7a2421e63e74 204
leihen 0:7a2421e63e74 205 // decompose string into a touple of <field name> : <field value>
leihen 3:d6224049b3bf 206 int value_start = 0;
leihen 3:d6224049b3bf 207 int buflen = strlen(buffer)+1;
leihen 3:d6224049b3bf 208 for (int i = 0 ; i < buflen ; i++) {
leihen 0:7a2421e63e74 209 if (buffer[i] == ':') {
leihen 0:7a2421e63e74 210 // touple found
leihen 3:d6224049b3bf 211 buffer[i] = 0;
leihen 3:d6224049b3bf 212 value_start = i+1;
leihen 3:d6224049b3bf 213 m_Msg.headers[buffer] = &buffer[value_start];
leihen 0:7a2421e63e74 214
leihen 3:d6224049b3bf 215 INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]);
leihen 0:7a2421e63e74 216 return 0;
leihen 0:7a2421e63e74 217 }
leihen 0:7a2421e63e74 218 }
leihen 0:7a2421e63e74 219
leihen 0:7a2421e63e74 220 ERR("Did not recieve a valid header : \"%s\".", buffer);
leihen 0:7a2421e63e74 221 return -1;
leihen 0:7a2421e63e74 222 }
leihen 4:d065642c32cc 223
leihen 4:d065642c32cc 224 int HTTPConnection::parseUriArgs(char *buffer, map<string,string>&args)
leihen 4:d065642c32cc 225 {
leihen 4:d065642c32cc 226 // Check if the buffer is invalid or if the content is too short to be meaningful
leihen 4:d065642c32cc 227 if ((strlen(buffer) <3) || (buffer == NULL))
leihen 4:d065642c32cc 228 return -1;
leihen 4:d065642c32cc 229
leihen 4:d065642c32cc 230 int args_start = -1;
leihen 4:d065642c32cc 231 int value_start = -1;
leihen 4:d065642c32cc 232 int buflen = strlen(buffer) +1;
leihen 4:d065642c32cc 233 const char* argname = NULL;
leihen 4:d065642c32cc 234 const char* valuename = NULL;
leihen 4:d065642c32cc 235 for (int i = 0; i < buflen ; i++) {
leihen 4:d065642c32cc 236 if (args_start == -1) { // args section not yet found
leihen 4:d065642c32cc 237 if (buffer[i] == '?') { // starts with a question mark, so got it
leihen 4:d065642c32cc 238 buffer[i] = 0;
leihen 4:d065642c32cc 239 args_start = i; // set the start of the args section
leihen 4:d065642c32cc 240 INFO("Argument section found !");
leihen 4:d065642c32cc 241 }
leihen 4:d065642c32cc 242 }
leihen 4:d065642c32cc 243 else { // search arg-value touples
leihen 4:d065642c32cc 244 if (argname == NULL) { // arg-name found ?
leihen 4:d065642c32cc 245 if (buffer[i] == '=') {
leihen 4:d065642c32cc 246 // yes, separate the arg-name
leihen 4:d065642c32cc 247 buffer[i] = 0;
leihen 4:d065642c32cc 248 argname = &buffer[args_start];
leihen 4:d065642c32cc 249 value_start = i+1;
leihen 4:d065642c32cc 250 INFO("Argument name %s", argname);
leihen 4:d065642c32cc 251 }
leihen 4:d065642c32cc 252 }
leihen 4:d065642c32cc 253 else { // search for end of value
leihen 4:d065642c32cc 254 if ((buffer[i] == '&') || (buffer[i] == 0) || (buffer[i] == '\r') || (buffer[i] == '\n')) {
leihen 4:d065642c32cc 255 buffer[i] = 0;
leihen 4:d065642c32cc 256 valuename = &buffer[value_start];
leihen 4:d065642c32cc 257 INFO("Argument value %s", valuename);
leihen 4:d065642c32cc 258 args[argname] = valuename;
leihen 4:d065642c32cc 259 // reset all indicators
leihen 4:d065642c32cc 260 argname = NULL;
leihen 4:d065642c32cc 261 valuename = NULL;
leihen 4:d065642c32cc 262 }
leihen 4:d065642c32cc 263 }
leihen 4:d065642c32cc 264 }
leihen 4:d065642c32cc 265 }
leihen 4:d065642c32cc 266
leihen 4:d065642c32cc 267 return 0;
leihen 4:d065642c32cc 268 }