Coverage Report

Created: 2026-09-14 07:04

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
#include "strcase.h"
31
32
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
33
34
/* Generate the curl_header struct for the user. This function MUST assign all
35
   struct fields in the output struct. */
36
static void copy_header_external(struct Curl_header_store *hs,
37
                                 size_t index,
38
                                 size_t amount,
39
                                 struct Curl_llist_node *e,
40
                                 struct curl_header *hout)
41
27.6k
{
42
27.6k
  struct curl_header *h = hout;
43
27.6k
  h->name = hs->name;
44
27.6k
  h->value = hs->value;
45
27.6k
  h->amount = amount;
46
27.6k
  h->index = index;
47
  /* this will randomly OR a reserved bit for the sole purpose of making it
48
     impossible for applications to do == comparisons, as that would otherwise
49
     be tempting and then lead to the reserved bits not being reserved
50
     anymore. */
51
27.6k
  h->origin = (unsigned int)(hs->type | (1 << 27));
52
27.6k
  h->anchor = e;
53
27.6k
}
54
55
/* public API */
56
CURLHcode curl_easy_header(CURL *curl,
57
                           const char *name,
58
                           size_t nameindex,
59
                           unsigned int origin,
60
                           int request,
61
                           struct curl_header **hout)
62
12.1k
{
63
12.1k
  struct Curl_eapi_guard guard;
64
12.1k
  CURLHcode hresult = CURLHE_OK;
65
12.1k
  CURLcode result;
66
67
12.1k
  if(CURL_EAPI_ENTER(&guard, curl, easy_header, &result)) {
68
12.1k
    struct Curl_easy *data = curl;
69
12.1k
    struct Curl_llist_node *e;
70
12.1k
    struct Curl_llist_node *e_pick = NULL;
71
12.1k
    size_t match = 0;
72
12.1k
    size_t amount = 0;
73
12.1k
    struct Curl_header_store *hs = NULL;
74
12.1k
    struct Curl_header_store *pick = NULL;
75
12.1k
    if(!name || !hout || !data ||
76
12.1k
       (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
77
12.1k
                  CURLH_PSEUDO)) || !origin || (request < -1)) {
78
0
      hresult = CURLHE_BAD_ARGUMENT;
79
0
      goto out;
80
0
    }
81
12.1k
    if(!Curl_llist_count(&data->state.httphdrs)) {
82
2.93k
      hresult = CURLHE_NOHEADERS; /* no headers available */
83
2.93k
      goto out;
84
2.93k
    }
85
9.22k
    if(request > data->state.requests) {
86
0
      hresult = CURLHE_NOREQUEST;
87
0
      goto out;
88
0
    }
89
9.22k
    if(request == -1)
90
9.22k
      request = data->state.requests;
91
92
    /* we need a first round to count amount of this header */
93
36.8k
    for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
94
27.6k
      hs = Curl_node_elem(e);
95
27.6k
      if(curl_strequal(hs->name, name) &&
96
0
         (hs->type & origin) &&
97
0
         (hs->request == request)) {
98
0
        amount++;
99
0
        pick = hs;
100
0
        e_pick = e;
101
0
      }
102
27.6k
    }
103
9.22k
    if(!amount)
104
9.22k
      hresult = CURLHE_MISSING;
105
0
    else if(nameindex >= amount)
106
0
      hresult = CURLHE_BADINDEX;
107
9.22k
    if(hresult)
108
9.22k
      goto out;
109
110
0
    if(nameindex == amount - 1)
111
      /* if the last or only occurrence is what's asked for, then we know it */
112
0
      hs = pick;
113
0
    else {
114
0
      for(e = Curl_llist_head(&data->state.httphdrs); e;
115
0
          e = Curl_node_next(e)) {
116
0
        hs = Curl_node_elem(e);
117
0
        if(curl_strequal(hs->name, name) &&
118
0
           (hs->type & origin) &&
119
0
           (hs->request == request) &&
120
0
           (match++ == nameindex)) {
121
0
          e_pick = e;
122
0
          break;
123
0
        }
124
0
      }
125
0
      if(!e) { /* this should not happen */
126
0
        hresult = CURLHE_MISSING;
127
0
        goto out;
128
0
      }
129
0
    }
130
    /* this is the name we want */
131
0
    copy_header_external(hs, nameindex, amount, e_pick,
132
0
                         &data->state.headerout[0]);
133
0
    *hout = &data->state.headerout[0];
134
0
    hresult = CURLHE_OK;
135
0
  }
136
12.1k
out:
137
12.1k
  CURL_EAPI_LEAVE(&guard);
138
12.1k
  if(result)
139
0
    hresult = Curl_eapi_hcode(result);
140
12.1k
  return hresult;
141
12.1k
}
142
143
struct nextheader_cache_entry {
144
  struct Curl_header_store *header;
145
  size_t order;
146
};
147
148
static int nextheader_compare(const void *p1, const void *p2)
149
27.6k
{
150
27.6k
  const struct nextheader_cache_entry *e1 = p1;
151
27.6k
  const struct nextheader_cache_entry *e2 = p2;
152
27.6k
  const struct Curl_header_store *h1 = e1->header;
153
27.6k
  const struct Curl_header_store *h2 = e2->header;
154
27.6k
  const char *n1 = h1->name;
155
27.6k
  const char *n2 = h2->name;
156
157
27.6k
  while(*n1 && *n2) {
158
27.6k
    unsigned char c1 = (unsigned char)Curl_raw_toupper(*n1++);
159
27.6k
    unsigned char c2 = (unsigned char)Curl_raw_toupper(*n2++);
160
27.6k
    if(c1 != c2)
161
27.6k
      return c1 > c2 ? 1 : -1;
162
27.6k
  }
163
0
  if(*n1)
164
0
    return 1;
165
0
  if(*n2)
166
0
    return -1;
167
0
  if(e1->order != e2->order)
168
0
    return e1->order > e2->order ? 1 : -1;
169
0
  return 0;
170
0
}
171
172
static bool nextheader_cache_build(struct Curl_easy *data,
173
                                   unsigned int origin,
174
                                   int request, size_t count)
175
9.22k
{
176
9.22k
  struct nextheader_cache_entry *headers;
177
9.22k
  struct Curl_llist_node *e;
178
9.22k
  size_t i = 0;
179
9.22k
  size_t first;
180
181
9.22k
  headers = curlx_malloc(sizeof(*headers) * count);
182
9.22k
  if(!headers)
183
0
    return FALSE;
184
185
36.8k
  for(e = Curl_llist_head(&data->state.httphdrs); e;
186
27.6k
      e = Curl_node_next(e)) {
187
27.6k
    struct Curl_header_store *hs = Curl_node_elem(e);
188
27.6k
    if((hs->type & origin) && (hs->request == request)) {
189
27.6k
      headers[i].header = hs;
190
27.6k
      headers[i].order = i;
191
27.6k
      i++;
192
27.6k
    }
193
27.6k
  }
194
195
9.22k
  qsort(headers, i, sizeof(*headers), nextheader_compare);
196
36.8k
  for(first = 0; first < i;) {
197
27.6k
    size_t last = first + 1;
198
27.6k
    size_t index;
199
27.6k
    while((last < i) &&
200
18.4k
          curl_strequal(headers[first].header->name,
201
18.4k
                        headers[last].header->name))
202
0
      last++;
203
55.3k
    for(index = first; index < last; index++) {
204
27.6k
      headers[index].header->nh_amount = last - first;
205
27.6k
      headers[index].header->nh_index = index - first;
206
27.6k
    }
207
27.6k
    first = last;
208
27.6k
  }
209
9.22k
  curlx_free(headers);
210
211
9.22k
  data->state.nh_origin = origin;
212
9.22k
  data->state.nh_request = request;
213
9.22k
  data->state.nh_count = count;
214
9.22k
  return TRUE;
215
9.22k
}
216
217
/* public API */
218
struct curl_header *curl_easy_nextheader(CURL *curl,
219
                                         unsigned int origin,
220
                                         int request,
221
                                         struct curl_header *prev)
222
39.8k
{
223
39.8k
  struct Curl_eapi_guard guard;
224
39.8k
  struct curl_header *hd = NULL;
225
39.8k
  CURLcode result;
226
227
39.8k
  if(CURL_EAPI_ENTER(&guard, curl, easy_nextheader, &result)) {
228
39.8k
    struct Curl_easy *data = curl;
229
39.8k
    struct Curl_llist_node *pick;
230
39.8k
    struct Curl_llist_node *e;
231
39.8k
    struct Curl_header_store *hs;
232
39.8k
    size_t amount = 0;
233
39.8k
    size_t index = 0;
234
39.8k
    size_t count;
235
236
39.8k
    if(request > data->state.requests)
237
0
      goto out;
238
39.8k
    if(request == -1)
239
39.8k
      request = data->state.requests;
240
241
39.8k
    if(prev) {
242
27.6k
      pick = prev->anchor;
243
27.6k
      if(!pick)
244
        /* something is wrong */
245
0
        goto out;
246
27.6k
      pick = Curl_node_next(pick);
247
27.6k
    }
248
12.1k
    else
249
12.1k
      pick = Curl_llist_head(&data->state.httphdrs);
250
251
39.8k
    if(pick) {
252
      /* make sure it is the next header of the desired type */
253
27.6k
      do {
254
27.6k
        hs = Curl_node_elem(pick);
255
27.6k
        if((hs->type & origin) && (hs->request == request))
256
27.6k
          break;
257
0
        pick = Curl_node_next(pick);
258
0
      } while(pick);
259
27.6k
    }
260
261
39.8k
    if(!pick)
262
      /* no more headers available */
263
12.1k
      goto out;
264
265
27.6k
    hs = Curl_node_elem(pick);
266
27.6k
    count = Curl_llist_count(&data->state.httphdrs);
267
268
27.6k
    if(((data->state.nh_count == count) &&
269
18.4k
        (data->state.nh_origin == origin) &&
270
18.4k
        (data->state.nh_request == request)) ||
271
27.6k
       nextheader_cache_build(data, origin, request, count)) {
272
27.6k
      amount = hs->nh_amount;
273
27.6k
      index = hs->nh_index;
274
27.6k
    }
275
0
    else {
276
      /* count number of occurrences of this name within the mask and figure
277
         out the index for the currently selected entry */
278
0
      for(e = Curl_llist_head(&data->state.httphdrs); e;
279
0
          e = Curl_node_next(e)) {
280
0
        struct Curl_header_store *check = Curl_node_elem(e);
281
0
        if(curl_strequal(hs->name, check->name) &&
282
0
           (check->request == request) &&
283
0
           (check->type & origin))
284
0
          amount++;
285
0
        if(e == pick)
286
0
          index = amount - 1;
287
0
      }
288
0
    }
289
27.6k
    copy_header_external(hs, index, amount, pick,
290
27.6k
                         &data->state.headerout[1]);
291
27.6k
    hd = &data->state.headerout[1];
292
27.6k
  }
293
39.8k
out:
294
39.8k
  CURL_EAPI_LEAVE(&guard);
295
39.8k
  return hd;
296
39.8k
}
297
298
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
299
                          char **name, char **value)
300
27.6k
{
301
27.6k
  char *end = header + hlen - 1; /* point to the last byte */
302
27.6k
  DEBUGASSERT(hlen);
303
27.6k
  *name = header;
304
305
27.6k
  if(type == CURLH_PSEUDO) {
306
0
    if(*header != ':')
307
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
308
0
    header++;
309
0
  }
310
311
  /* Find the end of the header name */
312
368k
  while(*header && (*header != ':'))
313
341k
    ++header;
314
315
27.6k
  if(*header)
316
    /* Skip over colon, null it */
317
27.6k
    *header++ = 0;
318
0
  else
319
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
320
321
  /* skip all leading blank letters */
322
55.3k
  while(ISBLANK(*header))
323
27.6k
    header++;
324
325
27.6k
  *value = header;
326
327
  /* skip all trailing space letters */
328
27.6k
  while((end > header) && ISBLANK(*end))
329
0
    *end-- = 0; /* null-terminate */
330
27.6k
  return CURLE_OK;
331
27.6k
}
332
333
/*
334
 * Curl_headers_push() gets passed a full HTTP header to store. It gets called
335
 * immediately before the header callback. The header is CRLF, CR or LF
336
 * terminated.
337
 */
338
CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
339
                           size_t hlen, /* length of header */
340
                           unsigned char type)
341
36.8k
{
342
36.8k
  char *value = NULL;
343
36.8k
  char *name = NULL;
344
36.8k
  struct Curl_header_store *hs;
345
36.8k
  CURLcode result = CURLE_OUT_OF_MEMORY;
346
36.8k
  const size_t ilen = hlen;
347
348
36.8k
  if((header[0] == '\r') || (header[0] == '\n'))
349
    /* ignore the body separator */
350
9.21k
    return CURLE_OK;
351
352
  /* trim off newline characters */
353
27.6k
  if(hlen && (header[hlen - 1] == '\n'))
354
27.6k
    hlen--;
355
27.6k
  if(hlen && (header[hlen - 1] == '\r'))
356
27.6k
    hlen--;
357
27.6k
  if(hlen == ilen)
358
    /* neither CR nor LF as terminator is not a valid header */
359
0
    return CURLE_WEIRD_SERVER_REPLY;
360
361
27.6k
  if(ISBLANK(header[0])) {
362
    /* pass leading blanks */
363
0
    while(hlen && ISBLANK(*header)) {
364
0
      header++;
365
0
      hlen--;
366
0
    }
367
0
    if(!hlen)
368
0
      return CURLE_WEIRD_SERVER_REPLY;
369
0
  }
370
27.6k
  if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
371
0
    failf(data, "Too many response headers, %d is max",
372
0
          MAX_HTTP_RESP_HEADER_COUNT);
373
0
    return CURLE_TOO_LARGE;
374
0
  }
375
376
27.6k
  hs = curlx_calloc(1, sizeof(*hs) + hlen);
377
27.6k
  if(!hs)
378
0
    return CURLE_OUT_OF_MEMORY;
379
27.6k
  memcpy(hs->buffer, header, hlen);
380
27.6k
  hs->buffer[hlen] = 0; /* null-terminate */
381
382
27.6k
  result = namevalue(hs->buffer, hlen, type, &name, &value);
383
27.6k
  if(!result) {
384
27.6k
    hs->name = name;
385
27.6k
    hs->value = value;
386
27.6k
    hs->type = type;
387
27.6k
    hs->request = data->state.requests;
388
389
    /* insert this node into the list of headers */
390
27.6k
    Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
391
27.6k
  }
392
0
  else {
393
0
    failf(data, "Invalid response header");
394
0
    curlx_free(hs);
395
0
  }
396
27.6k
  return result;
397
27.6k
}
398
399
/*
400
 * Curl_headers_reset(). Reset the headers subsystem.
401
 */
402
static void headers_reset(struct Curl_easy *data)
403
36.4k
{
404
36.4k
  Curl_llist_init(&data->state.httphdrs, NULL);
405
36.4k
  data->state.nh_count = 0;
406
36.4k
}
407
408
struct hds_cw_collect_ctx {
409
  struct Curl_cwriter super;
410
};
411
412
static CURLcode hds_cw_collect_write(struct Curl_easy *data,
413
                                     struct Curl_cwriter *writer, int type,
414
                                     const char *buf, size_t blen)
415
49.7k
{
416
49.7k
  if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
417
36.8k
    unsigned char htype = (unsigned char)
418
36.8k
      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
419
36.8k
       (type & CLIENTWRITE_1XX ? CURLH_1XX :
420
36.8k
        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
421
0
         CURLH_HEADER)));
422
36.8k
    CURLcode result = Curl_headers_push(data, buf, blen, htype);
423
36.8k
    CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
424
36.8k
                   htype, blen, (int)result);
425
36.8k
    if(result)
426
0
      return result;
427
36.8k
  }
428
49.7k
  return Curl_cwriter_write(data, writer->next, type, buf, blen);
429
49.7k
}
430
431
static const struct Curl_cwtype hds_cw_collect = {
432
  "hds-collect",
433
  NULL,
434
  0,
435
  Curl_cwriter_def_init,
436
  hds_cw_collect_write,
437
  Curl_cwriter_def_flush,
438
  Curl_cwriter_def_close,
439
  sizeof(struct hds_cw_collect_ctx)
440
};
441
442
CURLcode Curl_headers_init(struct Curl_easy *data)
443
19.3k
{
444
19.3k
  struct Curl_cwriter *writer;
445
19.3k
  CURLcode result;
446
447
19.3k
  if(data->conn && (data->conn->scheme->protocol & PROTO_FAMILY_HTTP)) {
448
    /* avoid installing it twice */
449
19.3k
    if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
450
9.64k
      return CURLE_OK;
451
452
9.65k
    result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
453
9.65k
                                 CURL_CW_PROTOCOL);
454
9.65k
    if(result)
455
0
      return result;
456
457
9.65k
    result = Curl_cwriter_add(data, writer);
458
9.65k
    if(result) {
459
0
      Curl_cwriter_free(data, writer);
460
0
      return result;
461
0
    }
462
9.65k
  }
463
9.65k
  return CURLE_OK;
464
19.3k
}
465
466
/*
467
 * Curl_headers_cleanup(). Free all stored headers and associated memory.
468
 */
469
CURLcode Curl_headers_cleanup(struct Curl_easy *data)
470
36.4k
{
471
36.4k
  struct Curl_llist_node *e;
472
36.4k
  struct Curl_llist_node *n;
473
474
64.0k
  for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
475
27.6k
    struct Curl_header_store *hs = Curl_node_elem(e);
476
27.6k
    n = Curl_node_next(e);
477
27.6k
    curlx_free(hs);
478
27.6k
  }
479
36.4k
  headers_reset(data);
480
36.4k
  return CURLE_OK;
481
36.4k
}
482
483
#else /* HTTP-disabled builds below */
484
485
CURLHcode curl_easy_header(CURL *easy,
486
                           const char *name,
487
                           size_t index,
488
                           unsigned int origin,
489
                           int request,
490
                           struct curl_header **hout)
491
{
492
  (void)easy;
493
  (void)name;
494
  (void)index;
495
  (void)origin;
496
  (void)request;
497
  (void)hout;
498
  return CURLHE_NOT_BUILT_IN;
499
}
500
501
struct curl_header *curl_easy_nextheader(CURL *easy,
502
                                         unsigned int type,
503
                                         int request,
504
                                         struct curl_header *prev)
505
{
506
  (void)easy;
507
  (void)type;
508
  (void)request;
509
  (void)prev;
510
  return NULL;
511
}
512
#endif