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:
daniele
Date:
Fri May 24 15:25:25 2013 +0000
Revision:
3:b4047e8a0123
Child:
51:ab4529a384a6
Updated from main repo + fixed Mutexes;

Who changed what in which revision?

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