Free (GPLv2) TCP/IP stack developed by TASS Belgium

Fork of PicoTCP by Daniele Lacamera

Committer:
tass
Date:
Fri May 17 12:09:59 2013 +0000
Revision:
1:cfe8984a32b4
Parent:
libraries/picotcp/modules/pico_http_client.c@0:d7f2341ab245
Update for smaller SOCKETQ

Who changed what in which revision?

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