Last active
August 29, 2015 14:20
-
-
Save liviuchircu/9452e4b8964b226043d6 to your computer and use it in GitHub Desktop.
rest_client local HTTP
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 a720515..e9805c9 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; | |
@@ -195,6 +196,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, | |
@@ -310,8 +313,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"); | |
@@ -330,11 +336,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 f3922dd..fc83251 100644 | |
--- a/modules/rest_client/rest_methods.c | |
+++ b/modules/rest_client/rest_methods.c | |
@@ -52,6 +52,8 @@ 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; | |
@@ -73,6 +75,8 @@ static inline char del_transfer(int fd) | |
{ | |
int it; | |
+ LM_DBG("del fd %d\n", fd); | |
+ | |
for (it = 0; it < transfers; it++) { | |
if (fd == read_fds[it]) { | |
transfers--; | |
@@ -110,12 +114,20 @@ int start_async_http_req(struct sip_msg *msg, enum rest_client_method method, | |
struct curl_slist *list = NULL; | |
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); | |
@@ -164,46 +176,71 @@ 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 (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; | |
+ } | |
- if (max_fd != -1) { | |
- for (fd = 0; fd <= max_fd; fd++) { | |
- if (FD_ISSET(fd, &rset)) { | |
+ busy_wait = retry_time < timeout ? retry_time : timeout; | |
- 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 (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; | |
+ } | |
} | |
} | |
} | |
- } | |
- /** | |
- * 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); | |
@@ -223,7 +260,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) | |
@@ -242,7 +279,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; | |
@@ -315,7 +351,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 131350f..8859556 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