CDC/ECM driver for mbed, based on USBDevice by mbed-official. Uses PicoTCP to access Ethernet USB device. License: GPLv2

Dependents:   USBEthernet_TEST

Fork of USB_Ethernet by Daniele Lacamera

Committer:
daniele
Date:
Sat Aug 03 13:16:14 2013 +0000
Revision:
2:540f6e142d59
Moved to single package

Who changed what in which revision?

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