CDC/ECM driver for mbed, based on USBDevice by mbed-official. Uses PicoTCP to access Ethernet USB device. License: GPLv2

Dependents:   USBEthernet_TEST

Fork of USB_Ethernet by Daniele Lacamera

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pico_http_server.c Source File

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