Skip to content

Instantly share code, notes, and snippets.

@liviuchircu
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save liviuchircu/9452e4b8964b226043d6 to your computer and use it in GitHub Desktop.
Save liviuchircu/9452e4b8964b226043d6 to your computer and use it in GitHub Desktop.
rest_client local HTTP
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,
&param->handle, &param->body, ctype_pv ? &param->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