blob: 499d0e88d7f6df8e4fcbc50838d9bc6d9b103258 [file] [log] [blame]
#include "http/lynq_http.h"
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
const int kSelectRead = 1 << 0;
const int kSelectWrite = 1 << 1;
const int kSelectError = 1 << 2;
#define DEFAULT_USER_AGENT_STR "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0\r\n"
#define CONNECT_KEEP_STR "Connection: keep-alive\r\n"
#define CONNECT_CLOSE_STR "Connection: close\r\n"
#define ACCEPT_STR "Accept: */*\r\n"
#define CONTENT_LENGTH_STR "Content-Length"
#define CONTENT_TYPE_STR "Content-Type:application/x-www-form-urlencoded\r\n"
#define CONTENT_DISPOSITION_STR "Content-Disposition"
#define CRLF "\r\n"
#define CA_ROOT "cert/ca.crt"
#define CA_CLICRT "cert/client.crt"
#define CA_CLIKEY "cert/client.key"
static char* _strdup(const char* src)
{
char* dst = NULL;
int len = 0;
if(src == NULL)
{
return NULL;
}
len = strlen(src);
dst = (char*)malloc(len + 1);
if(dst == NULL)
{
return NULL;
}
strcpy(dst, src);
return dst;
}
static void http_ssl_free(lynq_http_client_t* http)
{
if(http->ssl != NULL)
{
SSL_shutdown(http->ssl);
SSL_free(http->ssl);
http->ssl = NULL;
}
if(http->ctx != NULL)
{
SSL_CTX_free(http->ctx);
http->ctx = NULL;
}
}
int lynq_http_init()
{
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
if(SSL_library_init() < 0)
{
LYVERBLOG("+[http][init]: error num = %d\n", ERR_SSL_CREATE_SSL);
return ERR_SSL_CREATE_SSL;
}
return 0;
}
lynq_http_client_t* lynq_http_new()
{
lynq_http_client_t* http = (lynq_http_client_t*)calloc(1, sizeof(lynq_http_client_t));
return http;
}
void lynq_http_destroy(lynq_http_client_t* http)
{
if(http == NULL) return;
free_member(http->body);
free_member(http->header_field);
free_member(http->header_value);
free_member(http->redirect_url);
free_member(http->filename);
close_socket(http->fd);
close_file(http->pf);
http_ssl_free(http);
free(http);
}
int lynq_http_get_error_code(lynq_http_client_t* http)
{
if(http == NULL)
{
return -1;
}
return http->error_code;
}
static int socket_noblocking(socket_t fd, int noblocking)
{
int flags;
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
{
return -1;
}
if(noblocking)
{
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
else
{
return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
return 0;
}
static int last_error()
{
return errno;
}
static int socket_select(lynq_http_client_t* http, int mode, int timeout)
{
fd_set rfd, wfd, efd;
int r = 0;
int error = 0;
int remaind = timeout;
socklen_t len = sizeof(error);
struct timeval tv, start, elapsed;
gettimeofday(&start, 0);
while(remaind > 0)
{
if(mode & kSelectRead) { FD_ZERO(&rfd); FD_SET(http->fd, &rfd); }
if(mode & kSelectWrite){ FD_ZERO(&wfd); FD_SET(http->fd, &wfd); }
if(mode & kSelectError){ FD_ZERO(&efd); FD_SET(http->fd, &efd); }
tv.tv_sec = remaind / 1000;
tv.tv_usec = remaind % 1000 * 1000;
r = select(http->fd+1,
(mode & kSelectRead) ? &rfd : NULL,
(mode & kSelectWrite) ? &wfd : NULL,
(mode & kSelectError) ? &efd : NULL,
&tv);
if( r == 0)
{
return -1;
}
if( r > 0)
{
if(getsockopt(http->fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) == 0 && error == 0)
{
return 0;
}
else
{
return -1;
}
}
if( r < 0 )
{
if(last_error() == HTTP_EINTR)
{
gettimeofday(&elapsed, 0);
remaind = timeout - ((int)(elapsed.tv_sec - start.tv_sec) * 1000 + (int)(elapsed.tv_usec - start.tv_usec) / 1000);
continue;
}
else
{
return -1;
}
}
};
return -1;
}
static int _field_value_malloc(char** str, unsigned short* cur_size, unsigned short* size, const char* at, size_t length)
{
if(*str == NULL)
{
#define DEFAULT_HEADER_SIZE 128
*size = length > DEFAULT_HEADER_SIZE ? length: DEFAULT_HEADER_SIZE;
*str = (char*)calloc(1, *size + 1);
if(*str == NULL)
{
return -1;
}
*cur_size = 0;
}
else if(*cur_size + length > *size)
{
*size = *cur_size + length;
*str = (char*)realloc(*str, *size + 1);
if(*str == NULL)
{
return -1;
}
}
memcpy(*str + *cur_size, at, length);
*cur_size += length;
(*str)[*cur_size] = '\0';
return 0;
}
static int parser_field_value(lynq_http_client_t* http)
{
if(http->cur_value_size > 0 &&
http->cur_field_size > 0 &&
http->header_field && http->header_value)
{
if(_stricmp(http->header_field, "Location") == 0)
{
free_member(http->redirect_url);
http->redirect_url = _strdup(http->header_value);
http->redirect = 1;
return -1;
}
else if(_stricmp(http->header_field, CONTENT_LENGTH_STR) == 0)
{
http->content_length = atol(http->header_value);
}
else
{
/* extract other header field value */
}
http->cur_field_size = 0;
http->cur_value_size = 0;
}
return 0;
}
static int on_header_field_cb(http_parser* parser, const char *at, size_t length)
{
lynq_http_client_t* http = (lynq_http_client_t*)parser->data;
if(http->parser_statue == PARSERD_VALUE)
{
if( parser_field_value(http) != 0)
{
return -1;
}
}
http->parser_statue = PARSERD_FIELD;
return _field_value_malloc(&http->header_field, &http->cur_field_size, &http->field_size, at, length);
}
static int on_header_value_cb(http_parser* parser, const char *at, size_t length)
{
lynq_http_client_t* http = (lynq_http_client_t*)parser->data;
if(http->parser_statue == PARSERD_FIELD || http->parser_statue == PARSERD_VALUE)
{
http->parser_statue = PARSERD_VALUE;
return _field_value_malloc(&http->header_value, &http->cur_value_size, &http->value_size, at, length);
}
return 0;
}
static int on_status_cb(http_parser* parser, const char *at, size_t length)
{
lynq_http_client_t* http = (lynq_http_client_t*)parser->data;
http->status_code = parser->status_code;
return 0;
}
static int on_headers_complete_cb(http_parser* parser)
{
lynq_http_client_t* http = (lynq_http_client_t*)parser->data;
if(parser_field_value(http) != 0)
{
return -1;
}
free_member(http->header_field);
free_member(http->header_value);
http->parser_statue = PARSERD_BODY;
http->cur_field_size = http->cur_value_size = 0;
return 0;
}
static int on_download_file_cb(http_parser* parser, const char *at, size_t length)
{
lynq_http_client_t* http = (lynq_http_client_t*)parser->data;
if(http->status_code >= 200 && http->status_code <= 299)
{
if(http->pf == NULL)
{
if(http->filename != NULL)
{
http->pf = fopen(http->filename, "wb");
}
}
if(http->pf == NULL)
{
return -1;
}
if( http->recv_cb && (http->recv_cb)(http, at, length, http->content_length, http->user) != 0)
{
return -1;
}
fwrite(at, 1, length, http->pf);
}
return 0;
}
static int on_body_cb(http_parser* parser, const char *at, size_t length)
{
lynq_http_client_t* http = (lynq_http_client_t*)parser->data;
if(http->body == NULL)
{
if(http->content_length > 0)
{
http->body = (char*)calloc(1, http->content_length + 1);
}
else
{
http->body = (char*)calloc(1, length + 1);
}
}
else
{
if(http->content_length <= 0)
{
http->body = (char*)realloc(http->body, http->body_len + length + 1);
}
}
if(http->body == NULL)
{
return -1;
}
memcpy(http->body + http->body_len, at, length);
http->body_len += length;
return 0;
}
static int on_message_complete_cb(http_parser* parser)
{
return 0;
}
static int http_check_error(lynq_http_client_t* http, int mode, int ret)
{
int error_code;
if(http->proto_type == PROTO_HTTPS)
{
int error = SSL_get_error(http->ssl, ret);
if(SSL_ERROR_ZERO_RETURN == error)
{
return -1;
}
else if(error == SSL_ERROR_WANT_WRITE ||
error == SSL_ERROR_WANT_READ)
{
return 0;
}
else if(SSL_ERROR_SYSCALL == error)
{
goto check_select;
}
else
{
return -1;
}
}
check_select:
error_code = last_error();
if(error_code == HTTP_EINTR)
{
return 0;
}
else if(error_code == HTTP_EINPROGRESS || error_code == HTTP_EWOULDBLOCK)
{
if(socket_select(http, mode, http->timeout) == 0)
{
return 0;
}
}
return -1;
}
static int http_read_write(lynq_http_client_t* http, const char* data, int len, int read)
{
int n = 0, r = 0;
do
{
if(http->exit == 1)
{
return -1;
}
if(http->proto_type == PROTO_HTTPS)
{
r = read ? SSL_read(http->ssl, (char*)data + n, len - n) : SSL_write(http->ssl, data + n, len - n);
}
else
{
r = read ? recv(http->fd, (char*)data + n, len - n, 0) : send(http->fd, data + n, len - n, 0);
}
if(r > 0)
{
n += r;
}
else if(r == 0)
{
return n;
}
else
{
if(http_check_error(http, read ? kSelectRead : kSelectWrite, r) == 0)
{
continue;
}
return -1;
}
http->data = data;
LYDBGLOG("[%s-%d] +http%s, session = %d, data = %s \n", __FUNCTION__, __LINE__, http->action, http->session, data);
LYVERBLOG("+[http][%s][session%d]: data = %s\n", http->action, http->session, data);
LYVERBLOG("+[http][%s][session%d]: ok!!\n", http->action, http->session);
}while(n < len);
http->data = NULL;
return n;
}
#define HTTP_KEY "self.key"
#define HTTP_CERTIFICATE "self.crt"
static int sslSetCertFile(SSL_CTX *sslctx, char *certFile)
{
if (sslctx == NULL)
{
return -1;
}
if (SSL_CTX_use_certificate_file(sslctx, certFile, SSL_FILETYPE_PEM) <= 0) {
if (SSL_CTX_use_certificate_file(sslctx, certFile, SSL_FILETYPE_ASN1) <= 0) {
return -1;
}
}
if(!SSL_CTX_check_private_key(sslctx)){
return -1;
}
return 0;
}
static int sslSetKeyFile(SSL_CTX *sslctx, char *keyFile)
{
if (sslctx == NULL)
{
return -1;
}
if (SSL_CTX_use_PrivateKey_file(sslctx, keyFile, SSL_FILETYPE_PEM) <= 0) {
if (SSL_CTX_use_PrivateKey_file(sslctx, keyFile, SSL_FILETYPE_ASN1) <= 0) {
return -1;
}
return -1;
}
return 0;
}
#define VERIFY_DEPTH 10
static int verify_X509Certificate(int ok, X509_STORE_CTX *xContext)
{
X509 *cert;
char subject[260], issuer[260], peer[260];
int error, depth;
subject[0] = issuer[0] = '\0';
cert = X509_STORE_CTX_get_current_cert(xContext);
error = X509_STORE_CTX_get_error(xContext);
depth = X509_STORE_CTX_get_error_depth(xContext);
ok = 1;
if (X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject) - 1) < 0) {
ok = 0;
}
if (X509_NAME_oneline(X509_get_issuer_name(cert), issuer, sizeof(issuer) - 1) < 0) {
ok = 0;
}
if (ok && VERIFY_DEPTH < depth) {
if (error == 0) {
error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
}
}
switch (error) {
case X509_V_OK:
break ;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
ok = 0;
break;
case X509_V_ERR_UNABLE_TO_GET_CRL:
ok = 0;
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
ok = 0;
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
ok = 0;
break;
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
ok = 0;
break;
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
ok = 0;
break;
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
ok = 0;
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
ok = 0;
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
ok = 0;
break;
case X509_V_ERR_CRL_NOT_YET_VALID:
ok = 0;
break;
case X509_V_ERR_CRL_HAS_EXPIRED:
ok = 0;
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
ok = 0;
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
ok = 0;
break;
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
ok = 0;
break;
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
ok = 0;
break;
case X509_V_ERR_OUT_OF_MEM:
ok = 0;
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
ok = 0;
break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
ok = 0;
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
ok = 0;
break;
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
ok = 0;
break;
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
ok = 0;
break;
case X509_V_ERR_CERT_REVOKED:
ok = 0;
break;
case X509_V_ERR_INVALID_CA:
ok = 0;
break;
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
ok = 0;
break;
case X509_V_ERR_INVALID_PURPOSE:
ok = 0;
break;
case X509_V_ERR_CERT_UNTRUSTED:
ok = 0;
break;
case X509_V_ERR_CERT_REJECTED:
ok = 0;
break;
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
ok = 0;
break;
case X509_V_ERR_AKID_SKID_MISMATCH:
ok = 0;
break;
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
ok = 0;
break;
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
ok = 0;
break;
case X509_V_ERR_INVALID_EXTENSION:
ok = 0;
break;
case X509_V_ERR_INVALID_POLICY_EXTENSION:
ok = 0;
break;
case X509_V_ERR_NO_EXPLICIT_POLICY:
ok = 0;
break;
case X509_V_ERR_DIFFERENT_CRL_SCOPE:
ok = 0;
break;
case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE:
ok = 0;
break;
case X509_V_ERR_PERMITTED_VIOLATION:
ok = 0;
break;
case X509_V_ERR_EXCLUDED_VIOLATION:
ok = 0;
break;
case X509_V_ERR_SUBTREE_MINMAX:
ok = 0;
break;
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:
ok = 0;
break;
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX:
ok = 0;
break;
case X509_V_ERR_CRL_PATH_VALIDATION_ERROR:
ok = 0;
break;
case X509_V_ERR_APPLICATION_VERIFICATION:
ok = 0;
break;
default:
ok = 0;
break;
}
return ok;
}
void https_certificate_validation(SSL * ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if(SSL_get_verify_result(ssl) == X509_V_OK){
LYDBGLOG("Certificate verification passed\n");
}
if (cert != NULL) {
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
free(line);
X509_free(cert);
} else
LYDBGLOG("[%s %d] No certificate information\n", __FUNCTION__, __LINE__);
LYVERBLOG("+[http]: error num = %d\n", ERR_NOCERT);
}
static int http_ssl_connect(lynq_http_client_t* http)
{
int ssl_ret = 0;
int remaind = http->timeout;
struct timeval start, elapsed;
http->ctx = SSL_CTX_new(SSLv23_client_method());
if(http->ctx == NULL)
{
return -1;
}
SSL_CTX_set_verify(http->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_X509Certificate);
SSL_CTX_set_verify_depth(http->ctx, VERIFY_DEPTH);
if (SSL_CTX_load_verify_locations(http->ctx, CA_ROOT, NULL) <= 0){
LYVERBLOG("+[http][%s][session%d]: error num = %d\n", http->action, http->session, ERR_SSL_CREATE_CTX);
return ERR_SSL_CREATE_CTX;
}
sslSetCertFile(http->ctx, CA_CLICRT);
sslSetKeyFile(http->ctx, CA_CLIKEY);
http->ssl = SSL_new(http->ctx);
if(http->ssl == NULL)
{
return -1;
}
if(SSL_set_fd(http->ssl, http->fd) == 0)
{
return -1;
}
gettimeofday(&start, 0);
do
{
ssl_ret = SSL_connect(http->ssl);
if (ssl_ret == -1) {
ERR_print_errors_fp(stderr);
}
else {
LYDBGLOG("[%s %d] Connected with %s encryption\n", __FUNCTION__, __LINE__, SSL_get_cipher(http->ssl));
//Show certificate information
//https_certificate_validation(http->ssl);
}
gettimeofday(&elapsed, 0);
remaind = http->timeout - ((int)(elapsed.tv_sec - start.tv_sec) * 1000 + (int)(elapsed.tv_usec - start.tv_usec) / 1000);
if(ssl_ret > 0)
{
return 0;
}
else
{
if(remaind <= 0)
{
return -1;
}
else if(http_check_error(http, kSelectRead+kSelectWrite, ssl_ret) == 0)
{
continue;
}
return -1;
}
}while(1);
return -1;
}
static int http_connect_host(lynq_http_client_t* http, const char* url, struct http_parser_url* u)
{
struct sockaddr_in sin;
char host[256] = {0};
int r;
unsigned short port = 80;
if(u->field_set & (1 << UF_SCHEMA))
{
if(_strnicmp("http://", url + u->field_data[UF_SCHEMA].off, 7) == 0)
{
port = 80; http->proto_type = PROTO_HTTP;
}
else if(_strnicmp("https://", url + u->field_data[UF_SCHEMA].off, 8) == 0)
{
port = 443; http->proto_type = PROTO_HTTPS;
}
else
{
return ERR_URL_INVALID_PROTO;
}
}
if(!(u->field_set & (1 << UF_HOST)))
{
return ERR_URL_INVALID_HOST;
}
if(u->field_set & (1 << UF_PORT))
{
port = (unsigned short)atoi(url + u->field_data[UF_PORT].off);
}
memset(&sin, 0, sizeof(struct sockaddr_in));
memcpy(host, url + u->field_data[UF_HOST].off, u->field_data[UF_HOST].len);
if(host[0] >= '0' && host[0] <= '9')
{
sin.sin_addr.s_addr = (unsigned long)inet_addr(host);
}
else
{
struct hostent* he = gethostbyname(host);
if(he == NULL || he->h_addrtype != AF_INET)
{
return ERR_URL_RESOLVED_HOST;
}
sin.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
}
if(sin.sin_addr.s_addr == INADDR_NONE)
{
return ERR_URL_RESOLVED_HOST;
}
sin.sin_port = htons(port);
sin.sin_family = AF_INET;
http->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(http->fd == HTTP_INVALID_SOCKET)
{
return ERR_SOCKET_CREATE;
}
{
struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 0;
if(setsockopt(http->fd,SOL_SOCKET, SO_LINGER,(const char *) &linger,sizeof(linger)) != 0)
{
return ERR_SOCKET_SET_OPT;
}
if(socket_noblocking(http->fd, 1) != 0)
{
return ERR_SOCKET_NOBLOCKING;
}
}
do
{
r = connect(http->fd, (struct sockaddr*)&sin, sizeof(sin));
if(r < 0)
{
int error = last_error();
if(error == HTTP_EINTR)
{
continue;
}
else if( error == HTTP_EINPROGRESS || error == HTTP_EWOULDBLOCK || error == HTTP_EALREADY)
{
if(socket_select(http, kSelectWrite, http->timeout) == 0)
{
break;
}
else
{
return ERR_SOCKET_CONNECT_TIMEOUT;
}
}
return ERR_SOCKET_CONNECT;
}
}while(1);
if(http->proto_type == PROTO_HTTPS)
{
if(http_ssl_connect(http) != 0)
{
return ERR_SSL_CONNECT;
}
}
return ERR_OK;
}
void http_init(lynq_http_client_t* http)
{
http->redirect = 0;
http->body_len = 0;
http->content_length = 0;
http->cur_field_size = 0;
http->cur_value_size = 0;
http->field_size = 0;
http->value_size = 0;
http->parser_statue = 0;
http->error_code = 0;
http->user_header = 0;
http->user_header_len = 0;
if(http->timeout == 0) http->timeout = 1500000;
}
int http_connect(lynq_http_client_t* http)
{
int r = 0;
if( http_parser_parse_url(http->url, strlen(http->url), 0, &http->u) != 0 )
{
LYDBGLOG("[%s-%d] ==============\n", __FUNCTION__, __LINE__);
return (http->error_code = ERR_URL_INVALID);
}
r = http_connect_host(http, http->url, &http->u);
if(r != ERR_OK)
{
LYDBGLOG("[%s-%d] ==============\n", __FUNCTION__, __LINE__);
return (http->error_code = r);
}
return 0;
}
int CHECK(int ret, lynq_http_client_t* http)
{
if(ret <= 0)
return (http->error_code = ERR_SOCKET_WRITE);
return 0;
}
void http_write_headers(lynq_http_client_t* http)
{
char *url = NULL;
if(http->redirect == 1)
url = http->redirect_url;
else
url = http->url;
if(http->method == M_GET)
{
CHECK(http_read_write(http, "GET ", 4, 0), http);
}
else if(http->method == M_POST)
{
CHECK(http_read_write(http, "POST ", 5, 0), http);
}
if(http->u.field_set & (1 << UF_PATH))
{
CHECK(http_read_write(http, url + http->u.field_data[UF_PATH].off, http->u.field_data[UF_PATH].len, 0), http);
}
else
{
CHECK(http_read_write(http, "/", 1, 0), http);
}
if(http->u.field_set & (1 << UF_QUERY))
{
CHECK(http_read_write(http, "?", 1, 0), http);
CHECK(http_read_write(http, url + http->u.field_data[UF_QUERY].off, http->u.field_data[UF_QUERY].len, 0), http);
}
CHECK(http_read_write(http, " HTTP/1.1\r\nHost:", 16, 0), http);
CHECK(http_read_write(http, url + http->u.field_data[UF_HOST].off, http->u.field_data[UF_HOST].len, 0), http);
if(http->conn_method == M_KEEP) {
CHECK(http_read_write(http, CRLF CONNECT_KEEP_STR ACCEPT_STR DEFAULT_USER_AGENT_STR ,
2 + strlen(CONNECT_KEEP_STR) + strlen(ACCEPT_STR) + strlen(DEFAULT_USER_AGENT_STR), 0), http);
}
else {
CHECK(http_read_write(http, CRLF CONNECT_CLOSE_STR ACCEPT_STR DEFAULT_USER_AGENT_STR ,
2 + strlen(CONNECT_CLOSE_STR) + strlen(ACCEPT_STR) + strlen(DEFAULT_USER_AGENT_STR), 0), http);
}
if(http->user_header != NULL)
{
CHECK(http_read_write(http, http->user_header, http->user_header_len, 0), http);
}
if(http->post_data && http->post_data_len > 0)
{
char len_data[256] = {0};
int n = sprintf(len_data, "%s:%d\r\n", CONTENT_TYPE_STR CONTENT_LENGTH_STR, http->post_data_len);
CHECK(http_read_write(http, len_data, n, 0), http);
}
CHECK(http_read_write(http, CRLF, 2, 0), http);
}
void http_write_data(lynq_http_client_t* http)
{
CHECK(http_read_write(http, http->post_data, http->post_data_len, 0), http);
}
int lynq_http_data_send(char *data)
{
struct mymesg ckxmsg;
ckxmsg.mtype = 1;
strcpy(ckxmsg.mtext, data);
int id = lynq_msgq_init("/tmp", 0666);
lynq_msgq_send(id, &ckxmsg);
}
void *lynq_http_write_head_data(lynq_http_client_t* http)
{
http_write_headers(http);
if(http->post_data && http->post_data_len > 0)
{
http_write_data(http);
}
http->post_data = "";
}
void *http_write_head_data_thread(void* arg)
{
int id = 0;
int runing = 1 ;
struct mymesg ckxmsg;
lynq_http_client_t* http = (lynq_http_client_t*)arg;
id = lynq_msgq_init("/tmp", 0666);
while(runing)
{
#if 1
if(!strcmp(http->post_data, "") && http->conn_method == M_KEEP)
{
if(msgrcv(id, (void *)&ckxmsg, 512, 1, 0) < 0)
{
LYDBGLOG("[%s-%d] receive msg error \n", __FUNCTION__, __LINE__);
LYVERBLOG("+[http]: error num = %d\n", ERR_MSG);
return ERR_MSG;
}
LYDBGLOG("[%s-%d] mtext :%s\n", __FUNCTION__, __LINE__, ckxmsg.mtext);
if (!strcmp(ckxmsg.mtext,"close"))
return NULL;
http->post_data = ckxmsg.mtext;
}
#endif
lynq_http_write_head_data(http);
}
}
void *http_read_data_thread(void* arg)
{
int parsed = 0;
lynq_http_client_t* http = (lynq_http_client_t*)arg;
do
{
int nread = 0;
if(http->download == 0 && http->parser_statue == PARSERD_BODY && http->body && http->content_length > 0)
{
nread = http_read_write(http, http->body+http->body_len, http->content_length - http->body_len, 1);
if(nread > 0)
{
http->body_len += nread;
break;
}
}
else
{
char buf[RECV_BUF_SIZE + 1] = {0};
nread = http_read_write(http, buf, RECV_BUF_SIZE, 1);
if(nread > 0)
{
parsed = http_parser_execute(&http->parser, &http->parser_setting, buf, nread);
if(http->redirect)
{
break;
}
if(parsed != nread)
{
return NULL;
}
}
}
if(nread == 0)
{
break;
}
else if(nread < 0)
{
return NULL;
}
} while (1);
return NULL;
}
static int http_internal_sync_request(lynq_http_client_t* http)
{
int ret = 0;
http_init(http);
ret = http_connect(http);
if(ret != ERR_OK)
{
LYDBGLOG("[%s %d] return error\n", __FUNCTION__, __LINE__);
LYVERBLOG("+[http][%s][session%d]: error num = %d\n", http->action, http->session, http->error_code);
return (http->error_code);
}
if(http->conn_method == M_CLOSE)
lynq_http_write_head_data(http);
else {
pthread_t tid;
pthread_create(&tid, NULL, http_write_head_data_thread, http);
}
memset(&http->parser_setting, 0, sizeof(http->parser_setting));
http->parser_setting.on_body = http->download ? on_download_file_cb : on_body_cb;
http->parser_setting.on_message_complete = on_message_complete_cb;
http->parser_setting.on_header_field = on_header_field_cb;
http->parser_setting.on_header_value = on_header_value_cb;
http->parser_setting.on_headers_complete = on_headers_complete_cb;
http->parser_setting.on_status = on_status_cb;
http_parser_init(&http->parser, HTTP_RESPONSE);
http->parser.data = http;
if(http->conn_method == M_CLOSE)
http_read_data_thread(http);
else {
pthread_t read_tid;
pthread_create(&read_tid, NULL, http_read_data_thread, http);
}
if(http->redirect == 1)
{
return http_internal_sync_request(http);
}
else
{
if(http->download)
{
if(http->pf)
{
fclose(http->pf);
http->pf = NULL;
}
}
if(http->body && http->body_len > 0)
{
http->body[http->body_len] = '\0';
}
}
return http->error_code;
}
const char* lynq_http_sync_request(lynq_http_client_t* http, const char* url, http_request_method_e m, http_connent_method_e c_m)
{
if(http == NULL)
{
return NULL;
}
http->method = m;
http->conn_method = c_m;
http->download = 0;
http->url = (char *)url;
http->error_code = http_internal_sync_request(http);
return http->body;
}
const char* lynq_http_sync_post_request(lynq_http_client_t* http, char* url, char* post_data, http_request_method_e m, http_connent_method_e c_m)
{
if(http == NULL)
{
return NULL;
}
http->method = m;
http->conn_method = c_m;
http->download = 0;
free_member(http->post_data);
http->post_data = post_data;
http->url = url;
http->post_data_len = strlen(http->post_data);
http->error_code = http_internal_sync_request(http);
return http->body;
}
int lynq_http_sync_download_file(lynq_http_client_t* http, char* url, char* filepath, http_request_method_e m, http_connent_method_e c_m)
{
if(http == NULL)
{
return -1;
}
http->method = m;
http->conn_method = c_m;
http->download = 1;
http->post_data = "";
free_member(http->url);
http->url = url;
free_member(http->filename);
if(filepath == NULL || !strcmp(filepath, ""))
{
filepath = strrchr(http->url, '/') + 1 ;
}
if(filepath != NULL)
{
http->filename = _strdup(filepath);
if(http->filename == NULL)
{
return http->error_code = ERR_OUT_MEMORY;
}
}
if(http_internal_sync_request(http) == ERR_OK)
{
return ERR_OK;
}
return http->error_code;
}
int lynq_http_exit(lynq_http_client_t* http)
{
if(http) http->exit = 1;
return 0;
}
int ft_http_set_data_recv_cb(lynq_http_client_t* http, data_recv_cb_t cb, void* user)
{
if(http)
{
http->user = user;
http->recv_cb = cb;
}
return 0;
}