Free (GPLv2) TCP/IP stack developed by TASS Belgium
Fork of PicoTCP by
modules/pico_http_client.c@3:b4047e8a0123, 2013-05-24 (annotated)
- Committer:
- daniele
- Date:
- Fri May 24 15:25:25 2013 +0000
- Revision:
- 3:b4047e8a0123
Updated from main repo + fixed Mutexes;
Who changed what in which revision?
User | Revision | Line number | New 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 |