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

Fork of PicoTCP by Daniele Lacamera

Committer:
daniele
Date:
Sat Aug 03 08:50:27 2013 +0000
Revision:
51:18637a3d071f
Parent:
3:b4047e8a0123
Branch for CDC-ECM: Work in progress

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