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 Belgium NV
Date:
Mon Dec 16 11:25:54 2013 +0100
Revision:
131:4758606c9316
Parent:
73:dfb737147f6e
Child:
134:cc4e6d2654d9
Syncronized with master branch

Who changed what in which revision?

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