//
// Created by HISONA on 2016. 2. 29..
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ca_cert.h"
#include "https.h"
/*---------------------------------------------------------------------*/
static int _error;
/*---------------------------------------------------------------------*/
char *strtoken(char *src, char *dst, int size);
static int parse_url(char *src_url, int *https, char *host, char *port, char *url);
static int http_header(HTTP_INFO *hi, char *param);
static int http_parse(HTTP_INFO *hi);
static int https_init(HTTP_INFO *hi, BOOL https, BOOL verify, netio_t *io);
static int https_close(HTTP_INFO *hi);
static int https_connect(HTTP_INFO *hi, char *host, char *port);
static int https_write(HTTP_INFO *hi, char *buffer, int len);
static int https_read(HTTP_INFO *hi, char *buffer, int len);
/* Change to a number between 1 and 4 to debug the TLS connection */
#define DEBUG_LEVEL 0
#if DEBUG_LEVEL > 0
#include "mbedtls/debug.h"
#endif
#if DEBUG_LEVEL > 0
/**
* Debug callback for mbed TLS
* Just prints on the USB serial port
*/
static void my_debug(void *ctx, int level, const char *file, int line,
const char *str)
{
const char *p, *basename;
(void) ctx;
/* Extract basename from file */
for(p = basename = file; *p != '\0'; p++) {
if(*p == '/' || *p == '\\') {
basename = p + 1;
}
}
mbedtls_printf("%s:%04d: |%d| %s", basename, line, level, str);
}
#endif
/*---------------------------------------------------------------------------*/
char *strtoken(char *src, char *dst, int size)
{
char *p, *st, *ed;
int len = 0;
// l-trim
p = src;
while(TRUE)
{
if((*p == '\n') || (*p == 0)) return NULL; /* value is not exists */
if((*p != ' ') && (*p != '\t')) break;
p++;
}
st = p;
while(TRUE)
{
ed = p;
if(*p == ' ') {
p++;
break;
}
if((*p == '\n') || (*p == 0)) break;
p++;
}
// r-trim
while(TRUE)
{
ed--;
if(st == ed) break;
if((*ed != ' ') && (*ed != '\t')) break;
}
len = (int)(ed - st + 1);
if((size > 0) && (len >= size)) len = size - 1;
strncpy(dst, st, len);
dst[len]=0;
return p;
}
/*---------------------------------------------------------------------*/
static int parse_url(char *src_url, int *https, char *host, char *port, char *url)
{
char *p1, *p2;
char str[1024];
memset(str, 0, 1024);
if(strncmp(src_url, "http://", 7)==0) {
p1=&src_url[7];
*https = 0;
} else if(strncmp(src_url, "https://", 8)==0) {
p1=&src_url[8];
*https = 1;
} else {
p1 = &src_url[0];
*https = 0;
}
if((p2=strstr(p1, "/")) == NULL)
{
sprintf(str, "%s", p1);
sprintf(url, "/");
}
else
{
strncpy(str, p1, p2-p1);
snprintf(url, 256, "%s", p2);
}
if((p1=strstr(str, ":")) != NULL)
{
*p1=0;
snprintf(host, 256, "%s", str);
snprintf(port, 5, "%s", p1+1);
}
else
{
snprintf(host, 256, "%s", str);
if(*https == 0)
snprintf(port, 5, "80");
else
snprintf(port, 5, "443");
}
return 0;
}
/*---------------------------------------------------------------------*/
static int http_header(HTTP_INFO *hi, char *param)
{
char *token;
char t1[256], t2[256];
int len;
token = param;
if((token=strtoken(token, t1, 256)) == 0) return -1;
if((token=strtoken(token, t2, 256)) == 0) return -1;
if(strncasecmp(t1, "HTTP", 4) == 0)
{
hi->response.status = atoi(t2);
}
else if(strncasecmp(t1, "set-cookie:", 11) == 0)
{
snprintf(hi->response.cookie, 512, "%s", t2);
}
else if(strncasecmp(t1, "location:", 9) == 0)
{
len = (int)strlen(t2);
strncpy(hi->response.location, t2, len);
hi->response.location[len] = 0;
}
else if(strncasecmp(t1, "content-length:", 15) == 0)
{
hi->response.content_length = atoi(t2);
}
else if(strncasecmp(t1, "transfer-encoding:", 18) == 0)
{
if(strncasecmp(t2, "chunked", 7) == 0)
{
hi->response.chunked = TRUE;
}
}
else if(strncasecmp(t1, "connection:", 11) == 0)
{
if(strncasecmp(t2, "close", 5) == 0)
{
hi->response.close = TRUE;
}
}
else if(strncasecmp(t1, "content-range:", 14) == 0)
{
if(strncasecmp(t2, "bytes", 5) == 0)
{
if ((token = strtoken(token, t2, 256)) == 0) {
printf("Failed to parse content-range\n");
}
else {
int range_start = 0;
int range_end = 0;
int content_len = 0;
int res = sscanf(t2, "%d-%d/%d", &range_start, &range_end, &content_len);
if (res == 3 && hi->range_start == range_start && hi->range_end == range_end) {
hi->content_length = content_len;
} else {
hi->content_length = 0;
}
}
}
}
return 1;
}
/*---------------------------------------------------------------------*/
static int http_parse(HTTP_INFO *hi)
{
char *p1, *p2;
long len;
if(hi->r_len <= 0) return -1;
p1 = hi->r_buf;
while(1)
{
if(hi->header_end == FALSE) // header parser
{
if((p2 = strstr(p1, "\r\n")) != NULL)
{
len = (long)(p2 - p1);
*p2 = 0;
if(len > 0)
{
// printf("header: %s(%ld)\n", p1, len);
http_header(hi, p1);
p1 = p2 + 2; // skip CR+LF
}
else
{
hi->header_end = TRUE; // reach the header-end.
// printf("header_end .... \n");
p1 = p2 + 2; // skip CR+LF
if(hi->response.chunked == TRUE)
{
len = hi->r_len - (p1 - hi->r_buf);
if(len > 0)
{
if((p2 = strstr(p1, "\r\n")) != NULL)
{
*p2 = 0;
if((hi->length = strtol(p1, NULL, 16)) == 0)
{
hi->response.chunked = FALSE;
}
else
{
hi->response.content_length += hi->length;
}
p1 = p2 + 2; // skip CR+LF
}
else
{
// copy the data as chunked size ...
strncpy(hi->r_buf, p1, len);
hi->r_buf[len] = 0;
hi->r_len = len;
hi->length = -1;
break;
}
}
else
{
hi->r_len = 0;
hi->length = -1;
break;
}
}
else
{
hi->length = hi->response.content_length;
if (hi->header_only) {
hi->length = 0;
}
}
}
}
else
{
len = hi->r_len - (p1 - hi->r_buf);
if(len > 0)
{
// keep the partial header data ...
strncpy(hi->r_buf, p1, len);
hi->r_buf[len] = 0;
hi->r_len = len;
}
else
{
hi->r_len = 0;
}
break;
}
}
else // body parser ...
{
if(hi->response.chunked == TRUE && hi->length == -1)
{
len = hi->r_len - (p1 - hi->r_buf);
if(len > 0)
{
if ((p2 = strstr(p1, "\r\n")) != NULL)
{
*p2 = 0;
if((hi->length = strtol(p1, NULL, 16)) == 0)
{
hi->response.chunked = FALSE;
}
else
{
hi->response.content_length += hi->length;
}
p1 = p2 + 2; // skip CR+LF
}
else
{
// copy the remain data as chunked size ...
strncpy(hi->r_buf, p1, len);
hi->r_buf[len] = 0;
hi->r_len = len;
hi->length = -1;
break;
}
}
else
{
hi->r_len = 0;
break;
}
}
else
{
if(hi->length > 0)
{
len = hi->r_len - (p1 - hi->r_buf);
if(len > hi->length)
{
// copy the data for response ..
if(hi->body_len < hi->body_size-1)
{
if (hi->body_size > (hi->body_len + hi->length))
{
memcpy(&(hi->body[hi->body_len]), p1, hi->length);
hi->body_len += hi->length;
hi->body[hi->body_len] = 0;
}
else
{
memcpy(&(hi->body[hi->body_len]), p1, hi->body_size - hi->body_len - 1);
hi->body_len = hi->body_size - 1;
hi->body[hi->body_len] = 0;
}
}
p1 += hi->length;
len -= hi->length;
if(hi->response.chunked == TRUE && len >= 2)
{
p1 += 2; // skip CR+LF
hi->length = -1;
}
else
{
return -1;
}
}
else
{
// copy the data for response ..
if(hi->body_len < hi->body_size-1)
{
if (hi->body_size > (hi->body_len + len))
{
memcpy(&(hi->body[hi->body_len]), p1, len);
hi->body_len += len;
hi->body[hi->body_len] = 0;
}
else
{
memcpy(&(hi->body[hi->body_len]), p1, hi->body_size - hi->body_len - 1);
hi->body_len = hi->body_size - 1;
hi->body[hi->body_len] = 0;
}
}
hi->length -= len;
hi->r_len = 0;
if(hi->response.chunked == FALSE && hi->length <= 0) return 1;
break;
}
}
else
{
if(hi->response.chunked == FALSE) return 1;
// chunked size check ..
if((hi->r_len > 2) && (memcmp(p1, "\r\n", 2) == 0))
{
p1 += 2;
hi->length = -1;
}
else
{
hi->length = -1;
hi->r_len = 0;
}
}
}
}
}
return 0;
}
/*---------------------------------------------------------------------*/
static int https_init(HTTP_INFO *hi, BOOL https, BOOL verify, netio_t *io)
{
ptrdiff_t zero_len = (void *)&hi->ranged - (void *)hi;
memset(hi, 0, zero_len);
if(https == TRUE)
{
mbedtls_ssl_init( &hi->tls.ssl );
mbedtls_ssl_config_init( &hi->tls.conf );
mbedtls_x509_crt_init( &hi->tls.cacert );
mbedtls_ctr_drbg_init( &hi->tls.ctr_drbg );
}
hi->tls.io = io;
mbedtls_ssl_set_bio(&hi->tls.ssl, io, io->send, NULL, io->recv_timeout);
#if DEBUG_LEVEL > 0
mbedtls_ssl_conf_dbg(&hi->tls.conf, my_debug, NULL);
mbedtls_debug_set_threshold(DEBUG_LEVEL);
#endif
hi->tls.verify = verify;
hi->url.https = https;
return 0;
}
/*---------------------------------------------------------------------*/
static int https_close(HTTP_INFO *hi)
{
if(hi->url.https == 1)
{
mbedtls_ssl_close_notify(&hi->tls.ssl);
}
if(hi->url.https == 1)
{
mbedtls_x509_crt_free(&hi->tls.cacert);
mbedtls_ssl_free(&hi->tls.ssl);
mbedtls_ssl_config_free(&hi->tls.conf);
mbedtls_ctr_drbg_free(&hi->tls.ctr_drbg);
mbedtls_entropy_free(&hi->tls.entropy);
}
// printf("https_close ... \n");
return 0;
}
/*---------------------------------------------------------------------*/
static int https_connect(HTTP_INFO *hi, char *host, char *port)
{
int ret, https;
https = hi->url.https;
if(https == 1)
{
//printf("Start mbedtls configure\n");
mbedtls_entropy_init( &hi->tls.entropy );
ret = mbedtls_ctr_drbg_seed( &hi->tls.ctr_drbg, mbedtls_entropy_func, &hi->tls.entropy, NULL, 0);
if( ret != 0 )
{
return ret;
}
// printf("Done mbedtls drbg_seed\n");
ret = mbedtls_x509_crt_parse(&hi->tls.cacert, (uint8_t *)ca_cert_globalsign, strlen(ca_cert_globalsign) + 1);
if( ret != 0 )
{
return ret;
}
// printf("Done mbedtls crt_parse\n");
ret = mbedtls_ssl_config_defaults( &hi->tls.conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT );
if( ret != 0 )
{
return ret;
}
//printf("Done mbedtls config_defaults\n");
/* OPTIONAL is not optimal for security,
* but makes interop easier in this simplified example */
mbedtls_ssl_conf_authmode( &hi->tls.conf, MBEDTLS_SSL_VERIFY_NONE );
mbedtls_ssl_conf_ca_chain( &hi->tls.conf, &hi->tls.cacert, NULL );
mbedtls_ssl_conf_rng( &hi->tls.conf, mbedtls_ctr_drbg_random, &hi->tls.ctr_drbg );
mbedtls_ssl_conf_read_timeout( &hi->tls.conf, 5000 );
ret = mbedtls_ssl_setup( &hi->tls.ssl, &hi->tls.conf );
if( ret != 0 )
{
return ret;
}
//printf("Done mbedtls setup\n");
ret = mbedtls_ssl_set_hostname( &hi->tls.ssl, host );
if( ret != 0 )
{
return ret;
}
printf("Done mbedtls configure\n");
}
ret = hi->tls.io->connect(hi->tls.io, host, port);
if( ret != 0 )
{
return ret;
}
if(https == 1)
{
//mbedtls_ssl_set_bio(&hi->tls.ssl, &hi->tls.ssl_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
while ((ret = mbedtls_ssl_handshake(&hi->tls.ssl)) != 0)
{
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
{
return ret;
}
}
/* In real life, we probably want to bail out when ret != 0 */
if( hi->tls.verify && (mbedtls_ssl_get_verify_result(&hi->tls.ssl) != 0) )
{
return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
}
}
return 0;
}
/*---------------------------------------------------------------------*/
static int https_write(HTTP_INFO *hi, char *buffer, int len)
{
int ret, slen = 0;
while(1)
{
if(hi->url.https == 1)
ret = mbedtls_ssl_write(&hi->tls.ssl, (u_char *)&buffer[slen], (size_t)(len-slen));
else
ret = hi->tls.io->send(hi->tls.io, (u_char *)&buffer[slen], (size_t)(len-slen));
if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) continue;
else if(ret <= 0) return ret;
slen += ret;
if(slen >= len) break;
}
return slen;
}
/*---------------------------------------------------------------------*/
static int https_read(HTTP_INFO *hi, char *buffer, int len)
{
if(hi->url.https == 1)
{
return mbedtls_ssl_read(&hi->tls.ssl, (u_char *)buffer, (size_t)len);
}
else
{
return hi->tls.io->recv_timeout(hi->tls.io, (u_char *)buffer, (size_t)len, 5000);
}
}
/*---------------------------------------------------------------------*/
int http_init(HTTP_INFO *hi, BOOL verify, netio_t *io)
{
return https_init(hi, 0, verify, io);
}
/*---------------------------------------------------------------------*/
int http_close(HTTP_INFO *hi)
{
return https_close(hi);
}
/*---------------------------------------------------------------------*/
static int http_get_ranged_impl(HTTP_INFO *hi, char *url, char *response, netio_t *io)
{
char request[1024], err[100];
char host[256], port[10], dir[1024];
int sock_fd, https, verify;
int ret, opt, len;
socklen_t slen;
if(NULL == hi) return -1;
verify = hi->tls.verify;
parse_url(url, &https, host, port, dir);
if( !hi->tls.io->opened(hi->tls.io) || (hi->url.https != https) ||
(strcmp(hi->url.host, host) != 0) || (strcmp(hi->url.port, port) != 0) )
{
https_close(hi);
https_init(hi, https, verify, io);
if((ret=https_connect(hi, host, port)) < 0)
{
https_close(hi);
mbedtls_strerror(ret, err, 100);
snprintf(response, 256, "socket error: %s(%d)", err, ret);
return -1;
}
}
else
{
if(!hi->tls.io->connected(hi->tls.io))
{
https_close(hi);
https_init(hi, https, verify, io);
if((ret=https_connect(hi, host, port)) < 0)
{
https_close(hi);
mbedtls_strerror(ret, err, 100);
snprintf(response, 256, "socket error: %s(%d)", err, ret);
return -1;
}
}
}
char range_buf[256];
if (hi->ranged) {
snprintf(range_buf, 256, "Range: bytes=%lu-%lu\r\n",
hi->range_start, hi->range_end);
}
/* Send HTTP request. */
len = snprintf(request, 1024,
"%s %s HTTP/1.1\r\n"
"User-Agent: Mozilla/4.0\r\n"
"Host: %s:%s\r\n"
"Content-Type: application/json; charset=utf-8\r\n"
"%s"
"Connection: Keep-Alive\r\n"
"%s\r\n",
hi->header_only ? "HEAD" : "GET",
dir, host, port,
(hi->ranged ? range_buf : ""),
hi->request.cookie);
if((ret = https_write(hi, request, len)) != len)
{
https_close(hi);
mbedtls_strerror(ret, err, 100);
snprintf(response, 256, "socket error: %s(%d)", err, ret);
return -1;
}
//printf("request: %s \r\n\r\n", request);
hi->response.status = 0;
hi->response.content_length = 0;
hi->response.close = 0;
hi->r_len = 0;
hi->header_end = 0;
hi->body = response;
hi->body_size = hi->range_end - hi->range_start + 2;
hi->body_len = 0;
while(1)
{
ret = https_read(hi, &hi->r_buf[hi->r_len], (int)(H_READ_SIZE - hi->r_len));
if(ret == MBEDTLS_ERR_SSL_WANT_READ) continue;
else if(ret < 0)
{
https_close(hi);
mbedtls_strerror(ret, err, 100);
snprintf(response, 256, "socket error: %s(%d)", err, ret);
return -1;
}
else if(ret == 0)
{
https_close(hi);
break;
}
hi->r_len += ret;
hi->r_buf[hi->r_len] = 0;
// printf("read(%ld): |%s| \n", hi->r_len, hi->r_buf);
// printf("read(%ld) ... \n", hi->r_len);
if(http_parse(hi) != 0) break;
}
if(hi->response.close == 1)
{
https_close(hi);
}
else
{
strncpy(hi->url.host, host, strlen(host));
strncpy(hi->url.port, port, strlen(port));
strncpy(hi->url.path, dir, strlen(dir));
}
/*
printf("status: %d \n", hi->response.status);
printf("cookie: %s \n", hi->response.cookie);
printf("location: %s \n", hi->response.location);
printf("referrer: %s \n", hi->response.referrer);
printf("length: %ld \n", hi->response.content_length);
printf("body: %ld \n", hi->body_len);
*/
return hi->response.status;
}
int http_get_header(HTTP_INFO *hi, char *url, char *response, int size, netio_t *io)
{
hi->ranged = 0;
hi->range_start = 0;
hi->range_end = size - 1;
hi->header_only = 1;
return http_get_ranged_impl(hi, url, response, io);
}
int http_get(HTTP_INFO *hi, char *url, char *response, int size, netio_t *io)
{
hi->ranged = 0;
hi->range_start = 0;
hi->range_end = size - 1;
hi->header_only = 0;
return http_get_ranged_impl(hi, url, response, io);
}
int http_get_ranged(HTTP_INFO *hi, char *url, char *response,
size_t range_start, size_t range_end, size_t *content_len,
netio_t *io)
{
hi->ranged = 1;
hi->range_start = range_start;
hi->range_end = range_end;
hi->header_only = 0;
int ret = http_get_ranged_impl(hi, url, response, io);
*content_len = hi->content_length;
return ret;
}
/*---------------------------------------------------------------------*/
void http_strerror(char *buf, int len)
{
mbedtls_strerror(_error, buf, len);
}
/*---------------------------------------------------------------------*/
int http_open(HTTP_INFO *hi, char *url, netio_t *io)
{
char host[256], port[10], dir[1024];
int sock_fd, https, verify;
int ret, opt;
socklen_t slen;
if (NULL == hi) return -1;
verify = hi->tls.verify;
parse_url(url, &https, host, port, dir);
if (!hi->tls.io->opened(hi->tls.io) || (hi->url.https != https) ||
(strcmp(hi->url.host, host) != 0) || (strcmp(hi->url.port, port) != 0))
{
if (hi->tls.io->opened(hi->tls.io))
https_close(hi);
https_init(hi, https, verify, io);
if ((ret = https_connect(hi, host, port)) < 0)
{
https_close(hi);
_error = ret;
return -1;
}
}
else
{
if (!hi->tls.io->connected(hi->tls.io))
{
https_close(hi);
https_init(hi, https, verify, io);
if ((ret = https_connect(hi, host, port)) < 0)
{
https_close(hi);
_error = ret;
return -1;
}
}
// else
// printf("socket reuse: %d \n", sock_fd);
}
strncpy(hi->url.host, host, strlen(host));
strncpy(hi->url.port, port, strlen(port));
strncpy(hi->url.path, dir, strlen(dir));
return 0;
}
/*---------------------------------------------------------------------*/
int http_write_header(HTTP_INFO *hi)
{
char request[4096], buf[H_FIELD_SIZE];
int ret, len, l;
if (NULL == hi) return -1;
/* Send HTTP request. */
len = snprintf(request, 1024,
"%s %s HTTP/1.1\r\n"
"User-Agent: Mozilla/4.0\r\n"
"Host: %s:%s\r\n"
"Content-Type: %s\r\n",
hi->request.method, hi->url.path,
hi->url.host, hi->url.port,
hi->request.content_type);
if(hi->request.referrer[0] != 0)
{
len += snprintf(&request[len], H_FIELD_SIZE,
"Referer: %s\r\n", hi->request.referrer);
}
if(hi->request.chunked == TRUE)
{
len += snprintf(&request[len], H_FIELD_SIZE,
"Transfer-Encoding: chunked\r\n");
}
else
{
len += snprintf(&request[len], H_FIELD_SIZE,
"Content-Length: %ld\r\n", hi->request.content_length);
}
if(hi->request.close == TRUE)
{
len += snprintf(&request[len], H_FIELD_SIZE,
"Connection: close\r\n");
}
else
{
len += snprintf(&request[len], H_FIELD_SIZE,
"Connection: Keep-Alive\r\n");
}
if(hi->request.cookie[0] != 0)
{
len += snprintf(&request[len], H_FIELD_SIZE,
"Cookie: %s\r\n", hi->request.cookie);
}
len += snprintf(&request[len], H_FIELD_SIZE, "\r\n");
printf("%s", request);
if ((ret = https_write(hi, request, len)) != len)
{
https_close(hi);
_error = ret;
return -1;
}
return 0;
}
/*---------------------------------------------------------------------*/
int http_write(HTTP_INFO *hi, char *data, int len)
{
char str[10];
int ret, l;
if(NULL == hi || len <= 0) return -1;
if(hi->request.chunked == TRUE)
{
l = snprintf(str, 10, "%x\r\n", len);
if ((ret = https_write(hi, str, l)) != l)
{
https_close(hi);
_error = ret;
return -1;
}
}
if((ret = https_write(hi, data, len)) != len)
{
https_close(hi);
_error = ret;
return -1;
}
if(hi->request.chunked == TRUE)
{
if ((ret = https_write(hi, "\r\n", 2)) != 2)
{
https_close(hi);
_error = ret;
return -1;
}
}
return len;
}
/*---------------------------------------------------------------------*/
int http_write_end(HTTP_INFO *hi)
{
char str[10];
int ret, len;
if (NULL == hi) return -1;
if(hi->request.chunked == TRUE)
{
len = snprintf(str, 10, "0\r\n\r\n");
}
else
{
len = snprintf(str, 10, "\r\n\r\n");
}
if ((ret = https_write(hi, str, len)) != len)
{
https_close(hi);
_error = ret;
return -1;
}
return len;
}
/*---------------------------------------------------------------------*/
int http_read_chunked(HTTP_INFO *hi, char *response, int size)
{
int ret;
if (NULL == hi) return -1;
// printf("request: %s \r\n\r\n", request);
hi->response.status = 0;
hi->response.content_length = 0;
hi->response.close = 0;
hi->r_len = 0;
hi->header_end = 0;
hi->body = response;
hi->body_size = size;
hi->body_len = 0;
hi->body[0] = 0;
while(1)
{
ret = https_read(hi, &hi->r_buf[hi->r_len], (int)(H_READ_SIZE - hi->r_len));
if(ret == MBEDTLS_ERR_SSL_WANT_READ) continue;
else if(ret < 0)
{
https_close(hi);
_error = ret;
return -1;
}
else if(ret == 0)
{
https_close(hi);
break;
}
hi->r_len += ret;
hi->r_buf[hi->r_len] = 0;
// printf("read(%ld): %s \n", hi->r_len, hi->r_buf);
// printf("read(%ld) \n", hi->r_len);
if(http_parse(hi) != 0) break;
}
if(hi->response.close == 1)
{
https_close(hi);
}
/*
printf("status: %d \n", hi->status);
printf("cookie: %s \n", hi->cookie);
printf("location: %s \n", hi->location);
printf("referrer: %s \n", hi->referrer);
printf("length: %d \n", hi->content_length);
printf("body: %d \n", hi->body_len);
*/
return hi->response.status;
}