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