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

Fork of PicoTCP by Daniele Lacamera

Committer:
daniele
Date:
Sat Aug 03 08:50:27 2013 +0000
Revision:
51:18637a3d071f
Parent:
3:b4047e8a0123
Branch for CDC-ECM: Work in progress

Who changed what in which revision?

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