Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

Committer:
tass
Date:
Fri May 17 12:09:59 2013 +0000
Revision:
1:cfe8984a32b4
Parent:
libraries/picotcp/modules/pico_http_server.c@0:d7f2341ab245
Update for smaller SOCKETQ

Who changed what in which revision?

UserRevisionLine numberNew contents of line
daniele 0:d7f2341ab245 1 /*********************************************************************
daniele 0:d7f2341ab245 2 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
daniele 0:d7f2341ab245 3 See LICENSE and COPYING for usage.
daniele 0:d7f2341ab245 4
daniele 0:d7f2341ab245 5 Author: Andrei Carp <andrei.carp@tass.be>
daniele 0:d7f2341ab245 6 *********************************************************************/
daniele 0:d7f2341ab245 7
daniele 0:d7f2341ab245 8 #include "pico_stack.h"
daniele 0:d7f2341ab245 9 #include "pico_http_server.h"
daniele 0:d7f2341ab245 10 #include "pico_tcp.h"
daniele 0:d7f2341ab245 11 #include "pico_tree.h"
daniele 0:d7f2341ab245 12 #include "pico_socket.h"
daniele 0:d7f2341ab245 13
daniele 0:d7f2341ab245 14 #ifdef PICO_SUPPORT_HTTP_SERVER
daniele 0:d7f2341ab245 15
daniele 0:d7f2341ab245 16 #define BACKLOG 10
daniele 0:d7f2341ab245 17
daniele 0:d7f2341ab245 18 #define HTTP_SERVER_CLOSED 0
daniele 0:d7f2341ab245 19 #define HTTP_SERVER_LISTEN 1
daniele 0:d7f2341ab245 20
daniele 0:d7f2341ab245 21 #define HTTP_HEADER_MAX_LINE 256u
daniele 0:d7f2341ab245 22
daniele 0:d7f2341ab245 23 #define consumeChar(c) (pico_socket_read(client->sck,&c,1u))
daniele 0:d7f2341ab245 24
daniele 0:d7f2341ab245 25 static char returnOkHeader[] =
daniele 0:d7f2341ab245 26 "HTTP/1.1 200 OK\r\n\
daniele 0:d7f2341ab245 27 Host: localhost\r\n\
daniele 0:d7f2341ab245 28 Transfer-Encoding: chunked\r\n\
daniele 0:d7f2341ab245 29 Connection: close\r\n\
daniele 0:d7f2341ab245 30 \r\n";
daniele 0:d7f2341ab245 31
daniele 0:d7f2341ab245 32 static char returnFailHeader[] =
daniele 0:d7f2341ab245 33 "HTTP/1.1 404 Not Found\r\n\
daniele 0:d7f2341ab245 34 Host: localhost\r\n\
daniele 0:d7f2341ab245 35 Connection: close\r\n\
daniele 0:d7f2341ab245 36 \r\n\
daniele 0:d7f2341ab245 37 <html><body>The resource you requested cannot be found !</body></html>";
daniele 0:d7f2341ab245 38
daniele 0:d7f2341ab245 39 static char errorHeader[] =
daniele 0:d7f2341ab245 40 "HTTP/1.1 400 Bad Request\r\n\
daniele 0:d7f2341ab245 41 Host: localhost\r\n\
daniele 0:d7f2341ab245 42 Connection: close\r\n\
daniele 0:d7f2341ab245 43 \r\n\
daniele 0:d7f2341ab245 44 <html><body>There was a problem with your request !</body></html>";
daniele 0:d7f2341ab245 45
daniele 0:d7f2341ab245 46 struct httpServer
daniele 0:d7f2341ab245 47 {
daniele 0:d7f2341ab245 48 uint16_t state;
daniele 0:d7f2341ab245 49 struct pico_socket * sck;
daniele 0:d7f2341ab245 50 uint16_t port;
daniele 0:d7f2341ab245 51 void (*wakeup)(uint16_t ev, uint16_t param);
daniele 0:d7f2341ab245 52 uint8_t accepted;
daniele 0:d7f2341ab245 53 };
daniele 0:d7f2341ab245 54
daniele 0:d7f2341ab245 55 struct httpClient
daniele 0:d7f2341ab245 56 {
daniele 0:d7f2341ab245 57 uint16_t connectionID;
daniele 0:d7f2341ab245 58 struct pico_socket * sck;
daniele 0:d7f2341ab245 59 void * buffer;
daniele 0:d7f2341ab245 60 uint16_t bufferSize;
daniele 0:d7f2341ab245 61 uint16_t bufferSent;
daniele 0:d7f2341ab245 62 char * resource;
daniele 0:d7f2341ab245 63 uint16_t state;
daniele 0:d7f2341ab245 64 };
daniele 0:d7f2341ab245 65
daniele 0:d7f2341ab245 66 /* Local states for clients */
daniele 0:d7f2341ab245 67 #define HTTP_WAIT_HDR 0
daniele 0:d7f2341ab245 68 #define HTTP_WAIT_EOF_HDR 1
daniele 0:d7f2341ab245 69 #define HTTP_EOF_HDR 2
daniele 0:d7f2341ab245 70 #define HTTP_WAIT_RESPONSE 3
daniele 0:d7f2341ab245 71 #define HTTP_WAIT_DATA 4
daniele 0:d7f2341ab245 72 #define HTTP_SENDING_DATA 5
daniele 0:d7f2341ab245 73 #define HTTP_ERROR 6
daniele 0:d7f2341ab245 74 #define HTTP_CLOSED 7
daniele 0:d7f2341ab245 75
daniele 0:d7f2341ab245 76 static struct httpServer server = {};
daniele 0:d7f2341ab245 77
daniele 0:d7f2341ab245 78 /*
daniele 0:d7f2341ab245 79 * Private functions
daniele 0:d7f2341ab245 80 */
daniele 0:d7f2341ab245 81 static int parseRequest(struct httpClient * client);
daniele 0:d7f2341ab245 82 static int readRemainingHeader(struct httpClient * client);
daniele 0:d7f2341ab245 83 static void sendData(struct httpClient * client);
daniele 0:d7f2341ab245 84 static inline int readData(struct httpClient * client); // used only in a place
daniele 0:d7f2341ab245 85 static inline struct httpClient * findClient(uint16_t conn);
daniele 0:d7f2341ab245 86
daniele 0:d7f2341ab245 87 static int compareClients(void * ka, void * kb)
daniele 0:d7f2341ab245 88 {
daniele 0:d7f2341ab245 89 return ((struct httpClient *)ka)->connectionID - ((struct httpClient *)kb)->connectionID;
daniele 0:d7f2341ab245 90 }
daniele 0:d7f2341ab245 91
daniele 0:d7f2341ab245 92 PICO_TREE_DECLARE(pico_http_clients,compareClients);
daniele 0:d7f2341ab245 93
daniele 0:d7f2341ab245 94 void httpServerCbk(uint16_t ev, struct pico_socket *s)
daniele 0:d7f2341ab245 95 {
daniele 0:d7f2341ab245 96 struct pico_tree_node * index;
daniele 0:d7f2341ab245 97 struct httpClient * client = NULL;
daniele 0:d7f2341ab245 98 uint8_t serverEvent = FALSE;
daniele 0:d7f2341ab245 99
daniele 0:d7f2341ab245 100 // determine the client for the socket
daniele 0:d7f2341ab245 101 if( s == server.sck)
daniele 0:d7f2341ab245 102 {
daniele 0:d7f2341ab245 103 serverEvent = TRUE;
daniele 0:d7f2341ab245 104 }
daniele 0:d7f2341ab245 105 else
daniele 0:d7f2341ab245 106 {
daniele 0:d7f2341ab245 107 pico_tree_foreach(index,&pico_http_clients)
daniele 0:d7f2341ab245 108 {
daniele 0:d7f2341ab245 109 client = index->keyValue;
daniele 0:d7f2341ab245 110 if(client->sck == s) break;
daniele 0:d7f2341ab245 111 client = NULL;
daniele 0:d7f2341ab245 112 }
daniele 0:d7f2341ab245 113 }
daniele 0:d7f2341ab245 114
daniele 0:d7f2341ab245 115 if(!client && !serverEvent)
daniele 0:d7f2341ab245 116 {
daniele 0:d7f2341ab245 117 return;
daniele 0:d7f2341ab245 118 }
daniele 0:d7f2341ab245 119
daniele 0:d7f2341ab245 120 if (ev & PICO_SOCK_EV_RD)
daniele 0:d7f2341ab245 121 {
daniele 0:d7f2341ab245 122
daniele 0:d7f2341ab245 123 if(readData(client) == HTTP_RETURN_ERROR)
daniele 0:d7f2341ab245 124 {
daniele 0:d7f2341ab245 125 // send out error
daniele 0:d7f2341ab245 126 client->state = HTTP_ERROR;
daniele 0:d7f2341ab245 127 pico_socket_write(client->sck,errorHeader,sizeof(errorHeader)-1);
daniele 0:d7f2341ab245 128 server.wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 0:d7f2341ab245 129 }
daniele 0:d7f2341ab245 130 }
daniele 0:d7f2341ab245 131
daniele 0:d7f2341ab245 132 if(ev & PICO_SOCK_EV_WR)
daniele 0:d7f2341ab245 133 {
daniele 0:d7f2341ab245 134 if(client->state == HTTP_SENDING_DATA)
daniele 0:d7f2341ab245 135 {
daniele 0:d7f2341ab245 136 sendData(client);
daniele 0:d7f2341ab245 137 }
daniele 0:d7f2341ab245 138 }
daniele 0:d7f2341ab245 139
daniele 0:d7f2341ab245 140 if(ev & PICO_SOCK_EV_CONN)
daniele 0:d7f2341ab245 141 {
daniele 0:d7f2341ab245 142 server.accepted = FALSE;
daniele 0:d7f2341ab245 143 server.wakeup(EV_HTTP_CON,HTTP_SERVER_ID);
daniele 0:d7f2341ab245 144 if(!server.accepted)
daniele 0:d7f2341ab245 145 {
daniele 0:d7f2341ab245 146 pico_socket_close(s); // reject socket
daniele 0:d7f2341ab245 147 }
daniele 0:d7f2341ab245 148 }
daniele 0:d7f2341ab245 149
daniele 0:d7f2341ab245 150 if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) )
daniele 0:d7f2341ab245 151 {
daniele 0:d7f2341ab245 152 server.wakeup(EV_HTTP_CLOSE,(serverEvent ? HTTP_SERVER_ID : client->connectionID));
daniele 0:d7f2341ab245 153 }
daniele 0:d7f2341ab245 154
daniele 0:d7f2341ab245 155 if(ev & PICO_SOCK_EV_ERR)
daniele 0:d7f2341ab245 156 {
daniele 0:d7f2341ab245 157 server.wakeup(EV_HTTP_ERROR,(serverEvent ? HTTP_SERVER_ID : client->connectionID));
daniele 0:d7f2341ab245 158 }
daniele 0:d7f2341ab245 159 }
daniele 0:d7f2341ab245 160
daniele 0:d7f2341ab245 161 /*
daniele 0:d7f2341ab245 162 * API for starting the server. If 0 is passed as a port, the port 80
daniele 0:d7f2341ab245 163 * will be used.
daniele 0:d7f2341ab245 164 */
daniele 0:d7f2341ab245 165 int pico_http_server_start(uint16_t port, void (*wakeup)(uint16_t ev, uint16_t conn))
daniele 0:d7f2341ab245 166 {
daniele 0:d7f2341ab245 167 struct pico_ip4 anything = {};
daniele 0:d7f2341ab245 168
daniele 0:d7f2341ab245 169 server.port = port ? short_be(port) : short_be(80u);
daniele 0:d7f2341ab245 170
daniele 0:d7f2341ab245 171 if(!wakeup)
daniele 0:d7f2341ab245 172 {
daniele 0:d7f2341ab245 173 pico_err = PICO_ERR_EINVAL;
daniele 0:d7f2341ab245 174 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 175 }
daniele 0:d7f2341ab245 176
daniele 0:d7f2341ab245 177 server.sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &httpServerCbk);
daniele 0:d7f2341ab245 178
daniele 0:d7f2341ab245 179 if(!server.sck)
daniele 0:d7f2341ab245 180 {
daniele 0:d7f2341ab245 181 pico_err = PICO_ERR_EFAULT;
daniele 0:d7f2341ab245 182 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 183 }
daniele 0:d7f2341ab245 184
daniele 0:d7f2341ab245 185 if(pico_socket_bind(server.sck , &anything, &server.port)!=0)
daniele 0:d7f2341ab245 186 {
daniele 0:d7f2341ab245 187 pico_err = PICO_ERR_EADDRNOTAVAIL;
daniele 0:d7f2341ab245 188 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 189 }
daniele 0:d7f2341ab245 190
daniele 0:d7f2341ab245 191 if (pico_socket_listen(server.sck, BACKLOG) != 0)
daniele 0:d7f2341ab245 192 {
daniele 0:d7f2341ab245 193 pico_err = PICO_ERR_EADDRINUSE;
daniele 0:d7f2341ab245 194 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 195 }
daniele 0:d7f2341ab245 196 server.wakeup = wakeup;
daniele 0:d7f2341ab245 197 server.state = HTTP_SERVER_LISTEN;
daniele 0:d7f2341ab245 198 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 199 }
daniele 0:d7f2341ab245 200
daniele 0:d7f2341ab245 201 /*
daniele 0:d7f2341ab245 202 * API for accepting new connections. This function should be
daniele 0:d7f2341ab245 203 * called when the event EV_HTTP_CON is triggered, if not called
daniele 0:d7f2341ab245 204 * when noticed the connection will be considered rejected and the
daniele 0:d7f2341ab245 205 * socket will be dropped.
daniele 0:d7f2341ab245 206 *
daniele 0:d7f2341ab245 207 * Returns the ID of the new connection or a negative value if error.
daniele 0:d7f2341ab245 208 */
daniele 0:d7f2341ab245 209 int pico_http_server_accept(void)
daniele 0:d7f2341ab245 210 {
daniele 0:d7f2341ab245 211 struct pico_ip4 orig;
daniele 0:d7f2341ab245 212 struct httpClient * client;
daniele 0:d7f2341ab245 213 uint16_t port;
daniele 0:d7f2341ab245 214
daniele 0:d7f2341ab245 215 client = pico_zalloc(sizeof(struct httpClient));
daniele 0:d7f2341ab245 216 if(!client)
daniele 0:d7f2341ab245 217 {
daniele 0:d7f2341ab245 218 pico_err = PICO_ERR_ENOMEM;
daniele 0:d7f2341ab245 219 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 220 }
daniele 0:d7f2341ab245 221
daniele 0:d7f2341ab245 222 client->sck = pico_socket_accept(server.sck,&orig,&port);
daniele 0:d7f2341ab245 223
daniele 0:d7f2341ab245 224 if(!client->sck)
daniele 0:d7f2341ab245 225 {
daniele 0:d7f2341ab245 226 pico_err = PICO_ERR_ENOMEM;
daniele 0:d7f2341ab245 227 pico_free(client);
daniele 0:d7f2341ab245 228 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 229 }
daniele 0:d7f2341ab245 230
daniele 0:d7f2341ab245 231 server.accepted = TRUE;
daniele 0:d7f2341ab245 232 // buffer used for async sending
daniele 0:d7f2341ab245 233 client->state = HTTP_WAIT_HDR;
daniele 0:d7f2341ab245 234 client->buffer = NULL;
daniele 0:d7f2341ab245 235 client->bufferSize = 0;
daniele 0:d7f2341ab245 236 client->connectionID = pico_rand() & 0x7FFF;
daniele 0:d7f2341ab245 237
daniele 0:d7f2341ab245 238 //add element to the tree, if duplicate because the rand
daniele 0:d7f2341ab245 239 //regenerate
daniele 0:d7f2341ab245 240 while(pico_tree_insert(&pico_http_clients,client)!=NULL)
daniele 0:d7f2341ab245 241 client->connectionID = pico_rand() & 0x7FFF;
daniele 0:d7f2341ab245 242
daniele 0:d7f2341ab245 243 return client->connectionID;
daniele 0:d7f2341ab245 244 }
daniele 0:d7f2341ab245 245
daniele 0:d7f2341ab245 246 /*
daniele 0:d7f2341ab245 247 * Function used for getting the resource asked by the
daniele 0:d7f2341ab245 248 * client. It is useful after the request header (EV_HTTP_REQ)
daniele 0:d7f2341ab245 249 * from client was received, otherwise NULL is returned.
daniele 0:d7f2341ab245 250 */
daniele 0:d7f2341ab245 251 char * pico_http_getResource(uint16_t conn)
daniele 0:d7f2341ab245 252 {
daniele 0:d7f2341ab245 253 struct httpClient * client = findClient(conn);
daniele 0:d7f2341ab245 254
daniele 0:d7f2341ab245 255 if(!client)
daniele 0:d7f2341ab245 256 return NULL;
daniele 0:d7f2341ab245 257 else
daniele 0:d7f2341ab245 258 return client->resource;
daniele 0:d7f2341ab245 259 }
daniele 0:d7f2341ab245 260
daniele 0:d7f2341ab245 261 /*
daniele 0:d7f2341ab245 262 * After the resource was asked by the client (EV_HTTP_REQ)
daniele 0:d7f2341ab245 263 * before doing anything else, the server has to let know
daniele 0:d7f2341ab245 264 * the client if the resource can be provided or not.
daniele 0:d7f2341ab245 265 *
daniele 0:d7f2341ab245 266 * This is controlled via the code parameter which can
daniele 0:d7f2341ab245 267 * have two values :
daniele 0:d7f2341ab245 268 *
daniele 0:d7f2341ab245 269 * HTTP_RESOURCE_FOUND or HTTP_RESOURCE_NOT_FOUND
daniele 0:d7f2341ab245 270 *
daniele 0:d7f2341ab245 271 * If a resource is reported not found the 404 header will be sent and the connection
daniele 0:d7f2341ab245 272 * will be closed , otherwise the 200 header is sent and the user should
daniele 0:d7f2341ab245 273 * immediately submit data.
daniele 0:d7f2341ab245 274 *
daniele 0:d7f2341ab245 275 */
daniele 0:d7f2341ab245 276 int pico_http_respond(uint16_t conn, uint16_t code)
daniele 0:d7f2341ab245 277 {
daniele 0:d7f2341ab245 278 struct httpClient * client = findClient(conn);
daniele 0:d7f2341ab245 279
daniele 0:d7f2341ab245 280 if(!client)
daniele 0:d7f2341ab245 281 {
daniele 0:d7f2341ab245 282 dbg("Client not found !\n");
daniele 0:d7f2341ab245 283 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 284 }
daniele 0:d7f2341ab245 285
daniele 0:d7f2341ab245 286 if(client->state == HTTP_WAIT_RESPONSE)
daniele 0:d7f2341ab245 287 {
daniele 0:d7f2341ab245 288 if(code == HTTP_RESOURCE_FOUND)
daniele 0:d7f2341ab245 289 {
daniele 0:d7f2341ab245 290 client->state = HTTP_WAIT_DATA;
daniele 0:d7f2341ab245 291 return pico_socket_write(client->sck,returnOkHeader,sizeof(returnOkHeader)-1);//remove \0
daniele 0:d7f2341ab245 292 }
daniele 0:d7f2341ab245 293 else
daniele 0:d7f2341ab245 294 {
daniele 0:d7f2341ab245 295 int length;
daniele 0:d7f2341ab245 296
daniele 0:d7f2341ab245 297 length = pico_socket_write(client->sck,returnFailHeader,sizeof(returnFailHeader)-1);//remove \0
daniele 0:d7f2341ab245 298 pico_socket_close(client->sck);
daniele 0:d7f2341ab245 299 client->state = HTTP_CLOSED;
daniele 0:d7f2341ab245 300 return length;
daniele 0:d7f2341ab245 301
daniele 0:d7f2341ab245 302 }
daniele 0:d7f2341ab245 303 }
daniele 0:d7f2341ab245 304 else
daniele 0:d7f2341ab245 305 {
daniele 0:d7f2341ab245 306 dbg("Bad state for the client \n");
daniele 0:d7f2341ab245 307 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 308 }
daniele 0:d7f2341ab245 309
daniele 0:d7f2341ab245 310 }
daniele 0:d7f2341ab245 311
daniele 0:d7f2341ab245 312 /*
daniele 0:d7f2341ab245 313 * API used to submit data to the client.
daniele 0:d7f2341ab245 314 * Server sends data only using Transfer-Encoding: chunked.
daniele 0:d7f2341ab245 315 *
daniele 0:d7f2341ab245 316 * With this function the user will submit a data chunk to
daniele 0:d7f2341ab245 317 * be sent.
daniele 0:d7f2341ab245 318 * The function will send the chunk size in hex and the rest will
daniele 0:d7f2341ab245 319 * be sent using WR event from sockets.
daniele 0:d7f2341ab245 320 * After each transmision EV_HTTP_PROGRESS is called and at the
daniele 0:d7f2341ab245 321 * end of the chunk EV_HTTP_SENT is called.
daniele 0:d7f2341ab245 322 *
daniele 0:d7f2341ab245 323 * To let the client know this is the last chunk, the user
daniele 0:d7f2341ab245 324 * should pass a NULL buffer.
daniele 0:d7f2341ab245 325 */
daniele 0:d7f2341ab245 326 int pico_http_submitData(uint16_t conn, void * buffer, int len)
daniele 0:d7f2341ab245 327 {
daniele 0:d7f2341ab245 328
daniele 0:d7f2341ab245 329 struct httpClient * client = findClient(conn);
daniele 0:d7f2341ab245 330 char chunkStr[10];
daniele 0:d7f2341ab245 331 int chunkCount;
daniele 0:d7f2341ab245 332
daniele 0:d7f2341ab245 333 if(client->state != HTTP_WAIT_DATA)
daniele 0:d7f2341ab245 334 {
daniele 0:d7f2341ab245 335 dbg("Client is in a different state than accepted\n");
daniele 0:d7f2341ab245 336 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 337 }
daniele 0:d7f2341ab245 338
daniele 0:d7f2341ab245 339 if(client->buffer)
daniele 0:d7f2341ab245 340 {
daniele 0:d7f2341ab245 341 dbg("Already a buffer submited\n");
daniele 0:d7f2341ab245 342 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 343 }
daniele 0:d7f2341ab245 344
daniele 0:d7f2341ab245 345 if(!client)
daniele 0:d7f2341ab245 346 {
daniele 0:d7f2341ab245 347 dbg("Wrong connection ID\n");
daniele 0:d7f2341ab245 348 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 349 }
daniele 0:d7f2341ab245 350
daniele 0:d7f2341ab245 351 if(!buffer)
daniele 0:d7f2341ab245 352 {
daniele 0:d7f2341ab245 353 len = 0;
daniele 0:d7f2341ab245 354 }
daniele 0:d7f2341ab245 355
daniele 0:d7f2341ab245 356 if(len > 0)
daniele 0:d7f2341ab245 357 {
daniele 0:d7f2341ab245 358 client->buffer = pico_zalloc(len);
daniele 0:d7f2341ab245 359 if(!client->buffer)
daniele 0:d7f2341ab245 360 {
daniele 0:d7f2341ab245 361 pico_err = PICO_ERR_ENOMEM;
daniele 0:d7f2341ab245 362 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 363 }
daniele 0:d7f2341ab245 364 // taking over the buffer
daniele 0:d7f2341ab245 365 memcpy(client->buffer,buffer,len);
daniele 0:d7f2341ab245 366 }
daniele 0:d7f2341ab245 367 else
daniele 0:d7f2341ab245 368 client->buffer = NULL;
daniele 0:d7f2341ab245 369
daniele 0:d7f2341ab245 370
daniele 0:d7f2341ab245 371 client->bufferSize = len;
daniele 0:d7f2341ab245 372 client->bufferSent = 0;
daniele 0:d7f2341ab245 373
daniele 0:d7f2341ab245 374 // create the chunk size and send it
daniele 0:d7f2341ab245 375 if(len > 0)
daniele 0:d7f2341ab245 376 {
daniele 0:d7f2341ab245 377 client->state = HTTP_SENDING_DATA;
daniele 0:d7f2341ab245 378 chunkCount = pico_itoaHex(client->bufferSize,chunkStr);
daniele 0:d7f2341ab245 379 chunkStr[chunkCount++] = '\r';
daniele 0:d7f2341ab245 380 chunkStr[chunkCount++] = '\n';
daniele 0:d7f2341ab245 381 pico_socket_write(client->sck,chunkStr,chunkCount);
daniele 0:d7f2341ab245 382 }
daniele 0:d7f2341ab245 383 else if(len == 0)
daniele 0:d7f2341ab245 384 {
daniele 0:d7f2341ab245 385 dbg("->\n");
daniele 0:d7f2341ab245 386 // end of transmision
daniele 0:d7f2341ab245 387 pico_socket_write(client->sck,"0\r\n\r\n",5u);
daniele 0:d7f2341ab245 388 // nothing left, close the client
daniele 0:d7f2341ab245 389 pico_socket_close(client->sck);
daniele 0:d7f2341ab245 390 client->state = HTTP_CLOSED;
daniele 0:d7f2341ab245 391 }
daniele 0:d7f2341ab245 392
daniele 0:d7f2341ab245 393 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 394 }
daniele 0:d7f2341ab245 395
daniele 0:d7f2341ab245 396 /*
daniele 0:d7f2341ab245 397 * When EV_HTTP_PROGRESS is triggered you can use this
daniele 0:d7f2341ab245 398 * function to check the state of the chunk.
daniele 0:d7f2341ab245 399 */
daniele 0:d7f2341ab245 400
daniele 0:d7f2341ab245 401 int pico_http_getProgress(uint16_t conn, uint16_t * sent, uint16_t *total)
daniele 0:d7f2341ab245 402 {
daniele 0:d7f2341ab245 403 struct httpClient * client = findClient(conn);
daniele 0:d7f2341ab245 404
daniele 0:d7f2341ab245 405 if(!client)
daniele 0:d7f2341ab245 406 {
daniele 0:d7f2341ab245 407 dbg("Wrong connection id !\n");
daniele 0:d7f2341ab245 408 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 409 }
daniele 0:d7f2341ab245 410
daniele 0:d7f2341ab245 411 *sent = client->bufferSent;
daniele 0:d7f2341ab245 412 *total = client->bufferSize;
daniele 0:d7f2341ab245 413
daniele 0:d7f2341ab245 414 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 415 }
daniele 0:d7f2341ab245 416
daniele 0:d7f2341ab245 417 /*
daniele 0:d7f2341ab245 418 * This API can be used to close either a client
daniele 0:d7f2341ab245 419 * or the server ( if you pass HTTP_SERVER_ID as a connection ID).
daniele 0:d7f2341ab245 420 */
daniele 0:d7f2341ab245 421 int pico_http_close(uint16_t conn)
daniele 0:d7f2341ab245 422 {
daniele 0:d7f2341ab245 423 // close the server
daniele 0:d7f2341ab245 424 if(conn == HTTP_SERVER_ID)
daniele 0:d7f2341ab245 425 {
daniele 0:d7f2341ab245 426 if(server.state == HTTP_SERVER_LISTEN)
daniele 0:d7f2341ab245 427 {
daniele 0:d7f2341ab245 428 struct pico_tree_node * index, * tmp;
daniele 0:d7f2341ab245 429 // close the server
daniele 0:d7f2341ab245 430 pico_socket_close(server.sck);
daniele 0:d7f2341ab245 431 server.sck = NULL;
daniele 0:d7f2341ab245 432
daniele 0:d7f2341ab245 433 // destroy the tree
daniele 0:d7f2341ab245 434 pico_tree_foreach_safe(index,&pico_http_clients,tmp)
daniele 0:d7f2341ab245 435 {
daniele 0:d7f2341ab245 436 struct httpClient * client = index->keyValue;
daniele 0:d7f2341ab245 437
daniele 0:d7f2341ab245 438 if(client->resource)
daniele 0:d7f2341ab245 439 pico_free(client->resource);
daniele 0:d7f2341ab245 440
daniele 0:d7f2341ab245 441 pico_socket_close(client->sck);
daniele 0:d7f2341ab245 442 pico_tree_delete(&pico_http_clients,client);
daniele 0:d7f2341ab245 443 }
daniele 0:d7f2341ab245 444
daniele 0:d7f2341ab245 445 server.state = HTTP_SERVER_CLOSED;
daniele 0:d7f2341ab245 446 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 447 }
daniele 0:d7f2341ab245 448 else // nothing to close
daniele 0:d7f2341ab245 449 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 450 } // close a connection in this case
daniele 0:d7f2341ab245 451 else
daniele 0:d7f2341ab245 452 {
daniele 0:d7f2341ab245 453
daniele 0:d7f2341ab245 454 struct httpClient * client = findClient(conn);
daniele 0:d7f2341ab245 455
daniele 0:d7f2341ab245 456 if(!client)
daniele 0:d7f2341ab245 457 {
daniele 0:d7f2341ab245 458 dbg("Client not found..\n");
daniele 0:d7f2341ab245 459 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 460 }
daniele 0:d7f2341ab245 461
daniele 0:d7f2341ab245 462 pico_tree_delete(&pico_http_clients,client);
daniele 0:d7f2341ab245 463
daniele 0:d7f2341ab245 464 if(client->resource)
daniele 0:d7f2341ab245 465 pico_free(client->resource);
daniele 0:d7f2341ab245 466
daniele 0:d7f2341ab245 467 if(client->buffer)
daniele 0:d7f2341ab245 468 pico_free(client->buffer);
daniele 0:d7f2341ab245 469
daniele 0:d7f2341ab245 470 if(client->state != HTTP_CLOSED || !client->sck)
daniele 0:d7f2341ab245 471 pico_socket_close(client->sck);
daniele 0:d7f2341ab245 472
daniele 0:d7f2341ab245 473 pico_free(client);
daniele 0:d7f2341ab245 474 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 475 }
daniele 0:d7f2341ab245 476 }
daniele 0:d7f2341ab245 477
daniele 0:d7f2341ab245 478 // check the integrity of the request
daniele 0:d7f2341ab245 479 int parseRequest(struct httpClient * client)
daniele 0:d7f2341ab245 480 {
daniele 0:d7f2341ab245 481 char c;
daniele 0:d7f2341ab245 482 //read first line
daniele 0:d7f2341ab245 483 consumeChar(c);
daniele 0:d7f2341ab245 484 if(c == 'G')
daniele 0:d7f2341ab245 485 { // possible GET
daniele 0:d7f2341ab245 486
daniele 0:d7f2341ab245 487 char line[HTTP_HEADER_MAX_LINE];
daniele 0:d7f2341ab245 488 int index = 0;
daniele 0:d7f2341ab245 489
daniele 0:d7f2341ab245 490 line[index] = c;
daniele 0:d7f2341ab245 491
daniele 0:d7f2341ab245 492 // consume the full line
daniele 0:d7f2341ab245 493 while(consumeChar(c)>0) // read char by char only the first line
daniele 0:d7f2341ab245 494 {
daniele 0:d7f2341ab245 495 line[++index] = c;
daniele 0:d7f2341ab245 496 if(c == '\n')
daniele 0:d7f2341ab245 497 break;
daniele 0:d7f2341ab245 498
daniele 0:d7f2341ab245 499 if(index >= HTTP_HEADER_MAX_LINE)
daniele 0:d7f2341ab245 500 {
daniele 0:d7f2341ab245 501 dbg("Size exceeded \n");
daniele 0:d7f2341ab245 502 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 503 }
daniele 0:d7f2341ab245 504 }
daniele 0:d7f2341ab245 505
daniele 0:d7f2341ab245 506 // extract the function and the resource
daniele 0:d7f2341ab245 507 if(memcmp(line,"GET",3u) || line[3u]!=' ' || index < 10u || line[index] !='\n')
daniele 0:d7f2341ab245 508 {
daniele 0:d7f2341ab245 509 dbg("Wrong command or wrong ending\n");
daniele 0:d7f2341ab245 510 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 511 }
daniele 0:d7f2341ab245 512
daniele 0:d7f2341ab245 513 // start reading the resource
daniele 0:d7f2341ab245 514 index = 4u; // go after ' '
daniele 0:d7f2341ab245 515 while(line[index]!=' ')
daniele 0:d7f2341ab245 516 {
daniele 0:d7f2341ab245 517 if(line[index]=='\n') // no terminator ' '
daniele 0:d7f2341ab245 518 {
daniele 0:d7f2341ab245 519 dbg("No terminator...\n");
daniele 0:d7f2341ab245 520 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 521 }
daniele 0:d7f2341ab245 522
daniele 0:d7f2341ab245 523 index++;
daniele 0:d7f2341ab245 524 }
daniele 0:d7f2341ab245 525
daniele 0:d7f2341ab245 526 client->resource = pico_zalloc(index - 3u);// allocate without the GET in front + 1 which is \0
daniele 0:d7f2341ab245 527
daniele 0:d7f2341ab245 528 if(!client)
daniele 0:d7f2341ab245 529 {
daniele 0:d7f2341ab245 530 pico_err = PICO_ERR_ENOMEM;
daniele 0:d7f2341ab245 531 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 532 }
daniele 0:d7f2341ab245 533
daniele 0:d7f2341ab245 534 // copy the resource
daniele 0:d7f2341ab245 535 memcpy(client->resource,line+4u,index-4u);// copy without the \0 which was already set by pico_zalloc
daniele 0:d7f2341ab245 536
daniele 0:d7f2341ab245 537 client->state = HTTP_WAIT_EOF_HDR;
daniele 0:d7f2341ab245 538 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 539
daniele 0:d7f2341ab245 540 }
daniele 0:d7f2341ab245 541
daniele 0:d7f2341ab245 542 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 543 }
daniele 0:d7f2341ab245 544
daniele 0:d7f2341ab245 545
daniele 0:d7f2341ab245 546
daniele 0:d7f2341ab245 547 int readRemainingHeader(struct httpClient * client)
daniele 0:d7f2341ab245 548 {
daniele 0:d7f2341ab245 549 char line[100];
daniele 0:d7f2341ab245 550 int count = 0;
daniele 0:d7f2341ab245 551 int len;
daniele 0:d7f2341ab245 552
daniele 0:d7f2341ab245 553 while( (len = pico_socket_read(client->sck,line,100u)) > 0)
daniele 0:d7f2341ab245 554 {
daniele 0:d7f2341ab245 555 char c;
daniele 0:d7f2341ab245 556 int index = 0;
daniele 0:d7f2341ab245 557 // parse the response
daniele 0:d7f2341ab245 558 while(index < len)
daniele 0:d7f2341ab245 559 {
daniele 0:d7f2341ab245 560 c = line[index++];
daniele 0:d7f2341ab245 561 if(c!='\r' && c!='\n')
daniele 0:d7f2341ab245 562 count++;
daniele 0:d7f2341ab245 563 if(c=='\n')
daniele 0:d7f2341ab245 564 {
daniele 0:d7f2341ab245 565 if(!count)
daniele 0:d7f2341ab245 566 {
daniele 0:d7f2341ab245 567 client->state = HTTP_EOF_HDR;
daniele 0:d7f2341ab245 568 dbg("End of header !\n");
daniele 0:d7f2341ab245 569 break;
daniele 0:d7f2341ab245 570 }
daniele 0:d7f2341ab245 571 count = 0;
daniele 0:d7f2341ab245 572
daniele 0:d7f2341ab245 573 }
daniele 0:d7f2341ab245 574 }
daniele 0:d7f2341ab245 575 }
daniele 0:d7f2341ab245 576
daniele 0:d7f2341ab245 577 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 578 }
daniele 0:d7f2341ab245 579
daniele 0:d7f2341ab245 580 void sendData(struct httpClient * client)
daniele 0:d7f2341ab245 581 {
daniele 0:d7f2341ab245 582 int length;
daniele 0:d7f2341ab245 583 while( client->bufferSent < client->bufferSize &&
daniele 0:d7f2341ab245 584 (length = pico_socket_write(client->sck,client->buffer+client->bufferSent,client->bufferSize-client->bufferSent)) > 0 )
daniele 0:d7f2341ab245 585 {
daniele 0:d7f2341ab245 586 client->bufferSent += length;
daniele 0:d7f2341ab245 587 server.wakeup(EV_HTTP_PROGRESS,client->connectionID);
daniele 0:d7f2341ab245 588 }
daniele 0:d7f2341ab245 589
daniele 0:d7f2341ab245 590 if(client->bufferSent == client->bufferSize && client->bufferSize)
daniele 0:d7f2341ab245 591 {
daniele 0:d7f2341ab245 592 //send chunk trail
daniele 0:d7f2341ab245 593 if(pico_socket_write(client->sck,"\r\n",2) > 0)
daniele 0:d7f2341ab245 594 {
daniele 0:d7f2341ab245 595 client->state = HTTP_WAIT_DATA;
daniele 0:d7f2341ab245 596 //free the buffer
daniele 0:d7f2341ab245 597 pico_free(client->buffer);
daniele 0:d7f2341ab245 598 client->buffer = NULL;
daniele 0:d7f2341ab245 599 server.wakeup(EV_HTTP_SENT,client->connectionID);
daniele 0:d7f2341ab245 600 }
daniele 0:d7f2341ab245 601 }
daniele 0:d7f2341ab245 602
daniele 0:d7f2341ab245 603 }
daniele 0:d7f2341ab245 604
daniele 0:d7f2341ab245 605 int readData(struct httpClient * client)
daniele 0:d7f2341ab245 606 {
daniele 0:d7f2341ab245 607 if(client->state == HTTP_WAIT_HDR)
daniele 0:d7f2341ab245 608 {
daniele 0:d7f2341ab245 609 if(parseRequest(client)<0 || readRemainingHeader(client)<0)
daniele 0:d7f2341ab245 610 {
daniele 0:d7f2341ab245 611 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 612 }
daniele 0:d7f2341ab245 613 } // continue with this in case the header comes line by line not a big chunk
daniele 0:d7f2341ab245 614 else if(client->state == HTTP_WAIT_EOF_HDR)
daniele 0:d7f2341ab245 615 {
daniele 0:d7f2341ab245 616 if(readRemainingHeader(client)<0 )
daniele 0:d7f2341ab245 617 return HTTP_RETURN_ERROR;
daniele 0:d7f2341ab245 618 }
daniele 0:d7f2341ab245 619
daniele 0:d7f2341ab245 620 if(client->state == HTTP_EOF_HDR)
daniele 0:d7f2341ab245 621 {
daniele 0:d7f2341ab245 622 client->state = HTTP_WAIT_RESPONSE;
daniele 0:d7f2341ab245 623 pico_socket_shutdown(client->sck,PICO_SHUT_RD);
daniele 0:d7f2341ab245 624 server.wakeup(EV_HTTP_REQ,client->connectionID);
daniele 0:d7f2341ab245 625 }
daniele 0:d7f2341ab245 626
daniele 0:d7f2341ab245 627 return HTTP_RETURN_OK;
daniele 0:d7f2341ab245 628 }
daniele 0:d7f2341ab245 629
daniele 0:d7f2341ab245 630 struct httpClient * findClient(uint16_t conn)
daniele 0:d7f2341ab245 631 {
daniele 0:d7f2341ab245 632 struct httpClient dummy = {.connectionID = conn};
daniele 0:d7f2341ab245 633
daniele 0:d7f2341ab245 634 return pico_tree_findKey(&pico_http_clients,&dummy);
daniele 0:d7f2341ab245 635 }
daniele 0:d7f2341ab245 636 #endif