From: Nathan Ward Date: Thu, 8 Jul 2021 14:15:20 +0000 (+1200) Subject: Fix chunked rlm_rest HTTP body (#4131) X-Git-Tag: release_3_0_24~185 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c6e2717ccea297b9bd161a576bae98932cab70e;p=thirdparty%2Ffreeradius-server.git Fix chunked rlm_rest HTTP body (#4131) * Fix chunked HTTP bodies in rlm_rest by passing section (configuration) around in the request object rather than to the rest_encode_* functions directly. Fixes #4130 * Document the rlm_rest chunk configuration parameter --- diff --git a/raddb/mods-available/rest b/raddb/mods-available/rest index 36b01c6d93e..ac163f01fa9 100644 --- a/raddb/mods-available/rest +++ b/raddb/mods-available/rest @@ -90,6 +90,11 @@ rest { # password - Password to use for authentication, will be expanded. # require_auth - Require HTTP authentication. # timeout - HTTP request timeout in seconds, defaults to 4.0. + # chunk - Chunk size to use. If set, HTTP chunked encoding is used to + # send data to the REST server. Make sure that this is large + # enough to fit your largest attribute value's text + #  representation. + # A number like 8192 is good. # # Additional HTTP headers may be specified with control:REST-HTTP-Header. # The values of those attributes should be in the format: diff --git a/src/modules/rlm_rest/rest.c b/src/modules/rlm_rest/rest.c index e307b7275c0..15490842252 100644 --- a/src/modules/rlm_rest/rest.c +++ b/src/modules/rlm_rest/rest.c @@ -504,12 +504,10 @@ int mod_conn_alive(void *instance, void *handle) * @param[in] size Multiply by nmemb to get the length of ptr. * @param[in] nmemb Multiply by size to get the length of ptr. * @param[in] userdata rlm_rest_request_t to keep encoding state between calls. - * @param[in] section configuration data. * @return length of data (including NULL) written to ptr, or 0 if no more * data to write. */ -static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *userdata, - UNUSED rlm_rest_section_t *section) +static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *userdata) { rlm_rest_request_t *ctx = userdata; rest_custom_data_t *data = ctx->encoder; @@ -552,12 +550,10 @@ static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *use * @param[in] size Multiply by nmemb to get the length of ptr. * @param[in] nmemb Multiply by size to get the length of ptr. * @param[in] userdata rlm_rest_request_t to keep encoding state between calls. - * @param[in] section configuration data. * @return length of data (including NULL) written to ptr, or 0 if no more * data to write. */ -static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata, - UNUSED rlm_rest_section_t *section) +static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata) { rlm_rest_request_t *ctx = userdata; REQUEST *request = ctx->request; /* Used by RDEBUG */ @@ -656,7 +652,7 @@ static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userd ctx->state = READ_STATE_END; break; } - + if (freespace < 1) goto no_space; *p++ = '&'; freespace--; @@ -743,12 +739,10 @@ no_space: * @param[in] size Multiply by nmemb to get the length of ptr. * @param[in] nmemb Multiply by size to get the length of ptr. * @param[in] userdata rlm_rest_request_t to keep encoding state between calls. - * @param[in] section configuration data. * @return length of data (including NULL) written to ptr, or 0 if no more * data to write. */ -static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata, - rlm_rest_section_t *section) +static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata) { rlm_rest_request_t *ctx = userdata; REQUEST *request = ctx->request; /* Used by RDEBUG */ @@ -807,7 +801,7 @@ static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userd type = fr_int2str(dict_attr_types, vp->da->type, ""); - if (section->attr_num) { + if (ctx->section->attr_num) { len = snprintf(p, freespace + 1, "\"%s\":{\"attr_num\":%d,\"type\":\"%s\",\"value\":[", vp->da->name, vp->da->attr, type); } else { @@ -849,7 +843,7 @@ static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userd * write that out. */ attr_space = fr_cursor_next_peek(&ctx->cursor) ? freespace - 1 : freespace; - len = vp_prints_value_json(p, attr_space + 1, vp, section->raw_value); + len = vp_prints_value_json(p, attr_space + 1, vp, ctx->section->raw_value); if (is_truncated(len, attr_space + 1)) goto no_space; /* @@ -959,12 +953,10 @@ no_space: * @param[in] limit Maximum buffer size to alloc. * @param[in] userdata rlm_rest_request_t to keep encoding state between calls to * stream function. - * @param[in] section configuration data. * @return the length of the data written to the buffer (excluding NULL) or -1 * if alloc >= limit. */ -static ssize_t rest_request_encode_wrapper(char **buffer, rest_read_t func, size_t limit, void *userdata, - rlm_rest_section_t *section) +static ssize_t rest_request_encode_wrapper(char **buffer, rest_read_t func, size_t limit, void *userdata) { char *previous = NULL; char *current = NULL; @@ -981,7 +973,7 @@ static ssize_t rest_request_encode_wrapper(char **buffer, rest_read_t func, size free(previous); } - len = func(current + used, alloc - used, 1, userdata, section); + len = func(current + used, alloc - used, 1, userdata); used += len; if (!len) { *buffer = current; @@ -1995,7 +1987,7 @@ static int rest_request_config_body(UNUSED rlm_rest_t *instance, rlm_rest_sectio * If were not doing chunked encoding then we read the entire * body into a buffer, and send it in one go. */ - len = rest_request_encode_wrapper(&ctx->body, func, REST_BODY_MAX_LEN, &ctx->request, section); + len = rest_request_encode_wrapper(&ctx->body, func, REST_BODY_MAX_LEN, &ctx->request); if (len <= 0) { REDEBUG("Failed creating HTTP body content"); return -1; @@ -2082,6 +2074,9 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section, ctx->headers = curl_slist_append(ctx->headers, buffer); if (!ctx->headers) goto error_header; + // Pass configuration to the request + ctx->request.section = section; + SET_OPTION(CURLOPT_CONNECTTIMEOUT_MS, instance->connect_timeout); SET_OPTION(CURLOPT_TIMEOUT_MS, section->timeout); diff --git a/src/modules/rlm_rest/rest.h b/src/modules/rlm_rest/rest.h index d077ccc5c2c..4c41cf46c28 100644 --- a/src/modules/rlm_rest/rest.h +++ b/src/modules/rlm_rest/rest.h @@ -221,6 +221,8 @@ typedef struct rlm_rest_request_t { size_t chunk; //!< Chunk size + rlm_rest_section_t *section; //!< Configuration data + void *encoder; //!< Encoder specific data. } rlm_rest_request_t; @@ -271,7 +273,7 @@ typedef struct rlm_rest_handle_t { * CURLOPT_READFUNCTION prototype. */ typedef size_t (*rest_read_t)(void *ptr, size_t size, size_t nmemb, - void *userdata, rlm_rest_section_t *section); + void *userdata); /* * Connection API callbacks