1 /*
2  * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3  */
4 
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 
9 #include "httpclient.h"
10 #include "http_wrappers.h"
11 #include "http_form_data.h"
12 #include "http_opts.h"
13 
14 /* base64 encode */
httpclient_base64enc(char * out,const char * in)15 static void httpclient_base64enc(char *out, const char *in)
16 {
17     const char code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ;
18     int i = 0, x = 0, l = 0;
19 
20     for (; *in; in++) {
21         x = x << 8 | *in;
22         for (l += 8; l >= 6; l -= 6) {
23             out[i++] = code[(x >> (l - 6)) & 0x3f];
24         }
25     }
26     if (l > 0) {
27         x <<= 6 - l;
28         out[i++] = code[x & 0x3f];
29     }
30     for (; i % 4;) {
31         out[i++] = '=';
32     }
33     out[i] = '\0' ;
34 }
35 
36 /* parse url according to http[s]://host[:port][/[path]] */
httpclient_parse_url(const char * url,char * scheme,size_t max_scheme_len,char * host,size_t maxhost_len,int * port,char * path,size_t max_path_len)37 static int httpclient_parse_url(const char *url, char *scheme, size_t max_scheme_len, char *host, size_t maxhost_len, int *port, char *path, size_t max_path_len)
38 {
39     char *scheme_ptr = (char *) url;
40     char *host_ptr = NULL;
41     size_t host_len = 0;
42     size_t path_len;
43     char *port_ptr;
44     char *path_ptr;
45     char *fragment_ptr;
46 
47     if (url == NULL) {
48         http_err("Could not find url");
49         return HTTP_EPARSE;
50     }
51 
52     host_ptr = (char *) strstr(url, "://");
53 
54     if (host_ptr == NULL) {
55         http_err("Could not find host");
56         return HTTP_EPARSE; /* URL is invalid */
57     }
58 
59     if ( max_scheme_len < host_ptr - scheme_ptr + 1 ) { /* including NULL-terminating char */
60         http_err("Scheme str is too small (%d >= %d)", max_scheme_len, host_ptr - scheme_ptr + 1);
61         return HTTP_EPARSE;
62     }
63     memcpy(scheme, scheme_ptr, host_ptr - scheme_ptr);
64     scheme[host_ptr - scheme_ptr] = '\0';
65 
66     host_ptr += 3;
67 
68     port_ptr = strchr(host_ptr, ':');
69     if ( port_ptr != NULL ) {
70         uint16_t tport;
71         host_len = port_ptr - host_ptr;
72         port_ptr++;
73         if ( sscanf(port_ptr, "%hu", &tport) != 1) {
74             http_err("Could not find port");
75             return HTTP_EPARSE;
76         }
77         *port = (int)tport;
78     } else {
79         *port = 0;
80     }
81     path_ptr = strchr(host_ptr, '/');
82     if(path_ptr == NULL) {
83         http_err("Could not find '/'");
84         return HTTP_EPARSE;
85     }
86     if ( host_len == 0 ) {
87         host_len = path_ptr - host_ptr;
88     }
89 
90     if ( maxhost_len < host_len + 1 ) { /* including NULL-terminating char */
91         http_err("Host str is too small (%d >= %d)", maxhost_len, host_len + 1);
92         return HTTP_EPARSE;
93     }
94     memcpy(host, host_ptr, host_len);
95     host[host_len] = '\0';
96 
97     fragment_ptr = strchr(host_ptr, '#');
98     if (fragment_ptr != NULL) {
99         path_len = fragment_ptr - path_ptr;
100     } else {
101         path_len = strlen(path_ptr);
102     }
103 
104     if ( max_path_len < path_len + 1 ) { /* including NULL-terminating char */
105         http_err("Path str is too small (%d >= %d)", max_path_len, path_len + 1);
106         return HTTP_EPARSE;
107     }
108     memcpy(path, path_ptr, path_len);
109     path[path_len] = '\0';
110 
111     return HTTP_SUCCESS;
112 }
113 
httpclient_get_info(httpclient_t * client,char * send_buf,int * send_idx,char * buf,size_t len)114 static int httpclient_get_info(httpclient_t *client, char *send_buf, int *send_idx, char *buf, size_t len)   /* 0 on success, err code on failure */
115 {
116     int ret ;
117     int cp_len ;
118     int idx = *send_idx;
119 
120     if (len == 0) {
121         len = strlen(buf);
122     }
123 
124     do {
125         if ((HTTPCLIENT_SEND_BUF_SIZE - idx) >= len) {
126             cp_len = len ;
127         } else {
128             cp_len = HTTPCLIENT_SEND_BUF_SIZE - idx ;
129         }
130 
131         memcpy(send_buf + idx, buf, cp_len) ;
132         idx += cp_len ;
133         len -= cp_len ;
134 
135         if (idx == HTTPCLIENT_SEND_BUF_SIZE) {
136             if (client->is_http == false) {
137                 http_err("send buffer overflow");
138                 return HTTP_EUNKOWN;
139             }
140             ret = http_tcp_send_wrapper(client, send_buf, HTTPCLIENT_SEND_BUF_SIZE) ;
141             if (ret) {
142                 return (ret) ;
143             }
144         }
145     } while (len) ;
146 
147     *send_idx = idx;
148     return HTTP_SUCCESS;
149 }
150 
httpclient_set_custom_header(httpclient_t * client,char * header)151 void httpclient_set_custom_header(httpclient_t *client, char *header)
152 {
153     client->header = header ;
154 }
155 
httpclient_basic_auth(httpclient_t * client,char * user,char * password)156 int httpclient_basic_auth(httpclient_t *client, char *user, char *password)
157 {
158     if ((strlen(user) + strlen(password)) >= HTTPCLIENT_AUTHB_SIZE) {
159         return HTTP_EUNKOWN;
160     }
161     client->auth_user = user;
162     client->auth_password = password;
163     return HTTP_SUCCESS;
164 }
165 
httpclient_send_auth(httpclient_t * client,char * send_buf,int * send_idx)166 static int httpclient_send_auth(httpclient_t *client, char *send_buf, int *send_idx)
167 {
168     char b_auth[(int)((HTTPCLIENT_AUTHB_SIZE + 3) * 4 / 3 + 3)] ;
169     char base64buff[HTTPCLIENT_AUTHB_SIZE + 3] ;
170 
171     httpclient_get_info(client, send_buf, send_idx, "Authorization: Basic ", 0) ;
172     snprintf(base64buff, sizeof(base64buff), "%s:%s", client->auth_user, client->auth_password) ;
173     http_debug("bAuth: %s", base64buff) ;
174     httpclient_base64enc(b_auth, base64buff) ;
175     b_auth[strlen(b_auth) + 2] = '\0' ;
176     b_auth[strlen(b_auth) + 1] = '\n' ;
177     b_auth[strlen(b_auth)] = '\r' ;
178     http_debug("b_auth:%s", b_auth) ;
179     httpclient_get_info(client, send_buf, send_idx, b_auth, 0) ;
180     return HTTP_SUCCESS;
181 }
182 
183 static const char *boundary = "----WebKitFormBoundarypNjgoVtFRlzPquKE";
184 /* send request header */
httpclient_send_header(httpclient_t * client,const char * url,int method,httpclient_data_t * client_data)185 static int httpclient_send_header(httpclient_t *client, const char *url, int method, httpclient_data_t *client_data)
186 {
187     char scheme[8] = {0};
188     char *host = NULL;
189     char *path = NULL;
190     int host_size = HTTPCLIENT_MAX_HOST_LEN;
191     int path_size = HTTPCLIENT_MAX_URL_LEN;
192     int len, formdata_len;
193     int total_len = 0;
194     char *send_buf = NULL;
195     char *buf = NULL;
196     int send_buf_size = HTTPCLIENT_SEND_BUF_SIZE;
197     int buf_size = HTTPCLIENT_SEND_BUF_SIZE;
198     char *meth = (method == HTTP_GET) ? "GET" : (method == HTTP_POST) ? "POST" : (method == HTTP_PUT) ? "PUT" : (method == HTTP_DELETE) ? "DELETE" : (method == HTTP_HEAD) ? "HEAD" : "";
199     int ret, port;
200 
201     host = (char *) malloc(host_size);
202     if (!host) {
203         http_err("host malloc failed");
204         ret = HTTP_ENOBUFS;
205         goto exit;
206     }
207     memset(host, 0, host_size);
208 
209     path = (char *) malloc(path_size);
210     if (!path) {
211         http_err("path malloc failed");
212         ret = HTTP_ENOBUFS;
213         goto exit;
214     }
215     memset(path, 0, path_size);
216 
217     send_buf = (char *) malloc(send_buf_size);
218     if (!send_buf) {
219         http_err("send_buf malloc failed");
220         ret = HTTP_ENOBUFS;
221         goto exit;
222     }
223     memset(send_buf, 0, send_buf_size);
224 
225     buf = (char *) malloc(buf_size);
226     if (!buf) {
227         http_err("buf malloc failed");
228         ret = HTTP_ENOBUFS;
229         goto exit;
230     }
231     memset(buf, 0, buf_size);
232 
233     /* First we need to parse the url (http[s]://host[:port][/[path]]) */
234     int res = httpclient_parse_url(url, scheme, sizeof(scheme), host, host_size, &(port), path, path_size);
235     if (res != HTTP_SUCCESS) {
236         http_err("httpclient_parse_url returned %d", res);
237         ret = res;
238         goto exit;
239     }
240 
241     /* Send request */
242     len = 0 ; /* Reset send buffer */
243 
244     snprintf(buf, buf_size, "%s %s HTTP/1.1\r\nUser-Agent: AliOS-HTTP-Client/2.1\r\nCache-Control: no-cache\r\nConnection: close\r\nHost: %s\r\n", meth, path, host); /* Write request */
245     ret = httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
246     if (ret) {
247         http_err("Could not write request");
248         ret = HTTP_ECONN;
249         goto exit;
250     }
251 
252     /* Send all headers */
253     if (client->auth_user) {
254         httpclient_send_auth(client, send_buf, &len) ; /* send out Basic Auth header */
255     }
256 
257     /* Add user header information */
258     if (client->header) {
259         httpclient_get_info(client, send_buf, &len, (char *)client->header, strlen(client->header));
260     }
261 
262     if ((formdata_len = httpclient_formdata_len(client_data)) > 0) {
263         total_len += formdata_len;
264 
265         memset(buf, 0, buf_size);
266         snprintf(buf, buf_size, "Accept: */*\r\n");
267         httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
268 
269         if (client_data->post_content_type != NULL)  {
270             memset(buf, 0, buf_size);
271             snprintf(buf, buf_size, "Content-Type: %s\r\n", client_data->post_content_type);
272             httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
273         } else {
274             memset(buf, 0, buf_size);
275             snprintf(buf, buf_size, "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);
276             httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
277         }
278 
279         total_len += strlen(boundary) + 8;
280         snprintf(buf, buf_size, "Content-Length: %d\r\n", total_len);
281         httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
282     } else if ( client_data->post_buf != NULL ) {
283         snprintf(buf, buf_size, "Content-Length: %d\r\n", client_data->post_buf_len);
284         httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
285 
286         if (client_data->post_content_type != NULL)  {
287             snprintf(buf, buf_size, "Content-Type: %s\r\n", client_data->post_content_type);
288             httpclient_get_info(client, send_buf, &len, buf, strlen(buf));
289         }
290     } else {
291         http_debug("Do nothing");
292     }
293 
294     /* Close headers */
295     httpclient_get_info(client, send_buf, &len, "\r\n", 0);
296 
297     http_debug("Trying to write %d bytes http header:%s", len, send_buf);
298 
299 #if CONFIG_HTTP_SECURE
300     if (client->is_http == false) {
301         if (http_ssl_send_wrapper(client, send_buf, len) != len) {
302             http_err("SSL_write failed");
303             ret = HTTP_EUNKOWN;
304             goto exit;
305         }
306 
307         ret = HTTP_SUCCESS;
308         goto exit;
309     }
310 #endif
311 
312     ret = http_tcp_send_wrapper(client, send_buf, len);
313     if (ret > 0) {
314         http_debug("Written %d bytes, socket = %d", ret, client->socket);
315     } else if ( ret == 0 ) {
316         http_err("ret == 0,Connection was closed by server");
317         ret = HTTP_ECLSD;
318         goto exit; /* Connection was closed by server */
319     } else {
320         http_err("Connection error (send returned %d)", ret);
321         ret = HTTP_ECONN;
322         goto exit;
323     }
324 
325     ret = HTTP_SUCCESS;
326 
327 exit:
328     if (host) {
329         free(host);
330         host = NULL;
331     }
332 
333     if (path) {
334         free(path);
335         path = NULL;
336     }
337 
338     if (send_buf) {
339         free(send_buf);
340         send_buf = NULL;
341     }
342 
343     if (buf) {
344         free(buf);
345         buf = NULL;
346     }
347 
348     return ret;
349 }
350 
httpclient_send_userdata(httpclient_t * client,httpclient_data_t * client_data)351 static int httpclient_send_userdata(httpclient_t *client, httpclient_data_t *client_data)
352 {
353     int ret = 0;
354 
355     if (client_data->post_buf && client_data->post_buf_len) {
356         http_debug("client_data->post_buf:%s", client_data->post_buf);
357 #if CONFIG_HTTP_SECURE
358         if (client->is_http == false) {
359             if (http_ssl_send_wrapper(client, client_data->post_buf, client_data->post_buf_len) != client_data->post_buf_len) {
360                 http_err("SSL_write failed");
361                 return HTTP_EUNKOWN;
362             }
363         } else
364 #endif
365         {
366             ret = http_tcp_send_wrapper(client, client_data->post_buf, client_data->post_buf_len);
367             if (ret > 0) {
368                 http_debug("Written %d bytes", ret);
369             } else if ( ret == 0 ) {
370                 http_debug("ret == 0,Connection was closed by server");
371                 return HTTP_ECLSD; /* Connection was closed by server */
372             } else {
373                 http_err("Connection error (send returned %d)", ret);
374                 return HTTP_ECONN;
375             }
376         }
377     } else if(httpclient_send_formdata(client, client_data) < 0) {
378         return HTTP_ECONN;
379     }
380 
381     return HTTP_SUCCESS;
382 }
383 
httpclient_recv_data(httpclient_t * client,char * buf,int min_len,int max_len,int * p_read_len)384 static int httpclient_recv_data(httpclient_t *client, char *buf, int min_len, int max_len, int *p_read_len)
385 {
386     int ret = 0;
387     int timeout_ms = 5000;
388 
389     if (client->is_http) {
390         ret = http_tcp_recv_wrapper(client, buf, max_len, timeout_ms, p_read_len);
391     }
392 #if CONFIG_HTTP_SECURE
393     else {
394         ret = http_ssl_recv_wrapper(client, buf, max_len, timeout_ms, p_read_len);
395 
396     }
397 #endif
398 
399     return ret;
400 }
401 
httpclient_retrieve_content(httpclient_t * client,char * data,int len,httpclient_data_t * client_data)402 static int httpclient_retrieve_content(httpclient_t *client, char *data, int len, httpclient_data_t *client_data)
403 {
404     int count = 0;
405     int templen = 0;
406     int crlf_pos;
407     client_data->is_more = true;
408 
409     if (client_data->response_content_len == -1 && client_data->is_chunked == false) {
410         while(true)
411         {
412             int ret, max_len;
413             if (count + len < client_data->response_buf_len - 1) {
414                 memcpy(client_data->response_buf + count, data, len);
415                 count += len;
416                 client_data->response_buf[count] = '\0';
417             } else {
418                 memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count);
419                 client_data->response_buf[client_data->response_buf_len - 1] = '\0';
420                 client_data->content_block_len = client_data->response_buf_len - 1;
421                 return HTTP_EAGAIN;
422             }
423 
424             max_len = MIN(HTTPCLIENT_CHUNK_SIZE - 1, client_data->response_buf_len - 1 - count);
425             if (max_len <= 0) {
426                 http_err("%s %d error max_len %d", __func__, __LINE__, max_len);
427                 return HTTP_EUNKOWN;
428             }
429             ret = httpclient_recv_data(client, data, 1, max_len, &len);
430 
431             /* Receive data */
432             http_debug("data len: %d %d", len, count);
433 
434             if (ret == HTTP_ECONN) {
435                 http_debug("ret == HTTP_ECONN");
436                 client_data->content_block_len = count;
437                 return ret;
438             }
439 
440             if (len == 0) {/* read no more data */
441                 http_debug("no more len == 0");
442                 client_data->is_more = false;
443                 return HTTP_SUCCESS;
444             }
445             http_debug("in loop %s %d ret %d len %d", __func__, __LINE__, ret, len);
446         }
447     }
448 
449     while (true) {
450         size_t readLen = 0;
451 
452         if ( client_data->is_chunked && client_data->retrieve_len <= 0) {
453             /* Read chunk header */
454             bool foundCrlf;
455             int n;
456             do {
457                 int ret = -1;
458                 foundCrlf = false;
459                 crlf_pos = 0;
460                 data[len] = 0;
461                 if (len >= 2) {
462                     for (; crlf_pos < len - 2; crlf_pos++) {
463                         if ( data[crlf_pos] == '\r' && data[crlf_pos + 1] == '\n' ) {
464                             foundCrlf = true;
465                             break;
466                         }
467                     }
468                 }
469                 if (!foundCrlf) { /* Try to read more */
470                     if ( len < HTTPCLIENT_CHUNK_SIZE ) {
471                         int new_trf_len;
472                         int max_recv = MIN(client_data->response_buf_len, HTTPCLIENT_CHUNK_SIZE);
473                         if (max_recv - len - 1 <= 0) {
474                             http_err("%s %d error max_len %d", __func__, __LINE__, max_recv - len - 1);
475                             return HTTP_EUNKOWN;
476                         }
477                         ret = httpclient_recv_data(client, data + len, 0,  max_recv - len - 1 , &new_trf_len);
478                         len += new_trf_len;
479                         if ((ret == HTTP_ECONN) || (ret == HTTP_ECLSD && new_trf_len == 0)) {
480                             return ret;
481                         } else {
482                             http_debug("in loop %s %d ret %d len %d", __func__, __LINE__, ret, len);
483                             continue;
484                         }
485                     } else {
486                         return HTTP_EUNKOWN;
487                     }
488                 }
489                 http_debug("in loop %s %d len %d ret %d", __func__, __LINE__, len, ret);
490             } while (!foundCrlf);
491             data[crlf_pos] = '\0';
492             n = sscanf(data, "%x", &readLen);/* chunk length */
493             client_data->retrieve_len = readLen;
494             client_data->response_content_len += client_data->retrieve_len;
495             if (n != 1) {
496                 http_err("Could not read chunk length");
497                 return HTTP_EPROTO;
498             }
499 
500             memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2)); /* Not need to move NULL-terminating char any more */
501             len -= (crlf_pos + 2);
502 
503             if ( readLen == 0 ) {
504                /* Last chunk */
505                 client_data->is_more = false;
506                 http_debug("no more (last chunk)");
507                 break;
508             }
509         } else {
510             readLen = client_data->retrieve_len;
511         }
512 
513         http_debug("Retrieving %d bytes, len:%d", readLen, len);
514 
515         do {
516             int ret;
517             http_debug("readLen %d, len:%d", readLen, len);
518             templen = MIN(len, readLen);
519             if (count + templen < client_data->response_buf_len - 1) {
520                 memcpy(client_data->response_buf + count, data, templen);
521                 count += templen;
522                 client_data->response_buf[count] = '\0';
523                 client_data->retrieve_len -= templen;
524             } else {
525                 memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count);
526                 client_data->response_buf[client_data->response_buf_len - 1] = '\0';
527                 client_data->retrieve_len -= (client_data->response_buf_len - 1 - count);
528                 client_data->content_block_len = client_data->response_buf_len - 1;
529                 return HTTP_EAGAIN;
530             }
531 
532             if ( len >= readLen ) {
533                 http_debug("memmove %d %d %d", readLen, len, client_data->retrieve_len);
534                 memmove(data, &data[readLen], len - readLen); /* chunk case, read between two chunks */
535                 len -= readLen;
536                 readLen = 0;
537                 client_data->retrieve_len = 0;
538             } else {
539                 readLen -= len;
540             }
541 
542             if (readLen) {
543                 int max_len = MIN(MIN(HTTPCLIENT_CHUNK_SIZE - 1, client_data->response_buf_len - 1 - count), readLen);
544                 if (max_len <= 0) {
545                     http_err("%s %d error max_len %d", __func__, __LINE__, max_len);
546                     return HTTP_EUNKOWN;
547                 }
548 
549                 ret = httpclient_recv_data(client, data, 1, max_len, &len);
550                 if (ret == HTTP_ECONN || (ret == HTTP_ECLSD && len == 0)) {
551                     return ret;
552                 }
553             }
554         } while (readLen);
555 
556         if ( client_data->is_chunked ) {
557             if (len < 2) {
558                 int new_trf_len = 0, ret;
559                 int max_recv = MIN(client_data->response_buf_len - 1 - count + 2, HTTPCLIENT_CHUNK_SIZE - len - 1);
560                 if (max_recv <= 0) {
561                     http_err("%s %d error max_len %d", __func__, __LINE__, max_recv);
562                     return HTTP_EUNKOWN;
563                 }
564 
565                 /* Read missing chars to find end of chunk */
566                 ret = httpclient_recv_data(client, data + len, 2 - len, max_recv, &new_trf_len);
567                 if ((ret == HTTP_ECONN) || (ret == HTTP_ECLSD && new_trf_len == 0)) {
568                     return ret;
569                 }
570                 len += new_trf_len;
571             }
572             if ( (data[0] != '\r') || (data[1] != '\n') ) {
573                 http_err("Format error, %s", data); /* after memmove, the beginning of next chunk */
574                 return HTTP_EPROTO;
575             }
576             memmove(data, &data[2], len - 2); /* remove the \r\n */
577             len -= 2;
578         } else {
579             http_err("no more(content-length)");
580             client_data->is_more = false;
581             break;
582         }
583 
584     }
585     client_data->content_block_len = count;
586 
587     return HTTP_SUCCESS;
588 }
589 
httpclient_response_parse(httpclient_t * client,char * data,int len,httpclient_data_t * client_data)590 static int httpclient_response_parse(httpclient_t *client, char *data, int len, httpclient_data_t *client_data)
591 {
592     int crlf_pos;
593     int header_buf_len = client_data->header_buf_len;
594     char *header_buf = client_data->header_buf;
595     int read_result;
596 
597     // reset the header buffer
598     if (header_buf) {
599         memset(header_buf, 0, header_buf_len);
600     }
601 
602     client_data->response_content_len = -1;
603 
604     char *crlf_ptr = strstr(data, "\r\n");
605     if (crlf_ptr == NULL) {
606         http_err("\r\n not found");
607         return HTTP_EPROTO;
608     }
609 
610     crlf_pos = crlf_ptr - data;
611     data[crlf_pos] = '\0';
612 
613     /* Parse HTTP response */
614     if ( sscanf(data, "HTTP/%*d.%*d %d %*[^\r\n]", &(client->response_code)) != 1 ) {
615         /* Cannot match string, error */
616         http_err("Not a correct HTTP answer : %s", data);
617         return HTTP_EPROTO;
618     }
619 
620     if ( (client->response_code < 200) || (client->response_code >= 400) ) {
621         /* Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers */
622         http_debug("Response code %d", client->response_code);
623 
624         if (client->response_code == 416) {
625             http_err("Requested Range Not Satisfiable");
626             return HTTP_EUNKOWN;
627         }
628     }
629 
630     memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1); /* Be sure to move NULL-terminating char as well */
631     len -= (crlf_pos + 2);
632 
633     client_data->is_chunked = false;
634 
635     /* Now get headers */
636     while ( true ) {
637         char *colon_ptr, *key_ptr, *value_ptr;
638         int key_len, value_len;
639 
640         crlf_ptr = strstr(data, "\r\n");
641         if (crlf_ptr == NULL) {
642             if ( len < HTTPCLIENT_CHUNK_SIZE - 1 ) {
643                 int new_trf_len = 0;
644                 if (HTTPCLIENT_CHUNK_SIZE - len - 1 <= 0) {
645                     http_err("%s %d error max_len %d", __func__, __LINE__, HTTPCLIENT_CHUNK_SIZE - len - 1);
646                     return HTTP_EUNKOWN;
647                 }
648                 read_result = httpclient_recv_data(client, data + len, 1, HTTPCLIENT_CHUNK_SIZE - len - 1, &new_trf_len);
649                 len += new_trf_len;
650                 data[len] = '\0';
651                 http_debug("Read %d chars; In buf: [%s]", new_trf_len, data);
652                 if ((read_result == HTTP_ECONN) || (read_result == HTTP_ECLSD && new_trf_len == 0)) {
653                     return read_result;
654                 } else {
655                     http_debug("in loop %s %d ret %d len %d", __func__, __LINE__, read_result, len);
656                     continue;
657                 }
658             } else {
659                 http_err("header len > chunksize");
660                 return HTTP_EUNKOWN;
661             }
662         }
663 
664         crlf_pos = crlf_ptr - data;
665         if (crlf_pos == 0) { /* End of headers */
666             memmove(data, &data[2], len - 2 + 1); /* Be sure to move NULL-terminating char as well */
667             len -= 2;
668             break;
669         }
670 
671         colon_ptr = strstr(data, ": ");
672         if (colon_ptr) {
673             if (header_buf_len >= crlf_pos + 2 && header_buf) {
674                 /* copy response header to caller buffer */
675                 memcpy(header_buf, data, crlf_pos + 2);
676                 header_buf += crlf_pos + 2;
677                 header_buf_len -= crlf_pos + 2;
678             }
679 
680             key_len = colon_ptr - data;
681             value_len = crlf_ptr - colon_ptr - strlen(": ");
682             key_ptr = data;
683             value_ptr = colon_ptr + strlen(": ");
684 
685             http_debug("Read header : %.*s: %.*s", key_len, key_ptr, value_len, value_ptr);
686             if (0 == strncasecmp(key_ptr, "Content-Length", key_len)) {
687                 sscanf(value_ptr, "%d[^\r]", &(client_data->response_content_len));
688                 client_data->retrieve_len = client_data->response_content_len;
689             } else if (0 == strncasecmp(key_ptr, "Transfer-Encoding", key_len)) {
690                 if (0 == strncasecmp(value_ptr, "Chunked", value_len)) {
691                     client_data->is_chunked = true;
692                     client_data->response_content_len = 0;
693                     client_data->retrieve_len = 0;
694                 }
695             } else if ((client->response_code >= 300 && client->response_code < 400) && (0 == strncasecmp(key_ptr, "Location", key_len))) {
696 
697                 if ( HTTPCLIENT_MAX_URL_LEN < value_len + 1 ) {
698                     http_err("url is too large (%d >= %d)", value_len + 1, HTTPCLIENT_MAX_URL_LEN);
699                     return HTTP_EUNKOWN;
700                 }
701 
702                 if(client_data->redirect_url == NULL) {
703                     client_data->redirect_url = (char* )malloc(HTTPCLIENT_MAX_URL_LEN);
704                 }
705 
706                 memset(client_data->redirect_url, 0, HTTPCLIENT_MAX_URL_LEN);
707                 memcpy(client_data->redirect_url, value_ptr, value_len);
708                 client_data->is_redirected = 1;
709            }
710 
711             memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1); /* Be sure to move NULL-terminating char as well */
712             len -= (crlf_pos + 2);
713         } else {
714             http_err("Could not parse header");
715             return HTTP_EUNKOWN;
716         }
717     }
718 
719     return httpclient_retrieve_content(client, data, len, client_data);
720 }
721 
722 
httpclient_conn(httpclient_t * client,const char * url)723 HTTPC_RESULT httpclient_conn(httpclient_t *client, const char *url)
724 {
725     int ret = HTTP_ECONN;
726     char *host = NULL;
727     char scheme[8] = {0};
728     char *path = NULL;
729     int host_size = HTTPCLIENT_MAX_HOST_LEN;
730     int path_size = HTTPCLIENT_MAX_URL_LEN;
731 
732     host = (char *) malloc(host_size);
733     if (!host) {
734         http_err("host malloc failed");
735         ret = HTTP_ENOBUFS;
736         goto exit;
737     }
738     memset(host, 0, host_size);
739 
740     path = (char *) malloc(path_size);
741     if (!path) {
742         http_err("path malloc failed");
743         ret = HTTP_ENOBUFS;
744         goto exit;
745     }
746     memset(path, 0, path_size);
747 
748     /* First we need to parse the url (http[s]://host[:port][/[path]]) */
749     int res = httpclient_parse_url(url, scheme, sizeof(scheme), host, host_size, &(client->remote_port), path, path_size);
750     if (res != HTTP_SUCCESS) {
751         http_err("httpclient_parse_url returned %d", res);
752         ret = res;
753         goto exit;
754     }
755 
756     // http or https
757     if (strcmp(scheme, "https") == 0) {
758         client->is_http = false;
759     }
760     else if (strcmp(scheme, "http") == 0)
761     {
762         client->is_http = true;
763     }
764 
765     // default http 80 port, https 443 port
766     if (client->remote_port == 0) {
767         if (client->is_http) {
768             client->remote_port = HTTP_PORT;
769         } else {
770             client->remote_port = HTTPS_PORT;
771         }
772     }
773 
774     client->socket = -1;
775     if (client->is_http) {
776         ret = http_tcp_conn_wrapper(client, host);
777     }
778 #if CONFIG_HTTP_SECURE
779     else {
780         ret = http_ssl_conn_wrapper(client, host);
781     }
782 #endif
783 
784 
785 exit:
786     if (host) {
787         free(host);
788         host = NULL;
789     }
790 
791     if (path) {
792         free(path);
793         path = NULL;
794     }
795 
796     http_debug("httpclient_conn() result:%d, client:%p", ret, client);
797     return (HTTPC_RESULT)ret;
798 }
799 
httpclient_send(httpclient_t * client,const char * url,int method,httpclient_data_t * client_data)800 HTTPC_RESULT httpclient_send(httpclient_t *client, const char *url, int method, httpclient_data_t *client_data)
801 {
802     int ret = HTTP_ECONN;
803 
804     if (client->socket < 0) {
805         return (HTTPC_RESULT)ret;
806     }
807 
808     ret = httpclient_send_header(client, url, method, client_data);
809     if (ret != 0) {
810         return (HTTPC_RESULT)ret;
811     }
812 
813     if (method == HTTP_POST || method == HTTP_PUT) {
814         ret = httpclient_send_userdata(client, client_data);
815     }
816 
817     http_debug("httpclient_send() result:%d, client:%p", ret, client);
818     return (HTTPC_RESULT)ret;
819 }
820 
821 
httpclient_recv(httpclient_t * client,httpclient_data_t * client_data)822 HTTPC_RESULT httpclient_recv(httpclient_t *client, httpclient_data_t *client_data)
823 {
824     int reclen = 0;
825     int ret = HTTP_ECONN;
826     // TODO: header format:  name + value must not bigger than HTTPCLIENT_CHUNK_SIZE.
827     char *buf = NULL;
828 
829     if (client_data->header_buf_len < HTTPCLIENT_CHUNK_SIZE ||
830         client_data->response_buf_len < HTTPCLIENT_CHUNK_SIZE) {
831         http_err("Error: header buffer or response buffer should not less than %d!", HTTPCLIENT_CHUNK_SIZE);
832         ret = HTTP_EARG;
833         goto exit;
834     }
835 
836     buf = (char *) malloc(HTTPCLIENT_CHUNK_SIZE);
837     if (!buf) {
838         http_err("Malloc failed socket fd %d!", client->socket);
839         ret = HTTP_ENOBUFS;
840         goto exit;
841     }
842     memset(buf, 0, HTTPCLIENT_CHUNK_SIZE);
843 
844     if (client->socket < 0) {
845         http_err("Invalid socket fd %d!", client->socket);
846         goto exit;
847     }
848 
849     if (client_data->is_more) {
850         client_data->response_buf[0] = '\0';
851         ret = httpclient_retrieve_content(client, buf, reclen, client_data);
852     } else {
853         ret = httpclient_recv_data(client, buf, 1, HTTPCLIENT_CHUNK_SIZE - 1, &reclen);
854         if (ret != HTTP_SUCCESS && ret != HTTP_ECLSD) {
855             goto exit;
856         }
857 
858         buf[reclen] = '\0';
859 
860         if (reclen) {
861             http_debug("reclen:%d, buf:%s", reclen, buf);
862             ret = httpclient_response_parse(client, buf, reclen, client_data);
863         }
864     }
865 
866     http_debug("httpclient_recv_data() result:%d, client:%p", ret, client);
867 
868 exit:
869     if (buf) {
870         free(buf);
871         buf = NULL;
872     }
873 
874     return (HTTPC_RESULT)ret;
875 }
876 
httpclient_clse(httpclient_t * client)877 void httpclient_clse(httpclient_t *client)
878 {
879     if (client->is_http) {
880         http_tcp_close_wrapper(client);
881     }
882 #if CONFIG_HTTP_SECURE
883     else
884         http_ssl_close_wrapper(client);
885 #endif
886 
887     client->socket = -1;
888     http_debug("httpclient_clse() client:%p", client);
889 }
890 
httpclient_get_response_code(httpclient_t * client)891 int httpclient_get_response_code(httpclient_t *client)
892 {
893     return client->response_code;
894 }
895 
httpclient_get_response_header_value(char * header_buf,char * name,int * val_pos,int * val_len)896 int httpclient_get_response_header_value(char *header_buf, char *name, int *val_pos, int *val_len)
897 {
898     char *data = header_buf;
899     char *crlf_ptr, *colon_ptr, *key_ptr, *value_ptr;
900     int key_len, value_len;
901 
902     if (header_buf == NULL || name == NULL || val_pos == NULL  || val_len == NULL )
903         return -1;
904 
905     while (true) {
906         crlf_ptr = strstr(data, "\r\n");
907         colon_ptr = strstr(data, ": ");
908         if (crlf_ptr && colon_ptr) {
909             key_len = colon_ptr - data;
910             value_len = crlf_ptr - colon_ptr - strlen(": ");
911             key_ptr = data;
912             value_ptr = colon_ptr + strlen(": ");
913 
914             http_debug("Response header: %.*s: %.*s", key_len, key_ptr, value_len, value_ptr);
915             if (0 == strncasecmp(key_ptr, name, key_len)) {
916                 *val_pos = value_ptr - header_buf;
917                 *val_len = value_len;
918                 return 0;
919             } else {
920                 data = crlf_ptr + 2;
921                 continue;
922             }
923         } else
924             return -1;
925 
926     }
927 }
928 
httpclient_prepare(httpclient_data_t * client_data,int header_size,int resp_size)929 HTTPC_RESULT httpclient_prepare(httpclient_data_t *client_data, int header_size, int resp_size)
930 {
931     HTTPC_RESULT ret = HTTP_SUCCESS;
932 
933     if (!client_data)
934         return HTTP_EUNKOWN;
935 
936     if (header_size < HTTPCLIENT_CHUNK_SIZE || resp_size < HTTPCLIENT_CHUNK_SIZE) {
937         http_err("Error: header buffer or response buffer should not less than %d!", HTTPCLIENT_CHUNK_SIZE);
938         return HTTP_EARG;
939     }
940 
941     memset(client_data, 0, sizeof(httpclient_data_t));
942 
943     client_data->header_buf   = (char *) malloc (header_size);
944     client_data->response_buf = (char *) malloc (resp_size);
945 
946     if (client_data->header_buf == NULL || client_data->response_buf == NULL){
947         http_err("httpc_prepare alloc memory failed");
948         if(client_data->header_buf){
949             free(client_data->header_buf);
950             client_data->header_buf = NULL;
951         }
952 
953         if(client_data->response_buf){
954             free(client_data->response_buf);
955             client_data->response_buf = NULL;
956         }
957         ret = HTTP_EUNKOWN;
958         goto finish;
959     }
960 
961     http_debug("httpc_prepare alloc memory");
962 
963     client_data->header_buf_len = header_size;
964     client_data->response_buf_len = resp_size;
965     client_data->post_buf_len = 0;
966 
967     client_data->is_redirected = 0;
968     client_data->redirect_url = NULL;
969 
970 finish:
971     return ret;
972 }
973 
httpclient_reset(httpclient_data_t * client_data)974 void httpclient_reset(httpclient_data_t *client_data)
975 {
976     char *response_buf = client_data->response_buf;
977     char *header_buf = client_data->header_buf;
978     int response_buf_len = client_data->response_buf_len;
979     int header_buf_len = client_data->header_buf_len;
980 
981     memset(client_data, 0, sizeof(httpclient_data_t));
982 
983     client_data->response_buf = response_buf;
984     client_data->header_buf = header_buf;
985     client_data->response_buf_len = response_buf_len;
986     client_data->header_buf_len = header_buf_len;
987 }
988 
httpclient_unprepare(httpclient_data_t * client_data)989 HTTPC_RESULT httpclient_unprepare(httpclient_data_t *client_data)
990 {
991     HTTPC_RESULT ret = HTTP_SUCCESS;
992 
993     if (!client_data){
994         ret = HTTP_EUNKOWN;
995         goto finish;
996     }
997 
998     if(client_data->header_buf){
999         free(client_data->header_buf);
1000         client_data->header_buf = NULL;
1001     }
1002 
1003     if(client_data->response_buf){
1004         free(client_data->response_buf);
1005         client_data->response_buf = NULL;
1006     }
1007 
1008     client_data->header_buf_len = 0;
1009     client_data->response_buf_len = 0;
1010 
1011     client_data->is_redirected = 0;
1012     if (client_data->redirect_url) {
1013         free(client_data->redirect_url);
1014         client_data->redirect_url = NULL;
1015     }
1016 
1017 finish:
1018     return ret;
1019 }
1020