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 12:38:53 2013 +0000
Revision:
63:97f481e33cb2
Parent:
51:ab4529a384a6
Update from the master branch

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 #include <string.h>
daniele 3:b4047e8a0123 8 #include <stdint.h>
daniele 3:b4047e8a0123 9 #include "pico_tree.h"
daniele 3:b4047e8a0123 10 #include "pico_config.h"
daniele 3:b4047e8a0123 11 #include "pico_socket.h"
daniele 3:b4047e8a0123 12 #include "pico_tcp.h"
daniele 3:b4047e8a0123 13 #include "pico_dns_client.h"
daniele 3:b4047e8a0123 14 #include "pico_http_client.h"
daniele 3:b4047e8a0123 15 #include "pico_ipv4.h"
daniele 3:b4047e8a0123 16 #include "pico_stack.h"
daniele 3:b4047e8a0123 17
daniele 3:b4047e8a0123 18 /*
daniele 3:b4047e8a0123 19 * This is the size of the following header
daniele 3:b4047e8a0123 20 *
daniele 3:b4047e8a0123 21 * GET <resource> HTTP/1.1<CRLF>
daniele 3:b4047e8a0123 22 * Host: <host>:<port><CRLF>
daniele 3:b4047e8a0123 23 * User-Agent: picoTCP<CRLF>
daniele 3:b4047e8a0123 24 * Connection: close<CRLF>
daniele 3:b4047e8a0123 25 * <CRLF>
daniele 3:b4047e8a0123 26 *
daniele 3:b4047e8a0123 27 * where <resource>,<host> and <port> will be added later.
daniele 3:b4047e8a0123 28 */
daniele 3:b4047e8a0123 29
daniele 3:b4047e8a0123 30 #ifdef PICO_SUPPORT_HTTP_CLIENT
daniele 3:b4047e8a0123 31
daniele 3:b4047e8a0123 32 #define HTTP_GET_BASIC_SIZE 63u
daniele 3:b4047e8a0123 33 #define HTTP_HEADER_LINE_SIZE 50u
tass 51:ab4529a384a6 34 #define RESPONSE_INDEX 9u
daniele 3:b4047e8a0123 35
tass 51:ab4529a384a6 36 #define HTTP_CHUNK_ERROR 0xFFFFFFFFu
daniele 3:b4047e8a0123 37
daniele 3:b4047e8a0123 38 #ifdef dbg
tass 51:ab4529a384a6 39 #undef dbg
tass 51:ab4529a384a6 40 #define dbg(...) do{}while(0);
daniele 3:b4047e8a0123 41 #endif
daniele 3:b4047e8a0123 42
tass 51:ab4529a384a6 43 #define consumeChar(c) (pico_socket_read(client->sck,&c,1u))
tass 51:ab4529a384a6 44 #define isLocation(line) (memcmp(line,"Location",8u) == 0)
tass 51:ab4529a384a6 45 #define isContentLength(line) (memcmp(line,"Content-Length",14u) == 0u)
tass 51:ab4529a384a6 46 #define isTransferEncoding(line) (memcmp(line,"Transfer-Encoding",17u) == 0u)
tass 51:ab4529a384a6 47 #define isChunked(line) (memcmp(line," chunked",8u) == 0u)
tass 51:ab4529a384a6 48 #define isNotHTTPv1(line) (memcmp(line,"HTTP/1.",7u))
daniele 3:b4047e8a0123 49 #define is_hex_digit(x) ( ('0' <= x && x <= '9') || ('a' <= x && x <= 'f') )
daniele 3:b4047e8a0123 50 #define hex_digit_to_dec(x) ( ('0' <= x && x <= '9') ? x-'0' : ( ('a' <= x && x <= 'f') ? x-'a' + 10 : -1) )
daniele 3:b4047e8a0123 51
daniele 3:b4047e8a0123 52 struct pico_http_client
daniele 3:b4047e8a0123 53 {
tass 51:ab4529a384a6 54 uint16_t connectionID;
tass 51:ab4529a384a6 55 uint8_t state;
tass 51:ab4529a384a6 56 struct pico_socket * sck;
tass 51:ab4529a384a6 57 void (*wakeup)(uint16_t ev, uint16_t conn);
tass 51:ab4529a384a6 58 struct pico_ip4 ip;
tass 51:ab4529a384a6 59 struct pico_http_uri * uriKey;
tass 51:ab4529a384a6 60 struct pico_http_header * header;
daniele 3:b4047e8a0123 61 };
daniele 3:b4047e8a0123 62
daniele 3:b4047e8a0123 63 // HTTP Client internal states
daniele 3:b4047e8a0123 64 #define HTTP_READING_HEADER 0
tass 51:ab4529a384a6 65 #define HTTP_READING_BODY 1
daniele 3:b4047e8a0123 66 #define HTTP_READING_CHUNK_VALUE 2
daniele 3:b4047e8a0123 67 #define HTTP_READING_CHUNK_TRAIL 3
daniele 3:b4047e8a0123 68
daniele 3:b4047e8a0123 69
daniele 3:b4047e8a0123 70 static int compareClients(void * ka, void * kb)
daniele 3:b4047e8a0123 71 {
tass 51:ab4529a384a6 72 return ((struct pico_http_client *)ka)->connectionID - ((struct pico_http_client *)kb)->connectionID;
daniele 3:b4047e8a0123 73 }
daniele 3:b4047e8a0123 74
daniele 3:b4047e8a0123 75 PICO_TREE_DECLARE(pico_client_list,compareClients);
daniele 3:b4047e8a0123 76
daniele 3:b4047e8a0123 77 // Local functions
daniele 3:b4047e8a0123 78 int parseHeaderFromServer(struct pico_http_client * client, struct pico_http_header * header);
daniele 3:b4047e8a0123 79 int readChunkLine(struct pico_http_client * client);
daniele 3:b4047e8a0123 80
daniele 3:b4047e8a0123 81 void tcpCallback(uint16_t ev, struct pico_socket *s)
daniele 3:b4047e8a0123 82 {
daniele 3:b4047e8a0123 83
tass 51:ab4529a384a6 84 struct pico_http_client * client = NULL;
tass 51:ab4529a384a6 85 struct pico_tree_node * index;
daniele 3:b4047e8a0123 86
tass 51:ab4529a384a6 87 // find httpClient
tass 51:ab4529a384a6 88 pico_tree_foreach(index,&pico_client_list)
tass 51:ab4529a384a6 89 {
tass 51:ab4529a384a6 90 if( ((struct pico_http_client *)index->keyValue)->sck == s )
tass 51:ab4529a384a6 91 {
tass 51:ab4529a384a6 92 client = (struct pico_http_client *)index->keyValue;
tass 51:ab4529a384a6 93 break;
tass 51:ab4529a384a6 94 }
tass 51:ab4529a384a6 95 }
daniele 3:b4047e8a0123 96
tass 51:ab4529a384a6 97 if(!client)
tass 51:ab4529a384a6 98 {
tass 51:ab4529a384a6 99 dbg("Client not found...Something went wrong !\n");
tass 51:ab4529a384a6 100 return;
tass 51:ab4529a384a6 101 }
daniele 3:b4047e8a0123 102
tass 51:ab4529a384a6 103 if(ev & PICO_SOCK_EV_CONN)
tass 51:ab4529a384a6 104 client->wakeup(EV_HTTP_CON,client->connectionID);
daniele 3:b4047e8a0123 105
tass 51:ab4529a384a6 106 if(ev & PICO_SOCK_EV_RD)
tass 51:ab4529a384a6 107 {
daniele 3:b4047e8a0123 108
tass 51:ab4529a384a6 109 // read the header, if not read
tass 51:ab4529a384a6 110 if(client->state == HTTP_READING_HEADER)
tass 51:ab4529a384a6 111 {
tass 51:ab4529a384a6 112 // wait for header
tass 51:ab4529a384a6 113 client->header = pico_zalloc(sizeof(struct pico_http_header));
tass 51:ab4529a384a6 114 if(!client->header)
tass 51:ab4529a384a6 115 {
tass 51:ab4529a384a6 116 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 117 return;
tass 51:ab4529a384a6 118 }
daniele 3:b4047e8a0123 119
tass 51:ab4529a384a6 120 // wait for header
tass 51:ab4529a384a6 121 if(parseHeaderFromServer(client,client->header) < 0)
tass 51:ab4529a384a6 122 {
tass 51:ab4529a384a6 123 client->wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 124 }
tass 51:ab4529a384a6 125 else
tass 51:ab4529a384a6 126 {
tass 51:ab4529a384a6 127 // call wakeup
tass 51:ab4529a384a6 128 if(client->header->responseCode != HTTP_CONTINUE)
tass 51:ab4529a384a6 129 {
tass 51:ab4529a384a6 130 client->wakeup(
tass 51:ab4529a384a6 131 client->header->responseCode == HTTP_OK ?
tass 51:ab4529a384a6 132 EV_HTTP_REQ | EV_HTTP_BODY : // data comes for sure only when 200 is received
tass 51:ab4529a384a6 133 EV_HTTP_REQ
tass 51:ab4529a384a6 134 ,client->connectionID);
tass 51:ab4529a384a6 135 }
tass 51:ab4529a384a6 136 }
tass 51:ab4529a384a6 137 }
tass 51:ab4529a384a6 138 else
tass 51:ab4529a384a6 139 {
tass 51:ab4529a384a6 140 // just let the user know that data has arrived, if chunked data comes, will be treated in the
tass 51:ab4529a384a6 141 // read api.
tass 51:ab4529a384a6 142 client->wakeup(EV_HTTP_BODY,client->connectionID);
tass 51:ab4529a384a6 143 }
tass 51:ab4529a384a6 144 }
daniele 3:b4047e8a0123 145
tass 51:ab4529a384a6 146 if(ev & PICO_SOCK_EV_ERR)
tass 51:ab4529a384a6 147 {
tass 51:ab4529a384a6 148 client->wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 149 }
daniele 3:b4047e8a0123 150
tass 51:ab4529a384a6 151 if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) )
tass 51:ab4529a384a6 152 {
tass 51:ab4529a384a6 153 client->wakeup(EV_HTTP_CLOSE,client->connectionID);
tass 51:ab4529a384a6 154 }
daniele 3:b4047e8a0123 155
daniele 3:b4047e8a0123 156 }
daniele 3:b4047e8a0123 157
daniele 3:b4047e8a0123 158 // used for getting a response from DNS servers
daniele 3:b4047e8a0123 159 static void dnsCallback(char *ip, void * ptr)
daniele 3:b4047e8a0123 160 {
tass 51:ab4529a384a6 161 struct pico_http_client * client = (struct pico_http_client *)ptr;
daniele 3:b4047e8a0123 162
tass 51:ab4529a384a6 163 if(!client)
tass 51:ab4529a384a6 164 {
tass 51:ab4529a384a6 165 dbg("Who made the request ?!\n");
tass 51:ab4529a384a6 166 return;
tass 51:ab4529a384a6 167 }
daniele 3:b4047e8a0123 168
tass 51:ab4529a384a6 169 if(ip)
tass 51:ab4529a384a6 170 {
tass 51:ab4529a384a6 171 client->wakeup(EV_HTTP_DNS,client->connectionID);
daniele 3:b4047e8a0123 172
tass 51:ab4529a384a6 173 // add the ip address to the client, and start a tcp connection socket
tass 51:ab4529a384a6 174 pico_string_to_ipv4(ip,&client->ip.addr);
tass 51:ab4529a384a6 175 pico_free(ip);
tass 51:ab4529a384a6 176 client->sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &tcpCallback);
tass 51:ab4529a384a6 177 if(!client->sck)
tass 51:ab4529a384a6 178 {
tass 51:ab4529a384a6 179 client->wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 180 return;
tass 51:ab4529a384a6 181 }
daniele 3:b4047e8a0123 182
tass 51:ab4529a384a6 183 if(pico_socket_connect(client->sck,&client->ip,short_be(client->uriKey->port)) < 0)
tass 51:ab4529a384a6 184 {
tass 51:ab4529a384a6 185 client->wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 186 return;
tass 51:ab4529a384a6 187 }
daniele 3:b4047e8a0123 188
tass 51:ab4529a384a6 189 }
tass 51:ab4529a384a6 190 else
tass 51:ab4529a384a6 191 {
tass 51:ab4529a384a6 192 // wakeup client and let know error occured
tass 51:ab4529a384a6 193 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 194
tass 51:ab4529a384a6 195 // close the client (free used heap)
tass 51:ab4529a384a6 196 pico_http_client_close(client->connectionID);
tass 51:ab4529a384a6 197 }
daniele 3:b4047e8a0123 198 }
daniele 3:b4047e8a0123 199
daniele 3:b4047e8a0123 200 /*
daniele 3:b4047e8a0123 201 * API used for opening a new HTTP Client.
daniele 3:b4047e8a0123 202 *
daniele 3:b4047e8a0123 203 * The accepted uri's are [http://]hostname[:port]/resource
daniele 3:b4047e8a0123 204 * no relative uri's are accepted.
daniele 3:b4047e8a0123 205 *
daniele 3:b4047e8a0123 206 * The function returns a connection ID >= 0 if successful
daniele 3:b4047e8a0123 207 * -1 if an error occured.
daniele 3:b4047e8a0123 208 */
daniele 3:b4047e8a0123 209 int pico_http_client_open(char * uri, void (*wakeup)(uint16_t ev, uint16_t conn))
daniele 3:b4047e8a0123 210 {
tass 51:ab4529a384a6 211 struct pico_http_client * client;
daniele 3:b4047e8a0123 212
tass 51:ab4529a384a6 213 if(!wakeup)
tass 51:ab4529a384a6 214 {
tass 51:ab4529a384a6 215 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 216 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 217 }
daniele 3:b4047e8a0123 218
tass 51:ab4529a384a6 219 client = pico_zalloc(sizeof(struct pico_http_client));
tass 51:ab4529a384a6 220 if(!client)
tass 51:ab4529a384a6 221 {
tass 51:ab4529a384a6 222 // memory error
tass 51:ab4529a384a6 223 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 224 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 225 }
daniele 3:b4047e8a0123 226
tass 51:ab4529a384a6 227 client->wakeup = wakeup;
tass 51:ab4529a384a6 228 client->connectionID = (uint16_t)pico_rand() & 0x7FFFu; // negative values mean error, still not good generation
daniele 3:b4047e8a0123 229
tass 51:ab4529a384a6 230 client->uriKey = pico_zalloc(sizeof(struct pico_http_uri));
daniele 3:b4047e8a0123 231
tass 51:ab4529a384a6 232 if(!client->uriKey)
tass 51:ab4529a384a6 233 {
tass 51:ab4529a384a6 234 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 235 pico_free(client);
tass 51:ab4529a384a6 236 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 237 }
daniele 3:b4047e8a0123 238
tass 51:ab4529a384a6 239 pico_processURI(uri,client->uriKey);
daniele 3:b4047e8a0123 240
tass 51:ab4529a384a6 241 if(pico_tree_insert(&pico_client_list,client))
tass 51:ab4529a384a6 242 {
tass 51:ab4529a384a6 243 // already in
tass 51:ab4529a384a6 244 pico_err = PICO_ERR_EEXIST;
tass 51:ab4529a384a6 245 pico_free(client->uriKey);
tass 51:ab4529a384a6 246 pico_free(client);
tass 51:ab4529a384a6 247 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 248 }
daniele 3:b4047e8a0123 249
tass 51:ab4529a384a6 250 // dns query
tass 51:ab4529a384a6 251 dbg("Querying : %s \n",client->uriKey->host);
tass 51:ab4529a384a6 252 pico_dns_client_getaddr(client->uriKey->host, dnsCallback,client);
daniele 3:b4047e8a0123 253
tass 51:ab4529a384a6 254 // return the connection ID
tass 51:ab4529a384a6 255 return client->connectionID;
daniele 3:b4047e8a0123 256 }
daniele 3:b4047e8a0123 257
daniele 3:b4047e8a0123 258 /*
daniele 3:b4047e8a0123 259 * API for sending a header to the client.
daniele 3:b4047e8a0123 260 *
daniele 3:b4047e8a0123 261 * if hdr == HTTP_HEADER_RAW , then the parameter header
daniele 3:b4047e8a0123 262 * is sent as it is to client.
daniele 3:b4047e8a0123 263 *
daniele 3:b4047e8a0123 264 * if hdr == HTTP_HEADER_DEFAULT, then the parameter header
daniele 3:b4047e8a0123 265 * is ignored and the library will build the response header
daniele 3:b4047e8a0123 266 * based on the uri passed when opening the client.
daniele 3:b4047e8a0123 267 *
daniele 3:b4047e8a0123 268 */
daniele 3:b4047e8a0123 269 int pico_http_client_sendHeader(uint16_t conn, char * header, int hdr)
daniele 3:b4047e8a0123 270 {
tass 51:ab4529a384a6 271 struct pico_http_client search = {.connectionID = conn};
tass 51:ab4529a384a6 272 struct pico_http_client * http = pico_tree_findKey(&pico_client_list,&search);
tass 51:ab4529a384a6 273 int length ;
tass 51:ab4529a384a6 274 if(!http)
tass 51:ab4529a384a6 275 {
tass 51:ab4529a384a6 276 dbg("Client not found !\n");
tass 51:ab4529a384a6 277 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 278 }
daniele 3:b4047e8a0123 279
tass 51:ab4529a384a6 280 // the api gives the possibility to the user to build the GET header
tass 51:ab4529a384a6 281 // based on the uri passed when opening the client, less headache for the user
tass 51:ab4529a384a6 282 if(hdr == HTTP_HEADER_DEFAULT)
tass 51:ab4529a384a6 283 {
tass 51:ab4529a384a6 284 header = pico_http_client_buildHeader(http->uriKey);
daniele 3:b4047e8a0123 285
tass 51:ab4529a384a6 286 if(!header)
tass 51:ab4529a384a6 287 {
tass 51:ab4529a384a6 288 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 289 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 290 }
tass 51:ab4529a384a6 291 }
daniele 3:b4047e8a0123 292
tass 51:ab4529a384a6 293 length = pico_socket_write(http->sck,(void *)header,strlen(header)+1);
daniele 3:b4047e8a0123 294
tass 51:ab4529a384a6 295 if(hdr == HTTP_HEADER_DEFAULT)
tass 51:ab4529a384a6 296 pico_free(header);
daniele 3:b4047e8a0123 297
tass 51:ab4529a384a6 298 return length;
daniele 3:b4047e8a0123 299 }
daniele 3:b4047e8a0123 300
daniele 3:b4047e8a0123 301 /*
daniele 3:b4047e8a0123 302 * API for reading received data.
daniele 3:b4047e8a0123 303 *
daniele 3:b4047e8a0123 304 * This api hides from the user if the transfer-encoding
daniele 3:b4047e8a0123 305 * was chunked or a full length was provided, in case of
daniele 3:b4047e8a0123 306 * a chunked transfer encoding will "de-chunk" the data
daniele 3:b4047e8a0123 307 * and pass it to the user.
daniele 3:b4047e8a0123 308 */
daniele 3:b4047e8a0123 309 int pico_http_client_readData(uint16_t conn, char * data, uint16_t size)
daniele 3:b4047e8a0123 310 {
tass 51:ab4529a384a6 311 struct pico_http_client dummy = {.connectionID = conn};
tass 51:ab4529a384a6 312 struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
daniele 3:b4047e8a0123 313
tass 51:ab4529a384a6 314 if(!client)
tass 51:ab4529a384a6 315 {
tass 51:ab4529a384a6 316 dbg("Wrong connection id !\n");
tass 51:ab4529a384a6 317 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 318 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 319 }
daniele 3:b4047e8a0123 320
tass 51:ab4529a384a6 321 // for the moment just read the data, do not care if it's chunked or not
tass 51:ab4529a384a6 322 if(client->header->transferCoding == HTTP_TRANSFER_FULL)
tass 51:ab4529a384a6 323 return pico_socket_read(client->sck,(void *)data,size);
tass 51:ab4529a384a6 324 else
tass 51:ab4529a384a6 325 {
tass 51:ab4529a384a6 326 int lenRead = 0;
daniele 3:b4047e8a0123 327
tass 51:ab4529a384a6 328 // read the chunk line
tass 51:ab4529a384a6 329 if(readChunkLine(client) == HTTP_RETURN_ERROR)
tass 51:ab4529a384a6 330 {
tass 51:ab4529a384a6 331 dbg("Probably the chunk is malformed or parsed wrong...\n");
tass 51:ab4529a384a6 332 client->wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 333 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 334 }
daniele 3:b4047e8a0123 335
tass 51:ab4529a384a6 336 // nothing to read, no use to try
tass 51:ab4529a384a6 337 if(client->state != HTTP_READING_BODY)
tass 51:ab4529a384a6 338 {
tass 51:ab4529a384a6 339 pico_err = PICO_ERR_EAGAIN;
tass 51:ab4529a384a6 340 return HTTP_RETURN_OK;
tass 51:ab4529a384a6 341 }
daniele 3:b4047e8a0123 342
tass 51:ab4529a384a6 343 // check if we need more than one chunk
tass 51:ab4529a384a6 344 if(size >= client->header->contentLengthOrChunk)
tass 51:ab4529a384a6 345 {
tass 51:ab4529a384a6 346 // read the rest of the chunk, if chunk is done, proceed to the next chunk
tass 51:ab4529a384a6 347 while(lenRead <= size)
tass 51:ab4529a384a6 348 {
tass 51:ab4529a384a6 349 int tmpLenRead = 0;
daniele 3:b4047e8a0123 350
tass 51:ab4529a384a6 351 if(client->state == HTTP_READING_BODY)
tass 51:ab4529a384a6 352 {
daniele 3:b4047e8a0123 353
tass 51:ab4529a384a6 354 // if needed truncate the data
tass 51:ab4529a384a6 355 tmpLenRead = pico_socket_read(client->sck,data + lenRead,
tass 63:97f481e33cb2 356 client->header->contentLengthOrChunk < size-lenRead ? client->header->contentLengthOrChunk : size-lenRead);
daniele 3:b4047e8a0123 357
tass 51:ab4529a384a6 358 if(tmpLenRead > 0)
tass 51:ab4529a384a6 359 {
tass 51:ab4529a384a6 360 client->header->contentLengthOrChunk -= tmpLenRead;
tass 51:ab4529a384a6 361 }
tass 51:ab4529a384a6 362 else if(tmpLenRead < 0)
tass 51:ab4529a384a6 363 {
tass 51:ab4529a384a6 364 // error on reading
tass 51:ab4529a384a6 365 dbg(">>> Error returned pico_socket_read\n");
tass 51:ab4529a384a6 366 pico_err = PICO_ERR_EBUSY;
tass 51:ab4529a384a6 367 // return how much data was read until now
tass 51:ab4529a384a6 368 return lenRead;
tass 51:ab4529a384a6 369 }
tass 51:ab4529a384a6 370 }
daniele 3:b4047e8a0123 371
tass 51:ab4529a384a6 372 lenRead += tmpLenRead;
tass 51:ab4529a384a6 373 if(readChunkLine(client) == HTTP_RETURN_ERROR)
tass 51:ab4529a384a6 374 {
tass 51:ab4529a384a6 375 dbg("Probably the chunk is malformed or parsed wrong...\n");
tass 51:ab4529a384a6 376 client->wakeup(EV_HTTP_ERROR,client->connectionID);
tass 51:ab4529a384a6 377 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 378 }
daniele 3:b4047e8a0123 379
tass 51:ab4529a384a6 380 if(client->state != HTTP_READING_BODY || !tmpLenRead) break;
daniele 3:b4047e8a0123 381
tass 51:ab4529a384a6 382 }
tass 51:ab4529a384a6 383 }
tass 51:ab4529a384a6 384 else
tass 51:ab4529a384a6 385 {
tass 51:ab4529a384a6 386 // read the data from the chunk
tass 51:ab4529a384a6 387 lenRead = pico_socket_read(client->sck,(void *)data,size);
daniele 3:b4047e8a0123 388
tass 51:ab4529a384a6 389 if(lenRead)
tass 51:ab4529a384a6 390 client->header->contentLengthOrChunk -= lenRead;
tass 51:ab4529a384a6 391 }
daniele 3:b4047e8a0123 392
tass 51:ab4529a384a6 393 return lenRead;
tass 51:ab4529a384a6 394 }
daniele 3:b4047e8a0123 395 }
daniele 3:b4047e8a0123 396
daniele 3:b4047e8a0123 397 /*
daniele 3:b4047e8a0123 398 * API for reading received data.
daniele 3:b4047e8a0123 399 *
daniele 3:b4047e8a0123 400 * Reads out the header struct received from server.
daniele 3:b4047e8a0123 401 */
daniele 3:b4047e8a0123 402 struct pico_http_header * pico_http_client_readHeader(uint16_t conn)
daniele 3:b4047e8a0123 403 {
tass 51:ab4529a384a6 404 struct pico_http_client dummy = {.connectionID = conn};
tass 51:ab4529a384a6 405 struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
daniele 3:b4047e8a0123 406
tass 51:ab4529a384a6 407 if(client)
tass 51:ab4529a384a6 408 {
tass 51:ab4529a384a6 409 return client->header;
tass 51:ab4529a384a6 410 }
tass 51:ab4529a384a6 411 else
tass 51:ab4529a384a6 412 {
tass 51:ab4529a384a6 413 // not found
tass 51:ab4529a384a6 414 dbg("Wrong connection id !\n");
tass 51:ab4529a384a6 415 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 416 return NULL;
tass 51:ab4529a384a6 417 }
daniele 3:b4047e8a0123 418 }
daniele 3:b4047e8a0123 419
daniele 3:b4047e8a0123 420 /*
daniele 3:b4047e8a0123 421 * API for reading received data.
daniele 3:b4047e8a0123 422 *
daniele 3:b4047e8a0123 423 * Reads out the uri struct after was processed.
daniele 3:b4047e8a0123 424 */
daniele 3:b4047e8a0123 425 struct pico_http_uri * pico_http_client_readUriData(uint16_t conn)
daniele 3:b4047e8a0123 426 {
tass 51:ab4529a384a6 427 struct pico_http_client dummy = {.connectionID = conn};
tass 51:ab4529a384a6 428 struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
tass 51:ab4529a384a6 429 //
tass 51:ab4529a384a6 430 if(client)
tass 51:ab4529a384a6 431 return client->uriKey;
tass 51:ab4529a384a6 432 else
tass 51:ab4529a384a6 433 {
tass 51:ab4529a384a6 434 // not found
tass 51:ab4529a384a6 435 dbg("Wrong connection id !\n");
tass 51:ab4529a384a6 436 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 437 return NULL;
tass 51:ab4529a384a6 438 }
daniele 3:b4047e8a0123 439 }
daniele 3:b4047e8a0123 440
daniele 3:b4047e8a0123 441 /*
daniele 3:b4047e8a0123 442 * API for reading received data.
daniele 3:b4047e8a0123 443 *
daniele 3:b4047e8a0123 444 * Close the client.
daniele 3:b4047e8a0123 445 */
daniele 3:b4047e8a0123 446 int pico_http_client_close(uint16_t conn)
daniele 3:b4047e8a0123 447 {
tass 51:ab4529a384a6 448 struct pico_http_client * toBeRemoved = NULL;
tass 63:97f481e33cb2 449 struct pico_http_client dummy = {};
tass 51:ab4529a384a6 450 dummy.connectionID = conn;
daniele 3:b4047e8a0123 451
tass 51:ab4529a384a6 452 dbg("Closing the client...\n");
tass 51:ab4529a384a6 453 toBeRemoved = pico_tree_delete(&pico_client_list,&dummy);
tass 51:ab4529a384a6 454 if(!toBeRemoved)
tass 51:ab4529a384a6 455 {
tass 51:ab4529a384a6 456 dbg("Warning ! Element not found ...");
tass 51:ab4529a384a6 457 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 458 }
daniele 3:b4047e8a0123 459
tass 51:ab4529a384a6 460 // close socket
tass 51:ab4529a384a6 461 if(toBeRemoved->sck)
tass 51:ab4529a384a6 462 pico_socket_close(toBeRemoved->sck);
daniele 3:b4047e8a0123 463
daniele 3:b4047e8a0123 464
tass 51:ab4529a384a6 465 if(toBeRemoved->header)
tass 51:ab4529a384a6 466 {
tass 51:ab4529a384a6 467 // free space used
tass 51:ab4529a384a6 468 if(toBeRemoved->header->location)
tass 51:ab4529a384a6 469 pico_free(toBeRemoved->header->location);
daniele 3:b4047e8a0123 470
tass 51:ab4529a384a6 471 pico_free(toBeRemoved->header);
tass 51:ab4529a384a6 472 }
daniele 3:b4047e8a0123 473
tass 51:ab4529a384a6 474 if(toBeRemoved->uriKey)
tass 51:ab4529a384a6 475 {
tass 51:ab4529a384a6 476 if(toBeRemoved->uriKey->host)
tass 51:ab4529a384a6 477 pico_free(toBeRemoved->uriKey->host);
daniele 3:b4047e8a0123 478
tass 51:ab4529a384a6 479 if(toBeRemoved->uriKey->resource)
tass 51:ab4529a384a6 480 pico_free(toBeRemoved->uriKey->resource);
tass 51:ab4529a384a6 481 pico_free(toBeRemoved->uriKey);
tass 51:ab4529a384a6 482 }
tass 51:ab4529a384a6 483 pico_free(toBeRemoved);
daniele 3:b4047e8a0123 484
tass 51:ab4529a384a6 485 return 0;
daniele 3:b4047e8a0123 486 }
daniele 3:b4047e8a0123 487
daniele 3:b4047e8a0123 488 /*
daniele 3:b4047e8a0123 489 * API for reading received data.
daniele 3:b4047e8a0123 490 *
daniele 3:b4047e8a0123 491 * Builds a GET header based on the fields on the uri.
daniele 3:b4047e8a0123 492 */
daniele 3:b4047e8a0123 493 char * pico_http_client_buildHeader(const struct pico_http_uri * uriData)
daniele 3:b4047e8a0123 494 {
tass 51:ab4529a384a6 495 char * header;
tass 51:ab4529a384a6 496 char port[6u]; // 6 = max length of a uint16 + \0
daniele 3:b4047e8a0123 497
tass 51:ab4529a384a6 498 uint16_t headerSize = HTTP_GET_BASIC_SIZE;
daniele 3:b4047e8a0123 499
tass 51:ab4529a384a6 500 if(!uriData->host || !uriData->resource || !uriData->port)
tass 51:ab4529a384a6 501 {
tass 51:ab4529a384a6 502 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 503 return NULL;
tass 51:ab4529a384a6 504 }
daniele 3:b4047e8a0123 505
tass 51:ab4529a384a6 506 //
tass 51:ab4529a384a6 507 headerSize += strlen(uriData->host) + strlen(uriData->resource) + pico_itoa(uriData->port,port) + 4u; // 3 = size(CRLF + \0)
tass 51:ab4529a384a6 508 header = pico_zalloc(headerSize);
daniele 3:b4047e8a0123 509
tass 51:ab4529a384a6 510 if(!header)
tass 51:ab4529a384a6 511 {
tass 51:ab4529a384a6 512 // not enought memory
tass 51:ab4529a384a6 513 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 514 return NULL;
tass 51:ab4529a384a6 515 }
daniele 3:b4047e8a0123 516
tass 51:ab4529a384a6 517 // build the actual header
tass 51:ab4529a384a6 518 strcpy(header,"GET ");
tass 51:ab4529a384a6 519 strcat(header,uriData->resource);
tass 51:ab4529a384a6 520 strcat(header," HTTP/1.1\r\n");
tass 51:ab4529a384a6 521 strcat(header,"Host: ");
tass 51:ab4529a384a6 522 strcat(header,uriData->host);
tass 51:ab4529a384a6 523 strcat(header,":");
tass 51:ab4529a384a6 524 strcat(header,port);
tass 51:ab4529a384a6 525 strcat(header,"\r\n");
tass 51:ab4529a384a6 526 strcat(header,"User-Agent: picoTCP\r\nConnection: close\r\n\r\n"); //?
daniele 3:b4047e8a0123 527
tass 51:ab4529a384a6 528 return header;
daniele 3:b4047e8a0123 529 }
daniele 3:b4047e8a0123 530
daniele 3:b4047e8a0123 531 int parseHeaderFromServer(struct pico_http_client * client, struct pico_http_header * header)
daniele 3:b4047e8a0123 532 {
tass 51:ab4529a384a6 533 char line[HTTP_HEADER_LINE_SIZE];
tass 51:ab4529a384a6 534 char c;
tass 63:97f481e33cb2 535 int index = 0;
daniele 3:b4047e8a0123 536
tass 51:ab4529a384a6 537 // read the first line of the header
tass 51:ab4529a384a6 538 while(consumeChar(c)>0 && c!='\r')
tass 51:ab4529a384a6 539 {
tass 51:ab4529a384a6 540 if(index < HTTP_HEADER_LINE_SIZE) // truncate if too long
tass 51:ab4529a384a6 541 line[index++] = c;
tass 51:ab4529a384a6 542 }
daniele 3:b4047e8a0123 543
tass 51:ab4529a384a6 544 consumeChar(c); // consume \n
daniele 3:b4047e8a0123 545
tass 51:ab4529a384a6 546 // check the integrity of the response
tass 51:ab4529a384a6 547 // make sure we have enough characters to include the response code
tass 51:ab4529a384a6 548 // make sure the server response starts with HTTP/1.
tass 51:ab4529a384a6 549 if(index < RESPONSE_INDEX+2 || isNotHTTPv1(line))
tass 51:ab4529a384a6 550 {
tass 51:ab4529a384a6 551 // wrong format of the the response
tass 51:ab4529a384a6 552 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 553 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 554 }
daniele 3:b4047e8a0123 555
tass 51:ab4529a384a6 556 // extract response code
tass 51:ab4529a384a6 557 header->responseCode = (line[RESPONSE_INDEX] - '0') * 100u +
tass 51:ab4529a384a6 558 (line[RESPONSE_INDEX+1] - '0') * 10u +
tass 51:ab4529a384a6 559 (line[RESPONSE_INDEX+2] - '0');
daniele 3:b4047e8a0123 560
daniele 3:b4047e8a0123 561
tass 51:ab4529a384a6 562 if(header->responseCode/100u > 5u)
tass 51:ab4529a384a6 563 {
tass 51:ab4529a384a6 564 // invalid response type
tass 51:ab4529a384a6 565 header->responseCode = 0;
tass 51:ab4529a384a6 566 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 567 }
daniele 3:b4047e8a0123 568
tass 51:ab4529a384a6 569 dbg("Server response : %d \n",header->responseCode);
daniele 3:b4047e8a0123 570
tass 51:ab4529a384a6 571 // parse the rest of the header
tass 51:ab4529a384a6 572 while(consumeChar(c)>0)
tass 51:ab4529a384a6 573 {
tass 51:ab4529a384a6 574 if(c==':')
tass 51:ab4529a384a6 575 {
tass 51:ab4529a384a6 576 // check for interesting fields
daniele 3:b4047e8a0123 577
tass 51:ab4529a384a6 578 // Location:
tass 51:ab4529a384a6 579 if(isLocation(line))
tass 51:ab4529a384a6 580 {
tass 51:ab4529a384a6 581 index = 0;
tass 51:ab4529a384a6 582 while(consumeChar(c)>0 && c!='\r')
tass 51:ab4529a384a6 583 {
tass 51:ab4529a384a6 584 line[index++] = c;
tass 51:ab4529a384a6 585 }
daniele 3:b4047e8a0123 586
tass 51:ab4529a384a6 587 // allocate space for the field
tass 51:ab4529a384a6 588 header->location = pico_zalloc(index+1u);
tass 51:ab4529a384a6 589 if(!header->location)
tass 51:ab4529a384a6 590 {
tass 51:ab4529a384a6 591 pico_err = PICO_ERR_ENOMEM;
tass 51:ab4529a384a6 592 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 593 }
daniele 3:b4047e8a0123 594
tass 51:ab4529a384a6 595 memcpy(header->location,line,index);
daniele 3:b4047e8a0123 596
tass 51:ab4529a384a6 597 }// Content-Length:
tass 51:ab4529a384a6 598 else if(isContentLength(line))
tass 51:ab4529a384a6 599 {
tass 51:ab4529a384a6 600 header->contentLengthOrChunk = 0u;
tass 51:ab4529a384a6 601 header->transferCoding = HTTP_TRANSFER_FULL;
tass 51:ab4529a384a6 602 // consume the first space
tass 51:ab4529a384a6 603 consumeChar(c);
tass 51:ab4529a384a6 604 while(consumeChar(c)>0 && c!='\r')
tass 51:ab4529a384a6 605 {
tass 51:ab4529a384a6 606 header->contentLengthOrChunk = header->contentLengthOrChunk*10u + (c-'0');
tass 51:ab4529a384a6 607 }
daniele 3:b4047e8a0123 608
tass 51:ab4529a384a6 609 }// Transfer-Encoding: chunked
tass 51:ab4529a384a6 610 else if(isTransferEncoding(line))
tass 51:ab4529a384a6 611 {
tass 51:ab4529a384a6 612 index = 0;
tass 51:ab4529a384a6 613 while(consumeChar(c)>0 && c!='\r')
tass 51:ab4529a384a6 614 {
tass 51:ab4529a384a6 615 line[index++] = c;
tass 51:ab4529a384a6 616 }
daniele 3:b4047e8a0123 617
tass 51:ab4529a384a6 618 if(isChunked(line))
tass 51:ab4529a384a6 619 {
tass 51:ab4529a384a6 620 header->contentLengthOrChunk = 0u;
tass 51:ab4529a384a6 621 header->transferCoding = HTTP_TRANSFER_CHUNKED;
tass 51:ab4529a384a6 622 }
daniele 3:b4047e8a0123 623
tass 51:ab4529a384a6 624 }// just ignore the line
tass 51:ab4529a384a6 625 else
tass 51:ab4529a384a6 626 {
tass 51:ab4529a384a6 627 while(consumeChar(c)>0 && c!='\r');
tass 51:ab4529a384a6 628 }
daniele 3:b4047e8a0123 629
tass 51:ab4529a384a6 630 // consume the next one
tass 51:ab4529a384a6 631 consumeChar(c);
tass 51:ab4529a384a6 632 // reset the index
tass 51:ab4529a384a6 633 index = 0u;
tass 51:ab4529a384a6 634 }
tass 51:ab4529a384a6 635 else if(c=='\r' && !index)
tass 51:ab4529a384a6 636 {
tass 51:ab4529a384a6 637 // consume the \n
tass 51:ab4529a384a6 638 consumeChar(c);
tass 51:ab4529a384a6 639 break;
tass 51:ab4529a384a6 640 }
tass 51:ab4529a384a6 641 else
tass 51:ab4529a384a6 642 {
tass 51:ab4529a384a6 643 line[index++] = c;
tass 51:ab4529a384a6 644 }
tass 51:ab4529a384a6 645 }
daniele 3:b4047e8a0123 646
tass 51:ab4529a384a6 647 if(header->transferCoding == HTTP_TRANSFER_CHUNKED)
tass 51:ab4529a384a6 648 {
tass 51:ab4529a384a6 649 // read the first chunk
tass 51:ab4529a384a6 650 header->contentLengthOrChunk = 0;
daniele 3:b4047e8a0123 651
tass 51:ab4529a384a6 652 client->state = HTTP_READING_CHUNK_VALUE;
tass 51:ab4529a384a6 653 readChunkLine(client);
daniele 3:b4047e8a0123 654
tass 51:ab4529a384a6 655 }
tass 51:ab4529a384a6 656 else
tass 51:ab4529a384a6 657 client->state = HTTP_READING_BODY;
daniele 3:b4047e8a0123 658
tass 51:ab4529a384a6 659 dbg("End of header\n");
tass 51:ab4529a384a6 660 return HTTP_RETURN_OK;
daniele 3:b4047e8a0123 661
daniele 3:b4047e8a0123 662 }
daniele 3:b4047e8a0123 663
daniele 3:b4047e8a0123 664 // an async read of the chunk part, since in theory a chunk can be split in 2 packets
daniele 3:b4047e8a0123 665 int readChunkLine(struct pico_http_client * client)
daniele 3:b4047e8a0123 666 {
tass 51:ab4529a384a6 667 char c = 0;
daniele 3:b4047e8a0123 668
tass 51:ab4529a384a6 669 if(client->header->contentLengthOrChunk==0 && client->state == HTTP_READING_BODY)
tass 51:ab4529a384a6 670 {
tass 51:ab4529a384a6 671 client->state = HTTP_READING_CHUNK_VALUE;
tass 51:ab4529a384a6 672 }
daniele 3:b4047e8a0123 673
tass 51:ab4529a384a6 674 if(client->state == HTTP_READING_CHUNK_VALUE)
tass 51:ab4529a384a6 675 {
tass 51:ab4529a384a6 676 while(consumeChar(c)>0 && c!='\r' && c!=';')
tass 51:ab4529a384a6 677 {
tass 51:ab4529a384a6 678 if(is_hex_digit(c))
tass 51:ab4529a384a6 679 client->header->contentLengthOrChunk = (client->header->contentLengthOrChunk << 4u) + hex_digit_to_dec(c);
tass 51:ab4529a384a6 680 else
tass 51:ab4529a384a6 681 {
tass 51:ab4529a384a6 682 pico_err = PICO_ERR_EINVAL;
tass 51:ab4529a384a6 683 // something went wrong
tass 51:ab4529a384a6 684 return HTTP_RETURN_ERROR;
tass 51:ab4529a384a6 685 }
tass 51:ab4529a384a6 686 }
daniele 3:b4047e8a0123 687
tass 51:ab4529a384a6 688 if(c=='\r' || c==';') client->state = HTTP_READING_CHUNK_TRAIL;
tass 51:ab4529a384a6 689 }
daniele 3:b4047e8a0123 690
tass 51:ab4529a384a6 691 if(client->state == HTTP_READING_CHUNK_TRAIL)
tass 51:ab4529a384a6 692 {
daniele 3:b4047e8a0123 693
tass 51:ab4529a384a6 694 while(consumeChar(c)>0 && c!='\n');
daniele 3:b4047e8a0123 695
tass 51:ab4529a384a6 696 if(c=='\n') client->state = HTTP_READING_BODY;
tass 51:ab4529a384a6 697 }
daniele 3:b4047e8a0123 698
tass 51:ab4529a384a6 699 return HTTP_RETURN_OK;
daniele 3:b4047e8a0123 700 }
daniele 3:b4047e8a0123 701 #endif