Skip to content

Commit 5dd88a0

Browse files
committed
support HTTP retries and return HTTP status from oauth2_token_verify
- http: add support for HTTP retries (default 1) and HTTP retry interval (300 ms default) - api: return HTTP status code from oauth2_token_verify Signed-off-by: Hans Zandbelt <[email protected]>
1 parent cde2c4e commit 5dd88a0

File tree

18 files changed

+235
-46
lines changed

18 files changed

+235
-46
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
12/21/2025
2+
- http: add support for HTTP retries (default 1) and HTTP retry interval (300 ms default)
3+
- api: return HTTP status code from oauth2_token_verify
4+
15
08/28/2025
26
- add oauth2_ipc_thread_mutex_t and use it for Redis, cURL and global lists
37
to improve performance across multiple processes running on the same host

include/oauth2/cfg.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ oauth2_flag_t
133133
oauth2_cfg_endpoint_get_ssl_verify(const oauth2_cfg_endpoint_t *cfg);
134134
oauth2_uint_t
135135
oauth2_cfg_endpoint_get_http_timeout(const oauth2_cfg_endpoint_t *cfg);
136+
oauth2_uint_t
137+
oauth2_cfg_endpoint_get_http_retries(const oauth2_cfg_endpoint_t *cfg);
138+
oauth2_uint_t
139+
oauth2_cfg_endpoint_get_http_retry_interval(const oauth2_cfg_endpoint_t *cfg);
136140
const char *
137141
oauth2_cfg_endpoint_get_outgoing_proxy(const oauth2_cfg_endpoint_t *cfg);
138142

include/oauth2/http.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ca_info, char *)
213213
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_cert, char *)
214214
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_key, char *)
215215
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, timeout, int)
216+
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, retries, int)
217+
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, retry_interval, int)
216218
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_verify, bool)
217219
OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, call_ctx, cookie)
218220
OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, call_ctx, hdr)

include/oauth2/oauth2.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ bool oauth2_http_ctx_auth_add(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
7878

7979
bool oauth2_token_verify(oauth2_log_t *log, oauth2_http_request_t *request,
8080
oauth2_cfg_token_verify_t *verify, const char *token,
81-
json_t **json_payload);
81+
json_t **json_payload,
82+
oauth2_http_status_code_t *status_code);
8283

8384
#endif /* _OAUTH2_H_ */

include/oauth2/openidc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, token_endpoint_auth,
118118
oauth2_cfg_endpoint_auth_t *)
119119
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, ssl_verify, oauth2_flag_t)
120120
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_timeout, oauth2_uint_t)
121+
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_retries, oauth2_uint_t)
122+
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_retry_interval,
123+
oauth2_uint_t)
121124

122125
char *oauth2_openidc_client_set_options(oauth2_log_t *log,
123126
oauth2_cfg_openidc_t *cfg,

include/oauth2/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ char *oauth2_strdup(const char *src);
134134
char *oauth2_strndup(const char *src, size_t len);
135135
char *oauth2_stradd(char *src, const char *add1, const char *add2,
136136
const char *add3);
137+
char *oauth2_intadd(char *src, const char *add1, const char *add2, int i);
137138
char *oauth2_getword(const char **line, char stop);
138139

139140
size_t oauth2_base64url_encode(oauth2_log_t *log, const uint8_t *src,

src/cfg/proto_cfg.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ oauth2_cfg_endpoint_t *oauth2_cfg_endpoint_init(oauth2_log_t *log)
4040
endpoint->auth = NULL;
4141
endpoint->ssl_verify = OAUTH2_CFG_FLAG_UNSET;
4242
endpoint->http_timeout = OAUTH2_CFG_UINT_UNSET;
43+
endpoint->http_retries = OAUTH2_CFG_UINT_UNSET;
44+
endpoint->http_retry_interval = OAUTH2_CFG_UINT_UNSET;
4345
endpoint->outgoing_proxy = NULL;
4446

4547
end:
@@ -80,6 +82,8 @@ oauth2_cfg_endpoint_clone(oauth2_log_t *log, const oauth2_cfg_endpoint_t *src)
8082
dst->auth = oauth2_cfg_endpoint_auth_clone(log, src->auth);
8183
dst->ssl_verify = src->ssl_verify;
8284
dst->http_timeout = src->http_timeout;
85+
dst->http_retries = src->http_retries;
86+
dst->http_retry_interval = src->http_retry_interval;
8387
dst->outgoing_proxy = oauth2_strdup(src->outgoing_proxy);
8488

8589
end:
@@ -88,6 +92,8 @@ oauth2_cfg_endpoint_clone(oauth2_log_t *log, const oauth2_cfg_endpoint_t *src)
8892

8993
#define OAUTH2_CFG_ENDPOINT_SSL_VERIFY_DEFAULT 1
9094
#define OAUTH2_CFG_ENDPOINT_HTTP_TIMEOUT_DEFAULT 20
95+
#define OAUTH2_CFG_ENDPOINT_HTTP_RETRIES_DEFAULT 1
96+
#define OAUTH2_CFG_ENDPOINT_HTTP_RETRY_INTERVAL_DEFAULT 300
9197

9298
char *oauth2_cfg_set_endpoint(oauth2_log_t *log, oauth2_cfg_endpoint_t *cfg,
9399
const char *url, const oauth2_nv_list_t *params,
@@ -144,6 +150,29 @@ char *oauth2_cfg_set_endpoint(oauth2_log_t *log, oauth2_cfg_endpoint_t *cfg,
144150
}
145151
oauth2_mem_free(key);
146152

153+
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
154+
"http_retries");
155+
value = oauth2_nv_list_get(log, params, key);
156+
if (value) {
157+
rv = oauth2_strdup(oauth2_cfg_set_uint_slot(
158+
cfg, offsetof(oauth2_cfg_endpoint_t, http_retries), value));
159+
if (rv)
160+
goto end;
161+
}
162+
oauth2_mem_free(key);
163+
164+
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
165+
"http_retry_interval");
166+
value = oauth2_nv_list_get(log, params, key);
167+
if (value) {
168+
rv = oauth2_strdup(oauth2_cfg_set_uint_slot(
169+
cfg, offsetof(oauth2_cfg_endpoint_t, http_retry_interval),
170+
value));
171+
if (rv)
172+
goto end;
173+
}
174+
oauth2_mem_free(key);
175+
147176
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
148177
"outgoing_proxy");
149178
value = oauth2_nv_list_get(log, params, key);
@@ -202,6 +231,23 @@ oauth2_cfg_endpoint_get_http_timeout(const oauth2_cfg_endpoint_t *cfg)
202231
return cfg->http_timeout;
203232
}
204233

234+
oauth2_uint_t
235+
oauth2_cfg_endpoint_get_http_retries(const oauth2_cfg_endpoint_t *cfg)
236+
{
237+
if ((cfg == NULL) || (cfg->http_retries == OAUTH2_CFG_UINT_UNSET))
238+
return OAUTH2_CFG_ENDPOINT_HTTP_RETRIES_DEFAULT;
239+
return cfg->http_retries;
240+
}
241+
242+
oauth2_uint_t
243+
oauth2_cfg_endpoint_get_http_retry_interval(const oauth2_cfg_endpoint_t *cfg)
244+
{
245+
if ((cfg == NULL) ||
246+
(cfg->http_retry_interval == OAUTH2_CFG_UINT_UNSET))
247+
return OAUTH2_CFG_ENDPOINT_HTTP_RETRY_INTERVAL_DEFAULT;
248+
return cfg->http_retry_interval;
249+
}
250+
205251
const char *
206252
oauth2_cfg_endpoint_get_outgoing_proxy(const oauth2_cfg_endpoint_t *cfg)
207253
{

src/cfg_int.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ typedef struct oauth2_cfg_endpoint_t {
4848
oauth2_cfg_endpoint_auth_t *auth;
4949
oauth2_flag_t ssl_verify;
5050
oauth2_uint_t http_timeout;
51+
oauth2_uint_t http_retries;
52+
oauth2_uint_t http_retry_interval;
5153
char *outgoing_proxy;
5254
} oauth2_cfg_endpoint_t;
5355

src/http.c

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <curl/curl.h>
2222
#include <stdio.h>
2323
#include <string.h>
24+
#include <unistd.h>
2425

2526
#include "oauth2/http.h"
2627
#include "oauth2/mem.h"
@@ -604,6 +605,8 @@ typedef struct oauth2_http_call_ctx_t {
604605
char *basic_auth_password;
605606
char *bearer_token;
606607
int timeout;
608+
int retries;
609+
int retry_interval;
607610
bool ssl_verify;
608611
char *outgoing_proxy;
609612
oauth2_nv_list_t *cookie;
@@ -615,6 +618,8 @@ typedef struct oauth2_http_call_ctx_t {
615618
} oauth2_http_call_ctx_t;
616619

617620
#define OAUTH2_HTTP_CALL_TIMEOUT_DEFAULT 15
621+
#define OAUTH2_HTTP_CALL_RETRIES_DEFAULT 1
622+
#define OAUTH2_HTTP_CALL_RETRY_INTERVAL_DEFAULT 300
618623
#define OAUTH2_HTTP_CALL_SSL_VERIFY_DEFAULT true
619624

620625
oauth2_http_call_ctx_t *oauth2_http_call_ctx_init(oauth2_log_t *log)
@@ -628,6 +633,10 @@ oauth2_http_call_ctx_t *oauth2_http_call_ctx_init(oauth2_log_t *log)
628633

629634
oauth2_http_call_ctx_timeout_set(log, ctx,
630635
OAUTH2_HTTP_CALL_TIMEOUT_DEFAULT);
636+
oauth2_http_call_ctx_retries_set(log, ctx,
637+
OAUTH2_HTTP_CALL_RETRIES_DEFAULT);
638+
oauth2_http_call_ctx_retry_interval_set(
639+
log, ctx, OAUTH2_HTTP_CALL_RETRY_INTERVAL_DEFAULT);
631640
oauth2_http_call_ctx_ssl_verify_set(
632641
log, ctx, OAUTH2_HTTP_CALL_SSL_VERIFY_DEFAULT);
633642
oauth2_http_call_ctx_outgoing_proxy_set(log, ctx, NULL);
@@ -680,6 +689,8 @@ void oauth2_http_call_ctx_free(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx)
680689
}
681690

682691
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, timeout, int, integer)
692+
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, retries, int, integer)
693+
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, retry_interval, int, integer)
683694
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, ssl_verify, bool, bln)
684695
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, outgoing_proxy, char *, str)
685696
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, ca_info, char *, str)
@@ -770,6 +781,13 @@ static char *_oauth2_http_call_ctx2s(oauth2_log_t *log,
770781
ctx->to_str = oauth2_stradd(ctx->to_str, " ssl_key",
771782
_OAUTH2_STR_EQUAL, ctx->ssl_key);
772783

784+
ctx->to_str = oauth2_intadd(ctx->to_str, " timeout", _OAUTH2_STR_EQUAL,
785+
ctx->timeout);
786+
ctx->to_str = oauth2_intadd(ctx->to_str, " retries", _OAUTH2_STR_EQUAL,
787+
ctx->retries);
788+
ctx->to_str = oauth2_intadd(ctx->to_str, " retry_interval",
789+
_OAUTH2_STR_EQUAL, ctx->retry_interval);
790+
773791
ptr = oauth2_nv_list2s(log, ctx->hdr);
774792
if (ptr) {
775793
ctx->to_str =
@@ -970,6 +988,8 @@ bool oauth2_http_call(oauth2_log_t *log, const char *url, const char *data,
970988
bool rc = false;
971989
char *str = NULL;
972990
long response_code = 0;
991+
int i = 0;
992+
int retries = 1;
973993

974994
char err[CURL_ERROR_SIZE];
975995
CURL *curl = NULL;
@@ -1095,29 +1115,54 @@ bool oauth2_http_call(oauth2_log_t *log, const char *url, const char *data,
10951115

10961116
curl_easy_setopt(curl, CURLOPT_URL, url);
10971117

1098-
errornum = curl_easy_perform(curl);
1099-
if (errornum != CURLE_OK) {
1100-
oauth2_error(log, "curl_easy_perform() failed on: %s (%s: %s)",
1101-
url, curl_easy_strerror(errornum),
1102-
err[0] ? err : "");
1103-
if (errornum == CURLE_OPERATION_TIMEDOUT)
1118+
if (ctx)
1119+
retries = ctx->retries;
1120+
1121+
oauth2_debug(log, "looping: i=%d, retries=%d", i, retries);
1122+
1123+
for (i = 0; i <= retries; i++) {
1124+
oauth2_debug(log, "looping: i=%d, retries=%d", i, retries);
1125+
errornum = curl_easy_perform(curl);
1126+
if (errornum == CURLE_OK) {
1127+
rc = true;
1128+
break;
1129+
}
1130+
if (errornum == CURLE_OPERATION_TIMEDOUT) {
1131+
/* in case of a request/transfer timeout (which includes
1132+
* the connect timeout) we'll not retry */
1133+
oauth2_error(log,
1134+
"curl_easy_perform failed with a timeout "
1135+
"for %s: [%s; %s]; won't retry",
1136+
url, curl_easy_strerror(errornum),
1137+
err[0] ? err : "");
11041138
// 408 Request Timeout
11051139
// 504 Gateway Timeout
11061140
*status_code = 504;
1107-
goto end;
1141+
break;
1142+
}
1143+
oauth2_error(
1144+
log,
1145+
"curl_easy_perform(%d/%d) failed for: %s with [%s: %s]",
1146+
i + 1, retries + 1, url, curl_easy_strerror(errornum),
1147+
err[0] ? err : "");
1148+
/* in case of a connectivity/network glitch we'll back off
1149+
* before retrying */
1150+
if (i < retries)
1151+
usleep((ctx ? ctx->retry_interval : 300) * 1000);
11081152
}
11091153

11101154
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
11111155
oauth2_debug(log, "HTTP response code=%ld", response_code);
11121156
if (status_code)
11131157
*status_code = (oauth2_uint_t)response_code;
11141158

1159+
if (rc == false)
1160+
goto end;
1161+
11151162
*response = oauth2_mem_alloc(buf.size + 1);
11161163
strncpy(*response, buf.memory, buf.size);
11171164
(*response)[buf.size] = '\0';
11181165

1119-
rc = true;
1120-
11211166
end:
11221167

11231168
if (buf.memory)
@@ -1126,8 +1171,9 @@ bool oauth2_http_call(oauth2_log_t *log, const char *url, const char *data,
11261171
curl_slist_free_all(h_list);
11271172
curl_easy_cleanup(curl);
11281173

1129-
oauth2_debug(log, "leave [%d]: %s", rc,
1130-
(response && *response) ? *response : "(null)");
1174+
oauth2_debug(log, "leave [%d]: %s (status=%d)", rc,
1175+
(response && *response) ? *response : "(null)",
1176+
status_code ? *status_code : -1);
11311177

11321178
return rc;
11331179
}

src/oauth2.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,8 @@ static bool oauth2_mtls_token_verify(oauth2_log_t *log,
864864

865865
bool oauth2_token_verify(oauth2_log_t *log, oauth2_http_request_t *request,
866866
oauth2_cfg_token_verify_t *verify, const char *token,
867-
json_t **json_payload)
867+
json_t **json_payload,
868+
oauth2_http_status_code_t *status_code)
868869
{
869870

870871
bool rc = false;

0 commit comments

Comments
 (0)