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:
Wed Nov 27 10:20:00 2013 +0000
Revision:
123:dd26752a4538
Parent:
73:dfb737147f6e
Child:
125:96003ae6f1d8
Update from masterbranch + created stack dummy macros.

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