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:
Fri Oct 18 09:41:50 2013 +0000
Revision:
101:37763e3777a7
Parent:
73:dfb737147f6e
Child:
123:dd26752a4538
merge with mainline Issue #39

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