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 #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
daniele 3:b4047e8a0123 34 #define RESPONSE_INDEX 9u
daniele 3:b4047e8a0123 35
daniele 3:b4047e8a0123 36 #define HTTP_CHUNK_ERROR 0xFFFFFFFFu
daniele 3:b4047e8a0123 37
daniele 3:b4047e8a0123 38 #ifdef dbg
daniele 3:b4047e8a0123 39 #undef dbg
daniele 3:b4047e8a0123 40 #define dbg(...) do{}while(0);
daniele 3:b4047e8a0123 41 #endif
daniele 3:b4047e8a0123 42
daniele 3:b4047e8a0123 43 #define consumeChar(c) (pico_socket_read(client->sck,&c,1u))
daniele 3:b4047e8a0123 44 #define isLocation(line) (memcmp(line,"Location",8u) == 0)
daniele 3:b4047e8a0123 45 #define isContentLength(line) (memcmp(line,"Content-Length",14u) == 0u)
daniele 3:b4047e8a0123 46 #define isTransferEncoding(line) (memcmp(line,"Transfer-Encoding",17u) == 0u)
daniele 3:b4047e8a0123 47 #define isChunked(line) (memcmp(line," chunked",8u) == 0u)
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 54 uint16_t connectionID;
daniele 3:b4047e8a0123 55 uint8_t state;
daniele 3:b4047e8a0123 56 struct pico_socket * sck;
daniele 3:b4047e8a0123 57 void (*wakeup)(uint16_t ev, uint16_t conn);
daniele 3:b4047e8a0123 58 struct pico_ip4 ip;
daniele 3:b4047e8a0123 59 struct pico_http_uri * uriKey;
daniele 3:b4047e8a0123 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
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 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
daniele 3:b4047e8a0123 84 struct pico_http_client * client = NULL;
daniele 3:b4047e8a0123 85 struct pico_tree_node * index;
daniele 3:b4047e8a0123 86
daniele 3:b4047e8a0123 87 // find httpClient
daniele 3:b4047e8a0123 88 pico_tree_foreach(index,&pico_client_list)
daniele 3:b4047e8a0123 89 {
daniele 3:b4047e8a0123 90 if( ((struct pico_http_client *)index->keyValue)->sck == s )
daniele 3:b4047e8a0123 91 {
daniele 3:b4047e8a0123 92 client = (struct pico_http_client *)index->keyValue;
daniele 3:b4047e8a0123 93 break;
daniele 3:b4047e8a0123 94 }
daniele 3:b4047e8a0123 95 }
daniele 3:b4047e8a0123 96
daniele 3:b4047e8a0123 97 if(!client)
daniele 3:b4047e8a0123 98 {
daniele 3:b4047e8a0123 99 dbg("Client not found...Something went wrong !\n");
daniele 3:b4047e8a0123 100 return;
daniele 3:b4047e8a0123 101 }
daniele 3:b4047e8a0123 102
daniele 3:b4047e8a0123 103 if(ev & PICO_SOCK_EV_CONN)
daniele 3:b4047e8a0123 104 client->wakeup(EV_HTTP_CON,client->connectionID);
daniele 3:b4047e8a0123 105
daniele 3:b4047e8a0123 106 if(ev & PICO_SOCK_EV_RD)
daniele 3:b4047e8a0123 107 {
daniele 3:b4047e8a0123 108
daniele 3:b4047e8a0123 109 // read the header, if not read
daniele 3:b4047e8a0123 110 if(client->state == HTTP_READING_HEADER)
daniele 3:b4047e8a0123 111 {
daniele 3:b4047e8a0123 112 // wait for header
daniele 3:b4047e8a0123 113 client->header = pico_zalloc(sizeof(struct pico_http_header));
daniele 3:b4047e8a0123 114 if(!client->header)
daniele 3:b4047e8a0123 115 {
daniele 3:b4047e8a0123 116 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 117 return;
daniele 3:b4047e8a0123 118 }
daniele 3:b4047e8a0123 119
daniele 3:b4047e8a0123 120 // wait for header
daniele 3:b4047e8a0123 121 if(parseHeaderFromServer(client,client->header) < 0)
daniele 3:b4047e8a0123 122 {
daniele 3:b4047e8a0123 123 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 124 }
daniele 3:b4047e8a0123 125 else
daniele 3:b4047e8a0123 126 {
daniele 3:b4047e8a0123 127 // call wakeup
daniele 3:b4047e8a0123 128 if(client->header->responseCode != HTTP_CONTINUE)
daniele 3:b4047e8a0123 129 {
daniele 3:b4047e8a0123 130 client->wakeup(
daniele 3:b4047e8a0123 131 client->header->responseCode == HTTP_OK ?
daniele 3:b4047e8a0123 132 EV_HTTP_REQ | EV_HTTP_BODY : // data comes for sure only when 200 is received
daniele 3:b4047e8a0123 133 EV_HTTP_REQ
daniele 3:b4047e8a0123 134 ,client->connectionID);
daniele 3:b4047e8a0123 135 }
daniele 3:b4047e8a0123 136 }
daniele 3:b4047e8a0123 137 }
daniele 3:b4047e8a0123 138 else
daniele 3:b4047e8a0123 139 {
daniele 3:b4047e8a0123 140 // just let the user know that data has arrived, if chunked data comes, will be treated in the
daniele 3:b4047e8a0123 141 // read api.
daniele 3:b4047e8a0123 142 client->wakeup(EV_HTTP_BODY,client->connectionID);
daniele 3:b4047e8a0123 143 }
daniele 3:b4047e8a0123 144 }
daniele 3:b4047e8a0123 145
daniele 3:b4047e8a0123 146 if(ev & PICO_SOCK_EV_ERR)
daniele 3:b4047e8a0123 147 {
daniele 3:b4047e8a0123 148 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 149 }
daniele 3:b4047e8a0123 150
daniele 3:b4047e8a0123 151 if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) )
daniele 3:b4047e8a0123 152 {
daniele 3:b4047e8a0123 153 client->wakeup(EV_HTTP_CLOSE,client->connectionID);
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 161 struct pico_http_client * client = (struct pico_http_client *)ptr;
daniele 3:b4047e8a0123 162
daniele 3:b4047e8a0123 163 if(!client)
daniele 3:b4047e8a0123 164 {
daniele 3:b4047e8a0123 165 dbg("Who made the request ?!\n");
daniele 3:b4047e8a0123 166 return;
daniele 3:b4047e8a0123 167 }
daniele 3:b4047e8a0123 168
daniele 3:b4047e8a0123 169 if(ip)
daniele 3:b4047e8a0123 170 {
daniele 3:b4047e8a0123 171 client->wakeup(EV_HTTP_DNS,client->connectionID);
daniele 3:b4047e8a0123 172
daniele 3:b4047e8a0123 173 // add the ip address to the client, and start a tcp connection socket
daniele 3:b4047e8a0123 174 pico_string_to_ipv4(ip,&client->ip.addr);
daniele 3:b4047e8a0123 175 pico_free(ip);
daniele 3:b4047e8a0123 176 client->sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &tcpCallback);
daniele 3:b4047e8a0123 177 if(!client->sck)
daniele 3:b4047e8a0123 178 {
daniele 3:b4047e8a0123 179 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 180 return;
daniele 3:b4047e8a0123 181 }
daniele 3:b4047e8a0123 182
daniele 3:b4047e8a0123 183 if(pico_socket_connect(client->sck,&client->ip,short_be(client->uriKey->port)) < 0)
daniele 3:b4047e8a0123 184 {
daniele 3:b4047e8a0123 185 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 186 return;
daniele 3:b4047e8a0123 187 }
daniele 3:b4047e8a0123 188
daniele 3:b4047e8a0123 189 }
daniele 3:b4047e8a0123 190 else
daniele 3:b4047e8a0123 191 {
daniele 3:b4047e8a0123 192 // wakeup client and let know error occured
daniele 3:b4047e8a0123 193 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 194
daniele 3:b4047e8a0123 195 // close the client (free used heap)
daniele 3:b4047e8a0123 196 pico_http_client_close(client->connectionID);
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 211 struct pico_http_client * client;
daniele 3:b4047e8a0123 212
daniele 3:b4047e8a0123 213 if(!wakeup)
daniele 3:b4047e8a0123 214 {
daniele 3:b4047e8a0123 215 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 216 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 217 }
daniele 3:b4047e8a0123 218
daniele 3:b4047e8a0123 219 client = pico_zalloc(sizeof(struct pico_http_client));
daniele 3:b4047e8a0123 220 if(!client)
daniele 3:b4047e8a0123 221 {
daniele 3:b4047e8a0123 222 // memory error
daniele 3:b4047e8a0123 223 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 224 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 225 }
daniele 3:b4047e8a0123 226
daniele 3:b4047e8a0123 227 client->wakeup = wakeup;
daniele 3:b4047e8a0123 228 client->connectionID = (uint16_t)pico_rand() & 0x7FFFu; // negative values mean error, still not good generation
daniele 3:b4047e8a0123 229
daniele 3:b4047e8a0123 230 client->uriKey = pico_zalloc(sizeof(struct pico_http_uri));
daniele 3:b4047e8a0123 231
daniele 3:b4047e8a0123 232 if(!client->uriKey)
daniele 3:b4047e8a0123 233 {
daniele 3:b4047e8a0123 234 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 235 pico_free(client);
daniele 3:b4047e8a0123 236 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 237 }
daniele 3:b4047e8a0123 238
daniele 3:b4047e8a0123 239 pico_processURI(uri,client->uriKey);
daniele 3:b4047e8a0123 240
daniele 3:b4047e8a0123 241 if(pico_tree_insert(&pico_client_list,client))
daniele 3:b4047e8a0123 242 {
daniele 3:b4047e8a0123 243 // already in
daniele 3:b4047e8a0123 244 pico_err = PICO_ERR_EEXIST;
daniele 3:b4047e8a0123 245 pico_free(client->uriKey);
daniele 3:b4047e8a0123 246 pico_free(client);
daniele 3:b4047e8a0123 247 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 248 }
daniele 3:b4047e8a0123 249
daniele 3:b4047e8a0123 250 // dns query
daniele 3:b4047e8a0123 251 dbg("Querying : %s \n",client->uriKey->host);
daniele 3:b4047e8a0123 252 pico_dns_client_getaddr(client->uriKey->host, dnsCallback,client);
daniele 3:b4047e8a0123 253
daniele 3:b4047e8a0123 254 // return the connection ID
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 271 struct pico_http_client search = {.connectionID = conn};
daniele 3:b4047e8a0123 272 struct pico_http_client * http = pico_tree_findKey(&pico_client_list,&search);
daniele 3:b4047e8a0123 273 int length ;
daniele 3:b4047e8a0123 274 if(!http)
daniele 3:b4047e8a0123 275 {
daniele 3:b4047e8a0123 276 dbg("Client not found !\n");
daniele 3:b4047e8a0123 277 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 278 }
daniele 3:b4047e8a0123 279
daniele 3:b4047e8a0123 280 // the api gives the possibility to the user to build the GET header
daniele 3:b4047e8a0123 281 // based on the uri passed when opening the client, less headache for the user
daniele 3:b4047e8a0123 282 if(hdr == HTTP_HEADER_DEFAULT)
daniele 3:b4047e8a0123 283 {
daniele 3:b4047e8a0123 284 header = pico_http_client_buildHeader(http->uriKey);
daniele 3:b4047e8a0123 285
daniele 3:b4047e8a0123 286 if(!header)
daniele 3:b4047e8a0123 287 {
daniele 3:b4047e8a0123 288 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 289 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 290 }
daniele 3:b4047e8a0123 291 }
daniele 3:b4047e8a0123 292
daniele 3:b4047e8a0123 293 length = pico_socket_write(http->sck,(void *)header,strlen(header)+1);
daniele 3:b4047e8a0123 294
daniele 3:b4047e8a0123 295 if(hdr == HTTP_HEADER_DEFAULT)
daniele 3:b4047e8a0123 296 pico_free(header);
daniele 3:b4047e8a0123 297
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 311 struct pico_http_client dummy = {.connectionID = conn};
daniele 3:b4047e8a0123 312 struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
daniele 3:b4047e8a0123 313
daniele 3:b4047e8a0123 314 if(!client)
daniele 3:b4047e8a0123 315 {
daniele 3:b4047e8a0123 316 dbg("Wrong connection id !\n");
daniele 3:b4047e8a0123 317 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 318 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 319 }
daniele 3:b4047e8a0123 320
daniele 3:b4047e8a0123 321 // for the moment just read the data, do not care if it's chunked or not
daniele 3:b4047e8a0123 322 if(client->header->transferCoding == HTTP_TRANSFER_FULL)
daniele 3:b4047e8a0123 323 return pico_socket_read(client->sck,(void *)data,size);
daniele 3:b4047e8a0123 324 else
daniele 3:b4047e8a0123 325 {
daniele 3:b4047e8a0123 326 int lenRead = 0;
daniele 3:b4047e8a0123 327
daniele 3:b4047e8a0123 328 // read the chunk line
daniele 3:b4047e8a0123 329 if(readChunkLine(client) == HTTP_RETURN_ERROR)
daniele 3:b4047e8a0123 330 {
daniele 3:b4047e8a0123 331 dbg("Probably the chunk is malformed or parsed wrong...\n");
daniele 3:b4047e8a0123 332 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 333 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 334 }
daniele 3:b4047e8a0123 335
daniele 3:b4047e8a0123 336 // nothing to read, no use to try
daniele 3:b4047e8a0123 337 if(client->state != HTTP_READING_BODY)
daniele 3:b4047e8a0123 338 {
daniele 3:b4047e8a0123 339 pico_err = PICO_ERR_EAGAIN;
daniele 3:b4047e8a0123 340 return HTTP_RETURN_OK;
daniele 3:b4047e8a0123 341 }
daniele 3:b4047e8a0123 342
daniele 3:b4047e8a0123 343 // check if we need more than one chunk
daniele 3:b4047e8a0123 344 if(size >= client->header->contentLengthOrChunk)
daniele 3:b4047e8a0123 345 {
daniele 3:b4047e8a0123 346 // read the rest of the chunk, if chunk is done, proceed to the next chunk
daniele 3:b4047e8a0123 347 while(lenRead <= size)
daniele 3:b4047e8a0123 348 {
daniele 3:b4047e8a0123 349 int tmpLenRead = 0;
daniele 3:b4047e8a0123 350
daniele 3:b4047e8a0123 351 if(client->state == HTTP_READING_BODY)
daniele 3:b4047e8a0123 352 {
daniele 3:b4047e8a0123 353
daniele 3:b4047e8a0123 354 // if needed truncate the data
daniele 3:b4047e8a0123 355 tmpLenRead = pico_socket_read(client->sck,data + lenRead,
daniele 3:b4047e8a0123 356 client->header->contentLengthOrChunk < size-lenRead ? client->header->contentLengthOrChunk : size-lenRead);
daniele 3:b4047e8a0123 357
daniele 3:b4047e8a0123 358 if(tmpLenRead > 0)
daniele 3:b4047e8a0123 359 {
daniele 3:b4047e8a0123 360 client->header->contentLengthOrChunk -= tmpLenRead;
daniele 3:b4047e8a0123 361 }
daniele 3:b4047e8a0123 362 else if(tmpLenRead < 0)
daniele 3:b4047e8a0123 363 {
daniele 3:b4047e8a0123 364 // error on reading
daniele 3:b4047e8a0123 365 dbg(">>> Error returned pico_socket_read\n");
daniele 3:b4047e8a0123 366 pico_err = PICO_ERR_EBUSY;
daniele 3:b4047e8a0123 367 // return how much data was read until now
daniele 3:b4047e8a0123 368 return lenRead;
daniele 3:b4047e8a0123 369 }
daniele 3:b4047e8a0123 370 }
daniele 3:b4047e8a0123 371
daniele 3:b4047e8a0123 372 lenRead += tmpLenRead;
daniele 3:b4047e8a0123 373 if(readChunkLine(client) == HTTP_RETURN_ERROR)
daniele 3:b4047e8a0123 374 {
daniele 3:b4047e8a0123 375 dbg("Probably the chunk is malformed or parsed wrong...\n");
daniele 3:b4047e8a0123 376 client->wakeup(EV_HTTP_ERROR,client->connectionID);
daniele 3:b4047e8a0123 377 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 378 }
daniele 3:b4047e8a0123 379
daniele 3:b4047e8a0123 380 if(client->state != HTTP_READING_BODY || !tmpLenRead) break;
daniele 3:b4047e8a0123 381
daniele 3:b4047e8a0123 382 }
daniele 3:b4047e8a0123 383 }
daniele 3:b4047e8a0123 384 else
daniele 3:b4047e8a0123 385 {
daniele 3:b4047e8a0123 386 // read the data from the chunk
daniele 3:b4047e8a0123 387 lenRead = pico_socket_read(client->sck,(void *)data,size);
daniele 3:b4047e8a0123 388
daniele 3:b4047e8a0123 389 if(lenRead)
daniele 3:b4047e8a0123 390 client->header->contentLengthOrChunk -= lenRead;
daniele 3:b4047e8a0123 391 }
daniele 3:b4047e8a0123 392
daniele 3:b4047e8a0123 393 return lenRead;
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 404 struct pico_http_client dummy = {.connectionID = conn};
daniele 3:b4047e8a0123 405 struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
daniele 3:b4047e8a0123 406
daniele 3:b4047e8a0123 407 if(client)
daniele 3:b4047e8a0123 408 {
daniele 3:b4047e8a0123 409 return client->header;
daniele 3:b4047e8a0123 410 }
daniele 3:b4047e8a0123 411 else
daniele 3:b4047e8a0123 412 {
daniele 3:b4047e8a0123 413 // not found
daniele 3:b4047e8a0123 414 dbg("Wrong connection id !\n");
daniele 3:b4047e8a0123 415 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 416 return NULL;
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 427 struct pico_http_client dummy = {.connectionID = conn};
daniele 3:b4047e8a0123 428 struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
daniele 3:b4047e8a0123 429 //
daniele 3:b4047e8a0123 430 if(client)
daniele 3:b4047e8a0123 431 return client->uriKey;
daniele 3:b4047e8a0123 432 else
daniele 3:b4047e8a0123 433 {
daniele 3:b4047e8a0123 434 // not found
daniele 3:b4047e8a0123 435 dbg("Wrong connection id !\n");
daniele 3:b4047e8a0123 436 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 437 return NULL;
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 448 struct pico_http_client * toBeRemoved = NULL;
daniele 3:b4047e8a0123 449 struct pico_http_client dummy = {};
daniele 3:b4047e8a0123 450 dummy.connectionID = conn;
daniele 3:b4047e8a0123 451
daniele 3:b4047e8a0123 452 dbg("Closing the client...\n");
daniele 3:b4047e8a0123 453 toBeRemoved = pico_tree_delete(&pico_client_list,&dummy);
daniele 3:b4047e8a0123 454 if(!toBeRemoved)
daniele 3:b4047e8a0123 455 {
daniele 3:b4047e8a0123 456 dbg("Warning ! Element not found ...");
daniele 3:b4047e8a0123 457 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 458 }
daniele 3:b4047e8a0123 459
daniele 3:b4047e8a0123 460 // close socket
daniele 3:b4047e8a0123 461 if(toBeRemoved->sck)
daniele 3:b4047e8a0123 462 pico_socket_close(toBeRemoved->sck);
daniele 3:b4047e8a0123 463
daniele 3:b4047e8a0123 464
daniele 3:b4047e8a0123 465 if(toBeRemoved->header)
daniele 3:b4047e8a0123 466 {
daniele 3:b4047e8a0123 467 // free space used
daniele 3:b4047e8a0123 468 if(toBeRemoved->header->location)
daniele 3:b4047e8a0123 469 pico_free(toBeRemoved->header->location);
daniele 3:b4047e8a0123 470
daniele 3:b4047e8a0123 471 pico_free(toBeRemoved->header);
daniele 3:b4047e8a0123 472 }
daniele 3:b4047e8a0123 473
daniele 3:b4047e8a0123 474 if(toBeRemoved->uriKey)
daniele 3:b4047e8a0123 475 {
daniele 3:b4047e8a0123 476 if(toBeRemoved->uriKey->host)
daniele 3:b4047e8a0123 477 pico_free(toBeRemoved->uriKey->host);
daniele 3:b4047e8a0123 478
daniele 3:b4047e8a0123 479 if(toBeRemoved->uriKey->resource)
daniele 3:b4047e8a0123 480 pico_free(toBeRemoved->uriKey->resource);
daniele 3:b4047e8a0123 481 pico_free(toBeRemoved->uriKey);
daniele 3:b4047e8a0123 482 }
daniele 3:b4047e8a0123 483 pico_free(toBeRemoved);
daniele 3:b4047e8a0123 484
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 495 char * header;
daniele 3:b4047e8a0123 496 char port[6u]; // 6 = max length of a uint16 + \0
daniele 3:b4047e8a0123 497
daniele 3:b4047e8a0123 498 uint16_t headerSize = HTTP_GET_BASIC_SIZE;
daniele 3:b4047e8a0123 499
daniele 3:b4047e8a0123 500 if(!uriData->host || !uriData->resource || !uriData->port)
daniele 3:b4047e8a0123 501 {
daniele 3:b4047e8a0123 502 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 503 return NULL;
daniele 3:b4047e8a0123 504 }
daniele 3:b4047e8a0123 505
daniele 3:b4047e8a0123 506 //
daniele 3:b4047e8a0123 507 headerSize += strlen(uriData->host) + strlen(uriData->resource) + pico_itoa(uriData->port,port) + 4u; // 3 = size(CRLF + \0)
daniele 3:b4047e8a0123 508 header = pico_zalloc(headerSize);
daniele 3:b4047e8a0123 509
daniele 3:b4047e8a0123 510 if(!header)
daniele 3:b4047e8a0123 511 {
daniele 3:b4047e8a0123 512 // not enought memory
daniele 3:b4047e8a0123 513 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 514 return NULL;
daniele 3:b4047e8a0123 515 }
daniele 3:b4047e8a0123 516
daniele 3:b4047e8a0123 517 // build the actual header
daniele 3:b4047e8a0123 518 strcpy(header,"GET ");
daniele 3:b4047e8a0123 519 strcat(header,uriData->resource);
daniele 3:b4047e8a0123 520 strcat(header," HTTP/1.1\r\n");
daniele 3:b4047e8a0123 521 strcat(header,"Host: ");
daniele 3:b4047e8a0123 522 strcat(header,uriData->host);
daniele 3:b4047e8a0123 523 strcat(header,":");
daniele 3:b4047e8a0123 524 strcat(header,port);
daniele 3:b4047e8a0123 525 strcat(header,"\r\n");
daniele 3:b4047e8a0123 526 strcat(header,"User-Agent: picoTCP\r\nConnection: close\r\n\r\n"); //?
daniele 3:b4047e8a0123 527
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 533 char line[HTTP_HEADER_LINE_SIZE];
daniele 3:b4047e8a0123 534 char c;
daniele 3:b4047e8a0123 535 int index = 0;
daniele 3:b4047e8a0123 536
daniele 3:b4047e8a0123 537 // read the first line of the header
daniele 3:b4047e8a0123 538 while(consumeChar(c)>0 && c!='\r')
daniele 3:b4047e8a0123 539 {
daniele 3:b4047e8a0123 540 if(index < HTTP_HEADER_LINE_SIZE) // truncate if too long
daniele 3:b4047e8a0123 541 line[index++] = c;
daniele 3:b4047e8a0123 542 }
daniele 3:b4047e8a0123 543
daniele 3:b4047e8a0123 544 consumeChar(c); // consume \n
daniele 3:b4047e8a0123 545
daniele 3:b4047e8a0123 546 // check the integrity of the response
daniele 3:b4047e8a0123 547 // make sure we have enough characters to include the response code
daniele 3:b4047e8a0123 548 // make sure the server response starts with HTTP/1.
daniele 3:b4047e8a0123 549 if(index < RESPONSE_INDEX+2 || isNotHTTPv1(line))
daniele 3:b4047e8a0123 550 {
daniele 3:b4047e8a0123 551 // wrong format of the the response
daniele 3:b4047e8a0123 552 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 553 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 554 }
daniele 3:b4047e8a0123 555
daniele 3:b4047e8a0123 556 // extract response code
daniele 3:b4047e8a0123 557 header->responseCode = (line[RESPONSE_INDEX] - '0') * 100u +
daniele 3:b4047e8a0123 558 (line[RESPONSE_INDEX+1] - '0') * 10u +
daniele 3:b4047e8a0123 559 (line[RESPONSE_INDEX+2] - '0');
daniele 3:b4047e8a0123 560
daniele 3:b4047e8a0123 561
daniele 3:b4047e8a0123 562 if(header->responseCode/100u > 5u)
daniele 3:b4047e8a0123 563 {
daniele 3:b4047e8a0123 564 // invalid response type
daniele 3:b4047e8a0123 565 header->responseCode = 0;
daniele 3:b4047e8a0123 566 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 567 }
daniele 3:b4047e8a0123 568
daniele 3:b4047e8a0123 569 dbg("Server response : %d \n",header->responseCode);
daniele 3:b4047e8a0123 570
daniele 3:b4047e8a0123 571 // parse the rest of the header
daniele 3:b4047e8a0123 572 while(consumeChar(c)>0)
daniele 3:b4047e8a0123 573 {
daniele 3:b4047e8a0123 574 if(c==':')
daniele 3:b4047e8a0123 575 {
daniele 3:b4047e8a0123 576 // check for interesting fields
daniele 3:b4047e8a0123 577
daniele 3:b4047e8a0123 578 // Location:
daniele 3:b4047e8a0123 579 if(isLocation(line))
daniele 3:b4047e8a0123 580 {
daniele 3:b4047e8a0123 581 index = 0;
daniele 3:b4047e8a0123 582 while(consumeChar(c)>0 && c!='\r')
daniele 3:b4047e8a0123 583 {
daniele 3:b4047e8a0123 584 line[index++] = c;
daniele 3:b4047e8a0123 585 }
daniele 3:b4047e8a0123 586
daniele 3:b4047e8a0123 587 // allocate space for the field
daniele 3:b4047e8a0123 588 header->location = pico_zalloc(index+1u);
daniele 3:b4047e8a0123 589 if(!header->location)
daniele 3:b4047e8a0123 590 {
daniele 3:b4047e8a0123 591 pico_err = PICO_ERR_ENOMEM;
daniele 3:b4047e8a0123 592 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 593 }
daniele 3:b4047e8a0123 594
daniele 3:b4047e8a0123 595 memcpy(header->location,line,index);
daniele 3:b4047e8a0123 596
daniele 3:b4047e8a0123 597 }// Content-Length:
daniele 3:b4047e8a0123 598 else if(isContentLength(line))
daniele 3:b4047e8a0123 599 {
daniele 3:b4047e8a0123 600 header->contentLengthOrChunk = 0u;
daniele 3:b4047e8a0123 601 header->transferCoding = HTTP_TRANSFER_FULL;
daniele 3:b4047e8a0123 602 // consume the first space
daniele 3:b4047e8a0123 603 consumeChar(c);
daniele 3:b4047e8a0123 604 while(consumeChar(c)>0 && c!='\r')
daniele 3:b4047e8a0123 605 {
daniele 3:b4047e8a0123 606 header->contentLengthOrChunk = header->contentLengthOrChunk*10u + (c-'0');
daniele 3:b4047e8a0123 607 }
daniele 3:b4047e8a0123 608
daniele 3:b4047e8a0123 609 }// Transfer-Encoding: chunked
daniele 3:b4047e8a0123 610 else if(isTransferEncoding(line))
daniele 3:b4047e8a0123 611 {
daniele 3:b4047e8a0123 612 index = 0;
daniele 3:b4047e8a0123 613 while(consumeChar(c)>0 && c!='\r')
daniele 3:b4047e8a0123 614 {
daniele 3:b4047e8a0123 615 line[index++] = c;
daniele 3:b4047e8a0123 616 }
daniele 3:b4047e8a0123 617
daniele 3:b4047e8a0123 618 if(isChunked(line))
daniele 3:b4047e8a0123 619 {
daniele 3:b4047e8a0123 620 header->contentLengthOrChunk = 0u;
daniele 3:b4047e8a0123 621 header->transferCoding = HTTP_TRANSFER_CHUNKED;
daniele 3:b4047e8a0123 622 }
daniele 3:b4047e8a0123 623
daniele 3:b4047e8a0123 624 }// just ignore the line
daniele 3:b4047e8a0123 625 else
daniele 3:b4047e8a0123 626 {
daniele 3:b4047e8a0123 627 while(consumeChar(c)>0 && c!='\r');
daniele 3:b4047e8a0123 628 }
daniele 3:b4047e8a0123 629
daniele 3:b4047e8a0123 630 // consume the next one
daniele 3:b4047e8a0123 631 consumeChar(c);
daniele 3:b4047e8a0123 632 // reset the index
daniele 3:b4047e8a0123 633 index = 0u;
daniele 3:b4047e8a0123 634 }
daniele 3:b4047e8a0123 635 else if(c=='\r' && !index)
daniele 3:b4047e8a0123 636 {
daniele 3:b4047e8a0123 637 // consume the \n
daniele 3:b4047e8a0123 638 consumeChar(c);
daniele 3:b4047e8a0123 639 break;
daniele 3:b4047e8a0123 640 }
daniele 3:b4047e8a0123 641 else
daniele 3:b4047e8a0123 642 {
daniele 3:b4047e8a0123 643 line[index++] = c;
daniele 3:b4047e8a0123 644 }
daniele 3:b4047e8a0123 645 }
daniele 3:b4047e8a0123 646
daniele 3:b4047e8a0123 647 if(header->transferCoding == HTTP_TRANSFER_CHUNKED)
daniele 3:b4047e8a0123 648 {
daniele 3:b4047e8a0123 649 // read the first chunk
daniele 3:b4047e8a0123 650 header->contentLengthOrChunk = 0;
daniele 3:b4047e8a0123 651
daniele 3:b4047e8a0123 652 client->state = HTTP_READING_CHUNK_VALUE;
daniele 3:b4047e8a0123 653 readChunkLine(client);
daniele 3:b4047e8a0123 654
daniele 3:b4047e8a0123 655 }
daniele 3:b4047e8a0123 656 else
daniele 3:b4047e8a0123 657 client->state = HTTP_READING_BODY;
daniele 3:b4047e8a0123 658
daniele 3:b4047e8a0123 659 dbg("End of header\n");
daniele 3:b4047e8a0123 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 {
daniele 3:b4047e8a0123 667 char c = 0;
daniele 3:b4047e8a0123 668
daniele 3:b4047e8a0123 669 if(client->header->contentLengthOrChunk==0 && client->state == HTTP_READING_BODY)
daniele 3:b4047e8a0123 670 {
daniele 3:b4047e8a0123 671 client->state = HTTP_READING_CHUNK_VALUE;
daniele 3:b4047e8a0123 672 }
daniele 3:b4047e8a0123 673
daniele 3:b4047e8a0123 674 if(client->state == HTTP_READING_CHUNK_VALUE)
daniele 3:b4047e8a0123 675 {
daniele 3:b4047e8a0123 676 while(consumeChar(c)>0 && c!='\r' && c!=';')
daniele 3:b4047e8a0123 677 {
daniele 3:b4047e8a0123 678 if(is_hex_digit(c))
daniele 3:b4047e8a0123 679 client->header->contentLengthOrChunk = (client->header->contentLengthOrChunk << 4u) + hex_digit_to_dec(c);
daniele 3:b4047e8a0123 680 else
daniele 3:b4047e8a0123 681 {
daniele 3:b4047e8a0123 682 pico_err = PICO_ERR_EINVAL;
daniele 3:b4047e8a0123 683 // something went wrong
daniele 3:b4047e8a0123 684 return HTTP_RETURN_ERROR;
daniele 3:b4047e8a0123 685 }
daniele 3:b4047e8a0123 686 }
daniele 3:b4047e8a0123 687
daniele 3:b4047e8a0123 688 if(c=='\r' || c==';') client->state = HTTP_READING_CHUNK_TRAIL;
daniele 3:b4047e8a0123 689 }
daniele 3:b4047e8a0123 690
daniele 3:b4047e8a0123 691 if(client->state == HTTP_READING_CHUNK_TRAIL)
daniele 3:b4047e8a0123 692 {
daniele 3:b4047e8a0123 693
daniele 3:b4047e8a0123 694 while(consumeChar(c)>0 && c!='\n');
daniele 3:b4047e8a0123 695
daniele 3:b4047e8a0123 696 if(c=='\n') client->state = HTTP_READING_BODY;
daniele 3:b4047e8a0123 697 }
daniele 3:b4047e8a0123 698
daniele 3:b4047e8a0123 699 return HTTP_RETURN_OK;
daniele 3:b4047e8a0123 700 }
daniele 3:b4047e8a0123 701 #endif