header api: add guards

Add API guards for curl_easy_header and curl_easy_nextheader.

urldata.h: remove prevhead member as not used

Closes #22530
This commit is contained in:
Stefan Eissing 2026-08-10 13:47:47 +02:00 committed by Daniel Stenberg
parent 743096a496
commit e9b291cf9b
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
5 changed files with 190 additions and 96 deletions

View file

@ -43,6 +43,8 @@ static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = {
{ CURL_EAPI_FN_easy_cleanup, 1, 0, 0, 0 },
{ CURL_EAPI_FN_easy_duphandle, 0, 1, 0, 0 },
{ CURL_EAPI_FN_easy_getinfo, 0, 1, 0, 0 },
{ CURL_EAPI_FN_easy_header, 0, 1, 0, 0 },
{ CURL_EAPI_FN_easy_nextheader, 0, 1, 0, 0 },
{ CURL_EAPI_FN_easy_pause, 0, 1, 1, 0 },
{ CURL_EAPI_FN_easy_perform_ev, 0, 0, 0, 1 },
{ CURL_EAPI_FN_easy_perform, 0, 0, 0, 1 },
@ -303,6 +305,24 @@ void Curl_eapi_leave(struct Curl_eapi_guard *guard)
}
}
CURLHcode Curl_eapi_hcode(CURLcode result)
{
switch(result) {
case CURLE_OK:
return CURLHE_OK;
case CURLE_BAD_FUNCTION_ARGUMENT:
return CURLHE_BAD_ARGUMENT;
case CURLE_OUT_OF_MEMORY:
return CURLHE_OUT_OF_MEMORY;
case CURLE_NOT_BUILT_IN:
return CURLHE_NOT_BUILT_IN;
default:
/* Unfortunately, we cannot convert RECURSIVE_API_CALL,
* but since the header API is reentrant, this should not happen. */
return CURLHE_BAD_ARGUMENT;
}
}
bool Curl_mapi_enter(struct Curl_mapi_guard *guard,
CURLM *m,
Curl_mapi_fn fn,

View file

@ -57,6 +57,8 @@ typedef enum {
CURL_EAPI_FN_easy_cleanup,
CURL_EAPI_FN_easy_duphandle,
CURL_EAPI_FN_easy_getinfo,
CURL_EAPI_FN_easy_header,
CURL_EAPI_FN_easy_nextheader,
CURL_EAPI_FN_easy_pause,
CURL_EAPI_FN_easy_perform_ev,
CURL_EAPI_FN_easy_perform,
@ -151,6 +153,9 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard,
CURLcode *presult);
void Curl_eapi_leave(struct Curl_eapi_guard *guard);
/* Convert an EAPI failure to a header API result */
CURLHcode Curl_eapi_hcode(CURLcode result);
/* Curl_eapi_enter() checks for curl being NULL, but windows compiler
* analyzers do not realize this. *sigh* */
#define CURL_EAPI_ENTER(g, curl, fn, r) \

View file

@ -59,62 +59,84 @@ CURLHcode curl_easy_header(CURL *curl,
int request,
struct curl_header **hout)
{
struct Curl_llist_node *e;
struct Curl_llist_node *e_pick = NULL;
struct Curl_easy *data = curl;
size_t match = 0;
size_t amount = 0;
struct Curl_header_store *hs = NULL;
struct Curl_header_store *pick = NULL;
if(!name || !hout || !data ||
(origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
CURLH_PSEUDO)) || !origin || (request < -1))
return CURLHE_BAD_ARGUMENT;
if(!Curl_llist_count(&data->state.httphdrs))
return CURLHE_NOHEADERS; /* no headers available */
if(request > data->state.requests)
return CURLHE_NOREQUEST;
if(request == -1)
request = data->state.requests;
struct Curl_eapi_guard guard;
CURLHcode hresult = CURLHE_OK;
CURLcode result;
/* we need a first round to count amount of this header */
for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
hs = Curl_node_elem(e);
if(curl_strequal(hs->name, name) &&
(hs->type & origin) &&
(hs->request == request)) {
amount++;
pick = hs;
e_pick = e;
if(CURL_EAPI_ENTER(&guard, curl, easy_header, &result)) {
struct Curl_easy *data = curl;
struct Curl_llist_node *e;
struct Curl_llist_node *e_pick = NULL;
size_t match = 0;
size_t amount = 0;
struct Curl_header_store *hs = NULL;
struct Curl_header_store *pick = NULL;
if(!name || !hout || !data ||
(origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
CURLH_PSEUDO)) || !origin || (request < -1)) {
hresult = CURLHE_BAD_ARGUMENT;
goto out;
}
}
if(!amount)
return CURLHE_MISSING;
else if(nameindex >= amount)
return CURLHE_BADINDEX;
if(!Curl_llist_count(&data->state.httphdrs)) {
hresult = CURLHE_NOHEADERS; /* no headers available */
goto out;
}
if(request > data->state.requests) {
hresult = CURLHE_NOREQUEST;
goto out;
}
if(request == -1)
request = data->state.requests;
if(nameindex == amount - 1)
/* if the last or only occurrence is what's asked for, then we know it */
hs = pick;
else {
/* we need a first round to count amount of this header */
for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
hs = Curl_node_elem(e);
if(curl_strequal(hs->name, name) &&
(hs->type & origin) &&
(hs->request == request) &&
(match++ == nameindex)) {
(hs->request == request)) {
amount++;
pick = hs;
e_pick = e;
break;
}
}
if(!e) /* this should not happen */
return CURLHE_MISSING;
if(!amount)
hresult = CURLHE_MISSING;
else if(nameindex >= amount)
hresult = CURLHE_BADINDEX;
if(hresult)
goto out;
if(nameindex == amount - 1)
/* if the last or only occurrence is what's asked for, then we know it */
hs = pick;
else {
for(e = Curl_llist_head(&data->state.httphdrs); e;
e = Curl_node_next(e)) {
hs = Curl_node_elem(e);
if(curl_strequal(hs->name, name) &&
(hs->type & origin) &&
(hs->request == request) &&
(match++ == nameindex)) {
e_pick = e;
break;
}
}
if(!e) { /* this should not happen */
hresult = CURLHE_MISSING;
goto out;
}
}
/* this is the name we want */
copy_header_external(hs, nameindex, amount, e_pick,
&data->state.headerout[0]);
*hout = &data->state.headerout[0];
hresult = CURLHE_OK;
}
/* this is the name we want */
copy_header_external(hs, nameindex, amount, e_pick,
&data->state.headerout[0]);
*hout = &data->state.headerout[0];
return CURLHE_OK;
out:
CURL_EAPI_LEAVE(&guard);
if(result)
hresult = Curl_eapi_hcode(result);
return hresult;
}
/* public API */
@ -123,59 +145,68 @@ struct curl_header *curl_easy_nextheader(CURL *curl,
int request,
struct curl_header *prev)
{
struct Curl_easy *data = curl;
struct Curl_llist_node *pick;
struct Curl_llist_node *e;
struct Curl_header_store *hs;
size_t amount = 0;
size_t index = 0;
struct Curl_eapi_guard guard;
struct curl_header *hd = NULL;
CURLcode result;
if(request > data->state.requests)
return NULL;
if(request == -1)
request = data->state.requests;
if(CURL_EAPI_ENTER(&guard, curl, easy_nextheader, &result)) {
struct Curl_easy *data = curl;
struct Curl_llist_node *pick;
struct Curl_llist_node *e;
struct Curl_header_store *hs;
size_t amount = 0;
size_t index = 0;
if(prev) {
pick = prev->anchor;
if(!pick)
/* something is wrong */
return NULL;
pick = Curl_node_next(pick);
}
else
pick = Curl_llist_head(&data->state.httphdrs);
if(request > data->state.requests)
goto out;
if(request == -1)
request = data->state.requests;
if(pick) {
/* make sure it is the next header of the desired type */
do {
hs = Curl_node_elem(pick);
if((hs->type & origin) && (hs->request == request))
break;
if(prev) {
pick = prev->anchor;
if(!pick)
/* something is wrong */
goto out;
pick = Curl_node_next(pick);
} while(pick);
}
else
pick = Curl_llist_head(&data->state.httphdrs);
if(pick) {
/* make sure it is the next header of the desired type */
do {
hs = Curl_node_elem(pick);
if((hs->type & origin) && (hs->request == request))
break;
pick = Curl_node_next(pick);
} while(pick);
}
if(!pick)
/* no more headers available */
goto out;
hs = Curl_node_elem(pick);
/* count number of occurrences of this name within the mask and figure out
the index for the currently selected entry */
for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
struct Curl_header_store *check = Curl_node_elem(e);
if(curl_strequal(hs->name, check->name) &&
(check->request == request) &&
(check->type & origin))
amount++;
if(e == pick)
index = amount - 1;
}
copy_header_external(hs, index, amount, pick,
&data->state.headerout[1]);
hd = &data->state.headerout[1];
}
if(!pick)
/* no more headers available */
return NULL;
hs = Curl_node_elem(pick);
/* count number of occurrences of this name within the mask and figure out
the index for the currently selected entry */
for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
struct Curl_header_store *check = Curl_node_elem(e);
if(curl_strequal(hs->name, check->name) &&
(check->request == request) &&
(check->type & origin))
amount++;
if(e == pick)
index = amount - 1;
}
copy_header_external(hs, index, amount, pick,
&data->state.headerout[1]);
return &data->state.headerout[1];
out:
CURL_EAPI_LEAVE(&guard);
return hd;
}
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
@ -271,7 +302,6 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
/* insert this node into the list of headers */
Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
data->state.prevhead = hs;
}
else {
failf(data, "Invalid response header");
@ -286,7 +316,6 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
static void headers_reset(struct Curl_easy *data)
{
Curl_llist_init(&data->state.httphdrs, NULL);
data->state.prevhead = NULL;
}
struct hds_cw_collect_ctx {

View file

@ -630,7 +630,6 @@ struct UrlState {
headers */
struct Curl_llist httphdrs; /* received headers */
struct curl_header headerout[2]; /* for external purposes */
struct Curl_header_store *prevhead; /* the latest added header */
#endif
#ifndef CURL_DISABLE_COOKIES
struct curl_slist *cookielist; /* list of cookie files set by

View file

@ -68,6 +68,45 @@ static void t1940_showem(CURL *curl, int header_request, unsigned int type)
}
}
static CURLHcode t1940_negative(CURL *curl, int req_index)
{
struct curl_header *hd1, *hd2;
CURLHcode result;
result = curl_easy_header(NULL, "x", 0, CURLH_HEADER, -1, &hd1);
if(result != CURLHE_BAD_ARGUMENT)
return result;
result = curl_easy_header(curl, NULL, 0, CURLH_HEADER, -1, &hd1);
if(result != CURLHE_BAD_ARGUMENT)
return result;
result = curl_easy_header(curl, "x", 0, CURLH_HEADER, -1, NULL);
if(result != CURLHE_BAD_ARGUMENT)
return result;
result = curl_easy_header(curl, "x", 0, 0, -1, &hd1);
if(result != CURLHE_BAD_ARGUMENT)
return result;
result = curl_easy_header(curl, "date", 0, CURLH_HEADER, req_index, &hd1);
if(result != CURLHE_OK)
return result;
if(!hd1)
return CURLHE_NOHEADERS;
/* Should give the first header */
hd2 = curl_easy_nextheader(curl, CURLH_HEADER, -1, NULL);
if(!hd2)
return CURLHE_BADINDEX;
hd2 = curl_easy_nextheader(NULL, CURLH_HEADER, -1, hd1);
if(hd2) /* should not work */
return CURLHE_BADINDEX;
return CURLHE_OK;
}
static CURLcode test_lib1940(const char *URL)
{
CURL *curl = NULL;
@ -106,6 +145,8 @@ static CURLcode test_lib1940(const char *URL)
t1940_showem(curl, header_request, CURLH_1XX);
t1940_showem(curl, header_request, CURLH_TRAILER);
result = (CURLcode)t1940_negative(curl, header_request);
test_cleanup:
curl_easy_cleanup(curl);
curl_global_cleanup();