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