Daniele Lacamera / PicoTCP-Experimental_CDC_ECM_Branch

Fork of PicoTCP by Daniele Lacamera

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