Coverage Report

Created: 2026-09-04 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/headers.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#include "urldata.h"
27
#include "sendf.h"
28
#include "curl_trc.h"
29
#include "headers.h"
30
31
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
32
33
/* Generate the curl_header struct for the user. This function MUST assign all
34
   struct fields in the output struct. */
35
static void copy_header_external(struct Curl_header_store *hs,
36
                                 size_t index,
37
                                 size_t amount,
38
                                 struct Curl_llist_node *e,
39
                                 struct curl_header *hout)
40
30.3k
{
41
30.3k
  struct curl_header *h = hout;
42
30.3k
  h->name = hs->name;
43
30.3k
  h->value = hs->value;
44
30.3k
  h->amount = amount;
45
30.3k
  h->index = index;
46
  /* this will randomly OR a reserved bit for the sole purpose of making it
47
     impossible for applications to do == comparisons, as that would otherwise
48
     be tempting and then lead to the reserved bits not being reserved
49
     anymore. */
50
30.3k
  h->origin = (unsigned int)(hs->type | (1 << 27));
51
30.3k
  h->anchor = e;
52
30.3k
}
53
54
/* public API */
55
CURLHcode curl_easy_header(CURL *curl,
56
                           const char *name,
57
                           size_t nameindex,
58
                           unsigned int origin,
59
                           int request,
60
                           struct curl_header **hout)
61
20.6k
{
62
20.6k
  struct Curl_eapi_guard guard;
63
20.6k
  CURLHcode hresult = CURLHE_OK;
64
20.6k
  CURLcode result;
65
66
20.6k
  if(CURL_EAPI_ENTER(&guard, curl, easy_header, &result)) {
67
20.6k
    struct Curl_easy *data = curl;
68
20.6k
    struct Curl_llist_node *e;
69
20.6k
    struct Curl_llist_node *e_pick = NULL;
70
20.6k
    size_t match = 0;
71
20.6k
    size_t amount = 0;
72
20.6k
    struct Curl_header_store *hs = NULL;
73
20.6k
    struct Curl_header_store *pick = NULL;
74
20.6k
    if(!name || !hout || !data ||
75
20.6k
       (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
76
20.6k
                  CURLH_PSEUDO)) || !origin || (request < -1)) {
77
0
      hresult = CURLHE_BAD_ARGUMENT;
78
0
      goto out;
79
0
    }
80
20.6k
    if(!Curl_llist_count(&data->state.httphdrs)) {
81
9.94k
      hresult = CURLHE_NOHEADERS; /* no headers available */
82
9.94k
      goto out;
83
9.94k
    }
84
10.6k
    if(request > data->state.requests) {
85
0
      hresult = CURLHE_NOREQUEST;
86
0
      goto out;
87
0
    }
88
10.6k
    if(request == -1)
89
10.6k
      request = data->state.requests;
90
91
    /* we need a first round to count amount of this header */
92
111k
    for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
93
101k
      hs = Curl_node_elem(e);
94
101k
      if(curl_strequal(hs->name, name) &&
95
1.24k
         (hs->type & origin) &&
96
1.24k
         (hs->request == request)) {
97
1.19k
        amount++;
98
1.19k
        pick = hs;
99
1.19k
        e_pick = e;
100
1.19k
      }
101
101k
    }
102
10.6k
    if(!amount)
103
10.2k
      hresult = CURLHE_MISSING;
104
459
    else if(nameindex >= amount)
105
0
      hresult = CURLHE_BADINDEX;
106
10.6k
    if(hresult)
107
10.2k
      goto out;
108
109
459
    if(nameindex == amount - 1)
110
      /* if the last or only occurrence is what's asked for, then we know it */
111
415
      hs = pick;
112
44
    else {
113
571
      for(e = Curl_llist_head(&data->state.httphdrs); e;
114
571
          e = Curl_node_next(e)) {
115
571
        hs = Curl_node_elem(e);
116
571
        if(curl_strequal(hs->name, name) &&
117
82
           (hs->type & origin) &&
118
82
           (hs->request == request) &&
119
44
           (match++ == nameindex)) {
120
44
          e_pick = e;
121
44
          break;
122
44
        }
123
571
      }
124
44
      if(!e) { /* this should not happen */
125
0
        hresult = CURLHE_MISSING;
126
0
        goto out;
127
0
      }
128
44
    }
129
    /* this is the name we want */
130
459
    copy_header_external(hs, nameindex, amount, e_pick,
131
459
                         &data->state.headerout[0]);
132
459
    *hout = &data->state.headerout[0];
133
459
    hresult = CURLHE_OK;
134
459
  }
135
20.6k
out:
136
20.6k
  CURL_EAPI_LEAVE(&guard);
137
20.6k
  if(result)
138
0
    hresult = Curl_eapi_hcode(result);
139
20.6k
  return hresult;
140
20.6k
}
141
142
/* public API */
143
struct curl_header *curl_easy_nextheader(CURL *curl,
144
                                         unsigned int origin,
145
                                         int request,
146
                                         struct curl_header *prev)
147
49.8k
{
148
49.8k
  struct Curl_eapi_guard guard;
149
49.8k
  struct curl_header *hd = NULL;
150
49.8k
  CURLcode result;
151
152
49.8k
  if(CURL_EAPI_ENTER(&guard, curl, easy_nextheader, &result)) {
153
49.8k
    struct Curl_easy *data = curl;
154
49.8k
    struct Curl_llist_node *pick;
155
49.8k
    struct Curl_llist_node *e;
156
49.8k
    struct Curl_header_store *hs;
157
49.8k
    size_t amount = 0;
158
49.8k
    size_t index = 0;
159
160
49.8k
    if(request > data->state.requests)
161
0
      goto out;
162
49.8k
    if(request == -1)
163
49.8k
      request = data->state.requests;
164
165
49.8k
    if(prev) {
166
29.2k
      pick = prev->anchor;
167
29.2k
      if(!pick)
168
        /* something is wrong */
169
0
        goto out;
170
29.2k
      pick = Curl_node_next(pick);
171
29.2k
    }
172
20.6k
    else
173
20.6k
      pick = Curl_llist_head(&data->state.httphdrs);
174
175
49.8k
    if(pick) {
176
      /* make sure it is the next header of the desired type */
177
60.6k
      do {
178
60.6k
        hs = Curl_node_elem(pick);
179
60.6k
        if((hs->type & origin) && (hs->request == request))
180
29.8k
          break;
181
30.8k
        pick = Curl_node_next(pick);
182
30.8k
      } while(pick);
183
32.7k
    }
184
185
49.8k
    if(!pick)
186
      /* no more headers available */
187
20.0k
      goto out;
188
189
29.8k
    hs = Curl_node_elem(pick);
190
191
    /* count number of occurrences of this name within the mask and figure out
192
       the index for the currently selected entry */
193
927k
    for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
194
897k
      struct Curl_header_store *check = Curl_node_elem(e);
195
897k
      if(curl_strequal(hs->name, check->name) &&
196
295k
         (check->request == request) &&
197
293k
         (check->type & origin))
198
293k
        amount++;
199
897k
      if(e == pick)
200
29.8k
        index = amount - 1;
201
897k
    }
202
203
29.8k
    copy_header_external(hs, index, amount, pick,
204
29.8k
                         &data->state.headerout[1]);
205
29.8k
    hd = &data->state.headerout[1];
206
29.8k
  }
207
49.8k
out:
208
49.8k
  CURL_EAPI_LEAVE(&guard);
209
49.8k
  return hd;
210
49.8k
}
211
212
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
213
                          char **name, char **value)
214
101k
{
215
101k
  char *end = header + hlen - 1; /* point to the last byte */
216
101k
  DEBUGASSERT(hlen);
217
101k
  *name = header;
218
219
101k
  if(type == CURLH_PSEUDO) {
220
1.12k
    if(*header != ':')
221
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
222
1.12k
    header++;
223
1.12k
  }
224
225
  /* Find the end of the header name */
226
1.24M
  while(*header && (*header != ':'))
227
1.14M
    ++header;
228
229
101k
  if(*header)
230
    /* Skip over colon, null it */
231
101k
    *header++ = 0;
232
407
  else
233
407
    return CURLE_BAD_FUNCTION_ARGUMENT;
234
235
  /* skip all leading blank letters */
236
193k
  while(ISBLANK(*header))
237
92.0k
    header++;
238
239
101k
  *value = header;
240
241
  /* skip all trailing space letters */
242
102k
  while((end > header) && ISBLANK(*end))
243
1.55k
    *end-- = 0; /* null-terminate */
244
101k
  return CURLE_OK;
245
101k
}
246
247
/*
248
 * Curl_headers_push() gets passed a full HTTP header to store. It gets called
249
 * immediately before the header callback. The header is CRLF, CR or LF
250
 * terminated.
251
 */
252
CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
253
                           size_t hlen, /* length of header */
254
                           unsigned char type)
255
134k
{
256
134k
  char *value = NULL;
257
134k
  char *name = NULL;
258
134k
  struct Curl_header_store *hs;
259
134k
  CURLcode result = CURLE_OUT_OF_MEMORY;
260
134k
  const size_t ilen = hlen;
261
262
134k
  if((header[0] == '\r') || (header[0] == '\n'))
263
    /* ignore the body separator */
264
31.1k
    return CURLE_OK;
265
266
  /* trim off newline characters */
267
103k
  if(hlen && (header[hlen - 1] == '\n'))
268
100k
    hlen--;
269
103k
  if(hlen && (header[hlen - 1] == '\r'))
270
77.8k
    hlen--;
271
103k
  if(hlen == ilen)
272
    /* neither CR nor LF as terminator is not a valid header */
273
1.58k
    return CURLE_WEIRD_SERVER_REPLY;
274
275
101k
  if(ISBLANK(header[0])) {
276
    /* pass leading blanks */
277
671
    while(hlen && ISBLANK(*header)) {
278
548
      header++;
279
548
      hlen--;
280
548
    }
281
123
    if(!hlen)
282
5
      return CURLE_WEIRD_SERVER_REPLY;
283
123
  }
284
101k
  if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
285
1
    failf(data, "Too many response headers, %d is max",
286
1
          MAX_HTTP_RESP_HEADER_COUNT);
287
1
    return CURLE_TOO_LARGE;
288
1
  }
289
290
101k
  hs = curlx_calloc(1, sizeof(*hs) + hlen);
291
101k
  if(!hs)
292
0
    return CURLE_OUT_OF_MEMORY;
293
101k
  memcpy(hs->buffer, header, hlen);
294
101k
  hs->buffer[hlen] = 0; /* null-terminate */
295
296
101k
  result = namevalue(hs->buffer, hlen, type, &name, &value);
297
101k
  if(!result) {
298
101k
    hs->name = name;
299
101k
    hs->value = value;
300
101k
    hs->type = type;
301
101k
    hs->request = data->state.requests;
302
303
    /* insert this node into the list of headers */
304
101k
    Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
305
101k
  }
306
407
  else {
307
407
    failf(data, "Invalid response header");
308
407
    curlx_free(hs);
309
407
  }
310
101k
  return result;
311
101k
}
312
313
/*
314
 * Curl_headers_reset(). Reset the headers subsystem.
315
 */
316
static void headers_reset(struct Curl_easy *data)
317
61.8k
{
318
61.8k
  Curl_llist_init(&data->state.httphdrs, NULL);
319
61.8k
}
320
321
struct hds_cw_collect_ctx {
322
  struct Curl_cwriter super;
323
};
324
325
static CURLcode hds_cw_collect_write(struct Curl_easy *data,
326
                                     struct Curl_cwriter *writer, int type,
327
                                     const char *buf, size_t blen)
328
182k
{
329
182k
  if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
330
133k
    unsigned char htype = (unsigned char)
331
133k
      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
332
133k
       (type & CLIENTWRITE_1XX ? CURLH_1XX :
333
133k
        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
334
124k
         CURLH_HEADER)));
335
133k
    CURLcode result = Curl_headers_push(data, buf, blen, htype);
336
133k
    CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
337
133k
                   htype, blen, (int)result);
338
133k
    if(result)
339
2.00k
      return result;
340
133k
  }
341
180k
  return Curl_cwriter_write(data, writer->next, type, buf, blen);
342
182k
}
343
344
static const struct Curl_cwtype hds_cw_collect = {
345
  "hds-collect",
346
  NULL,
347
  0,
348
  Curl_cwriter_def_init,
349
  hds_cw_collect_write,
350
  Curl_cwriter_def_flush,
351
  Curl_cwriter_def_close,
352
  sizeof(struct hds_cw_collect_ctx)
353
};
354
355
CURLcode Curl_headers_init(struct Curl_easy *data)
356
71.2k
{
357
71.2k
  struct Curl_cwriter *writer;
358
71.2k
  CURLcode result;
359
360
71.2k
  if(data->conn && (data->conn->scheme->protocol & PROTO_FAMILY_HTTP)) {
361
    /* avoid installing it twice */
362
70.5k
    if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
363
21.5k
      return CURLE_OK;
364
365
48.9k
    result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
366
48.9k
                                 CURL_CW_PROTOCOL);
367
48.9k
    if(result)
368
0
      return result;
369
370
48.9k
    result = Curl_cwriter_add(data, writer);
371
48.9k
    if(result) {
372
0
      Curl_cwriter_free(data, writer);
373
0
      return result;
374
0
    }
375
48.9k
  }
376
49.7k
  return CURLE_OK;
377
71.2k
}
378
379
/*
380
 * Curl_headers_cleanup(). Free all stored headers and associated memory.
381
 */
382
CURLcode Curl_headers_cleanup(struct Curl_easy *data)
383
61.8k
{
384
61.8k
  struct Curl_llist_node *e;
385
61.8k
  struct Curl_llist_node *n;
386
387
163k
  for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
388
101k
    struct Curl_header_store *hs = Curl_node_elem(e);
389
101k
    n = Curl_node_next(e);
390
101k
    curlx_free(hs);
391
101k
  }
392
61.8k
  headers_reset(data);
393
61.8k
  return CURLE_OK;
394
61.8k
}
395
396
#else /* HTTP-disabled builds below */
397
398
CURLHcode curl_easy_header(CURL *easy,
399
                           const char *name,
400
                           size_t index,
401
                           unsigned int origin,
402
                           int request,
403
                           struct curl_header **hout)
404
{
405
  (void)easy;
406
  (void)name;
407
  (void)index;
408
  (void)origin;
409
  (void)request;
410
  (void)hout;
411
  return CURLHE_NOT_BUILT_IN;
412
}
413
414
struct curl_header *curl_easy_nextheader(CURL *easy,
415
                                         unsigned int type,
416
                                         int request,
417
                                         struct curl_header *prev)
418
{
419
  (void)easy;
420
  (void)type;
421
  (void)request;
422
  (void)prev;
423
  return NULL;
424
}
425
#endif