aos_transport.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #include "aos_log.h"
  2. #include "aos_util.h"
  3. #include "aos_string.h"
  4. #include "aos_http_io.h"
  5. #include "aos_transport.h"
  6. #include "aos_crc64.h"
  7. static int aos_curl_code_to_status(CURLcode code);
  8. static void aos_init_curl_headers(aos_curl_http_transport_t *t);
  9. static void aos_transport_cleanup(aos_http_transport_t *t);
  10. static int aos_init_curl_url(aos_curl_http_transport_t *t);
  11. static void aos_curl_transport_headers_done(aos_curl_http_transport_t *t);
  12. static int aos_curl_transport_setup(aos_curl_http_transport_t *t);
  13. static void aos_curl_transport_finish(aos_curl_http_transport_t *t);
  14. static void aos_move_transport_state(aos_curl_http_transport_t *t, aos_transport_state_e s);
  15. static size_t aos_curl_default_header_callback(char *buffer, size_t size, size_t nitems, void *userdata);
  16. static size_t aos_curl_default_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
  17. static size_t aos_curl_default_read_callback(char *buffer, size_t size, size_t nitems, void *instream);
  18. static void aos_init_curl_headers(aos_curl_http_transport_t *t)
  19. {
  20. int pos;
  21. char *header;
  22. const aos_array_header_t *tarr;
  23. const aos_table_entry_t *telts;
  24. union aos_func_u func;
  25. if (t->req->method == HTTP_PUT || t->req->method == HTTP_POST) {
  26. header = apr_psprintf(t->pool, "Content-Length: %" APR_INT64_T_FMT, t->req->body_len);
  27. t->headers = curl_slist_append(t->headers, header);
  28. }
  29. tarr = aos_table_elts(t->req->headers);
  30. telts = (aos_table_entry_t*)tarr->elts;
  31. for (pos = 0; pos < tarr->nelts; ++pos) {
  32. header = apr_psprintf(t->pool, "%s: %s", telts[pos].key, telts[pos].val);
  33. t->headers = curl_slist_append(t->headers, header);
  34. }
  35. func.func1 = (aos_func1_pt)curl_slist_free_all;
  36. aos_fstack_push(t->cleanup, t->headers, func, 1);
  37. }
  38. static int aos_init_curl_url(aos_curl_http_transport_t *t)
  39. {
  40. int rs;
  41. const char *proto;
  42. aos_string_t querystr;
  43. char uristr[3*AOS_MAX_URI_LEN+1];
  44. uristr[0] = '\0';
  45. aos_str_null(&querystr);
  46. if ((rs = aos_url_encode(uristr, t->req->uri, AOS_MAX_URI_LEN)) != AOSE_OK) {
  47. t->controller->error_code = rs;
  48. t->controller->reason = "uri invalid argument.";
  49. return rs;
  50. }
  51. if ((rs = aos_query_params_to_string(t->pool, t->req->query_params, &querystr)) != AOSE_OK) {
  52. t->controller->error_code = rs;
  53. t->controller->reason = "query params invalid argument.";
  54. return rs;
  55. }
  56. proto = strlen(t->req->proto) != 0 ? t->req->proto : AOS_HTTP_PREFIX;
  57. if (querystr.len == 0) {
  58. t->url = apr_psprintf(t->pool, "%s%s/%s",
  59. proto,
  60. t->req->host,
  61. uristr);
  62. } else {
  63. t->url = apr_psprintf(t->pool, "%s%s/%s%.*s",
  64. proto,
  65. t->req->host,
  66. uristr,
  67. querystr.len,
  68. querystr.data);
  69. }
  70. aos_debug_log("url:%s.", t->url);
  71. return AOSE_OK;
  72. }
  73. static void aos_transport_cleanup(aos_http_transport_t *t)
  74. {
  75. int s;
  76. char buf[256];
  77. if (t->req->file_buf != NULL && t->req->file_buf->owner) {
  78. aos_trace_log("close request body file.");
  79. if ((s = apr_file_close(t->req->file_buf->file)) != APR_SUCCESS) {
  80. aos_warn_log("apr_file_close failure, %s.", apr_strerror(s, buf, sizeof(buf)));
  81. }
  82. t->req->file_buf = NULL;
  83. }
  84. if (t->resp->file_buf != NULL && t->resp->file_buf->owner) {
  85. aos_trace_log("close response body file.");
  86. if ((s = apr_file_close(t->resp->file_buf->file)) != APR_SUCCESS) {
  87. aos_warn_log("apr_file_close failure, %s.", apr_strerror(s, buf, sizeof(buf)));
  88. }
  89. t->resp->file_buf = NULL;
  90. }
  91. }
  92. aos_http_transport_t *aos_curl_http_transport_create(aos_pool_t *p)
  93. {
  94. aos_func_u func;
  95. aos_curl_http_transport_t *t;
  96. t = (aos_curl_http_transport_t *)aos_pcalloc(p, sizeof(aos_curl_http_transport_t));
  97. t->pool = p;
  98. t->options = aos_default_http_transport_options;
  99. t->cleanup = aos_fstack_create(p, 5);
  100. func.func1 = (aos_func1_pt)aos_transport_cleanup;
  101. aos_fstack_push(t->cleanup, t, func, 1);
  102. t->curl = aos_request_get();
  103. func.func1 = (aos_func1_pt)request_release;
  104. aos_fstack_push(t->cleanup, t->curl, func, 1);
  105. t->header_callback = aos_curl_default_header_callback;
  106. t->read_callback = aos_curl_default_read_callback;
  107. t->write_callback = aos_curl_default_write_callback;
  108. return (aos_http_transport_t *)t;
  109. }
  110. static void aos_move_transport_state(aos_curl_http_transport_t *t, aos_transport_state_e s)
  111. {
  112. if (t->state < s) {
  113. t->state = s;
  114. }
  115. }
  116. void aos_curl_response_headers_parse(aos_pool_t *p, aos_table_t *headers, char *buffer, int len)
  117. {
  118. char *pos;
  119. aos_string_t str;
  120. aos_string_t key;
  121. aos_string_t value;
  122. str.data = buffer;
  123. str.len = len;
  124. aos_trip_space_and_cntrl(&str);
  125. pos = aos_strlchr(str.data, str.data + str.len, ':');
  126. if (pos == NULL) {
  127. return;
  128. }
  129. key.data = str.data;
  130. key.len = pos - str.data;
  131. pos += 1;
  132. value.len = str.data + str.len - pos;
  133. value.data = pos;
  134. aos_strip_space(&value);
  135. apr_table_addn(headers, aos_pstrdup(p, &key), aos_pstrdup(p, &value));
  136. }
  137. size_t aos_curl_default_header_callback(char *buffer, size_t size, size_t nitems, void *userdata)
  138. {
  139. int len;
  140. aos_curl_http_transport_t *t;
  141. t = (aos_curl_http_transport_t *)(userdata);
  142. len = size * nitems;
  143. if (t->controller->first_byte_time == 0) {
  144. t->controller->first_byte_time = apr_time_now();
  145. }
  146. aos_curl_response_headers_parse(t->pool, t->resp->headers, buffer, len);
  147. aos_move_transport_state(t, TRANS_STATE_HEADER);
  148. return len;
  149. }
  150. static void aos_curl_transport_headers_done(aos_curl_http_transport_t *t)
  151. {
  152. long http_code;
  153. CURLcode code;
  154. const char *value;
  155. if (t->controller->error_code != AOSE_OK) {
  156. aos_debug_log("has error %d.", t->controller->error_code);
  157. return;
  158. }
  159. if (t->resp->status > 0) {
  160. aos_trace_log("http response status %d.", t->resp->status);
  161. return;
  162. }
  163. t->resp->status = 0;
  164. if ((code = curl_easy_getinfo(t->curl, CURLINFO_RESPONSE_CODE, &http_code)) != CURLE_OK) {
  165. t->controller->reason = apr_pstrdup(t->pool, curl_easy_strerror(code));
  166. t->controller->error_code = AOSE_INTERNAL_ERROR;
  167. return;
  168. } else {
  169. t->resp->status = http_code;
  170. }
  171. value = apr_table_get(t->resp->headers, "Content-Length");
  172. if (value != NULL) {
  173. t->resp->content_length = aos_atoi64(value);
  174. }
  175. }
  176. size_t aos_curl_default_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
  177. {
  178. int len;
  179. int bytes;
  180. aos_curl_http_transport_t *t;
  181. t = (aos_curl_http_transport_t *)(userdata);
  182. len = size * nmemb;
  183. if (t->controller->first_byte_time == 0) {
  184. t->controller->first_byte_time = apr_time_now();
  185. }
  186. aos_curl_transport_headers_done(t);
  187. if (t->controller->error_code != AOSE_OK) {
  188. aos_debug_log("write callback abort");
  189. return 0;
  190. }
  191. // On HTTP error, we expect to parse an HTTP error response
  192. if (t->resp->status < 200 || t->resp->status > 299) {
  193. bytes = aos_write_http_body_memory(t->resp, ptr, len);
  194. assert(bytes == len);
  195. aos_move_transport_state(t, TRANS_STATE_BODY_IN);
  196. return bytes;
  197. }
  198. if (t->resp->type == BODY_IN_MEMORY && t->resp->body_len >= (int64_t)t->controller->options->max_memory_size) {
  199. t->controller->reason = apr_psprintf(t->pool,
  200. "receive body too big, current body size: %" APR_INT64_T_FMT ", max memory size: %" APR_INT64_T_FMT,
  201. t->resp->body_len, t->controller->options->max_memory_size);
  202. t->controller->error_code = AOSE_OVER_MEMORY;
  203. aos_error_log("error reason:%s, ", t->controller->reason);
  204. return 0;
  205. }
  206. if ((bytes = t->resp->write_body(t->resp, ptr, len)) < 0) {
  207. aos_debug_log("write body failure, %d.", bytes);
  208. t->controller->error_code = AOSE_WRITE_BODY_ERROR;
  209. t->controller->reason = "write body failure.";
  210. return 0;
  211. }
  212. if (bytes >= 0) {
  213. // progress callback
  214. if (NULL != t->resp->progress_callback) {
  215. t->resp->progress_callback(t->resp->body_len, t->resp->content_length);
  216. }
  217. // crc
  218. if (t->controller->options->enable_crc) {
  219. t->resp->crc64 = aos_crc64(t->resp->crc64, ptr, bytes);
  220. }
  221. }
  222. aos_move_transport_state(t, TRANS_STATE_BODY_IN);
  223. return bytes;
  224. }
  225. size_t aos_curl_default_read_callback(char *buffer, size_t size, size_t nitems, void *instream)
  226. {
  227. int len;
  228. int bytes;
  229. aos_curl_http_transport_t *t;
  230. t = (aos_curl_http_transport_t *)(instream);
  231. len = size * nitems;
  232. if (t->controller->error_code != AOSE_OK) {
  233. aos_debug_log("abort read callback.");
  234. return CURL_READFUNC_ABORT;
  235. }
  236. if ((bytes = t->req->read_body(t->req, buffer, len)) < 0) {
  237. aos_debug_log("read body failure, %d.", bytes);
  238. t->controller->error_code = AOSE_READ_BODY_ERROR;
  239. t->controller->reason = "read body failure.";
  240. return CURL_READFUNC_ABORT;
  241. }
  242. if (bytes >= 0) {
  243. // progress callback
  244. t->req->consumed_bytes += bytes;
  245. if (NULL != t->req->progress_callback) {
  246. t->req->progress_callback(t->req->consumed_bytes, t->req->body_len);
  247. }
  248. // crc
  249. if (t->controller->options->enable_crc) {
  250. t->req->crc64 = aos_crc64(t->req->crc64, buffer, bytes);
  251. }
  252. }
  253. aos_move_transport_state(t, TRANS_STATE_BODY_OUT);
  254. return bytes;
  255. }
  256. static int aos_curl_code_to_status(CURLcode code)
  257. {
  258. switch (code) {
  259. case CURLE_OUT_OF_MEMORY:
  260. return AOSE_OUT_MEMORY;
  261. case CURLE_COULDNT_RESOLVE_PROXY:
  262. case CURLE_COULDNT_RESOLVE_HOST:
  263. return AOSE_NAME_LOOKUP_ERROR;
  264. case CURLE_COULDNT_CONNECT:
  265. return AOSE_FAILED_CONNECT;
  266. case CURLE_WRITE_ERROR:
  267. case CURLE_OPERATION_TIMEDOUT:
  268. return AOSE_CONNECTION_FAILED;
  269. case CURLE_PARTIAL_FILE:
  270. return AOSE_OK;
  271. case CURLE_SSL_CACERT:
  272. return AOSE_FAILED_VERIFICATION;
  273. default:
  274. return AOSE_INTERNAL_ERROR;
  275. }
  276. }
  277. static void aos_curl_transport_finish(aos_curl_http_transport_t *t)
  278. {
  279. aos_curl_transport_headers_done(t);
  280. if (t->cleanup != NULL) {
  281. aos_fstack_destory(t->cleanup);
  282. t->cleanup = NULL;
  283. }
  284. }
  285. int aos_curl_transport_setup(aos_curl_http_transport_t *t)
  286. {
  287. CURLcode code;
  288. #define curl_easy_setopt_safe(opt, val) \
  289. if ((code = curl_easy_setopt(t->curl, opt, val)) != CURLE_OK) { \
  290. t->controller->reason = apr_pstrdup(t->pool, curl_easy_strerror(code)); \
  291. t->controller->error_code = AOSE_FAILED_INITIALIZE; \
  292. aos_error_log("curl_easy_setopt failed, code:%d %s.", code, t->controller->reason); \
  293. return AOSE_FAILED_INITIALIZE; \
  294. }
  295. curl_easy_setopt_safe(CURLOPT_PRIVATE, t);
  296. curl_easy_setopt_safe(CURLOPT_HEADERDATA, t);
  297. curl_easy_setopt_safe(CURLOPT_HEADERFUNCTION, t->header_callback);
  298. curl_easy_setopt_safe(CURLOPT_READDATA, t);
  299. curl_easy_setopt_safe(CURLOPT_READFUNCTION, t->read_callback);
  300. curl_easy_setopt_safe(CURLOPT_WRITEDATA, t);
  301. curl_easy_setopt_safe(CURLOPT_WRITEFUNCTION, t->write_callback);
  302. curl_easy_setopt_safe(CURLOPT_FILETIME, 1);
  303. curl_easy_setopt_safe(CURLOPT_NOSIGNAL, 1);
  304. curl_easy_setopt_safe(CURLOPT_NOPROGRESS, 1);
  305. curl_easy_setopt_safe(CURLOPT_TCP_NODELAY, 1);
  306. curl_easy_setopt_safe(CURLOPT_NETRC, CURL_NETRC_IGNORED);
  307. // transport options
  308. curl_easy_setopt_safe(CURLOPT_SSL_VERIFYPEER, 0);
  309. curl_easy_setopt_safe(CURLOPT_USERAGENT, t->options->user_agent);
  310. // request options
  311. curl_easy_setopt_safe(CURLOPT_DNS_CACHE_TIMEOUT, t->controller->options->dns_cache_timeout);
  312. curl_easy_setopt_safe(CURLOPT_CONNECTTIMEOUT, t->controller->options->connect_timeout);
  313. curl_easy_setopt_safe(CURLOPT_LOW_SPEED_LIMIT, t->controller->options->speed_limit);
  314. curl_easy_setopt_safe(CURLOPT_LOW_SPEED_TIME, t->controller->options->speed_time);
  315. aos_init_curl_headers(t);
  316. curl_easy_setopt_safe(CURLOPT_HTTPHEADER, t->headers);
  317. if (t->controller->options->proxy_host != NULL) {
  318. // proxy
  319. curl_easy_setopt_safe(CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  320. curl_easy_setopt_safe(CURLOPT_PROXY, t->controller->options->proxy_host);
  321. // authorize
  322. if (t->controller->options->proxy_auth != NULL) {
  323. curl_easy_setopt_safe(CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
  324. curl_easy_setopt_safe(CURLOPT_PROXYUSERPWD, t->controller->options->proxy_auth);
  325. }
  326. }
  327. if (NULL == t->req->signed_url) {
  328. if (aos_init_curl_url(t) != AOSE_OK) {
  329. return t->controller->error_code;
  330. }
  331. }
  332. else {
  333. t->url = t->req->signed_url;
  334. }
  335. curl_easy_setopt_safe(CURLOPT_URL, t->url);
  336. switch (t->req->method) {
  337. case HTTP_HEAD:
  338. curl_easy_setopt_safe(CURLOPT_NOBODY, 1);
  339. break;
  340. case HTTP_PUT:
  341. curl_easy_setopt_safe(CURLOPT_UPLOAD, 1);
  342. break;
  343. case HTTP_POST:
  344. curl_easy_setopt_safe(CURLOPT_POST, 1);
  345. break;
  346. case HTTP_DELETE:
  347. curl_easy_setopt_safe(CURLOPT_CUSTOMREQUEST, "DELETE");
  348. break;
  349. default: // HTTP_GET
  350. break;
  351. }
  352. #undef curl_easy_setopt_safe
  353. t->state = TRANS_STATE_INIT;
  354. return AOSE_OK;
  355. }
  356. int aos_curl_http_transport_perform(aos_http_transport_t *t_)
  357. {
  358. int ecode;
  359. CURLcode code;
  360. aos_curl_http_transport_t *t = (aos_curl_http_transport_t *)(t_);
  361. ecode = aos_curl_transport_setup(t);
  362. if (ecode != AOSE_OK) {
  363. return ecode;
  364. }
  365. t->controller->start_time = apr_time_now();
  366. code = curl_easy_perform(t->curl);
  367. t->controller->finish_time = apr_time_now();
  368. aos_move_transport_state(t, TRANS_STATE_DONE);
  369. if ((code != CURLE_OK) && (t->controller->error_code == AOSE_OK)) {
  370. ecode = aos_curl_code_to_status(code);
  371. if (ecode != AOSE_OK) {
  372. t->controller->error_code = ecode;
  373. t->controller->reason = apr_pstrdup(t->pool, curl_easy_strerror(code));
  374. aos_error_log("transport failure curl code:%d error:%s", code, t->controller->reason);
  375. }
  376. }
  377. aos_curl_transport_finish(t);
  378. return t->controller->error_code;
  379. }