加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
http.c 21.45 KB
一键复制 编辑 原始数据 按行查看 历史
linxyruffy 提交于 2016-07-07 22:22 . no commit message
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
#include "http.h"
#include "http_parser.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef WIN32
#include <WinSock2.h>
#ifdef WINCE
#pragma comment( lib, "ws2.lib")
#else
#pragma comment( lib, "ws2_32.lib")
#endif
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/select.h>
#include <time.h>
#include <ctype.h>
#include <netdb.h>
#include <strings.h>
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
#endif
#if FT_SUPPORT_HTTPS
#ifdef USEOPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#else
#include "krypton.h"
#endif
#endif
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_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"
enum parser_statue_e { PARSERD_NONE = 0, PARSERD_FIELD, PARSERD_VALUE, PARSERD_BODY };
enum proto_type_e { PROTO_HTTP = 0, PROTO_HTTPS };
#ifdef WIN32
typedef SOCKET socket_t;
#define HTTP_INVALID_SOCKET INVALID_SOCKET
#define HTTP_EINTR WSAEINTR
#define HTTP_EINPROGRESS WSAEINPROGRESS
#define HTTP_EWOULDBLOCK WSAEWOULDBLOCK
#define HTTP_EALREADY WSAEALREADY
#else
typedef int socket_t;
#define HTTP_INVALID_SOCKET -1
#define HTTP_EINTR EINTR
#define HTTP_EINPROGRESS EINPROGRESS
#define HTTP_EWOULDBLOCK EWOULDBLOCK
#define HTTP_EALREADY EALREADY
#endif
#define RECV_BUF_SIZE 4 * 1024
struct ft_http_client_t
{
FILE* pf;
char* filename;
char* body;
char* redirect_url;
char* header_field;
char* header_value;
char* user;
data_recv_cb_t recv_cb;
unsigned long body_len;
unsigned long content_length;
enum http_request_method_e method;
enum proto_type_e proto_type;
unsigned short field_size;
unsigned short value_size;
unsigned short cur_field_size;
unsigned short cur_value_size;
#if FT_SUPPORT_HTTPS
SSL_CTX *ctx;
SSL *ssl;
#endif
socket_t fd;
int timeout;
short status_code;
char parser_statue;
char error_code;
unsigned cancel : 1;
unsigned exit : 1;
unsigned download : 1;
unsigned redirect : 1;
};
#ifdef WIN32
#define socket_close closesocket
#else
#define socket_close close
#endif
#define free_member(member) if((member)) { free(member); (member) = NULL; }
#define close_socket(fd) if(fd != HTTP_INVALID_SOCKET) { socket_close(fd); fd = HTTP_INVALID_SOCKET; }
#define close_file(pf) if(pf != NULL) { fclose(pf); pf = NULL; }
#ifdef WINCE
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;
}
#endif
#if FT_SUPPORT_HTTPS
static void http_ssl_free(ft_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;
}
}
#endif
HTTP_API int ft_http_init()
{
#if FT_SUPPORT_HTTPS
#ifdef USEOPENSSL
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
#endif
if(SSL_library_init() < 0)
{
return -1;
}
#endif
#ifdef WIN32
{
WORD version = MAKEWORD(2, 2);
WSADATA data;
WSAStartup(version, &data);
}
#endif
return 0;
}
HTTP_API void ft_http_deinit()
{
#ifdef WIN32
{
WSACleanup();
}
#endif
}
HTTP_API ft_http_client_t* ft_http_new()
{
ft_http_client_t* http = (ft_http_client_t*)calloc(1, sizeof(ft_http_client_t));
return http;
}
HTTP_API void ft_http_destroy(ft_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);
#if FT_SUPPORT_HTTPS
http_ssl_free(http);
#endif
free(http);
}
HTTP_API int ft_http_get_error_code(ft_http_client_t* http)
{
if(http == NULL)
{
return -1;
}
return http->error_code;
}
HTTP_API int ft_http_get_status_code(ft_http_client_t* http)
{
if(http == NULL)
{
return -1;
}
return http->status_code;
}
HTTP_API int ft_http_set_timeout(ft_http_client_t* http, int timeout)
{
if(http == NULL)
{
return -1;
}
if(timeout < 0)
{
http->timeout = 0;
}
else
{
http->timeout = timeout;
}
return 0;
}
static int socket_noblocking(socket_t fd, int noblocking)
{
#ifdef WIN32
return ioctlsocket(fd, FIONBIO, &noblocking);
#else
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);
}
#endif
return 0;
}
static int last_error()
{
#ifdef WIN32
return WSAGetLastError();
#else
return errno;
#endif
return 0;
}
#ifdef WIN32
static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
static int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
FILETIME file_time;
SYSTEMTIME system_time;
ULARGE_INTEGER ularge;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
ularge.LowPart = file_time.dwLowDateTime;
ularge.HighPart = file_time.dwHighDateTime;
tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
return 0;
}
#endif
static int socket_select(ft_http_client_t* http, int mode, int timeout)
{
fd_set rfd, wfd, efd;
int r = 0;
int error = 0;
int remaind = timeout;
#ifdef WIN32
int len = sizeof(error);
#else
socklen_t len = sizeof(error);
#endif
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; /* timeout */
}
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(ft_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)
{
ft_http_client_t* http = (ft_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)
{
ft_http_client_t* http = (ft_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)
{
ft_http_client_t* http = (ft_http_client_t*)parser->data;
http->status_code = parser->status_code;
return 0;
}
static int on_headers_complete_cb(http_parser* parser)
{
ft_http_client_t* http = (ft_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)
{
ft_http_client_t* http = (ft_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;
}
/* report download progress */
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)
{
ft_http_client_t* http = (ft_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(ft_http_client_t* http, int mode, int ret)
{
int error_code;
#if FT_SUPPORT_HTTPS
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:
#endif
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(ft_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)
{
#if FT_SUPPORT_HTTPS
r = read ? SSL_read(http->ssl, (char*)data + n, len - n) : SSL_write(http->ssl, data + n, len - n);
#endif
}
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;
}
}while(n < len);
return n;
}
#if FT_SUPPORT_HTTPS
static int http_ssl_connect(ft_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;
}
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);
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;
}
#endif
static int http_connect_host(ft_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;
}
#if FT_SUPPORT_HTTPS
else if(_strnicmp("https://", url + u->field_data[UF_SCHEMA].off, 8) == 0)
{
port = 443; http->proto_type = PROTO_HTTPS;
}
#endif
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;
close_socket(http->fd);
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; /* 关闭close wait */
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 FT_SUPPORT_HTTPS
if(http->proto_type == PROTO_HTTPS)
{
if(http_ssl_connect(http) != 0)
{
return ERR_SSL_CONNECT;
}
}
#endif
return ERR_OK;
}
static int http_internal_sync_request(ft_http_client_t* http, const char* url,
const char* post_data, int post_data_len,
const char* user_header, int user_header_len)
{
http_parser_settings parser_setting;
struct http_parser parser;
struct http_parser_url u;
int r = 0;
int parsed = 0;
unsigned short port = 80;
free_member(http->body);
free_member(http->header_field);
free_member(http->header_value);
close_socket(http->fd);
#if FT_SUPPORT_HTTPS
http_ssl_free(http);
#endif
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;
if(http->timeout == 0) http->timeout = 15000;
if( http_parser_parse_url(url, strlen(url), 0, &u) != 0 )
{
return (http->error_code = ERR_URL_INVALID);
}
r = http_connect_host(http, url, &u);
if(r != ERR_OK)
{
return http->error_code = r;
}
#define CHECK(nwirte) \
if((nwirte) <= 0) { return http->error_code = ERR_SOCKET_WRITE; }
if(http->method == M_GET)
{
CHECK(http_read_write(http, "GET ", 4, 0));
}
else if(http->method == M_POST)
{
CHECK(http_read_write(http, "POST ", 5, 0));
}
if(u.field_set & (1 << UF_PATH))
{
CHECK(http_read_write(http, url + u.field_data[UF_PATH].off, u.field_data[UF_PATH].len, 0));
}
else
{
CHECK(http_read_write(http, "/", 1, 0));
}
if(u.field_set & (1 << UF_QUERY))
{
CHECK(http_read_write(http, "?", 1, 0));
CHECK(http_read_write(http, url + u.field_data[UF_QUERY].off, u.field_data[UF_QUERY].len, 0));
}
CHECK(http_read_write(http, " HTTP/1.1\r\nHost:", 16, 0));
CHECK(http_read_write(http, url + u.field_data[UF_HOST].off, u.field_data[UF_HOST].len, 0));
CHECK(http_read_write(http, CRLF CONNECT_STR ACCEPT_STR DEFAULT_USER_AGENT_STR ,
2 + strlen(CONNECT_STR) + strlen(ACCEPT_STR) + strlen(DEFAULT_USER_AGENT_STR), 0));
if(user_header != NULL)
{
CHECK(http_read_write(http, user_header, user_header_len, 0));
}
if(post_data && post_data_len > 0)
{
char len_data[256] = {0};
int n = sprintf(len_data, "%s:%d\r\n", CONTENT_TYPE_STR CONTENT_LENGTH_STR, post_data_len);
CHECK(http_read_write(http, len_data, n, 0));
}
CHECK(http_read_write(http, CRLF, 2, 0));
if(post_data && post_data_len > 0)
{
CHECK(http_read_write(http, post_data, post_data_len, 0));
}
memset(&parser_setting, 0, sizeof(parser_setting));
parser_setting.on_body = http->download ? on_download_file_cb : on_body_cb;
parser_setting.on_message_complete = on_message_complete_cb;
parser_setting.on_header_field = on_header_field_cb;
parser_setting.on_header_value = on_header_value_cb;
parser_setting.on_headers_complete = on_headers_complete_cb;
parser_setting.on_status = on_status_cb;
http_parser_init(&parser, HTTP_RESPONSE);
parser.data = http;
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;
}
//todo: if iss download, use mmap to write data
}
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(&parser, &parser_setting, buf, nread);
if(http->redirect)
{
break;
}
if(parsed != nread)
{
return http->error_code = ERR_PARSE_REP;
}
}
}
if(nread == 0)
{
break;
}
else if(nread < 0)
{
return http->error_code = ERR_SOCKET_READ;
}
} while (1);
if(http->redirect == 1)
{
return http_internal_sync_request(http, http->redirect_url, post_data, post_data_len, user_header, user_header_len);
}
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;
}
HTTP_API const char* ft_http_sync_request(ft_http_client_t* http, const char* url, http_request_method_e m)
{
if(http == NULL)
{
return NULL;
}
http->method = m;
http->download = 0;
http->error_code = http_internal_sync_request(http, url, 0, 0, 0, 0);
return http->body;
}
HTTP_API const char* ft_http_sync_post_file(ft_http_client_t* http, const char* url, const char* filepath)
{
static const char* boundary = "-------------------------87142694621188";
return NULL;
}
HTTP_API int ft_http_sync_download_file(ft_http_client_t* http, const char* url, const char* filepath)
{
if(http == NULL)
{
return -1;
}
http->method = M_GET;
http->download = 1;
free_member(http->filename);
if(filepath != NULL)
{
http->filename = _strdup(filepath);
if(http->filename == NULL)
{
return http->error_code = ERR_OUT_MEMORY;
}
}
if(http_internal_sync_request(http, url, 0, 0, 0, 0) == ERR_OK)
{
return ERR_OK;
}
return http->error_code;
}
HTTP_API int ft_http_cancel_request(ft_http_client_t* http)
{
if(http && http->fd != HTTP_INVALID_SOCKET)
{
close_socket(http->fd);
}
return 0;
}
HTTP_API int ft_http_wait_done(ft_http_client_t* http)
{
return 0;
}
HTTP_API int ft_http_exit(ft_http_client_t* http)
{
if(http) http->exit = 1;
return 0;
}
HTTP_API int ft_http_set_data_recv_cb(ft_http_client_t* http, data_recv_cb_t cb, void* user)
{
if(http)
{
http->user = user;
http->recv_cb = cb;
}
return 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化