| #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; |
| } |