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:
Mon Sep 02 08:02:21 2013 +0000
Revision:
51:ab4529a384a6
Parent:
3:b4047e8a0123
Child:
63:97f481e33cb2
Updated from masterbranch

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
tass 51:ab4529a384a6 16 #define BACKLOG 10
daniele 3:b4047e8a0123 17
tass 51:ab4529a384a6 18 #define HTTP_SERVER_CLOSED 0
tass 51:ab4529a384a6 19 #define HTTP_SERVER_LISTEN 1
daniele 3:b4047e8a0123 20
tass 51:ab4529a384a6 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
tass 51:ab4529a384a6 25 static const 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
tass 51:ab4529a384a6 32 static const 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
tass 51:ab4529a384a6 39 static const 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 {
tass 51:ab4529a384a6 48 uint16_t state;
tass 51:ab4529a384a6 49 struct pico_socket * sck;
tass 51:ab4529a384a6 50 uint16_t port;
tass 51:ab4529a384a6 51 void (*wakeup)(uint16_t ev, uint16_t param);
tass 51:ab4529a384a6 52 uint8_t accepted;
daniele 3:b4047e8a0123 53 };
daniele 3:b4047e8a0123 54
daniele 3:b4047e8a0123 55 struct httpClient
daniele 3:b4047e8a0123 56 {
tass 51:ab4529a384a6 57 uint16_t connectionID;
tass 51:ab4529a384a6 58 struct pico_socket * sck;
tass 51:ab4529a384a6 59 void * buffer;
tass 51:ab4529a384a6 60 uint16_t bufferSize;
tass 51:ab4529a384a6 61 uint16_t bufferSent;
tass 51:ab4529a384a6 62 char * resource;
tass 51:ab4529a384a6 63 uint16_t state;
daniele 3:b4047e8a0123 64 };
daniele 3:b4047e8a0123 65
daniele 3:b4047e8a0123 66 /* Local states for clients */
tass 51:ab4529a384a6 67 #define HTTP_WAIT_HDR 0
tass 51:ab4529a384a6 68 #define HTTP_WAIT_EOF_HDR 1
tass 51:ab4529a384a6 69 #define HTTP_EOF_HDR 2
daniele 3:b4047e8a0123 70 #define HTTP_WAIT_RESPONSE 3
tass 51:ab4529a384a6 71 #define HTTP_WAIT_DATA 4
tass 51:ab4529a384a6 72 #define HTTP_SENDING_DATA 5
tass 51:ab4529a384a6 73 #define HTTP_ERROR 6
tass 51:ab4529a384a6 74 #define HTTP_CLOSED 7
daniele 3:b4047e8a0123 75
tass 51:ab4529a384a6 76 static struct httpServer server = {0};
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 {
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 96 struct pico_tree_node * index;
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 103 serverEvent = TRUE;
daniele 3:b4047e8a0123 104 }
daniele 3:b4047e8a0123 105 else
daniele 3:b4047e8a0123 106 {
tass 51:ab4529a384a6 107 pico_tree_foreach(index,&pico_http_clients)
tass 51:ab4529a384a6 108 {
tass 51:ab4529a384a6 109 client = index->keyValue;
tass 51:ab4529a384a6 110 if(client->sck == s) break;
tass 51:ab4529a384a6 111 client = NULL;
tass 51:ab4529a384a6 112 }
daniele 3:b4047e8a0123 113 }
daniele 3:b4047e8a0123 114
tass 51:ab4529a384a6 115 if(!client && !serverEvent)
tass 51:ab4529a384a6 116 {
tass 51:ab4529a384a6 117 return;
tass 51:ab4529a384a6 118 }
daniele 3:b4047e8a0123 119
tass 51:ab4529a384a6 120 if (ev & PICO_SOCK_EV_RD)
tass 51:ab4529a384a6 121 {
daniele 3:b4047e8a0123 122
tass 51:ab4529a384a6 123 if(readData(client) == HTTP_RETURN_ERROR)
tass 51:ab4529a384a6 124 {
tass 51:ab4529a384a6 125 // send out error
tass 51:ab4529a384a6 126 client->state = HTTP_ERROR;
tass 51:ab4529a384a6 127 pico_socket_write(client->sck,(char *)errorHeader,sizeof(errorHeader)-1);
tass 51:ab4529a384a6 128 server.wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 129 }
tass 51:ab4529a384a6 130 }
daniele 3:b4047e8a0123 131
tass 51:ab4529a384a6 132 if(ev & PICO_SOCK_EV_WR)
tass 51:ab4529a384a6 133 {
tass 51:ab4529a384a6 134 if(client->state == HTTP_SENDING_DATA)
tass 51:ab4529a384a6 135 {
tass 51:ab4529a384a6 136 sendData(client);
tass 51:ab4529a384a6 137 }
tass 51:ab4529a384a6 138 }
daniele 3:b4047e8a0123 139
tass 51:ab4529a384a6 140 if(ev & PICO_SOCK_EV_CONN)
tass 51:ab4529a384a6 141 {
tass 51:ab4529a384a6 142 server.accepted = FALSE;
tass 51:ab4529a384a6 143 server.wakeup(EV_HTTP_CON,HTTP_SERVER_ID);
tass 51:ab4529a384a6 144 if(!server.accepted)
tass 51:ab4529a384a6 145 {
tass 51:ab4529a384a6 146 pico_socket_close(s); // reject socket
tass 51:ab4529a384a6 147 }
tass 51:ab4529a384a6 148 }
daniele 3:b4047e8a0123 149
tass 51:ab4529a384a6 150 if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) )
tass 51:ab4529a384a6 151 {
tass 51:ab4529a384a6 152 server.wakeup(EV_HTTP_CLOSE,(serverEvent ? HTTP_SERVER_ID : client->connectionID));
tass 51:ab4529a384a6 153 }
daniele 3:b4047e8a0123 154
tass 51:ab4529a384a6 155 if(ev & PICO_SOCK_EV_ERR)
tass 51:ab4529a384a6 156 {
tass 51:ab4529a384a6 157 server.wakeup(EV_HTTP_ERROR,(serverEvent ? HTTP_SERVER_ID : client->connectionID));
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 167 struct pico_ip4 anything = {0};
daniele 3:b4047e8a0123 168
tass 51:ab4529a384a6 169 server.port = port ? short_be(port) : short_be(80u);
daniele 3:b4047e8a0123 170
tass 51:ab4529a384a6 171 if(!wakeup)
tass 51:ab4529a384a6 172 {
tass 51:ab4529a384a6 173 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 174 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 175 }
daniele 3:b4047e8a0123 176
tass 51:ab4529a384a6 177 server.sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &httpServerCbk);
daniele 3:b4047e8a0123 178
tass 51:ab4529a384a6 179 if(!server.sck)
tass 51:ab4529a384a6 180 {
tass 51:ab4529a384a6 181 pico_err = PICO_ERR_EFAULT;
tass 51:ab4529a384a6 182 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 183 }
daniele 3:b4047e8a0123 184
tass 51:ab4529a384a6 185 if(pico_socket_bind(server.sck , &anything, &server.port)!=0)
tass 51:ab4529a384a6 186 {
tass 51:ab4529a384a6 187 pico_err = PICO_ERR_EADDRNOTAVAIL;
tass 51:ab4529a384a6 188 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 189 }
daniele 3:b4047e8a0123 190
tass 51:ab4529a384a6 191 if (pico_socket_listen(server.sck, BACKLOG) != 0)
tass 51:ab4529a384a6 192 {
tass 51:ab4529a384a6 193 pico_err = PICO_ERR_EADDRINUSE;
tass 51:ab4529a384a6 194 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 195 }
tass 51:ab4529a384a6 196 server.wakeup = wakeup;
tass 51:ab4529a384a6 197 server.state = HTTP_SERVER_LISTEN;
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 218 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 219 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 220 }
daniele 3:b4047e8a0123 221
tass 51:ab4529a384a6 222 client->sck = pico_socket_accept(server.sck,&orig,&port);
daniele 3:b4047e8a0123 223
tass 51:ab4529a384a6 224 if(!client->sck)
tass 51:ab4529a384a6 225 {
tass 51:ab4529a384a6 226 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 227 pico_free(client);
tass 51:ab4529a384a6 228 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 229 }
daniele 3:b4047e8a0123 230
tass 51:ab4529a384a6 231 server.accepted = TRUE;
tass 51:ab4529a384a6 232 // buffer used for async sending
tass 51:ab4529a384a6 233 client->state = HTTP_WAIT_HDR;
tass 51:ab4529a384a6 234 client->buffer = NULL;
tass 51:ab4529a384a6 235 client->bufferSize = 0;
tass 51:ab4529a384a6 236 client->connectionID = pico_rand() & 0x7FFF;
daniele 3:b4047e8a0123 237
tass 51:ab4529a384a6 238 //add element to the tree, if duplicate because the rand
tass 51:ab4529a384a6 239 //regenerate
tass 51:ab4529a384a6 240 while(pico_tree_insert(&pico_http_clients,client)!=NULL)
tass 51:ab4529a384a6 241 client->connectionID = pico_rand() & 0x7FFF;
daniele 3:b4047e8a0123 242
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 253 struct httpClient * client = findClient(conn);
daniele 3:b4047e8a0123 254
tass 51:ab4529a384a6 255 if(!client)
tass 51:ab4529a384a6 256 return NULL;
tass 51:ab4529a384a6 257 else
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 278 struct httpClient * client = findClient(conn);
daniele 3:b4047e8a0123 279
tass 51:ab4529a384a6 280 if(!client)
tass 51:ab4529a384a6 281 {
tass 51:ab4529a384a6 282 dbg("Client not found !\n");
tass 51:ab4529a384a6 283 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 284 }
daniele 3:b4047e8a0123 285
tass 51:ab4529a384a6 286 if(client->state == HTTP_WAIT_RESPONSE)
tass 51:ab4529a384a6 287 {
tass 51:ab4529a384a6 288 if(code == HTTP_RESOURCE_FOUND)
tass 51:ab4529a384a6 289 {
tass 51:ab4529a384a6 290 client->state = HTTP_WAIT_DATA;
tass 51:ab4529a384a6 291 return pico_socket_write(client->sck,(char *)returnOkHeader,sizeof(returnOkHeader)-1);//remove \0
tass 51:ab4529a384a6 292 }
tass 51:ab4529a384a6 293 else
tass 51:ab4529a384a6 294 {
tass 51:ab4529a384a6 295 int length;
daniele 3:b4047e8a0123 296
tass 51:ab4529a384a6 297 length = pico_socket_write(client->sck,(char *)returnFailHeader,sizeof(returnFailHeader)-1);//remove \0
tass 51:ab4529a384a6 298 pico_socket_close(client->sck);
tass 51:ab4529a384a6 299 client->state = HTTP_CLOSED;
tass 51:ab4529a384a6 300 return length;
daniele 3:b4047e8a0123 301
tass 51:ab4529a384a6 302 }
tass 51:ab4529a384a6 303 }
tass 51:ab4529a384a6 304 else
tass 51:ab4529a384a6 305 {
tass 51:ab4529a384a6 306 dbg("Bad state for the client \n");
tass 51:ab4529a384a6 307 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 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
tass 51:ab4529a384a6 329 struct httpClient * client = findClient(conn);
tass 51:ab4529a384a6 330 char chunkStr[10];
tass 51:ab4529a384a6 331 int chunkCount;
daniele 3:b4047e8a0123 332
tass 51:ab4529a384a6 333 if(client->state != HTTP_WAIT_DATA)
tass 51:ab4529a384a6 334 {
tass 51:ab4529a384a6 335 dbg("Client is in a different state than accepted\n");
tass 51:ab4529a384a6 336 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 337 }
daniele 3:b4047e8a0123 338
tass 51:ab4529a384a6 339 if(client->buffer)
tass 51:ab4529a384a6 340 {
tass 51:ab4529a384a6 341 dbg("Already a buffer submited\n");
tass 51:ab4529a384a6 342 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 343 }
daniele 3:b4047e8a0123 344
tass 51:ab4529a384a6 345 if(!client)
tass 51:ab4529a384a6 346 {
tass 51:ab4529a384a6 347 dbg("Wrong connection ID\n");
tass 51:ab4529a384a6 348 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 349 }
daniele 3:b4047e8a0123 350
tass 51:ab4529a384a6 351 if(!buffer)
tass 51:ab4529a384a6 352 {
tass 51:ab4529a384a6 353 len = 0;
tass 51:ab4529a384a6 354 }
daniele 3:b4047e8a0123 355
tass 51:ab4529a384a6 356 if(len > 0)
tass 51:ab4529a384a6 357 {
tass 51:ab4529a384a6 358 client->buffer = pico_zalloc(len);
tass 51:ab4529a384a6 359 if(!client->buffer)
tass 51:ab4529a384a6 360 {
tass 51:ab4529a384a6 361 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 362 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 363 }
tass 51:ab4529a384a6 364 // taking over the buffer
tass 51:ab4529a384a6 365 memcpy(client->buffer,buffer,len);
tass 51:ab4529a384a6 366 }
tass 51:ab4529a384a6 367 else
tass 51:ab4529a384a6 368 client->buffer = NULL;
daniele 3:b4047e8a0123 369
daniele 3:b4047e8a0123 370
tass 51:ab4529a384a6 371 client->bufferSize = len;
tass 51:ab4529a384a6 372 client->bufferSent = 0;
daniele 3:b4047e8a0123 373
tass 51:ab4529a384a6 374 // create the chunk size and send it
tass 51:ab4529a384a6 375 if(len > 0)
tass 51:ab4529a384a6 376 {
tass 51:ab4529a384a6 377 client->state = HTTP_SENDING_DATA;
tass 51:ab4529a384a6 378 chunkCount = pico_itoaHex(client->bufferSize,chunkStr);
tass 51:ab4529a384a6 379 chunkStr[chunkCount++] = '\r';
tass 51:ab4529a384a6 380 chunkStr[chunkCount++] = '\n';
tass 51:ab4529a384a6 381 pico_socket_write(client->sck,chunkStr,chunkCount);
tass 51:ab4529a384a6 382 }
tass 51:ab4529a384a6 383 else if(len == 0)
tass 51:ab4529a384a6 384 {
tass 51:ab4529a384a6 385 dbg("->\n");
tass 51:ab4529a384a6 386 // end of transmision
tass 51:ab4529a384a6 387 pico_socket_write(client->sck,"0\r\n\r\n",5u);
tass 51:ab4529a384a6 388 // nothing left, close the client
tass 51:ab4529a384a6 389 pico_socket_close(client->sck);
tass 51:ab4529a384a6 390 client->state = HTTP_CLOSED;
tass 51:ab4529a384a6 391 }
daniele 3:b4047e8a0123 392
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 403 struct httpClient * client = findClient(conn);
daniele 3:b4047e8a0123 404
tass 51:ab4529a384a6 405 if(!client)
tass 51:ab4529a384a6 406 {
tass 51:ab4529a384a6 407 dbg("Wrong connection id !\n");
tass 51:ab4529a384a6 408 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 409 }
daniele 3:b4047e8a0123 410
tass 51:ab4529a384a6 411 *sent = client->bufferSent;
tass 51:ab4529a384a6 412 *total = client->bufferSize;
daniele 3:b4047e8a0123 413
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 423 // close the server
tass 51:ab4529a384a6 424 if(conn == HTTP_SERVER_ID)
tass 51:ab4529a384a6 425 {
tass 51:ab4529a384a6 426 if(server.state == HTTP_SERVER_LISTEN)
tass 51:ab4529a384a6 427 {
tass 51:ab4529a384a6 428 struct pico_tree_node * index, * tmp;
tass 51:ab4529a384a6 429 // close the server
tass 51:ab4529a384a6 430 pico_socket_close(server.sck);
tass 51:ab4529a384a6 431 server.sck = NULL;
daniele 3:b4047e8a0123 432
tass 51:ab4529a384a6 433 // destroy the tree
tass 51:ab4529a384a6 434 pico_tree_foreach_safe(index,&pico_http_clients,tmp)
tass 51:ab4529a384a6 435 {
tass 51:ab4529a384a6 436 struct httpClient * client = index->keyValue;
daniele 3:b4047e8a0123 437
tass 51:ab4529a384a6 438 if(client->resource)
tass 51:ab4529a384a6 439 pico_free(client->resource);
daniele 3:b4047e8a0123 440
tass 51:ab4529a384a6 441 pico_socket_close(client->sck);
tass 51:ab4529a384a6 442 pico_tree_delete(&pico_http_clients,client);
tass 51:ab4529a384a6 443 }
daniele 3:b4047e8a0123 444
tass 51:ab4529a384a6 445 server.state = HTTP_SERVER_CLOSED;
tass 51:ab4529a384a6 446 return HTTP_RETURN_OK;
tass 51:ab4529a384a6 447 }
tass 51:ab4529a384a6 448 else // nothing to close
tass 51:ab4529a384a6 449 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 450 } // close a connection in this case
tass 51:ab4529a384a6 451 else
tass 51:ab4529a384a6 452 {
daniele 3:b4047e8a0123 453
tass 51:ab4529a384a6 454 struct httpClient * client = findClient(conn);
daniele 3:b4047e8a0123 455
tass 51:ab4529a384a6 456 if(!client)
tass 51:ab4529a384a6 457 {
tass 51:ab4529a384a6 458 dbg("Client not found..\n");
tass 51:ab4529a384a6 459 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 460 }
daniele 3:b4047e8a0123 461
tass 51:ab4529a384a6 462 pico_tree_delete(&pico_http_clients,client);
daniele 3:b4047e8a0123 463
tass 51:ab4529a384a6 464 if(client->resource)
tass 51:ab4529a384a6 465 pico_free(client->resource);
daniele 3:b4047e8a0123 466
tass 51:ab4529a384a6 467 if(client->buffer)
tass 51:ab4529a384a6 468 pico_free(client->buffer);
daniele 3:b4047e8a0123 469
tass 51:ab4529a384a6 470 if(client->state != HTTP_CLOSED || !client->sck)
tass 51:ab4529a384a6 471 pico_socket_close(client->sck);
daniele 3:b4047e8a0123 472
tass 51:ab4529a384a6 473 pico_free(client);
tass 51:ab4529a384a6 474 return HTTP_RETURN_OK;
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 481 char c;
tass 51:ab4529a384a6 482 //read first line
tass 51:ab4529a384a6 483 consumeChar(c);
tass 51:ab4529a384a6 484 if(c == 'G')
tass 51:ab4529a384a6 485 { // possible GET
daniele 3:b4047e8a0123 486
tass 51:ab4529a384a6 487 char line[HTTP_HEADER_MAX_LINE];
tass 51:ab4529a384a6 488 uint32_t index = 0;
daniele 3:b4047e8a0123 489
tass 51:ab4529a384a6 490 line[index] = c;
daniele 3:b4047e8a0123 491
tass 51:ab4529a384a6 492 // consume the full line
tass 51:ab4529a384a6 493 while(consumeChar(c)>0) // read char by char only the first line
tass 51:ab4529a384a6 494 {
tass 51:ab4529a384a6 495 line[++index] = c;
tass 51:ab4529a384a6 496 if(c == '\n')
tass 51:ab4529a384a6 497 break;
daniele 3:b4047e8a0123 498
tass 51:ab4529a384a6 499 if(index >= HTTP_HEADER_MAX_LINE)
tass 51:ab4529a384a6 500 {
tass 51:ab4529a384a6 501 dbg("Size exceeded \n");
tass 51:ab4529a384a6 502 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 503 }
tass 51:ab4529a384a6 504 }
daniele 3:b4047e8a0123 505
tass 51:ab4529a384a6 506 // extract the function and the resource
tass 51:ab4529a384a6 507 if(memcmp(line,"GET",3u) || line[3u]!=' ' || index < 10u || line[index] !='\n')
tass 51:ab4529a384a6 508 {
tass 51:ab4529a384a6 509 dbg("Wrong command or wrong ending\n");
tass 51:ab4529a384a6 510 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 511 }
daniele 3:b4047e8a0123 512
tass 51:ab4529a384a6 513 // start reading the resource
tass 51:ab4529a384a6 514 index = 4u; // go after ' '
tass 51:ab4529a384a6 515 while(line[index]!=' ')
tass 51:ab4529a384a6 516 {
tass 51:ab4529a384a6 517 if(line[index]=='\n') // no terminator ' '
tass 51:ab4529a384a6 518 {
tass 51:ab4529a384a6 519 dbg("No terminator...\n");
tass 51:ab4529a384a6 520 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 521 }
daniele 3:b4047e8a0123 522
tass 51:ab4529a384a6 523 index++;
tass 51:ab4529a384a6 524 }
daniele 3:b4047e8a0123 525
tass 51:ab4529a384a6 526 client->resource = pico_zalloc(index - 3u);// allocate without the GET in front + 1 which is \0
daniele 3:b4047e8a0123 527
tass 51:ab4529a384a6 528 if(!client)
tass 51:ab4529a384a6 529 {
tass 51:ab4529a384a6 530 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 531 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 532 }
daniele 3:b4047e8a0123 533
tass 51:ab4529a384a6 534 // copy the resource
tass 51:ab4529a384a6 535 memcpy(client->resource,line+4u,index-4u);// copy without the \0 which was already set by pico_zalloc
daniele 3:b4047e8a0123 536
tass 51:ab4529a384a6 537 client->state = HTTP_WAIT_EOF_HDR;
tass 51:ab4529a384a6 538 return HTTP_RETURN_OK;
daniele 3:b4047e8a0123 539
tass 51:ab4529a384a6 540 }
daniele 3:b4047e8a0123 541
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 549 char line[100];
tass 51:ab4529a384a6 550 int count = 0;
tass 51:ab4529a384a6 551 int len;
daniele 3:b4047e8a0123 552
tass 51:ab4529a384a6 553 while( (len = pico_socket_read(client->sck,line,100u)) > 0)
tass 51:ab4529a384a6 554 {
tass 51:ab4529a384a6 555 char c;
tass 51:ab4529a384a6 556 int index = 0;
tass 51:ab4529a384a6 557 // parse the response
tass 51:ab4529a384a6 558 while(index < len)
tass 51:ab4529a384a6 559 {
tass 51:ab4529a384a6 560 c = line[index++];
tass 51:ab4529a384a6 561 if(c!='\r' && c!='\n')
tass 51:ab4529a384a6 562 count++;
tass 51:ab4529a384a6 563 if(c=='\n')
tass 51:ab4529a384a6 564 {
tass 51:ab4529a384a6 565 if(!count)
tass 51:ab4529a384a6 566 {
tass 51:ab4529a384a6 567 client->state = HTTP_EOF_HDR;
tass 51:ab4529a384a6 568 dbg("End of header !\n");
tass 51:ab4529a384a6 569 break;
tass 51:ab4529a384a6 570 }
tass 51:ab4529a384a6 571 count = 0;
daniele 3:b4047e8a0123 572
tass 51:ab4529a384a6 573 }
tass 51:ab4529a384a6 574 }
tass 51:ab4529a384a6 575 }
daniele 3:b4047e8a0123 576
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 582 int length;
tass 51:ab4529a384a6 583 while( client->bufferSent < client->bufferSize &&
tass 51:ab4529a384a6 584 (length = pico_socket_write(client->sck,client->buffer+client->bufferSent,client->bufferSize-client->bufferSent)) > 0 )
tass 51:ab4529a384a6 585 {
tass 51:ab4529a384a6 586 client->bufferSent += length;
tass 51:ab4529a384a6 587 server.wakeup(EV_HTTP_PROGRESS,client->connectionID);
tass 51:ab4529a384a6 588 }
daniele 3:b4047e8a0123 589
tass 51:ab4529a384a6 590 if(client->bufferSent == client->bufferSize && client->bufferSize)
tass 51:ab4529a384a6 591 {
tass 51:ab4529a384a6 592 //send chunk trail
tass 51:ab4529a384a6 593 if(pico_socket_write(client->sck,"\r\n",2) > 0)
tass 51:ab4529a384a6 594 {
tass 51:ab4529a384a6 595 client->state = HTTP_WAIT_DATA;
tass 51:ab4529a384a6 596 //free the buffer
tass 51:ab4529a384a6 597 pico_free(client->buffer);
tass 51:ab4529a384a6 598 client->buffer = NULL;
tass 51:ab4529a384a6 599 server.wakeup(EV_HTTP_SENT,client->connectionID);
tass 51:ab4529a384a6 600 }
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 607 if(client->state == HTTP_WAIT_HDR)
tass 51:ab4529a384a6 608 {
tass 51:ab4529a384a6 609 if(parseRequest(client)<0 || readRemainingHeader(client)<0)
tass 51:ab4529a384a6 610 {
tass 51:ab4529a384a6 611 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 612 }
tass 51:ab4529a384a6 613 } // continue with this in case the header comes line by line not a big chunk
tass 51:ab4529a384a6 614 else if(client->state == HTTP_WAIT_EOF_HDR)
tass 51:ab4529a384a6 615 {
tass 51:ab4529a384a6 616 if(readRemainingHeader(client)<0 )
tass 51:ab4529a384a6 617 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 618 }
daniele 3:b4047e8a0123 619
tass 51:ab4529a384a6 620 if(client->state == HTTP_EOF_HDR)
tass 51:ab4529a384a6 621 {
tass 51:ab4529a384a6 622 client->state = HTTP_WAIT_RESPONSE;
tass 51:ab4529a384a6 623 pico_socket_shutdown(client->sck,PICO_SHUT_RD);
tass 51:ab4529a384a6 624 server.wakeup(EV_HTTP_REQ,client->connectionID);
tass 51:ab4529a384a6 625 }
daniele 3:b4047e8a0123 626
tass 51:ab4529a384a6 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 {
tass 51:ab4529a384a6 632 struct httpClient dummy = {.connectionID = conn};
daniele 3:b4047e8a0123 633
tass 51:ab4529a384a6 634 return pico_tree_findKey(&pico_http_clients,&dummy);
daniele 3:b4047e8a0123 635 }
daniele 3:b4047e8a0123 636 #endif