Skip to content

Commit

Permalink
rest_client: Fix handling for async timeouts
Browse files Browse the repository at this point in the history
This patch improves all rest_client async operations such that they
now correctly time out after the minimum between:

* the "rest_client.curl_timeout" modparam (seconds)
* the async() statement timeout, if any (seconds)

Credits to Bence Szigeti for helping diagnose and fix the issue

(cherry picked from commit 1ea1852)

async: Add support for module-injected timeouts

In some cases, the module may have its own timeout for the async
operation which it would prefer to force into the reactor wait loop,
possibly even have it combined with the async(..., X) statement timeout
as well, with the minimum of the two timeouts to win out.

(cherry picked from commit 03db368)
(cherry picked from commit 3e61953)
  • Loading branch information
liviuchircu committed Dec 11, 2023
1 parent 427b4a7 commit 59ec630
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 4 deletions.
3 changes: 3 additions & 0 deletions async.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ typedef struct _async_ctx {
void *resume_param;
/* the function to be called upon a timeout event while waiting to read */
void *timeout_f;
/* the maximum allowed time for the async op to complete, hinted by the
* more complex async implementation (seconds). Default: 0 (no limit) */
unsigned int timeout_s;
} async_ctx;


Expand Down
1 change: 1 addition & 0 deletions modules/rest_client/rest_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@ int async_rest_method(enum rest_client_method method, struct sip_msg *msg,
}

ctx->resume_f = resume_async_http_req;
ctx->timeout_s = curl_timeout;
ctx->timeout_f = time_out_async_http_req;

param->method = method;
Expand Down
20 changes: 17 additions & 3 deletions modules/rest_client/rest_methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,14 @@ static enum async_ret_code _resume_async_http_req(int fd, struct sip_msg *msg,

multi_handle = param->multi_list->multi_handle;

if (timed_out) {
char *url = NULL;
curl_easy_getinfo(param->handle, CURLINFO_EFFECTIVE_URL, &url);
LM_INFO("async %s timed out, URL: %s\n",
rest_client_method_str(param->method), url);
goto cleanup;
}

retr = 0;
do {
/* When @enable_expect_100 is on, both the client body upload and the
Expand All @@ -885,9 +893,15 @@ static enum async_ret_code _resume_async_http_req(int fd, struct sip_msg *msg,
LM_DBG("perform result: %d, running: %d (break: %d)\n", mrc, running,
mrc != CURLM_CALL_MULTI_PERFORM && (mrc != CURLM_OK || !running));

if (mrc != CURLM_CALL_MULTI_PERFORM &&
(mrc != CURLM_OK || !running))
if (mrc == CURLM_OK && running) {
async_status = ASYNC_CONTINUE;
return 1;

/* this rc has been removed since cURL 7.20.0 (Feb 2010), but it's not
* yet marked as deprecated, so let's keep the do/while loop */
} else if (mrc != CURLM_CALL_MULTI_PERFORM) {
break;
}

usleep(_async_resume_retr_itv);
retr += _async_resume_retr_itv;
Expand Down Expand Up @@ -930,6 +944,7 @@ static enum async_ret_code _resume_async_http_req(int fd, struct sip_msg *msg,
return 1;
}

cleanup:
curl_slist_free_all(param->header_list);

if (del_transfer(fd) != 0) {
Expand Down Expand Up @@ -1027,7 +1042,6 @@ enum async_ret_code resume_async_http_req(int fd, struct sip_msg *msg, void *_pa

enum async_ret_code time_out_async_http_req(int fd, struct sip_msg *msg, void *_param)
{
LM_INFO("transfer timed out (async statement timeout)\n");
return _resume_async_http_req(fd, msg, (rest_async_param *)_param, 1);
}

Expand Down
3 changes: 3 additions & 0 deletions modules/rest_client/rest_methods.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ enum rest_client_method {
REST_CLIENT_PUT,
REST_CLIENT_POST
};
#define rest_client_method_str(_m) ( \
(_m) == REST_CLIENT_GET ? "GET" : \
(_m) == REST_CLIENT_POST ? "POST" : "PUT")

/* return codes for rest_client script functions */
#define RCL_OK 1
Expand Down
7 changes: 6 additions & 1 deletion modules/tm/async.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,19 @@ int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route,
reset_e2eack_t();

if (async_status!=ASYNC_NO_FD) {
LM_DBG("placing async job into reactor with timeouts %d/%d\n",
timeout, ctx->async.timeout_s);

/* check if timeout should be used */
if (timeout && ctx->async.timeout_f==NULL) {
timeout = 0;
LM_ERR("this async function has no support for timeouts -- "
"still using an infinite timeout!\n");
} else if (ctx->async.timeout_f && ctx->async.timeout_s
&& ctx->async.timeout_s < timeout) {
timeout = ctx->async.timeout_s;
}

LM_DBG("placing async job into reactor with timeout %d\n", timeout);
/* place the FD + resume function (as param) into reactor */
if (reactor_add_reader_with_timeout( fd, F_SCRIPT_ASYNC,
RCT_PRIO_ASYNC, timeout, (void*)ctx)<0) {
Expand Down

0 comments on commit 59ec630

Please sign in to comment.