Coverage Report

Created: 2026-01-09 07:14

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
0
{
41
0
  struct curl_header *h = hout;
42
0
  h->name = hs->name;
43
0
  h->value = hs->value;
44
0
  h->amount = amount;
45
0
  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
0
  h->origin = (unsigned int)(hs->type | (1 << 27));
51
0
  h->anchor = e;
52
0
}
53
54
/* public API */
55
CURLHcode curl_easy_header(CURL *easy,
56
                           const char *name,
57
                           size_t nameindex,
58
                           unsigned int type,
59
                           int request,
60
                           struct curl_header **hout)
61
0
{
62
0
  struct Curl_llist_node *e;
63
0
  struct Curl_llist_node *e_pick = NULL;
64
0
  struct Curl_easy *data = easy;
65
0
  size_t match = 0;
66
0
  size_t amount = 0;
67
0
  struct Curl_header_store *hs = NULL;
68
0
  struct Curl_header_store *pick = NULL;
69
0
  if(!name || !hout || !data ||
70
0
     (type > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
71
0
              CURLH_PSEUDO)) || !type || (request < -1))
72
0
    return CURLHE_BAD_ARGUMENT;
73
0
  if(!Curl_llist_count(&data->state.httphdrs))
74
0
    return CURLHE_NOHEADERS; /* no headers available */
75
0
  if(request > data->state.requests)
76
0
    return CURLHE_NOREQUEST;
77
0
  if(request == -1)
78
0
    request = data->state.requests;
79
80
  /* we need a first round to count amount of this header */
81
0
  for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
82
0
    hs = Curl_node_elem(e);
83
0
    if(curl_strequal(hs->name, name) &&
84
0
       (hs->type & type) &&
85
0
       (hs->request == request)) {
86
0
      amount++;
87
0
      pick = hs;
88
0
      e_pick = e;
89
0
    }
90
0
  }
91
0
  if(!amount)
92
0
    return CURLHE_MISSING;
93
0
  else if(nameindex >= amount)
94
0
    return CURLHE_BADINDEX;
95
96
0
  if(nameindex == amount - 1)
97
    /* if the last or only occurrence is what's asked for, then we know it */
98
0
    hs = pick;
99
0
  else {
100
0
    for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
101
0
      hs = Curl_node_elem(e);
102
0
      if(curl_strequal(hs->name, name) &&
103
0
         (hs->type & type) &&
104
0
         (hs->request == request) &&
105
0
         (match++ == nameindex)) {
106
0
        e_pick = e;
107
0
        break;
108
0
      }
109
0
    }
110
0
    if(!e) /* this should not happen */
111
0
      return CURLHE_MISSING;
112
0
  }
113
  /* this is the name we want */
114
0
  copy_header_external(hs, nameindex, amount, e_pick,
115
0
                       &data->state.headerout[0]);
116
0
  *hout = &data->state.headerout[0];
117
0
  return CURLHE_OK;
118
0
}
119
120
/* public API */
121
struct curl_header *curl_easy_nextheader(CURL *easy,
122
                                         unsigned int type,
123
                                         int request,
124
                                         struct curl_header *prev)
125
0
{
126
0
  struct Curl_easy *data = easy;
127
0
  struct Curl_llist_node *pick;
128
0
  struct Curl_llist_node *e;
129
0
  struct Curl_header_store *hs;
130
0
  size_t amount = 0;
131
0
  size_t index = 0;
132
133
0
  if(request > data->state.requests)
134
0
    return NULL;
135
0
  if(request == -1)
136
0
    request = data->state.requests;
137
138
0
  if(prev) {
139
0
    pick = prev->anchor;
140
0
    if(!pick)
141
      /* something is wrong */
142
0
      return NULL;
143
0
    pick = Curl_node_next(pick);
144
0
  }
145
0
  else
146
0
    pick = Curl_llist_head(&data->state.httphdrs);
147
148
0
  if(pick) {
149
    /* make sure it is the next header of the desired type */
150
0
    do {
151
0
      hs = Curl_node_elem(pick);
152
0
      if((hs->type & type) && (hs->request == request))
153
0
        break;
154
0
      pick = Curl_node_next(pick);
155
0
    } while(pick);
156
0
  }
157
158
0
  if(!pick)
159
    /* no more headers available */
160
0
    return NULL;
161
162
0
  hs = Curl_node_elem(pick);
163
164
  /* count number of occurrences of this name within the mask and figure out
165
     the index for the currently selected entry */
166
0
  for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
167
0
    struct Curl_header_store *check = Curl_node_elem(e);
168
0
    if(curl_strequal(hs->name, check->name) &&
169
0
       (check->request == request) &&
170
0
       (check->type & type))
171
0
      amount++;
172
0
    if(e == pick)
173
0
      index = amount - 1;
174
0
  }
175
176
0
  copy_header_external(hs, index, amount, pick,
177
0
                       &data->state.headerout[1]);
178
0
  return &data->state.headerout[1];
179
0
}
180
181
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
182
                          char **name, char **value)
183
0
{
184
0
  char *end = header + hlen - 1; /* point to the last byte */
185
0
  DEBUGASSERT(hlen);
186
0
  *name = header;
187
188
0
  if(type == CURLH_PSEUDO) {
189
0
    if(*header != ':')
190
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
191
0
    header++;
192
0
  }
193
194
  /* Find the end of the header name */
195
0
  while(*header && (*header != ':'))
196
0
    ++header;
197
198
0
  if(*header)
199
    /* Skip over colon, null it */
200
0
    *header++ = 0;
201
0
  else
202
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
203
204
  /* skip all leading blank letters */
205
0
  while(ISBLANK(*header))
206
0
    header++;
207
208
0
  *value = header;
209
210
  /* skip all trailing space letters */
211
0
  while((end > header) && ISBLANK(*end))
212
0
    *end-- = 0; /* null-terminate */
213
0
  return CURLE_OK;
214
0
}
215
216
/*
217
 * Curl_headers_push() gets passed a full HTTP header to store. It gets called
218
 * immediately before the header callback. The header is CRLF, CR or LF
219
 * terminated.
220
 */
221
CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
222
                           size_t hlen, /* length of header */
223
                           unsigned char type)
224
0
{
225
0
  char *value = NULL;
226
0
  char *name = NULL;
227
0
  struct Curl_header_store *hs;
228
0
  CURLcode result = CURLE_OUT_OF_MEMORY;
229
0
  const size_t ilen = hlen;
230
231
0
  if((header[0] == '\r') || (header[0] == '\n'))
232
    /* ignore the body separator */
233
0
    return CURLE_OK;
234
235
  /* trim off newline characters */
236
0
  if(hlen && (header[hlen - 1] == '\n'))
237
0
    hlen--;
238
0
  if(hlen && (header[hlen - 1] == '\r'))
239
0
    hlen--;
240
0
  if(hlen == ilen)
241
    /* neither CR nor LF as terminator is not a valid header */
242
0
    return CURLE_WEIRD_SERVER_REPLY;
243
244
0
  if(ISBLANK(header[0])) {
245
    /* pass leading blanks */
246
0
    while(hlen && ISBLANK(*header)) {
247
0
      header++;
248
0
      hlen--;
249
0
    }
250
0
    if(!hlen)
251
0
      return CURLE_WEIRD_SERVER_REPLY;
252
0
  }
253
0
  if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
254
0
    failf(data, "Too many response headers, %d is max",
255
0
          MAX_HTTP_RESP_HEADER_COUNT);
256
0
    return CURLE_TOO_LARGE;
257
0
  }
258
259
0
  hs = curlx_calloc(1, sizeof(*hs) + hlen);
260
0
  if(!hs)
261
0
    return CURLE_OUT_OF_MEMORY;
262
0
  memcpy(hs->buffer, header, hlen);
263
0
  hs->buffer[hlen] = 0; /* null-terminate */
264
265
0
  result = namevalue(hs->buffer, hlen, type, &name, &value);
266
0
  if(!result) {
267
0
    hs->name = name;
268
0
    hs->value = value;
269
0
    hs->type = type;
270
0
    hs->request = data->state.requests;
271
272
    /* insert this node into the list of headers */
273
0
    Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
274
0
    data->state.prevhead = hs;
275
0
  }
276
0
  else {
277
0
    failf(data, "Invalid response header");
278
0
    curlx_free(hs);
279
0
  }
280
0
  return result;
281
0
}
282
283
/*
284
 * Curl_headers_reset(). Reset the headers subsystem.
285
 */
286
static void headers_reset(struct Curl_easy *data)
287
0
{
288
0
  Curl_llist_init(&data->state.httphdrs, NULL);
289
0
  data->state.prevhead = NULL;
290
0
}
291
292
struct hds_cw_collect_ctx {
293
  struct Curl_cwriter super;
294
};
295
296
static CURLcode hds_cw_collect_write(struct Curl_easy *data,
297
                                     struct Curl_cwriter *writer, int type,
298
                                     const char *buf, size_t blen)
299
0
{
300
0
  if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
301
0
    unsigned char htype = (unsigned char)
302
0
      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
303
0
       (type & CLIENTWRITE_1XX ? CURLH_1XX :
304
0
        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
305
0
         CURLH_HEADER)));
306
0
    CURLcode result = Curl_headers_push(data, buf, blen, htype);
307
0
    CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
308
0
                   htype, blen, result);
309
0
    if(result)
310
0
      return result;
311
0
  }
312
0
  return Curl_cwriter_write(data, writer->next, type, buf, blen);
313
0
}
314
315
static const struct Curl_cwtype hds_cw_collect = {
316
  "hds-collect",
317
  NULL,
318
  Curl_cwriter_def_init,
319
  hds_cw_collect_write,
320
  Curl_cwriter_def_close,
321
  sizeof(struct hds_cw_collect_ctx)
322
};
323
324
CURLcode Curl_headers_init(struct Curl_easy *data)
325
0
{
326
0
  struct Curl_cwriter *writer;
327
0
  CURLcode result;
328
329
0
  if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) {
330
    /* avoid installing it twice */
331
0
    if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
332
0
      return CURLE_OK;
333
334
0
    result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
335
0
                                 CURL_CW_PROTOCOL);
336
0
    if(result)
337
0
      return result;
338
339
0
    result = Curl_cwriter_add(data, writer);
340
0
    if(result) {
341
0
      Curl_cwriter_free(data, writer);
342
0
      return result;
343
0
    }
344
0
  }
345
0
  return CURLE_OK;
346
0
}
347
348
/*
349
 * Curl_headers_cleanup(). Free all stored headers and associated memory.
350
 */
351
CURLcode Curl_headers_cleanup(struct Curl_easy *data)
352
0
{
353
0
  struct Curl_llist_node *e;
354
0
  struct Curl_llist_node *n;
355
356
0
  for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
357
0
    struct Curl_header_store *hs = Curl_node_elem(e);
358
0
    n = Curl_node_next(e);
359
0
    curlx_free(hs);
360
0
  }
361
0
  headers_reset(data);
362
0
  return CURLE_OK;
363
0
}
364
365
#else /* HTTP-disabled builds below */
366
367
CURLHcode curl_easy_header(CURL *easy,
368
                           const char *name,
369
                           size_t index,
370
                           unsigned int origin,
371
                           int request,
372
                           struct curl_header **hout)
373
{
374
  (void)easy;
375
  (void)name;
376
  (void)index;
377
  (void)origin;
378
  (void)request;
379
  (void)hout;
380
  return CURLHE_NOT_BUILT_IN;
381
}
382
383
struct curl_header *curl_easy_nextheader(CURL *easy,
384
                                         unsigned int type,
385
                                         int request,
386
                                         struct curl_header *prev)
387
{
388
  (void)easy;
389
  (void)type;
390
  (void)request;
391
  (void)prev;
392
  return NULL;
393
}
394
#endif