quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

headers.c (13446B)


      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 
     25 #include "curl_setup.h"
     26 
     27 #include "urldata.h"
     28 #include "strdup.h"
     29 #include "sendf.h"
     30 #include "headers.h"
     31 #include "curlx/strparse.h"
     32 
     33 /* The last 3 #include files should be in this order */
     34 #include "curl_printf.h"
     35 #include "curl_memory.h"
     36 #include "memdebug.h"
     37 
     38 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
     39 
     40 /* Generate the curl_header struct for the user. This function MUST assign all
     41    struct fields in the output struct. */
     42 static void copy_header_external(struct Curl_header_store *hs,
     43                                  size_t index,
     44                                  size_t amount,
     45                                  struct Curl_llist_node *e,
     46                                  struct curl_header *hout)
     47 {
     48   struct curl_header *h = hout;
     49   h->name = hs->name;
     50   h->value = hs->value;
     51   h->amount = amount;
     52   h->index = index;
     53   /* this will randomly OR a reserved bit for the sole purpose of making it
     54      impossible for applications to do == comparisons, as that would otherwise
     55      be very tempting and then lead to the reserved bits not being reserved
     56      anymore. */
     57   h->origin = (unsigned int)(hs->type | (1 << 27));
     58   h->anchor = e;
     59 }
     60 
     61 /* public API */
     62 CURLHcode curl_easy_header(CURL *easy,
     63                            const char *name,
     64                            size_t nameindex,
     65                            unsigned int type,
     66                            int request,
     67                            struct curl_header **hout)
     68 {
     69   struct Curl_llist_node *e;
     70   struct Curl_llist_node *e_pick = NULL;
     71   struct Curl_easy *data = easy;
     72   size_t match = 0;
     73   size_t amount = 0;
     74   struct Curl_header_store *hs = NULL;
     75   struct Curl_header_store *pick = NULL;
     76   if(!name || !hout || !data ||
     77      (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
     78               CURLH_PSEUDO)) || !type || (request < -1))
     79     return CURLHE_BAD_ARGUMENT;
     80   if(!Curl_llist_count(&data->state.httphdrs))
     81     return CURLHE_NOHEADERS; /* no headers available */
     82   if(request > data->state.requests)
     83     return CURLHE_NOREQUEST;
     84   if(request == -1)
     85     request = data->state.requests;
     86 
     87   /* we need a first round to count amount of this header */
     88   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     89     hs = Curl_node_elem(e);
     90     if(curl_strequal(hs->name, name) &&
     91        (hs->type & type) &&
     92        (hs->request == request)) {
     93       amount++;
     94       pick = hs;
     95       e_pick = e;
     96     }
     97   }
     98   if(!amount)
     99     return CURLHE_MISSING;
    100   else if(nameindex >= amount)
    101     return CURLHE_BADINDEX;
    102 
    103   if(nameindex == amount - 1)
    104     /* if the last or only occurrence is what's asked for, then we know it */
    105     hs = pick;
    106   else {
    107     for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
    108       hs = Curl_node_elem(e);
    109       if(curl_strequal(hs->name, name) &&
    110          (hs->type & type) &&
    111          (hs->request == request) &&
    112          (match++ == nameindex)) {
    113         e_pick = e;
    114         break;
    115       }
    116     }
    117     if(!e) /* this should not happen */
    118       return CURLHE_MISSING;
    119   }
    120   /* this is the name we want */
    121   copy_header_external(hs, nameindex, amount, e_pick,
    122                        &data->state.headerout[0]);
    123   *hout = &data->state.headerout[0];
    124   return CURLHE_OK;
    125 }
    126 
    127 /* public API */
    128 struct curl_header *curl_easy_nextheader(CURL *easy,
    129                                          unsigned int type,
    130                                          int request,
    131                                          struct curl_header *prev)
    132 {
    133   struct Curl_easy *data = easy;
    134   struct Curl_llist_node *pick;
    135   struct Curl_llist_node *e;
    136   struct Curl_header_store *hs;
    137   size_t amount = 0;
    138   size_t index = 0;
    139 
    140   if(request > data->state.requests)
    141     return NULL;
    142   if(request == -1)
    143     request = data->state.requests;
    144 
    145   if(prev) {
    146     pick = prev->anchor;
    147     if(!pick)
    148       /* something is wrong */
    149       return NULL;
    150     pick = Curl_node_next(pick);
    151   }
    152   else
    153     pick = Curl_llist_head(&data->state.httphdrs);
    154 
    155   if(pick) {
    156     /* make sure it is the next header of the desired type */
    157     do {
    158       hs = Curl_node_elem(pick);
    159       if((hs->type & type) && (hs->request == request))
    160         break;
    161       pick = Curl_node_next(pick);
    162     } while(pick);
    163   }
    164 
    165   if(!pick)
    166     /* no more headers available */
    167     return NULL;
    168 
    169   hs = Curl_node_elem(pick);
    170 
    171   /* count number of occurrences of this name within the mask and figure out
    172      the index for the currently selected entry */
    173   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
    174     struct Curl_header_store *check = Curl_node_elem(e);
    175     if(curl_strequal(hs->name, check->name) &&
    176        (check->request == request) &&
    177        (check->type & type))
    178       amount++;
    179     if(e == pick)
    180       index = amount - 1;
    181   }
    182 
    183   copy_header_external(hs, index, amount, pick,
    184                        &data->state.headerout[1]);
    185   return &data->state.headerout[1];
    186 }
    187 
    188 static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
    189                           char **name, char **value)
    190 {
    191   char *end = header + hlen - 1; /* point to the last byte */
    192   DEBUGASSERT(hlen);
    193   *name = header;
    194 
    195   if(type == CURLH_PSEUDO) {
    196     if(*header != ':')
    197       return CURLE_BAD_FUNCTION_ARGUMENT;
    198     header++;
    199   }
    200 
    201   /* Find the end of the header name */
    202   while(*header && (*header != ':'))
    203     ++header;
    204 
    205   if(*header)
    206     /* Skip over colon, null it */
    207     *header++ = 0;
    208   else
    209     return CURLE_BAD_FUNCTION_ARGUMENT;
    210 
    211   /* skip all leading blank letters */
    212   while(ISBLANK(*header))
    213     header++;
    214 
    215   *value = header;
    216 
    217   /* skip all trailing space letters */
    218   while((end > header) && ISBLANK(*end))
    219     *end-- = 0; /* null-terminate */
    220   return CURLE_OK;
    221 }
    222 
    223 static CURLcode unfold_value(struct Curl_easy *data, const char *value,
    224                              size_t vlen)  /* length of the incoming header */
    225 {
    226   struct Curl_header_store *hs;
    227   struct Curl_header_store *newhs;
    228   size_t olen; /* length of the old value */
    229   size_t oalloc; /* length of the old name + value + separator */
    230   size_t offset;
    231   DEBUGASSERT(data->state.prevhead);
    232   hs = data->state.prevhead;
    233   olen = strlen(hs->value);
    234   offset = hs->value - hs->buffer;
    235   oalloc = olen + offset + 1;
    236 
    237   /* skip all trailing space letters */
    238   while(vlen && ISBLANK(value[vlen - 1]))
    239     vlen--;
    240 
    241   /* save only one leading space */
    242   while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
    243     vlen--;
    244     value++;
    245   }
    246 
    247   /* since this header block might move in the realloc below, it needs to
    248      first be unlinked from the list and then re-added again after the
    249      realloc */
    250   Curl_node_remove(&hs->node);
    251 
    252   /* new size = struct + new value length + old name+value length */
    253   newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
    254   if(!newhs)
    255     return CURLE_OUT_OF_MEMORY;
    256   /* ->name and ->value point into ->buffer (to keep the header allocation
    257      in a single memory block), which now potentially have moved. Adjust
    258      them. */
    259   newhs->name = newhs->buffer;
    260   newhs->value = &newhs->buffer[offset];
    261 
    262   /* put the data at the end of the previous data, not the newline */
    263   memcpy(&newhs->value[olen], value, vlen);
    264   newhs->value[olen + vlen] = 0; /* null-terminate at newline */
    265 
    266   /* insert this node into the list of headers */
    267   Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node);
    268   data->state.prevhead = newhs;
    269   return CURLE_OK;
    270 }
    271 
    272 
    273 /*
    274  * Curl_headers_push() gets passed a full HTTP header to store. It gets called
    275  * immediately before the header callback. The header is CRLF terminated.
    276  */
    277 CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
    278                            unsigned char type)
    279 {
    280   char *value = NULL;
    281   char *name = NULL;
    282   char *end;
    283   size_t hlen; /* length of the incoming header */
    284   struct Curl_header_store *hs;
    285   CURLcode result = CURLE_OUT_OF_MEMORY;
    286 
    287   if((header[0] == '\r') || (header[0] == '\n'))
    288     /* ignore the body separator */
    289     return CURLE_OK;
    290 
    291   end = strchr(header, '\r');
    292   if(!end) {
    293     end = strchr(header, '\n');
    294     if(!end)
    295       /* neither CR nor LF as terminator is not a valid header */
    296       return CURLE_WEIRD_SERVER_REPLY;
    297   }
    298   hlen = end - header;
    299 
    300   if((header[0] == ' ') || (header[0] == '\t')) {
    301     if(data->state.prevhead)
    302       /* line folding, append value to the previous header's value */
    303       return unfold_value(data, header, hlen);
    304     else {
    305       /* cannot unfold without a previous header. Instead of erroring, just
    306          pass the leading blanks. */
    307       while(hlen && ISBLANK(*header)) {
    308         header++;
    309         hlen--;
    310       }
    311       if(!hlen)
    312         return CURLE_WEIRD_SERVER_REPLY;
    313     }
    314   }
    315   if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
    316     failf(data, "Too many response headers, %d is max",
    317           MAX_HTTP_RESP_HEADER_COUNT);
    318     return CURLE_TOO_LARGE;
    319   }
    320 
    321   hs = calloc(1, sizeof(*hs) + hlen);
    322   if(!hs)
    323     return CURLE_OUT_OF_MEMORY;
    324   memcpy(hs->buffer, header, hlen);
    325   hs->buffer[hlen] = 0; /* null-terminate */
    326 
    327   result = namevalue(hs->buffer, hlen, type, &name, &value);
    328   if(!result) {
    329     hs->name = name;
    330     hs->value = value;
    331     hs->type = type;
    332     hs->request = data->state.requests;
    333 
    334     /* insert this node into the list of headers */
    335     Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
    336     data->state.prevhead = hs;
    337   }
    338   else {
    339     failf(data, "Invalid response header");
    340     free(hs);
    341   }
    342   return result;
    343 }
    344 
    345 /*
    346  * Curl_headers_reset(). Reset the headers subsystem.
    347  */
    348 static void headers_reset(struct Curl_easy *data)
    349 {
    350   Curl_llist_init(&data->state.httphdrs, NULL);
    351   data->state.prevhead = NULL;
    352 }
    353 
    354 struct hds_cw_collect_ctx {
    355   struct Curl_cwriter super;
    356 };
    357 
    358 static CURLcode hds_cw_collect_write(struct Curl_easy *data,
    359                                      struct Curl_cwriter *writer, int type,
    360                                      const char *buf, size_t blen)
    361 {
    362   if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
    363     unsigned char htype = (unsigned char)
    364       (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
    365        (type & CLIENTWRITE_1XX ? CURLH_1XX :
    366         (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
    367          CURLH_HEADER)));
    368     CURLcode result = Curl_headers_push(data, buf, htype);
    369     CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
    370                    htype, blen, result);
    371     if(result)
    372       return result;
    373   }
    374   return Curl_cwriter_write(data, writer->next, type, buf, blen);
    375 }
    376 
    377 static const struct Curl_cwtype hds_cw_collect = {
    378   "hds-collect",
    379   NULL,
    380   Curl_cwriter_def_init,
    381   hds_cw_collect_write,
    382   Curl_cwriter_def_close,
    383   sizeof(struct hds_cw_collect_ctx)
    384 };
    385 
    386 CURLcode Curl_headers_init(struct Curl_easy *data)
    387 {
    388   struct Curl_cwriter *writer;
    389   CURLcode result;
    390 
    391   if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) {
    392     /* avoid installing it twice */
    393     if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
    394       return CURLE_OK;
    395 
    396     result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
    397                                  CURL_CW_PROTOCOL);
    398     if(result)
    399       return result;
    400 
    401     result = Curl_cwriter_add(data, writer);
    402     if(result) {
    403       Curl_cwriter_free(data, writer);
    404       return result;
    405     }
    406   }
    407   return CURLE_OK;
    408 }
    409 
    410 /*
    411  * Curl_headers_cleanup(). Free all stored headers and associated memory.
    412  */
    413 CURLcode Curl_headers_cleanup(struct Curl_easy *data)
    414 {
    415   struct Curl_llist_node *e;
    416   struct Curl_llist_node *n;
    417 
    418   for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
    419     struct Curl_header_store *hs = Curl_node_elem(e);
    420     n = Curl_node_next(e);
    421     free(hs);
    422   }
    423   headers_reset(data);
    424   return CURLE_OK;
    425 }
    426 
    427 #else /* HTTP-disabled builds below */
    428 
    429 CURLHcode curl_easy_header(CURL *easy,
    430                            const char *name,
    431                            size_t index,
    432                            unsigned int origin,
    433                            int request,
    434                            struct curl_header **hout)
    435 {
    436   (void)easy;
    437   (void)name;
    438   (void)index;
    439   (void)origin;
    440   (void)request;
    441   (void)hout;
    442   return CURLHE_NOT_BUILT_IN;
    443 }
    444 
    445 struct curl_header *curl_easy_nextheader(CURL *easy,
    446                                          unsigned int type,
    447                                          int request,
    448                                          struct curl_header *prev)
    449 {
    450   (void)easy;
    451   (void)type;
    452   (void)request;
    453   (void)prev;
    454   return NULL;
    455 }
    456 #endif