lnk commit front code
This commit is contained in:
463
include/oss_sdk/aos_transport.c
Normal file
463
include/oss_sdk/aos_transport.c
Normal file
@@ -0,0 +1,463 @@
|
||||
#include "aos_log.h"
|
||||
#include "aos_util.h"
|
||||
#include "aos_string.h"
|
||||
#include "aos_http_io.h"
|
||||
#include "aos_transport.h"
|
||||
#include "aos_crc64.h"
|
||||
|
||||
static int aos_curl_code_to_status(CURLcode code);
|
||||
static void aos_init_curl_headers(aos_curl_http_transport_t *t);
|
||||
static void aos_transport_cleanup(aos_http_transport_t *t);
|
||||
static int aos_init_curl_url(aos_curl_http_transport_t *t);
|
||||
static void aos_curl_transport_headers_done(aos_curl_http_transport_t *t);
|
||||
static int aos_curl_transport_setup(aos_curl_http_transport_t *t);
|
||||
static void aos_curl_transport_finish(aos_curl_http_transport_t *t);
|
||||
static void aos_move_transport_state(aos_curl_http_transport_t *t, aos_transport_state_e s);
|
||||
|
||||
static size_t aos_curl_default_header_callback(char *buffer, size_t size, size_t nitems, void *userdata);
|
||||
static size_t aos_curl_default_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
static size_t aos_curl_default_read_callback(char *buffer, size_t size, size_t nitems, void *instream);
|
||||
|
||||
static void aos_init_curl_headers(aos_curl_http_transport_t *t)
|
||||
{
|
||||
int pos;
|
||||
char *header;
|
||||
const aos_array_header_t *tarr;
|
||||
const aos_table_entry_t *telts;
|
||||
union aos_func_u func;
|
||||
|
||||
if (t->req->method == HTTP_PUT || t->req->method == HTTP_POST) {
|
||||
header = apr_psprintf(t->pool, "Content-Length: %" APR_INT64_T_FMT, t->req->body_len);
|
||||
t->headers = curl_slist_append(t->headers, header);
|
||||
}
|
||||
|
||||
tarr = aos_table_elts(t->req->headers);
|
||||
telts = (aos_table_entry_t*)tarr->elts;
|
||||
for (pos = 0; pos < tarr->nelts; ++pos) {
|
||||
header = apr_psprintf(t->pool, "%s: %s", telts[pos].key, telts[pos].val);
|
||||
t->headers = curl_slist_append(t->headers, header);
|
||||
}
|
||||
|
||||
func.func1 = (aos_func1_pt)curl_slist_free_all;
|
||||
aos_fstack_push(t->cleanup, t->headers, func, 1);
|
||||
}
|
||||
|
||||
static int aos_init_curl_url(aos_curl_http_transport_t *t)
|
||||
{
|
||||
int rs;
|
||||
const char *proto;
|
||||
aos_string_t querystr;
|
||||
char uristr[3*AOS_MAX_URI_LEN+1];
|
||||
|
||||
uristr[0] = '\0';
|
||||
aos_str_null(&querystr);
|
||||
|
||||
if ((rs = aos_url_encode(uristr, t->req->uri, AOS_MAX_URI_LEN)) != AOSE_OK) {
|
||||
t->controller->error_code = rs;
|
||||
t->controller->reason = "uri invalid argument.";
|
||||
return rs;
|
||||
}
|
||||
|
||||
if ((rs = aos_query_params_to_string(t->pool, t->req->query_params, &querystr)) != AOSE_OK) {
|
||||
t->controller->error_code = rs;
|
||||
t->controller->reason = "query params invalid argument.";
|
||||
return rs;
|
||||
}
|
||||
|
||||
proto = strlen(t->req->proto) != 0 ? t->req->proto : AOS_HTTP_PREFIX;
|
||||
if (querystr.len == 0) {
|
||||
t->url = apr_psprintf(t->pool, "%s%s/%s",
|
||||
proto,
|
||||
t->req->host,
|
||||
uristr);
|
||||
} else {
|
||||
t->url = apr_psprintf(t->pool, "%s%s/%s%.*s",
|
||||
proto,
|
||||
t->req->host,
|
||||
uristr,
|
||||
querystr.len,
|
||||
querystr.data);
|
||||
}
|
||||
aos_debug_log("url:%s.", t->url);
|
||||
|
||||
return AOSE_OK;
|
||||
}
|
||||
|
||||
static void aos_transport_cleanup(aos_http_transport_t *t)
|
||||
{
|
||||
int s;
|
||||
char buf[256];
|
||||
|
||||
if (t->req->file_buf != NULL && t->req->file_buf->owner) {
|
||||
aos_trace_log("close request body file.");
|
||||
if ((s = apr_file_close(t->req->file_buf->file)) != APR_SUCCESS) {
|
||||
aos_warn_log("apr_file_close failure, %s.", apr_strerror(s, buf, sizeof(buf)));
|
||||
}
|
||||
t->req->file_buf = NULL;
|
||||
}
|
||||
|
||||
if (t->resp->file_buf != NULL && t->resp->file_buf->owner) {
|
||||
aos_trace_log("close response body file.");
|
||||
if ((s = apr_file_close(t->resp->file_buf->file)) != APR_SUCCESS) {
|
||||
aos_warn_log("apr_file_close failure, %s.", apr_strerror(s, buf, sizeof(buf)));
|
||||
}
|
||||
t->resp->file_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
aos_http_transport_t *aos_curl_http_transport_create(aos_pool_t *p)
|
||||
{
|
||||
aos_func_u func;
|
||||
aos_curl_http_transport_t *t;
|
||||
|
||||
t = (aos_curl_http_transport_t *)aos_pcalloc(p, sizeof(aos_curl_http_transport_t));
|
||||
|
||||
t->pool = p;
|
||||
t->options = aos_default_http_transport_options;
|
||||
t->cleanup = aos_fstack_create(p, 5);
|
||||
|
||||
func.func1 = (aos_func1_pt)aos_transport_cleanup;
|
||||
aos_fstack_push(t->cleanup, t, func, 1);
|
||||
|
||||
t->curl = aos_request_get();
|
||||
func.func1 = (aos_func1_pt)request_release;
|
||||
aos_fstack_push(t->cleanup, t->curl, func, 1);
|
||||
|
||||
t->header_callback = aos_curl_default_header_callback;
|
||||
t->read_callback = aos_curl_default_read_callback;
|
||||
t->write_callback = aos_curl_default_write_callback;
|
||||
|
||||
return (aos_http_transport_t *)t;
|
||||
}
|
||||
|
||||
static void aos_move_transport_state(aos_curl_http_transport_t *t, aos_transport_state_e s)
|
||||
{
|
||||
if (t->state < s) {
|
||||
t->state = s;
|
||||
}
|
||||
}
|
||||
|
||||
void aos_curl_response_headers_parse(aos_pool_t *p, aos_table_t *headers, char *buffer, int len)
|
||||
{
|
||||
char *pos;
|
||||
aos_string_t str;
|
||||
aos_string_t key;
|
||||
aos_string_t value;
|
||||
|
||||
str.data = buffer;
|
||||
str.len = len;
|
||||
|
||||
aos_trip_space_and_cntrl(&str);
|
||||
|
||||
pos = aos_strlchr(str.data, str.data + str.len, ':');
|
||||
if (pos == NULL) {
|
||||
return;
|
||||
}
|
||||
key.data = str.data;
|
||||
key.len = pos - str.data;
|
||||
|
||||
pos += 1;
|
||||
value.len = str.data + str.len - pos;
|
||||
value.data = pos;
|
||||
aos_strip_space(&value);
|
||||
|
||||
apr_table_addn(headers, aos_pstrdup(p, &key), aos_pstrdup(p, &value));
|
||||
}
|
||||
|
||||
size_t aos_curl_default_header_callback(char *buffer, size_t size, size_t nitems, void *userdata)
|
||||
{
|
||||
int len;
|
||||
aos_curl_http_transport_t *t;
|
||||
|
||||
t = (aos_curl_http_transport_t *)(userdata);
|
||||
len = size * nitems;
|
||||
|
||||
if (t->controller->first_byte_time == 0) {
|
||||
t->controller->first_byte_time = apr_time_now();
|
||||
}
|
||||
|
||||
aos_curl_response_headers_parse(t->pool, t->resp->headers, buffer, len);
|
||||
|
||||
aos_move_transport_state(t, TRANS_STATE_HEADER);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void aos_curl_transport_headers_done(aos_curl_http_transport_t *t)
|
||||
{
|
||||
long http_code;
|
||||
CURLcode code;
|
||||
const char *value;
|
||||
|
||||
if (t->controller->error_code != AOSE_OK) {
|
||||
aos_debug_log("has error %d.", t->controller->error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->resp->status > 0) {
|
||||
aos_trace_log("http response status %d.", t->resp->status);
|
||||
return;
|
||||
}
|
||||
|
||||
t->resp->status = 0;
|
||||
if ((code = curl_easy_getinfo(t->curl, CURLINFO_RESPONSE_CODE, &http_code)) != CURLE_OK) {
|
||||
t->controller->reason = apr_pstrdup(t->pool, curl_easy_strerror(code));
|
||||
t->controller->error_code = AOSE_INTERNAL_ERROR;
|
||||
return;
|
||||
} else {
|
||||
t->resp->status = http_code;
|
||||
}
|
||||
|
||||
value = apr_table_get(t->resp->headers, "Content-Length");
|
||||
if (value != NULL) {
|
||||
t->resp->content_length = aos_atoi64(value);
|
||||
}
|
||||
}
|
||||
|
||||
size_t aos_curl_default_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
int len;
|
||||
int bytes;
|
||||
aos_curl_http_transport_t *t;
|
||||
|
||||
t = (aos_curl_http_transport_t *)(userdata);
|
||||
len = size * nmemb;
|
||||
|
||||
if (t->controller->first_byte_time == 0) {
|
||||
t->controller->first_byte_time = apr_time_now();
|
||||
}
|
||||
|
||||
aos_curl_transport_headers_done(t);
|
||||
|
||||
if (t->controller->error_code != AOSE_OK) {
|
||||
aos_debug_log("write callback abort");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// On HTTP error, we expect to parse an HTTP error response
|
||||
if (t->resp->status < 200 || t->resp->status > 299) {
|
||||
bytes = aos_write_http_body_memory(t->resp, ptr, len);
|
||||
assert(bytes == len);
|
||||
aos_move_transport_state(t, TRANS_STATE_BODY_IN);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
if (t->resp->type == BODY_IN_MEMORY && t->resp->body_len >= (int64_t)t->controller->options->max_memory_size) {
|
||||
t->controller->reason = apr_psprintf(t->pool,
|
||||
"receive body too big, current body size: %" APR_INT64_T_FMT ", max memory size: %" APR_INT64_T_FMT,
|
||||
t->resp->body_len, t->controller->options->max_memory_size);
|
||||
t->controller->error_code = AOSE_OVER_MEMORY;
|
||||
aos_error_log("error reason:%s, ", t->controller->reason);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((bytes = t->resp->write_body(t->resp, ptr, len)) < 0) {
|
||||
aos_debug_log("write body failure, %d.", bytes);
|
||||
t->controller->error_code = AOSE_WRITE_BODY_ERROR;
|
||||
t->controller->reason = "write body failure.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytes >= 0) {
|
||||
// progress callback
|
||||
if (NULL != t->resp->progress_callback) {
|
||||
t->resp->progress_callback(t->resp->body_len, t->resp->content_length);
|
||||
}
|
||||
|
||||
// crc
|
||||
if (t->controller->options->enable_crc) {
|
||||
t->resp->crc64 = aos_crc64(t->resp->crc64, ptr, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
aos_move_transport_state(t, TRANS_STATE_BODY_IN);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
size_t aos_curl_default_read_callback(char *buffer, size_t size, size_t nitems, void *instream)
|
||||
{
|
||||
int len;
|
||||
int bytes;
|
||||
aos_curl_http_transport_t *t;
|
||||
|
||||
t = (aos_curl_http_transport_t *)(instream);
|
||||
len = size * nitems;
|
||||
|
||||
if (t->controller->error_code != AOSE_OK) {
|
||||
aos_debug_log("abort read callback.");
|
||||
return CURL_READFUNC_ABORT;
|
||||
}
|
||||
|
||||
if ((bytes = t->req->read_body(t->req, buffer, len)) < 0) {
|
||||
aos_debug_log("read body failure, %d.", bytes);
|
||||
t->controller->error_code = AOSE_READ_BODY_ERROR;
|
||||
t->controller->reason = "read body failure.";
|
||||
return CURL_READFUNC_ABORT;
|
||||
}
|
||||
|
||||
if (bytes >= 0) {
|
||||
// progress callback
|
||||
t->req->consumed_bytes += bytes;
|
||||
if (NULL != t->req->progress_callback) {
|
||||
t->req->progress_callback(t->req->consumed_bytes, t->req->body_len);
|
||||
}
|
||||
|
||||
// crc
|
||||
if (t->controller->options->enable_crc) {
|
||||
t->req->crc64 = aos_crc64(t->req->crc64, buffer, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
aos_move_transport_state(t, TRANS_STATE_BODY_OUT);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int aos_curl_code_to_status(CURLcode code)
|
||||
{
|
||||
switch (code) {
|
||||
case CURLE_OUT_OF_MEMORY:
|
||||
return AOSE_OUT_MEMORY;
|
||||
case CURLE_COULDNT_RESOLVE_PROXY:
|
||||
case CURLE_COULDNT_RESOLVE_HOST:
|
||||
return AOSE_NAME_LOOKUP_ERROR;
|
||||
case CURLE_COULDNT_CONNECT:
|
||||
return AOSE_FAILED_CONNECT;
|
||||
case CURLE_WRITE_ERROR:
|
||||
case CURLE_OPERATION_TIMEDOUT:
|
||||
return AOSE_CONNECTION_FAILED;
|
||||
case CURLE_PARTIAL_FILE:
|
||||
return AOSE_OK;
|
||||
case CURLE_SSL_CACERT:
|
||||
return AOSE_FAILED_VERIFICATION;
|
||||
default:
|
||||
return AOSE_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static void aos_curl_transport_finish(aos_curl_http_transport_t *t)
|
||||
{
|
||||
aos_curl_transport_headers_done(t);
|
||||
|
||||
if (t->cleanup != NULL) {
|
||||
aos_fstack_destory(t->cleanup);
|
||||
t->cleanup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int aos_curl_transport_setup(aos_curl_http_transport_t *t)
|
||||
{
|
||||
CURLcode code;
|
||||
|
||||
#define curl_easy_setopt_safe(opt, val) \
|
||||
if ((code = curl_easy_setopt(t->curl, opt, val)) != CURLE_OK) { \
|
||||
t->controller->reason = apr_pstrdup(t->pool, curl_easy_strerror(code)); \
|
||||
t->controller->error_code = AOSE_FAILED_INITIALIZE; \
|
||||
aos_error_log("curl_easy_setopt failed, code:%d %s.", code, t->controller->reason); \
|
||||
return AOSE_FAILED_INITIALIZE; \
|
||||
}
|
||||
|
||||
curl_easy_setopt_safe(CURLOPT_PRIVATE, t);
|
||||
|
||||
curl_easy_setopt_safe(CURLOPT_HEADERDATA, t);
|
||||
curl_easy_setopt_safe(CURLOPT_HEADERFUNCTION, t->header_callback);
|
||||
|
||||
curl_easy_setopt_safe(CURLOPT_READDATA, t);
|
||||
curl_easy_setopt_safe(CURLOPT_READFUNCTION, t->read_callback);
|
||||
|
||||
curl_easy_setopt_safe(CURLOPT_WRITEDATA, t);
|
||||
curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, t->write_callback);
|
||||
|
||||
curl_easy_setopt_safe(CURLOPT_FILETIME, 1);
|
||||
curl_easy_setopt_safe(CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt_safe(CURLOPT_NOPROGRESS, 1);
|
||||
curl_easy_setopt_safe(CURLOPT_TCP_NODELAY, 1);
|
||||
curl_easy_setopt_safe(CURLOPT_NETRC, CURL_NETRC_IGNORED);
|
||||
|
||||
// transport options
|
||||
curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt_safe(CURLOPT_USERAGENT, t->options->user_agent);
|
||||
|
||||
// request options
|
||||
curl_easy_setopt_safe(CURLOPT_DNS_CACHE_TIMEOUT, t->controller->options->dns_cache_timeout);
|
||||
curl_easy_setopt_safe(CURLOPT_CONNECTTIMEOUT, t->controller->options->connect_timeout);
|
||||
curl_easy_setopt_safe(CURLOPT_LOW_SPEED_LIMIT, t->controller->options->speed_limit);
|
||||
curl_easy_setopt_safe(CURLOPT_LOW_SPEED_TIME, t->controller->options->speed_time);
|
||||
|
||||
aos_init_curl_headers(t);
|
||||
curl_easy_setopt_safe(CURLOPT_HTTPHEADER, t->headers);
|
||||
|
||||
if (t->controller->options->proxy_host != NULL) {
|
||||
// proxy
|
||||
curl_easy_setopt_safe(CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
||||
curl_easy_setopt_safe(CURLOPT_PROXY, t->controller->options->proxy_host);
|
||||
// authorize
|
||||
if (t->controller->options->proxy_auth != NULL) {
|
||||
curl_easy_setopt_safe(CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
|
||||
curl_easy_setopt_safe(CURLOPT_PROXYUSERPWD, t->controller->options->proxy_auth);
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == t->req->signed_url) {
|
||||
if (aos_init_curl_url(t) != AOSE_OK) {
|
||||
return t->controller->error_code;
|
||||
}
|
||||
}
|
||||
else {
|
||||
t->url = t->req->signed_url;
|
||||
}
|
||||
curl_easy_setopt_safe(CURLOPT_URL, t->url);
|
||||
|
||||
switch (t->req->method) {
|
||||
case HTTP_HEAD:
|
||||
curl_easy_setopt_safe(CURLOPT_NOBODY, 1);
|
||||
break;
|
||||
case HTTP_PUT:
|
||||
curl_easy_setopt_safe(CURLOPT_UPLOAD, 1);
|
||||
break;
|
||||
case HTTP_POST:
|
||||
curl_easy_setopt_safe(CURLOPT_POST, 1);
|
||||
break;
|
||||
case HTTP_DELETE:
|
||||
curl_easy_setopt_safe(CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
break;
|
||||
default: // HTTP_GET
|
||||
break;
|
||||
}
|
||||
|
||||
#undef curl_easy_setopt_safe
|
||||
|
||||
t->state = TRANS_STATE_INIT;
|
||||
|
||||
return AOSE_OK;
|
||||
}
|
||||
|
||||
int aos_curl_http_transport_perform(aos_http_transport_t *t_)
|
||||
{
|
||||
int ecode;
|
||||
CURLcode code;
|
||||
aos_curl_http_transport_t *t = (aos_curl_http_transport_t *)(t_);
|
||||
ecode = aos_curl_transport_setup(t);
|
||||
if (ecode != AOSE_OK) {
|
||||
return ecode;
|
||||
}
|
||||
|
||||
t->controller->start_time = apr_time_now();
|
||||
code = curl_easy_perform(t->curl);
|
||||
t->controller->finish_time = apr_time_now();
|
||||
aos_move_transport_state(t, TRANS_STATE_DONE);
|
||||
|
||||
if ((code != CURLE_OK) && (t->controller->error_code == AOSE_OK)) {
|
||||
ecode = aos_curl_code_to_status(code);
|
||||
if (ecode != AOSE_OK) {
|
||||
t->controller->error_code = ecode;
|
||||
t->controller->reason = apr_pstrdup(t->pool, curl_easy_strerror(code));
|
||||
aos_error_log("transport failure curl code:%d error:%s", code, t->controller->reason);
|
||||
}
|
||||
}
|
||||
|
||||
aos_curl_transport_finish(t);
|
||||
|
||||
return t->controller->error_code;
|
||||
}
|
||||
Reference in New Issue
Block a user