quickjs-tart

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

http1.c (9531B)


      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 #ifndef CURL_DISABLE_HTTP
     28 
     29 #include "urldata.h"
     30 #include <curl/curl.h>
     31 #include "http.h"
     32 #include "http1.h"
     33 #include "urlapi-int.h"
     34 
     35 /* The last 3 #include files should be in this order */
     36 #include "curl_printf.h"
     37 #include "curl_memory.h"
     38 #include "memdebug.h"
     39 
     40 
     41 #define H1_MAX_URL_LEN   (8*1024)
     42 
     43 void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len)
     44 {
     45   memset(parser, 0, sizeof(*parser));
     46   parser->max_line_len = max_line_len;
     47   curlx_dyn_init(&parser->scratch, max_line_len);
     48 }
     49 
     50 void Curl_h1_req_parse_free(struct h1_req_parser *parser)
     51 {
     52   if(parser) {
     53     Curl_http_req_free(parser->req);
     54     curlx_dyn_free(&parser->scratch);
     55     parser->req = NULL;
     56     parser->done = FALSE;
     57   }
     58 }
     59 
     60 static CURLcode trim_line(struct h1_req_parser *parser, int options)
     61 {
     62   DEBUGASSERT(parser->line);
     63   if(parser->line_len) {
     64     if(parser->line[parser->line_len - 1] == '\n')
     65       --parser->line_len;
     66     if(parser->line_len) {
     67       if(parser->line[parser->line_len - 1] == '\r')
     68         --parser->line_len;
     69       else if(options & H1_PARSE_OPT_STRICT)
     70         return CURLE_URL_MALFORMAT;
     71     }
     72     else if(options & H1_PARSE_OPT_STRICT)
     73       return CURLE_URL_MALFORMAT;
     74   }
     75   else if(options & H1_PARSE_OPT_STRICT)
     76     return CURLE_URL_MALFORMAT;
     77 
     78   if(parser->line_len > parser->max_line_len) {
     79     return CURLE_URL_MALFORMAT;
     80   }
     81   return CURLE_OK;
     82 }
     83 
     84 static ssize_t detect_line(struct h1_req_parser *parser,
     85                            const char *buf, const size_t buflen,
     86                            CURLcode *err)
     87 {
     88   const char  *line_end;
     89 
     90   DEBUGASSERT(!parser->line);
     91   line_end = memchr(buf, '\n', buflen);
     92   if(!line_end) {
     93     *err = CURLE_AGAIN;
     94     return -1;
     95   }
     96   parser->line = buf;
     97   parser->line_len = line_end - buf + 1;
     98   *err = CURLE_OK;
     99   return (ssize_t)parser->line_len;
    100 }
    101 
    102 static ssize_t next_line(struct h1_req_parser *parser,
    103                          const char *buf, const size_t buflen, int options,
    104                          CURLcode *err)
    105 {
    106   ssize_t nread = 0;
    107 
    108   if(parser->line) {
    109     parser->line = NULL;
    110     parser->line_len = 0;
    111     curlx_dyn_reset(&parser->scratch);
    112   }
    113 
    114   nread = detect_line(parser, buf, buflen, err);
    115   if(nread >= 0) {
    116     if(curlx_dyn_len(&parser->scratch)) {
    117       /* append detected line to scratch to have the complete line */
    118       *err = curlx_dyn_addn(&parser->scratch, parser->line, parser->line_len);
    119       if(*err)
    120         return -1;
    121       parser->line = curlx_dyn_ptr(&parser->scratch);
    122       parser->line_len = curlx_dyn_len(&parser->scratch);
    123     }
    124     *err = trim_line(parser, options);
    125     if(*err)
    126       return -1;
    127   }
    128   else if(*err == CURLE_AGAIN) {
    129     /* no line end in `buf`, add it to our scratch */
    130     *err = curlx_dyn_addn(&parser->scratch, (const unsigned char *)buf,
    131                           buflen);
    132     nread = (*err) ? -1 : (ssize_t)buflen;
    133   }
    134   return nread;
    135 }
    136 
    137 static CURLcode start_req(struct h1_req_parser *parser,
    138                           const char *scheme_default, int options)
    139 {
    140   const char  *p, *m, *target, *hv, *scheme, *authority, *path;
    141   size_t m_len, target_len, hv_len, scheme_len, authority_len, path_len;
    142   size_t i;
    143   CURLU *url = NULL;
    144   CURLcode result = CURLE_URL_MALFORMAT; /* Use this as default fail */
    145 
    146   DEBUGASSERT(!parser->req);
    147   /* line must match: "METHOD TARGET HTTP_VERSION" */
    148   p = memchr(parser->line, ' ', parser->line_len);
    149   if(!p || p == parser->line)
    150     goto out;
    151 
    152   m = parser->line;
    153   m_len = p - parser->line;
    154   target = p + 1;
    155   target_len = hv_len = 0;
    156   hv = NULL;
    157 
    158   /* URL may contain spaces so scan backwards */
    159   for(i = parser->line_len; i > m_len; --i) {
    160     if(parser->line[i] == ' ') {
    161       hv = &parser->line[i + 1];
    162       hv_len = parser->line_len - i;
    163       target_len = (hv - target) - 1;
    164       break;
    165     }
    166   }
    167   /* no SPACE found or empty TARGET or empty HTTP_VERSION */
    168   if(!target_len || !hv_len)
    169     goto out;
    170 
    171   (void)hv;
    172 
    173   /* The TARGET can be (rfc 9112, ch. 3.2):
    174    * origin-form:     path + optional query
    175    * absolute-form:   absolute URI
    176    * authority-form:  host+port for CONNECT
    177    * asterisk-form:   '*' for OPTIONS
    178    *
    179    * from TARGET, we derive `scheme` `authority` `path`
    180    * origin-form            --        --          TARGET
    181    * absolute-form          URL*      URL*        URL*
    182    * authority-form         --        TARGET      --
    183    * asterisk-form          --        --          TARGET
    184    */
    185   scheme = authority = path = NULL;
    186   scheme_len = authority_len = path_len = 0;
    187 
    188   if(target_len == 1 && target[0] == '*') {
    189     /* asterisk-form */
    190     path = target;
    191     path_len = target_len;
    192   }
    193   else if(!strncmp("CONNECT", m, m_len)) {
    194     /* authority-form */
    195     authority = target;
    196     authority_len = target_len;
    197   }
    198   else if(target[0] == '/') {
    199     /* origin-form */
    200     path = target;
    201     path_len = target_len;
    202   }
    203   else {
    204     /* origin-form OR absolute-form */
    205     CURLUcode uc;
    206     char tmp[H1_MAX_URL_LEN];
    207 
    208     /* default, unless we see an absolute URL */
    209     path = target;
    210     path_len = target_len;
    211 
    212     /* URL parser wants null-termination */
    213     if(target_len >= sizeof(tmp))
    214       goto out;
    215     memcpy(tmp, target, target_len);
    216     tmp[target_len] = '\0';
    217     /* See if treating TARGET as an absolute URL makes sense */
    218     if(Curl_is_absolute_url(tmp, NULL, 0, FALSE)) {
    219       unsigned int url_options;
    220 
    221       url = curl_url();
    222       if(!url) {
    223         result = CURLE_OUT_OF_MEMORY;
    224         goto out;
    225       }
    226       url_options = (CURLU_NON_SUPPORT_SCHEME|
    227                      CURLU_PATH_AS_IS|
    228                      CURLU_NO_DEFAULT_PORT);
    229       if(!(options & H1_PARSE_OPT_STRICT))
    230         url_options |= CURLU_ALLOW_SPACE;
    231       uc = curl_url_set(url, CURLUPART_URL, tmp, url_options);
    232       if(uc) {
    233         goto out;
    234       }
    235     }
    236 
    237     if(!url && (options & H1_PARSE_OPT_STRICT)) {
    238       /* we should have an absolute URL or have seen `/` earlier */
    239       goto out;
    240     }
    241   }
    242 
    243   if(url) {
    244     result = Curl_http_req_make2(&parser->req, m, m_len, url, scheme_default);
    245   }
    246   else {
    247     if(!scheme && scheme_default) {
    248       scheme = scheme_default;
    249       scheme_len = strlen(scheme_default);
    250     }
    251     result = Curl_http_req_make(&parser->req, m, m_len, scheme, scheme_len,
    252                                 authority, authority_len, path, path_len);
    253   }
    254 
    255 out:
    256   curl_url_cleanup(url);
    257   return result;
    258 }
    259 
    260 ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
    261                                const char *buf, size_t buflen,
    262                                const char *scheme_default, int options,
    263                                CURLcode *err)
    264 {
    265   ssize_t nread = 0, n;
    266 
    267   *err = CURLE_OK;
    268   while(!parser->done) {
    269     n = next_line(parser, buf, buflen, options, err);
    270     if(n < 0) {
    271       if(*err != CURLE_AGAIN) {
    272         nread = -1;
    273       }
    274       *err = CURLE_OK;
    275       goto out;
    276     }
    277 
    278     /* Consume this line */
    279     nread += (size_t)n;
    280     buf += (size_t)n;
    281     buflen -= (size_t)n;
    282 
    283     if(!parser->line) {
    284       /* consumed bytes, but line not complete */
    285       if(!buflen)
    286         goto out;
    287     }
    288     else if(!parser->req) {
    289       *err = start_req(parser, scheme_default, options);
    290       if(*err) {
    291         nread = -1;
    292         goto out;
    293       }
    294     }
    295     else if(parser->line_len == 0) {
    296       /* last, empty line, we are finished */
    297       if(!parser->req) {
    298         *err = CURLE_URL_MALFORMAT;
    299         nread = -1;
    300         goto out;
    301       }
    302       parser->done = TRUE;
    303       curlx_dyn_reset(&parser->scratch);
    304       /* last chance adjustments */
    305     }
    306     else {
    307       *err = Curl_dynhds_h1_add_line(&parser->req->headers,
    308                                      parser->line, parser->line_len);
    309       if(*err) {
    310         nread = -1;
    311         goto out;
    312       }
    313     }
    314   }
    315 
    316 out:
    317   return nread;
    318 }
    319 
    320 CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
    321                                 struct dynbuf *dbuf)
    322 {
    323   CURLcode result;
    324 
    325   result = curlx_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
    326                           req->method,
    327                           req->scheme ? req->scheme : "",
    328                           req->scheme ? "://" : "",
    329                           req->authority ? req->authority : "",
    330                           req->path ? req->path : "",
    331                           http_minor);
    332   if(result)
    333     goto out;
    334 
    335   result = Curl_dynhds_h1_dprint(&req->headers, dbuf);
    336   if(result)
    337     goto out;
    338 
    339   result = curlx_dyn_addn(dbuf, STRCONST("\r\n"));
    340 
    341 out:
    342   return result;
    343 }
    344 
    345 #endif /* !CURL_DISABLE_HTTP */