Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: EthernetInterface mbed-rtos mbed
Fork of HTTP_server by
HTTPServer.cpp
00001 #include "mbed.h" 00002 #include "HTTPServer.h" 00003 00004 RequestConfig rq_conf[] = { 00005 { "GET", HTTP_RT_GET }, 00006 { "POST", HTTP_RT_POST} 00007 }; 00008 00009 HTTPServer::HTTPServer (int port, const char* _path) { 00010 00011 INFO("Binding to port %d...", port); 00012 if (socketServer.bind (port) < 0) { 00013 ERR("Failed to bind to port !\n"); 00014 error("Binding"); 00015 } 00016 00017 INFO("Listening ..."); 00018 if (socketServer.listen(1) < 0) { 00019 ERR("Failed to listen !\n"); 00020 error("Listening"); 00021 } 00022 00023 INFO("Connected !"); 00024 // set into blocking operation 00025 socketServer.set_blocking (true); 00026 00027 path = _path; 00028 msg = NULL; 00029 cliente = NULL; 00030 00031 } 00032 00033 HTTPServer::~HTTPServer() { }; 00034 00035 int HTTPServer::poll () { 00036 00037 cliente = new TCPSocketConnection; 00038 cliente->set_blocking (false, TIMEOUT); 00039 00040 int retvalue = socketServer.accept (*cliente); 00041 00042 if (retvalue == OK) { 00043 // a new connection was received 00044 INFO("Client (IP=%s) is connected !", cliente->get_address()); 00045 msg = new HTTPMsg; // estructura para decodificar y alojar el mensaje 00046 00047 retvalue = pollConnection (); // esto parsea y llena las cosas contenidas en msg 00048 00049 if (retvalue == OK) { 00050 // Handle the request 00051 INFO("Handling request !"); 00052 00053 handleRequest (); // handling request 00054 00055 } 00056 delete msg; 00057 } else { // retvalue == ERROR 00058 ERR("Error accepting client"); 00059 } 00060 00061 delete cliente; 00062 00063 INFO("Leaving polling thread\n"); 00064 return retvalue; 00065 00066 } 00067 00068 int HTTPServer::pollConnection () { 00069 00070 INFO("Waiting for new data in connection"); 00071 00072 // Try receiving request line 00073 int received = receiveLine (); 00074 00075 if (received == ERROR) { 00076 // there was an error, probably the connection was closed, so close this connection as well 00077 INFO("No more data available. Will close this connection now."); 00078 return ERROR; 00079 } 00080 00081 // Request has not yet been received, so try it 00082 received = parseRequest (); 00083 00084 /*alternative (fast) parse request method: 00085 * ret = sscanf(buffer, "%s %s HTTP/%*d.%*d", request, uri); 00086 */ 00087 00088 if (received == ERROR) { 00089 // Invalid content received, so close the connection 00090 INFO("Invalid message received, so sending negative response and closing connection !"); 00091 tcpsend ("HTTP/1.1 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\r\n\r\n\r", 0); 00092 return ERROR; 00093 } 00094 00095 // Request has been received, try receive the headers section 00096 do { 00097 received = receiveLine (); 00098 if (received == ERROR) {return ERROR;} 00099 // First check if we received an empty line; 00100 // This would indicate the end of headers section. 00101 if (received == EMPTY) { 00102 // there was an empty line, so the headers section is complete 00103 INFO("Request Header was received completely. Performing request."); 00104 received = OK; 00105 break; 00106 } else { 00107 // parse header field 00108 if (parseHeader() != OK) { 00109 WARN("Invalid message header received !"); 00110 } 00111 } 00112 } while (received > 0); 00113 00114 INFO("Leaving poll function!"); 00115 return received; 00116 } 00117 00118 int HTTPServer::receiveLine () { 00119 00120 buffer[0] = 0; 00121 00122 int i; 00123 00124 // Try to receive up to the max number of characters 00125 for (i=0; i<BUFFER_SIZE-1; i++) { 00126 int c = cliente->receive (buffer+i, 1); 00127 // Check that - if no character was currently received - the timeout period is reached. 00128 if (c == 0 || c == -1) { 00129 // no character was read, so check if operation timed out 00130 ERR("Timeout occured in function 'receiveLine'."); 00131 return ERROR; 00132 } 00133 // Check if line terminating character was received 00134 if (buffer[i] == '\n') {break;} 00135 } 00136 // Terminate with \0 00137 buffer[i] = 0; 00138 00139 // Trim for '\r' linefeed at the end 00140 if (i>0 && buffer[i-1] == '\r') { 00141 i--; 00142 buffer[i] = 0; 00143 } 00144 00145 // return number of characters received in the line or return -2 if an empty line was received 00146 if (i==0 || (i==1 && buffer[0]=='\r')) { 00147 // empty line received, so return -2 00148 return EMPTY; 00149 } 00150 // retorna número de caracteres leidos 00151 return i; 00152 } 00153 00154 int HTTPServer::parseRequest () { 00155 00156 // Check if buffer content is not long enough. 00157 if (strlen(buffer) < MIN_LONG) { 00158 ERR("Buffer content is invalid or too short."); 00159 return ERROR; 00160 } 00161 00162 std::vector<std::string> args; 00163 00164 int argno = 0; 00165 // decompose string into a list of arguments 00166 int start = 0; // current starting char 00167 int nLen = strlen(buffer)+1; 00168 for (int i=0; i<nLen; i++) { 00169 if ((buffer[i] == ' ') || (buffer[i] == '\n') || (buffer[i] == 0)) { 00170 // new arg found 00171 buffer[i] = 0; 00172 if (argno++ == 1) { 00173 // it's the uri 00174 // parse the uri args 00175 parseUriArgs (&buffer[start]); 00176 } 00177 INFO("Found argument \"%s\"", &buffer[start]); 00178 args.push_back(&buffer[start]); 00179 start = i+1; 00180 } 00181 } 00182 00183 // store the uri and the HTTP version 00184 msg->uri = args[1]; 00185 msg->version = args[2]; 00186 00187 // Find matching request type 00188 for (int i=0; i<sizeof(rq_conf)/sizeof(RequestConfig) ; i++) { 00189 if (args.at(0) == rq_conf[i].request_string) { 00190 msg->request = rq_conf[i].request_type; 00191 } 00192 } 00193 00194 // init body section length 00195 msg->body_length = 0; 00196 00197 return OK; 00198 } 00199 00200 int HTTPServer::parseHeader () { 00201 00202 // Check if the buffer content is too short to be meaningful 00203 if (strlen(buffer) < MIN_LONG) {return ERROR;} 00204 00205 // decompose string into a touple of <field name> : <field value> 00206 int value_start = 0; 00207 int buflen = strlen(buffer)+1; 00208 for (int i=0; i<buflen; i++) { 00209 if (buffer[i] == ':') { 00210 // touple found 00211 buffer[i] = 0; 00212 value_start = i+1; 00213 // headers storage is disabled; uncomment next line to enable 00214 // msg->headers[buffer] = &buffer[value_start]; 00215 INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]); 00216 // Look for "Content-Length" header 00217 if (strcmp (buffer, "Content-Length") == 0) { 00218 msg->body_length = atoi(&buffer[value_start]); 00219 INFO ("Body section found. Length: %i", msg->body_length); 00220 } 00221 return OK; 00222 } 00223 } 00224 00225 ERR("Did not receive a valid header : \"%s\".", buffer); 00226 return ERROR; 00227 } 00228 00229 int HTTPServer::parseUriArgs (char *uri_buffer) { 00230 00231 // Check if the buffer content is too short to be meaningful 00232 if (strlen(uri_buffer) < MIN_LONG) {return ERROR;} 00233 00234 int args_start = -1; 00235 int value_start = -1; 00236 int buflen = strlen(uri_buffer) + 1; 00237 char* argname = NULL; 00238 char* valuename = NULL; 00239 for (int i=0; i<buflen; i++) { 00240 if (args_start == -1) { // args section not yet found 00241 if (uri_buffer[i] == '?') { // starts with a question mark, so got it 00242 uri_buffer[i] = 0; 00243 args_start = i; // set the start of the args section 00244 INFO("Argument section found !"); 00245 } 00246 } else { // search arg-value touples 00247 if (argname == NULL) { // arg-name found ? 00248 if (uri_buffer[i] == '=') { 00249 // yes, separate the arg-name 00250 uri_buffer[i] = 0; 00251 argname = &uri_buffer[args_start]; 00252 value_start = i+1; 00253 INFO("Argument name %s", argname); 00254 } 00255 } else { // search for end of value 00256 if ((uri_buffer[i] == '&') || (uri_buffer[i] == 0) || (uri_buffer[i] == '\r') || (uri_buffer[i] == '\n')) { 00257 buffer[i] = 0; 00258 valuename = &uri_buffer[value_start]; 00259 INFO("Argument value %s", valuename); 00260 msg->uri_args[argname] = valuename; 00261 // reset all indicators 00262 argname = NULL; 00263 valuename = NULL; 00264 } 00265 } 00266 } 00267 } 00268 return OK; 00269 } 00270 00271 void HTTPServer::handleRequest () { 00272 00273 int err_; 00274 00275 switch (msg->request) { 00276 case HTTP_RT_GET: 00277 INFO("Dispatching GET Request."); 00278 err_ = handleGetRequest(); 00279 break; 00280 case HTTP_RT_POST: 00281 INFO("Dispatching POST request."); 00282 err_ = handlePostRequest(); 00283 break; 00284 default: 00285 INFO("Error in handleRequest, unhandled request type."); 00286 err_ = 501; // HTTP_NotImplemented 00287 break; 00288 } 00289 00290 // if any of these functions returns a negative number, call the error handler 00291 if (err_ > 0) { 00292 handleError (err_); 00293 } 00294 00295 } 00296 00297 int HTTPServer::handleGetRequest() { 00298 00299 int retval = OK; //success 00300 00301 INFO("Handling Get Request."); 00302 00303 // maping to root path 00304 std::string reqPath = path + msg->uri.substr(1); 00305 00306 // Check if we received a directory with the local path 00307 if (reqPath.substr(reqPath.length()-1, 1) == "/") { 00308 // yes, we shall append the default page name 00309 reqPath += "index.htm"; 00310 } 00311 00312 INFO("Mapping \"%s\" to \"%s\"", msg->uri.c_str(), reqPath.c_str()); 00313 00314 FILE *file = fopen(reqPath.c_str(), "r"); 00315 if (file != NULL) { 00316 00317 // File was found and can be returned; first determine the size 00318 fseek (file, 0, SEEK_END); 00319 int size = ftell (file); 00320 fseek (file, 0, SEEK_SET); 00321 00322 startResponse (200, size); // response: 200 = HTTP_Ok 00323 while (!feof(file) && !ferror(file)) { 00324 // TODO: handle filesystem errors too 00325 int count = fread (buffer, 1, CHUNK_SIZE, file); 00326 INFO("Processing Response (%d bytes)!", count); 00327 tcpsend (buffer, count); 00328 } 00329 INFO("Ending Response !"); 00330 00331 fclose (file); 00332 00333 } else { 00334 retval = 404; 00335 ERR("Requested file was not found !"); 00336 } 00337 00338 return retval; 00339 00340 } 00341 00342 int HTTPServer::handlePostRequest() { 00343 00344 // Try receive the body data, if there is any 00345 if (msg->body_length > 0) { 00346 00347 char post_buffer [msg->body_length]; 00348 00349 INFO("Receiving body data. (%i bytes)", msg->body_length); 00350 00351 int bytes_read = 0; 00352 while (bytes_read < msg->body_length) { 00353 int result = cliente->receive_all(post_buffer+bytes_read, msg->body_length-bytes_read); 00354 if (result == ERROR) { 00355 WARN("Error receiving body data."); 00356 break; 00357 } 00358 bytes_read += result; 00359 } 00360 00361 INFO("Body data received."); 00362 00363 // do something 00364 // use the url_decode function :) 00365 00366 INFO("Done !\n"); 00367 00368 return handleGetRequest(); 00369 00370 } else { 00371 ERR("POST data not found !"); 00372 } 00373 00374 return 404; 00375 } 00376 00377 static const char hdrStandard[] = "DNT: 1\r\n" 00378 "MaxAge: 0\r\n" 00379 "Connection: Keep-Alive\r\n" 00380 "Content-Type: text/html\r\n" // TODO: handle file types 00381 "Server: mbed embedded\r\n" 00382 "Accessible: 1\r\n" 00383 "Pragma: no-cache\r\n" 00384 "Cache-control: no-cache;no-store\r\n" 00385 "Expires: 0\r\n" 00386 "\r\n"; 00387 00388 void HTTPServer::startResponse (int returnCode, int nLen) { 00389 00390 INFO("Starting response (%d bytes in total)!", nLen); 00391 00392 tcpsend ("HTTP/1.1 %d OK\r\n", returnCode); 00393 tcpsend ("Content-Length: %d\r\n", nLen); // Add 2 chars for the terminating CR+LF 00394 INFO("Sending standard headers !"); 00395 tcpsend (hdrStandard); 00396 00397 INFO("Done !"); 00398 00399 } 00400 00401 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"; 00402 00403 void HTTPServer::handleError (int errorCode) { 00404 00405 INFO("Handling error !"); 00406 00407 tcpsend ("HTTP/1.1 %d Error\r\n", errorCode); 00408 tcpsend ("Content-Length: %d\r\n", strlen(errorPage)); 00409 tcpsend ("Content-Type: text/html\r\nServer: mbed embedded\r\n\r\n"); 00410 tcpsend (errorPage); // TODO: better error page (handle error type) 00411 00412 INFO("Done !"); 00413 00414 } 00415 00416 //////////////////////////////////////////////////////////////////////////////////////////////////// 00417 // UTILS // 00418 //////////////////////////////////////////////////////////////////////////////////////////////////// 00419 00420 #include <ctype.h> 00421 00422 /* Converts a hex character to its integer value */ 00423 char from_hex (char ch) { 00424 return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; 00425 } 00426 00427 /* Returns a url-decoded version of str */ 00428 void url_decode (char *str) { 00429 char *lee = str, *escribe = str; 00430 while (*lee) { 00431 if (*lee == '%') { 00432 if (lee[1] && lee[2]) { 00433 *escribe++ = from_hex(lee[1])<<4 | from_hex(lee[2]); 00434 lee += 2; 00435 } 00436 } else if (*lee == '+') { 00437 *escribe++ = ' '; 00438 } else { 00439 *escribe++ = *lee; 00440 } 00441 lee++; 00442 } 00443 *escribe = 0; 00444 }
Generated on Thu Jul 14 2022 22:45:21 by
1.7.2
