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:
Thu Sep 19 13:26:14 2013 +0000
Revision:
68:0847e35d08a6
Child:
70:cd218dd180e5
Imported from masterbranch, again

Who changed what in which revision?

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