Created
May 6, 2015 16:04
-
-
Save jarrodb/abeac4447a30ff06791a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/modules/rest_client/rest_cb.c b/modules/rest_client/rest_cb.c | |
index 891834a..5bbc208 100644 | |
--- a/modules/rest_client/rest_cb.c | |
+++ b/modules/rest_client/rest_cb.c | |
@@ -48,6 +48,7 @@ size_t write_func(char *ptr, size_t size, size_t nmemb, void *body) | |
buff->s = pkg_realloc(buff->s, buff->len + len + 1); | |
if (!buff->s) { | |
+ buff->len = 0; | |
LM_ERR("No more pkg memory!\n"); | |
return E_OUT_OF_MEM; | |
} | |
diff --git a/modules/rest_client/rest_client.c b/modules/rest_client/rest_client.c | |
index c474e48..222ca52 100644 | |
--- a/modules/rest_client/rest_client.c | |
+++ b/modules/rest_client/rest_client.c | |
@@ -40,6 +40,7 @@ | |
* Module parameters | |
*/ | |
long connection_timeout = 20; | |
+long connection_timeout_ms; | |
long curl_timeout = 20; | |
char *ssl_capath; | |
@@ -199,6 +200,8 @@ static int mod_init(void) | |
{ | |
LM_DBG("Initializing...\n"); | |
+ connection_timeout_ms = connection_timeout * 1000L; | |
+ | |
curl_global_init_mem(CURL_GLOBAL_ALL, | |
osips_malloc, | |
osips_free, | |
@@ -314,8 +317,11 @@ static int w_async_rest_get(struct sip_msg *msg, async_resume_module **resume_f, | |
char *body_pv, char *ctype_pv, char *code_pv) | |
{ | |
rest_async_param *param; | |
+ pv_value_t val; | |
str url; | |
int read_fd; | |
+ long http_rc; | |
+ CURLcode rc; | |
if (fixup_get_svalue(msg, (gparam_p)gp_url, &url) != 0) { | |
LM_ERR("Invalid HTTP URL pseudo variable!\n"); | |
@@ -334,11 +340,47 @@ static int w_async_rest_get(struct sip_msg *msg, async_resume_module **resume_f, | |
read_fd = start_async_http_req(msg, REST_CLIENT_GET, url.s, NULL, NULL, | |
¶m->handle, ¶m->body, ctype_pv ? ¶m->ctype : NULL); | |
- if (read_fd < 0) { | |
+ /* error occurred; no transfer done */ | |
+ if (read_fd == ASYNC_NO_IO) { | |
*resume_param = NULL; | |
*resume_f = NULL; | |
/* keep default async status of NO_IO */ | |
return -1; | |
+ /* no need for async - transfer already completed! */ | |
+ } else if (read_fd == ASYNC_SYNC) { | |
+ val.flags = PV_VAL_STR; | |
+ val.rs = param->body; | |
+ | |
+ if (pv_set_value(msg, (pv_spec_p)body_pv, 0, &val) != 0) | |
+ LM_ERR("failed to set output body pv\n"); | |
+ | |
+ if (ctype_pv) { | |
+ val.rs = param->ctype; | |
+ if (pv_set_value(msg, (pv_spec_p)ctype_pv, 0, &val) != 0) | |
+ LM_ERR("failed to set output ctype pv\n"); | |
+ | |
+ if (param->ctype.s) | |
+ pkg_free(param->ctype.s); | |
+ } | |
+ | |
+ if (code_pv) { | |
+ rc = curl_easy_getinfo(param->handle, CURLINFO_RESPONSE_CODE, &http_rc); | |
+ if (rc != CURLE_OK) { | |
+ LM_ERR("curl_easy_getinfo: %s\n", curl_easy_strerror(rc)); | |
+ http_rc = 0; | |
+ } | |
+ | |
+ LM_DBG("Last response code: %ld\n", http_rc); | |
+ | |
+ val.flags = PV_VAL_INT|PV_TYPE_INT; | |
+ val.ri = (int)http_rc; | |
+ if (pv_set_value(msg, (pv_spec_p)code_pv, 0, &val) != 0) | |
+ LM_ERR("failed to set output code pv\n"); | |
+ } | |
+ | |
+ curl_easy_cleanup(param->handle); | |
+ | |
+ return ASYNC_SYNC; | |
} | |
*resume_f = resume_async_http_req; | |
diff --git a/modules/rest_client/rest_methods.c b/modules/rest_client/rest_methods.c | |
index 3d9fe3f..59968b0 100644 | |
--- a/modules/rest_client/rest_methods.c | |
+++ b/modules/rest_client/rest_methods.c | |
@@ -55,10 +55,14 @@ static int read_fds[FD_SETSIZE]; | |
/* libcurl's reported running handles */ | |
static int running_handles; | |
+static long sleep_on_bad_timeout = 500; /* ms */ | |
+ | |
static inline char is_new_transfer(int fd) | |
{ | |
int it; | |
+ LM_DBG("del fd %d\n", fd); | |
+ | |
for (it = 0; it < transfers; it++) { | |
if (fd == read_fds[it]) | |
return 0; | |
@@ -112,12 +116,20 @@ int start_async_http_req(struct sip_msg *msg, enum rest_client_method method, | |
CURLMcode mrc; | |
fd_set rset, wset, eset; | |
int max_fd, fd, i; | |
- long lim, check_time; | |
+ long busy_wait, timeout; | |
+ long retry_time, check_time = 5; /* 5ms looping time */ | |
+ int msgs_in_queue; | |
+ CURLMsg *cmsg; | |
+ | |
+ if (transfers == FD_SETSIZE) { | |
+ LM_ERR("too many ongoing tranfers: %d\n", FD_SETSIZE); | |
+ return ASYNC_NO_IO; | |
+ } | |
handle = curl_easy_init(); | |
if (!handle) { | |
LM_ERR("Init curl handle failed!\n"); | |
- return -1; | |
+ return ASYNC_NO_IO; | |
} | |
w_curl_easy_setopt(handle, CURLOPT_URL, url); | |
@@ -169,46 +181,70 @@ int start_async_http_req(struct sip_msg *msg, enum rest_client_method method, | |
curl_multi_add_handle(multi_handle, handle); | |
- check_time = 10000; | |
+ timeout = connection_timeout_ms; | |
/* obtain a read fd in "connection_timeout" seconds at worst */ | |
- for (i = 0, lim = connection_timeout * 1000000L / check_time; i < lim; i++) { | |
- | |
+ for (timeout = connection_timeout_ms; timeout > 0; timeout -= busy_wait) { | |
mrc = curl_multi_perform(multi_handle, &running_handles); | |
if (mrc != CURLM_OK) { | |
LM_ERR("curl_multi_perform: %s\n", curl_multi_strerror(mrc)); | |
goto error; | |
} | |
- LM_DBG("running handles: %d\n", running_handles); | |
- LM_DBG("DATA:\n%.*s\n", body->len, body->s); | |
- | |
- FD_ZERO(&rset); | |
- mrc = curl_multi_fdset(multi_handle, &rset, &wset, &eset, &max_fd); | |
+ mrc = curl_multi_timeout(multi_handle, &retry_time); | |
if (mrc != CURLM_OK) { | |
- LM_ERR("curl_multi_fdset: %s\n", curl_multi_strerror(mrc)); | |
+ LM_ERR("curl_multi_timeout: %s\n", curl_multi_strerror(mrc)); | |
goto error; | |
} | |
- LM_DBG("max fd: %d\n", max_fd); | |
- | |
- if (max_fd != -1) { | |
- for (fd = 0; fd <= max_fd; fd++) { | |
- if (FD_ISSET(fd, &rset)) { | |
+ if (retry_time == -1) { | |
+ LM_INFO("curl_multi_timeout() returned -1, pausing %ldms...\n", | |
+ sleep_on_bad_timeout); | |
+ busy_wait = sleep_on_bad_timeout; | |
+ usleep(1000UL * busy_wait); | |
+ continue; | |
+ } | |
+ | |
+ busy_wait = retry_time < timeout ? retry_time : timeout; | |
+ | |
+ /** | |
+ * libcurl is currently stuck in internal operations (connect) | |
+ * we have to wait a bit until we receive a read fd | |
+ */ | |
+ for (i = 0; i < busy_wait; i += check_time) { | |
+ /* transfer may have already been completed!! */ | |
+ while ((cmsg = curl_multi_info_read(multi_handle, &msgs_in_queue))) { | |
+ if (cmsg->easy_handle == handle && cmsg->msg == CURLMSG_DONE) { | |
+ LM_DBG("done, no need for async!\n"); | |
+ if (list) | |
+ curl_slist_free_all(list); | |
+ | |
+ *out_handle = handle; | |
+ return ASYNC_SYNC; | |
+ } | |
+ } | |
+ | |
+ FD_ZERO(&rset); | |
+ mrc = curl_multi_fdset(multi_handle, &rset, &wset, &eset, &max_fd); | |
+ if (mrc != CURLM_OK) { | |
+ LM_ERR("curl_multi_fdset: %s\n", curl_multi_strerror(mrc)); | |
+ goto error; | |
+ } | |
+ | |
+ if (max_fd != -1) { | |
+ for (fd = 0; fd <= max_fd; fd++) { | |
+ if (FD_ISSET(fd, &rset)) { | |
+ | |
+ LM_DBG(" >>>>>>>>>> fd %d ISSET(read)\n", fd); | |
+ if (is_new_transfer(fd)) { | |
+ LM_DBG("add fd to read list: %d\n", fd); | |
+ add_transfer(fd); | |
+ goto success; | |
+ } | |
- LM_DBG(" >>>>>>>>>> fd %d ISSET(read)\n", fd); | |
- if (is_new_transfer(fd)) { | |
- LM_DBG("add fd to read list: %d\n", fd); | |
- add_transfer(fd); | |
- goto success; | |
} | |
} | |
- } | |
} | |
- | |
- /** | |
- * libcurl is currently stuck in internal operations - probably connect() | |
- * we have to poll it a few times until it gives us a read fd | |
- */ | |
- usleep(check_time); | |
+ usleep(1000UL * check_time); | |
+ } | |
} | |
LM_ERR("timeout while connecting to '%s' (%ld sec)\n", url, connection_timeout); | |
@@ -233,7 +269,7 @@ error: | |
cleanup: | |
curl_easy_cleanup(handle); | |
- return -1; | |
+ return ASYNC_NO_IO; | |
} | |
enum async_ret_code resume_async_http_req(int fd, struct sip_msg *msg, void *_param) | |
@@ -252,7 +288,6 @@ enum async_ret_code resume_async_http_req(int fd, struct sip_msg *msg, void *_pa | |
return -1; | |
} | |
LM_DBG("running handles: %d\n", running); | |
- LM_DBG("DATA:\n%.*s\n", param->body.len, param->body.s); | |
if (running == running_handles) { | |
async_status = ASYNC_CONTINUE; | |
@@ -325,7 +360,7 @@ enum async_ret_code resume_async_http_req(int fd, struct sip_msg *msg, void *_pa | |
rc = curl_easy_getinfo(param->handle, CURLINFO_RESPONSE_CODE, &http_rc); | |
if (rc != CURLE_OK) { | |
LM_ERR("curl_easy_getinfo: %s\n", curl_easy_strerror(rc)); | |
- http_rc = -1; | |
+ http_rc = 0; | |
} | |
LM_DBG("Last response code: %ld\n", http_rc); | |
diff --git a/modules/rest_client/rest_methods.h b/modules/rest_client/rest_methods.h | |
index 149155c..45d626c 100644 | |
--- a/modules/rest_client/rest_methods.h | |
+++ b/modules/rest_client/rest_methods.h | |
@@ -33,6 +33,7 @@ | |
extern CURLM *multi_handle; | |
extern long connection_timeout; | |
+extern long connection_timeout_ms; | |
extern long curl_timeout; | |
extern char *ssl_capath; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment