CDC/ECM driver for mbed, based on USBDevice by mbed-official. Uses PicoTCP to access Ethernet USB device. License: GPLv2
Fork of USB_Ethernet by
pico_http_server.c
00001 /********************************************************************* 00002 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. 00003 See LICENSE and COPYING for usage. 00004 00005 Author: Andrei Carp <andrei.carp@tass.be> 00006 *********************************************************************/ 00007 00008 #include "pico_stack.h" 00009 #include "pico_http_server.h" 00010 #include "pico_tcp.h" 00011 #include "pico_tree.h" 00012 #include "pico_socket.h" 00013 00014 #ifdef PICO_SUPPORT_HTTP_SERVER 00015 00016 #define BACKLOG 10 00017 00018 #define HTTP_SERVER_CLOSED 0 00019 #define HTTP_SERVER_LISTEN 1 00020 00021 #define HTTP_HEADER_MAX_LINE 256u 00022 00023 #define consumeChar(c) (pico_socket_read(client->sck,&c,1u)) 00024 00025 static char returnOkHeader[] = 00026 "HTTP/1.1 200 OK\r\n\ 00027 Host: localhost\r\n\ 00028 Transfer-Encoding: chunked\r\n\ 00029 Connection: close\r\n\ 00030 \r\n"; 00031 00032 static char returnFailHeader[] = 00033 "HTTP/1.1 404 Not Found\r\n\ 00034 Host: localhost\r\n\ 00035 Connection: close\r\n\ 00036 \r\n\ 00037 <html><body>The resource you requested cannot be found !</body></html>"; 00038 00039 static char errorHeader[] = 00040 "HTTP/1.1 400 Bad Request\r\n\ 00041 Host: localhost\r\n\ 00042 Connection: close\r\n\ 00043 \r\n\ 00044 <html><body>There was a problem with your request !</body></html>"; 00045 00046 struct httpServer 00047 { 00048 uint16_t state; 00049 struct pico_socket * sck; 00050 uint16_t port; 00051 void (*wakeup)(uint16_t ev, uint16_t param); 00052 uint8_t accepted; 00053 }; 00054 00055 struct httpClient 00056 { 00057 uint16_t connectionID; 00058 struct pico_socket * sck; 00059 void * buffer; 00060 uint16_t bufferSize; 00061 uint16_t bufferSent; 00062 char * resource; 00063 uint16_t state; 00064 }; 00065 00066 /* Local states for clients */ 00067 #define HTTP_WAIT_HDR 0 00068 #define HTTP_WAIT_EOF_HDR 1 00069 #define HTTP_EOF_HDR 2 00070 #define HTTP_WAIT_RESPONSE 3 00071 #define HTTP_WAIT_DATA 4 00072 #define HTTP_SENDING_DATA 5 00073 #define HTTP_ERROR 6 00074 #define HTTP_CLOSED 7 00075 00076 static struct httpServer server = {}; 00077 00078 /* 00079 * Private functions 00080 */ 00081 static int parseRequest(struct httpClient * client); 00082 static int readRemainingHeader(struct httpClient * client); 00083 static void sendData(struct httpClient * client); 00084 static inline int readData(struct httpClient * client); // used only in a place 00085 static inline struct httpClient * findClient(uint16_t conn); 00086 00087 static int compareClients(void * ka, void * kb) 00088 { 00089 return ((struct httpClient *)ka)->connectionID - ((struct httpClient *)kb)->connectionID; 00090 } 00091 00092 PICO_TREE_DECLARE(pico_http_clients,compareClients); 00093 00094 void httpServerCbk(uint16_t ev, struct pico_socket *s) 00095 { 00096 struct pico_tree_node * index; 00097 struct httpClient * client = NULL; 00098 uint8_t serverEvent = FALSE; 00099 00100 // determine the client for the socket 00101 if( s == server.sck) 00102 { 00103 serverEvent = TRUE; 00104 } 00105 else 00106 { 00107 pico_tree_foreach(index,&pico_http_clients) 00108 { 00109 client = index->keyValue; 00110 if(client->sck == s) break; 00111 client = NULL; 00112 } 00113 } 00114 00115 if(!client && !serverEvent) 00116 { 00117 return; 00118 } 00119 00120 if (ev & PICO_SOCK_EV_RD) 00121 { 00122 00123 if(readData(client) == HTTP_RETURN_ERROR) 00124 { 00125 // send out error 00126 client->state = HTTP_ERROR; 00127 pico_socket_write(client->sck,errorHeader,sizeof(errorHeader)-1); 00128 server.wakeup(EV_HTTP_ERROR,client->connectionID); 00129 } 00130 } 00131 00132 if(ev & PICO_SOCK_EV_WR) 00133 { 00134 if(client->state == HTTP_SENDING_DATA) 00135 { 00136 sendData(client); 00137 } 00138 } 00139 00140 if(ev & PICO_SOCK_EV_CONN) 00141 { 00142 server.accepted = FALSE; 00143 server.wakeup(EV_HTTP_CON,HTTP_SERVER_ID); 00144 if(!server.accepted) 00145 { 00146 pico_socket_close(s); // reject socket 00147 } 00148 } 00149 00150 if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) ) 00151 { 00152 server.wakeup(EV_HTTP_CLOSE,(serverEvent ? HTTP_SERVER_ID : client->connectionID)); 00153 } 00154 00155 if(ev & PICO_SOCK_EV_ERR) 00156 { 00157 server.wakeup(EV_HTTP_ERROR,(serverEvent ? HTTP_SERVER_ID : client->connectionID)); 00158 } 00159 } 00160 00161 /* 00162 * API for starting the server. If 0 is passed as a port, the port 80 00163 * will be used. 00164 */ 00165 int pico_http_server_start(uint16_t port, void (*wakeup)(uint16_t ev, uint16_t conn)) 00166 { 00167 struct pico_ip4 anything = {}; 00168 00169 server.port = port ? short_be(port) : short_be(80u); 00170 00171 if(!wakeup) 00172 { 00173 pico_err = PICO_ERR_EINVAL; 00174 return HTTP_RETURN_ERROR; 00175 } 00176 00177 server.sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &httpServerCbk); 00178 00179 if(!server.sck) 00180 { 00181 pico_err = PICO_ERR_EFAULT; 00182 return HTTP_RETURN_ERROR; 00183 } 00184 00185 if(pico_socket_bind(server.sck , &anything, &server.port)!=0) 00186 { 00187 pico_err = PICO_ERR_EADDRNOTAVAIL; 00188 return HTTP_RETURN_ERROR; 00189 } 00190 00191 if (pico_socket_listen(server.sck, BACKLOG) != 0) 00192 { 00193 pico_err = PICO_ERR_EADDRINUSE; 00194 return HTTP_RETURN_ERROR; 00195 } 00196 server.wakeup = wakeup; 00197 server.state = HTTP_SERVER_LISTEN; 00198 return HTTP_RETURN_OK; 00199 } 00200 00201 /* 00202 * API for accepting new connections. This function should be 00203 * called when the event EV_HTTP_CON is triggered, if not called 00204 * when noticed the connection will be considered rejected and the 00205 * socket will be dropped. 00206 * 00207 * Returns the ID of the new connection or a negative value if error. 00208 */ 00209 int pico_http_server_accept(void) 00210 { 00211 struct pico_ip4 orig; 00212 struct httpClient * client; 00213 uint16_t port; 00214 00215 client = pico_zalloc(sizeof(struct httpClient)); 00216 if(!client) 00217 { 00218 pico_err = PICO_ERR_ENOMEM; 00219 return HTTP_RETURN_ERROR; 00220 } 00221 00222 client->sck = pico_socket_accept(server.sck,&orig,&port); 00223 00224 if(!client->sck) 00225 { 00226 pico_err = PICO_ERR_ENOMEM; 00227 pico_free(client); 00228 return HTTP_RETURN_ERROR; 00229 } 00230 00231 server.accepted = TRUE; 00232 // buffer used for async sending 00233 client->state = HTTP_WAIT_HDR; 00234 client->buffer = NULL; 00235 client->bufferSize = 0; 00236 client->connectionID = pico_rand() & 0x7FFF; 00237 00238 //add element to the tree, if duplicate because the rand 00239 //regenerate 00240 while(pico_tree_insert(&pico_http_clients,client)!=NULL) 00241 client->connectionID = pico_rand() & 0x7FFF; 00242 00243 return client->connectionID; 00244 } 00245 00246 /* 00247 * Function used for getting the resource asked by the 00248 * client. It is useful after the request header (EV_HTTP_REQ) 00249 * from client was received, otherwise NULL is returned. 00250 */ 00251 char * pico_http_getResource(uint16_t conn) 00252 { 00253 struct httpClient * client = findClient(conn); 00254 00255 if(!client) 00256 return NULL; 00257 else 00258 return client->resource; 00259 } 00260 00261 /* 00262 * After the resource was asked by the client (EV_HTTP_REQ) 00263 * before doing anything else, the server has to let know 00264 * the client if the resource can be provided or not. 00265 * 00266 * This is controlled via the code parameter which can 00267 * have two values : 00268 * 00269 * HTTP_RESOURCE_FOUND or HTTP_RESOURCE_NOT_FOUND 00270 * 00271 * If a resource is reported not found the 404 header will be sent and the connection 00272 * will be closed , otherwise the 200 header is sent and the user should 00273 * immediately submit data. 00274 * 00275 */ 00276 int pico_http_respond(uint16_t conn, uint16_t code) 00277 { 00278 struct httpClient * client = findClient(conn); 00279 00280 if(!client) 00281 { 00282 dbg("Client not found !\n"); 00283 return HTTP_RETURN_ERROR; 00284 } 00285 00286 if(client->state == HTTP_WAIT_RESPONSE) 00287 { 00288 if(code == HTTP_RESOURCE_FOUND) 00289 { 00290 client->state = HTTP_WAIT_DATA; 00291 return pico_socket_write(client->sck,returnOkHeader,sizeof(returnOkHeader)-1);//remove \0 00292 } 00293 else 00294 { 00295 int length; 00296 00297 length = pico_socket_write(client->sck,returnFailHeader,sizeof(returnFailHeader)-1);//remove \0 00298 pico_socket_close(client->sck); 00299 client->state = HTTP_CLOSED; 00300 return length; 00301 00302 } 00303 } 00304 else 00305 { 00306 dbg("Bad state for the client \n"); 00307 return HTTP_RETURN_ERROR; 00308 } 00309 00310 } 00311 00312 /* 00313 * API used to submit data to the client. 00314 * Server sends data only using Transfer-Encoding: chunked. 00315 * 00316 * With this function the user will submit a data chunk to 00317 * be sent. 00318 * The function will send the chunk size in hex and the rest will 00319 * be sent using WR event from sockets. 00320 * After each transmision EV_HTTP_PROGRESS is called and at the 00321 * end of the chunk EV_HTTP_SENT is called. 00322 * 00323 * To let the client know this is the last chunk, the user 00324 * should pass a NULL buffer. 00325 */ 00326 int pico_http_submitData(uint16_t conn, void * buffer, int len) 00327 { 00328 00329 struct httpClient * client = findClient(conn); 00330 char chunkStr[10]; 00331 int chunkCount; 00332 00333 if(client->state != HTTP_WAIT_DATA) 00334 { 00335 dbg("Client is in a different state than accepted\n"); 00336 return HTTP_RETURN_ERROR; 00337 } 00338 00339 if(client->buffer) 00340 { 00341 dbg("Already a buffer submited\n"); 00342 return HTTP_RETURN_ERROR; 00343 } 00344 00345 if(!client) 00346 { 00347 dbg("Wrong connection ID\n"); 00348 return HTTP_RETURN_ERROR; 00349 } 00350 00351 if(!buffer) 00352 { 00353 len = 0; 00354 } 00355 00356 if(len > 0) 00357 { 00358 client->buffer = pico_zalloc(len); 00359 if(!client->buffer) 00360 { 00361 pico_err = PICO_ERR_ENOMEM; 00362 return HTTP_RETURN_ERROR; 00363 } 00364 // taking over the buffer 00365 memcpy(client->buffer,buffer,len); 00366 } 00367 else 00368 client->buffer = NULL; 00369 00370 00371 client->bufferSize = len; 00372 client->bufferSent = 0; 00373 00374 // create the chunk size and send it 00375 if(len > 0) 00376 { 00377 client->state = HTTP_SENDING_DATA; 00378 chunkCount = pico_itoaHex(client->bufferSize,chunkStr); 00379 chunkStr[chunkCount++] = '\r'; 00380 chunkStr[chunkCount++] = '\n'; 00381 pico_socket_write(client->sck,chunkStr,chunkCount); 00382 } 00383 else if(len == 0) 00384 { 00385 dbg("->\n"); 00386 // end of transmision 00387 pico_socket_write(client->sck,"0\r\n\r\n",5u); 00388 // nothing left, close the client 00389 pico_socket_close(client->sck); 00390 client->state = HTTP_CLOSED; 00391 } 00392 00393 return HTTP_RETURN_OK; 00394 } 00395 00396 /* 00397 * When EV_HTTP_PROGRESS is triggered you can use this 00398 * function to check the state of the chunk. 00399 */ 00400 00401 int pico_http_getProgress(uint16_t conn, uint16_t * sent, uint16_t *total) 00402 { 00403 struct httpClient * client = findClient(conn); 00404 00405 if(!client) 00406 { 00407 dbg("Wrong connection id !\n"); 00408 return HTTP_RETURN_ERROR; 00409 } 00410 00411 *sent = client->bufferSent; 00412 *total = client->bufferSize; 00413 00414 return HTTP_RETURN_OK; 00415 } 00416 00417 /* 00418 * This API can be used to close either a client 00419 * or the server ( if you pass HTTP_SERVER_ID as a connection ID). 00420 */ 00421 int pico_http_close(uint16_t conn) 00422 { 00423 // close the server 00424 if(conn == HTTP_SERVER_ID) 00425 { 00426 if(server.state == HTTP_SERVER_LISTEN) 00427 { 00428 struct pico_tree_node * index, * tmp; 00429 // close the server 00430 pico_socket_close(server.sck); 00431 server.sck = NULL; 00432 00433 // destroy the tree 00434 pico_tree_foreach_safe(index,&pico_http_clients,tmp) 00435 { 00436 struct httpClient * client = index->keyValue; 00437 00438 if(client->resource) 00439 pico_free(client->resource); 00440 00441 pico_socket_close(client->sck); 00442 pico_tree_delete(&pico_http_clients,client); 00443 } 00444 00445 server.state = HTTP_SERVER_CLOSED; 00446 return HTTP_RETURN_OK; 00447 } 00448 else // nothing to close 00449 return HTTP_RETURN_ERROR; 00450 } // close a connection in this case 00451 else 00452 { 00453 00454 struct httpClient * client = findClient(conn); 00455 00456 if(!client) 00457 { 00458 dbg("Client not found..\n"); 00459 return HTTP_RETURN_ERROR; 00460 } 00461 00462 pico_tree_delete(&pico_http_clients,client); 00463 00464 if(client->resource) 00465 pico_free(client->resource); 00466 00467 if(client->buffer) 00468 pico_free(client->buffer); 00469 00470 if(client->state != HTTP_CLOSED || !client->sck) 00471 pico_socket_close(client->sck); 00472 00473 pico_free(client); 00474 return HTTP_RETURN_OK; 00475 } 00476 } 00477 00478 // check the integrity of the request 00479 int parseRequest(struct httpClient * client) 00480 { 00481 char c; 00482 //read first line 00483 consumeChar(c); 00484 if(c == 'G') 00485 { // possible GET 00486 00487 char line[HTTP_HEADER_MAX_LINE]; 00488 int index = 0; 00489 00490 line[index] = c; 00491 00492 // consume the full line 00493 while(consumeChar(c)>0) // read char by char only the first line 00494 { 00495 line[++index] = c; 00496 if(c == '\n') 00497 break; 00498 00499 if(index >= HTTP_HEADER_MAX_LINE) 00500 { 00501 dbg("Size exceeded \n"); 00502 return HTTP_RETURN_ERROR; 00503 } 00504 } 00505 00506 // extract the function and the resource 00507 if(memcmp(line,"GET",3u) || line[3u]!=' ' || index < 10u || line[index] !='\n') 00508 { 00509 dbg("Wrong command or wrong ending\n"); 00510 return HTTP_RETURN_ERROR; 00511 } 00512 00513 // start reading the resource 00514 index = 4u; // go after ' ' 00515 while(line[index]!=' ') 00516 { 00517 if(line[index]=='\n') // no terminator ' ' 00518 { 00519 dbg("No terminator...\n"); 00520 return HTTP_RETURN_ERROR; 00521 } 00522 00523 index++; 00524 } 00525 00526 client->resource = pico_zalloc(index - 3u);// allocate without the GET in front + 1 which is \0 00527 00528 if(!client) 00529 { 00530 pico_err = PICO_ERR_ENOMEM; 00531 return HTTP_RETURN_ERROR; 00532 } 00533 00534 // copy the resource 00535 memcpy(client->resource,line+4u,index-4u);// copy without the \0 which was already set by pico_zalloc 00536 00537 client->state = HTTP_WAIT_EOF_HDR; 00538 return HTTP_RETURN_OK; 00539 00540 } 00541 00542 return HTTP_RETURN_ERROR; 00543 } 00544 00545 00546 00547 int readRemainingHeader(struct httpClient * client) 00548 { 00549 char line[100]; 00550 int count = 0; 00551 int len; 00552 00553 while( (len = pico_socket_read(client->sck,line,100u)) > 0) 00554 { 00555 char c; 00556 int index = 0; 00557 // parse the response 00558 while(index < len) 00559 { 00560 c = line[index++]; 00561 if(c!='\r' && c!='\n') 00562 count++; 00563 if(c=='\n') 00564 { 00565 if(!count) 00566 { 00567 client->state = HTTP_EOF_HDR; 00568 dbg("End of header !\n"); 00569 break; 00570 } 00571 count = 0; 00572 00573 } 00574 } 00575 } 00576 00577 return HTTP_RETURN_OK; 00578 } 00579 00580 void sendData(struct httpClient * client) 00581 { 00582 int length; 00583 while( client->bufferSent < client->bufferSize && 00584 (length = pico_socket_write(client->sck,client->buffer+client->bufferSent,client->bufferSize-client->bufferSent)) > 0 ) 00585 { 00586 client->bufferSent += length; 00587 server.wakeup(EV_HTTP_PROGRESS,client->connectionID); 00588 } 00589 00590 if(client->bufferSent == client->bufferSize && client->bufferSize) 00591 { 00592 //send chunk trail 00593 if(pico_socket_write(client->sck,"\r\n",2) > 0) 00594 { 00595 client->state = HTTP_WAIT_DATA; 00596 //free the buffer 00597 pico_free(client->buffer); 00598 client->buffer = NULL; 00599 server.wakeup(EV_HTTP_SENT,client->connectionID); 00600 } 00601 } 00602 00603 } 00604 00605 int readData(struct httpClient * client) 00606 { 00607 if(client->state == HTTP_WAIT_HDR) 00608 { 00609 if(parseRequest(client)<0 || readRemainingHeader(client)<0) 00610 { 00611 return HTTP_RETURN_ERROR; 00612 } 00613 } // continue with this in case the header comes line by line not a big chunk 00614 else if(client->state == HTTP_WAIT_EOF_HDR) 00615 { 00616 if(readRemainingHeader(client)<0 ) 00617 return HTTP_RETURN_ERROR; 00618 } 00619 00620 if(client->state == HTTP_EOF_HDR) 00621 { 00622 client->state = HTTP_WAIT_RESPONSE; 00623 pico_socket_shutdown(client->sck,PICO_SHUT_RD); 00624 server.wakeup(EV_HTTP_REQ,client->connectionID); 00625 } 00626 00627 return HTTP_RETURN_OK; 00628 } 00629 00630 struct httpClient * findClient(uint16_t conn) 00631 { 00632 struct httpClient dummy = {.connectionID = conn}; 00633 00634 return pico_tree_findKey(&pico_http_clients,&dummy); 00635 } 00636 #endif
Generated on Wed Jul 13 2022 02:20:45 by 1.7.2