xref: /third_party/curl/lib/http1.c (revision 13498266)
113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifndef CURL_DISABLE_HTTP
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include "urldata.h"
3013498266Sopenharmony_ci#include <curl/curl.h>
3113498266Sopenharmony_ci#include "http.h"
3213498266Sopenharmony_ci#include "http1.h"
3313498266Sopenharmony_ci#include "urlapi-int.h"
3413498266Sopenharmony_ci
3513498266Sopenharmony_ci/* The last 3 #include files should be in this order */
3613498266Sopenharmony_ci#include "curl_printf.h"
3713498266Sopenharmony_ci#include "curl_memory.h"
3813498266Sopenharmony_ci#include "memdebug.h"
3913498266Sopenharmony_ci
4013498266Sopenharmony_ci
4113498266Sopenharmony_ci#define H1_MAX_URL_LEN   (8*1024)
4213498266Sopenharmony_ci
4313498266Sopenharmony_civoid Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len)
4413498266Sopenharmony_ci{
4513498266Sopenharmony_ci  memset(parser, 0, sizeof(*parser));
4613498266Sopenharmony_ci  parser->max_line_len = max_line_len;
4713498266Sopenharmony_ci  Curl_dyn_init(&parser->scratch, max_line_len);
4813498266Sopenharmony_ci}
4913498266Sopenharmony_ci
5013498266Sopenharmony_civoid Curl_h1_req_parse_free(struct h1_req_parser *parser)
5113498266Sopenharmony_ci{
5213498266Sopenharmony_ci  if(parser) {
5313498266Sopenharmony_ci    Curl_http_req_free(parser->req);
5413498266Sopenharmony_ci    Curl_dyn_free(&parser->scratch);
5513498266Sopenharmony_ci    parser->req = NULL;
5613498266Sopenharmony_ci    parser->done = FALSE;
5713498266Sopenharmony_ci  }
5813498266Sopenharmony_ci}
5913498266Sopenharmony_ci
6013498266Sopenharmony_cistatic CURLcode trim_line(struct h1_req_parser *parser, int options)
6113498266Sopenharmony_ci{
6213498266Sopenharmony_ci  DEBUGASSERT(parser->line);
6313498266Sopenharmony_ci  if(parser->line_len) {
6413498266Sopenharmony_ci    if(parser->line[parser->line_len - 1] == '\n')
6513498266Sopenharmony_ci      --parser->line_len;
6613498266Sopenharmony_ci    if(parser->line_len) {
6713498266Sopenharmony_ci      if(parser->line[parser->line_len - 1] == '\r')
6813498266Sopenharmony_ci        --parser->line_len;
6913498266Sopenharmony_ci      else if(options & H1_PARSE_OPT_STRICT)
7013498266Sopenharmony_ci        return CURLE_URL_MALFORMAT;
7113498266Sopenharmony_ci    }
7213498266Sopenharmony_ci    else if(options & H1_PARSE_OPT_STRICT)
7313498266Sopenharmony_ci      return CURLE_URL_MALFORMAT;
7413498266Sopenharmony_ci  }
7513498266Sopenharmony_ci  else if(options & H1_PARSE_OPT_STRICT)
7613498266Sopenharmony_ci    return CURLE_URL_MALFORMAT;
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci  if(parser->line_len > parser->max_line_len) {
7913498266Sopenharmony_ci    return CURLE_URL_MALFORMAT;
8013498266Sopenharmony_ci  }
8113498266Sopenharmony_ci  return CURLE_OK;
8213498266Sopenharmony_ci}
8313498266Sopenharmony_ci
8413498266Sopenharmony_cistatic ssize_t detect_line(struct h1_req_parser *parser,
8513498266Sopenharmony_ci                           const char *buf, const size_t buflen,
8613498266Sopenharmony_ci                           CURLcode *err)
8713498266Sopenharmony_ci{
8813498266Sopenharmony_ci  const char  *line_end;
8913498266Sopenharmony_ci
9013498266Sopenharmony_ci  DEBUGASSERT(!parser->line);
9113498266Sopenharmony_ci  line_end = memchr(buf, '\n', buflen);
9213498266Sopenharmony_ci  if(!line_end) {
9313498266Sopenharmony_ci    *err = CURLE_AGAIN;
9413498266Sopenharmony_ci    return -1;
9513498266Sopenharmony_ci  }
9613498266Sopenharmony_ci  parser->line = buf;
9713498266Sopenharmony_ci  parser->line_len = line_end - buf + 1;
9813498266Sopenharmony_ci  *err = CURLE_OK;
9913498266Sopenharmony_ci  return (ssize_t)parser->line_len;
10013498266Sopenharmony_ci}
10113498266Sopenharmony_ci
10213498266Sopenharmony_cistatic ssize_t next_line(struct h1_req_parser *parser,
10313498266Sopenharmony_ci                         const char *buf, const size_t buflen, int options,
10413498266Sopenharmony_ci                         CURLcode *err)
10513498266Sopenharmony_ci{
10613498266Sopenharmony_ci  ssize_t nread = 0;
10713498266Sopenharmony_ci
10813498266Sopenharmony_ci  if(parser->line) {
10913498266Sopenharmony_ci    parser->line = NULL;
11013498266Sopenharmony_ci    parser->line_len = 0;
11113498266Sopenharmony_ci    Curl_dyn_reset(&parser->scratch);
11213498266Sopenharmony_ci  }
11313498266Sopenharmony_ci
11413498266Sopenharmony_ci  nread = detect_line(parser, buf, buflen, err);
11513498266Sopenharmony_ci  if(nread >= 0) {
11613498266Sopenharmony_ci    if(Curl_dyn_len(&parser->scratch)) {
11713498266Sopenharmony_ci      /* append detected line to scratch to have the complete line */
11813498266Sopenharmony_ci      *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len);
11913498266Sopenharmony_ci      if(*err)
12013498266Sopenharmony_ci        return -1;
12113498266Sopenharmony_ci      parser->line = Curl_dyn_ptr(&parser->scratch);
12213498266Sopenharmony_ci      parser->line_len = Curl_dyn_len(&parser->scratch);
12313498266Sopenharmony_ci    }
12413498266Sopenharmony_ci    *err = trim_line(parser, options);
12513498266Sopenharmony_ci    if(*err)
12613498266Sopenharmony_ci      return -1;
12713498266Sopenharmony_ci  }
12813498266Sopenharmony_ci  else if(*err == CURLE_AGAIN) {
12913498266Sopenharmony_ci    /* no line end in `buf`, add it to our scratch */
13013498266Sopenharmony_ci    *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen);
13113498266Sopenharmony_ci    nread = (*err)? -1 : (ssize_t)buflen;
13213498266Sopenharmony_ci  }
13313498266Sopenharmony_ci  return nread;
13413498266Sopenharmony_ci}
13513498266Sopenharmony_ci
13613498266Sopenharmony_cistatic CURLcode start_req(struct h1_req_parser *parser,
13713498266Sopenharmony_ci                          const char *scheme_default, int options)
13813498266Sopenharmony_ci{
13913498266Sopenharmony_ci  const char  *p, *m, *target, *hv, *scheme, *authority, *path;
14013498266Sopenharmony_ci  size_t m_len, target_len, hv_len, scheme_len, authority_len, path_len;
14113498266Sopenharmony_ci  size_t i;
14213498266Sopenharmony_ci  CURLU *url = NULL;
14313498266Sopenharmony_ci  CURLcode result = CURLE_URL_MALFORMAT; /* Use this as default fail */
14413498266Sopenharmony_ci
14513498266Sopenharmony_ci  DEBUGASSERT(!parser->req);
14613498266Sopenharmony_ci  /* line must match: "METHOD TARGET HTTP_VERSION" */
14713498266Sopenharmony_ci  p = memchr(parser->line, ' ', parser->line_len);
14813498266Sopenharmony_ci  if(!p || p == parser->line)
14913498266Sopenharmony_ci    goto out;
15013498266Sopenharmony_ci
15113498266Sopenharmony_ci  m = parser->line;
15213498266Sopenharmony_ci  m_len = p - parser->line;
15313498266Sopenharmony_ci  target = p + 1;
15413498266Sopenharmony_ci  target_len = hv_len = 0;
15513498266Sopenharmony_ci  hv = NULL;
15613498266Sopenharmony_ci
15713498266Sopenharmony_ci  /* URL may contain spaces so scan backwards */
15813498266Sopenharmony_ci  for(i = parser->line_len; i > m_len; --i) {
15913498266Sopenharmony_ci    if(parser->line[i] == ' ') {
16013498266Sopenharmony_ci      hv = &parser->line[i + 1];
16113498266Sopenharmony_ci      hv_len = parser->line_len - i;
16213498266Sopenharmony_ci      target_len = (hv - target) - 1;
16313498266Sopenharmony_ci      break;
16413498266Sopenharmony_ci    }
16513498266Sopenharmony_ci  }
16613498266Sopenharmony_ci  /* no SPACE found or empty TARGET or empty HTTP_VERSION */
16713498266Sopenharmony_ci  if(!target_len || !hv_len)
16813498266Sopenharmony_ci    goto out;
16913498266Sopenharmony_ci
17013498266Sopenharmony_ci  /* TODO: we do not check HTTP_VERSION for conformity, should
17113498266Sopenharmony_ci   + do that when STRICT option is supplied. */
17213498266Sopenharmony_ci  (void)hv;
17313498266Sopenharmony_ci
17413498266Sopenharmony_ci  /* The TARGET can be (rfc 9112, ch. 3.2):
17513498266Sopenharmony_ci   * origin-form:     path + optional query
17613498266Sopenharmony_ci   * absolute-form:   absolute URI
17713498266Sopenharmony_ci   * authority-form:  host+port for CONNECT
17813498266Sopenharmony_ci   * asterisk-form:   '*' for OPTIONS
17913498266Sopenharmony_ci   *
18013498266Sopenharmony_ci   * from TARGET, we derive `scheme` `authority` `path`
18113498266Sopenharmony_ci   * origin-form            --        --          TARGET
18213498266Sopenharmony_ci   * absolute-form          URL*      URL*        URL*
18313498266Sopenharmony_ci   * authority-form         --        TARGET      --
18413498266Sopenharmony_ci   * asterisk-form          --        --          TARGET
18513498266Sopenharmony_ci   */
18613498266Sopenharmony_ci  scheme = authority = path = NULL;
18713498266Sopenharmony_ci  scheme_len = authority_len = path_len = 0;
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci  if(target_len == 1 && target[0] == '*') {
19013498266Sopenharmony_ci    /* asterisk-form */
19113498266Sopenharmony_ci    path = target;
19213498266Sopenharmony_ci    path_len = target_len;
19313498266Sopenharmony_ci  }
19413498266Sopenharmony_ci  else if(!strncmp("CONNECT", m, m_len)) {
19513498266Sopenharmony_ci    /* authority-form */
19613498266Sopenharmony_ci    authority = target;
19713498266Sopenharmony_ci    authority_len = target_len;
19813498266Sopenharmony_ci  }
19913498266Sopenharmony_ci  else if(target[0] == '/') {
20013498266Sopenharmony_ci    /* origin-form */
20113498266Sopenharmony_ci    path = target;
20213498266Sopenharmony_ci    path_len = target_len;
20313498266Sopenharmony_ci  }
20413498266Sopenharmony_ci  else {
20513498266Sopenharmony_ci    /* origin-form OR absolute-form */
20613498266Sopenharmony_ci    CURLUcode uc;
20713498266Sopenharmony_ci    char tmp[H1_MAX_URL_LEN];
20813498266Sopenharmony_ci
20913498266Sopenharmony_ci    /* default, unless we see an absolute URL */
21013498266Sopenharmony_ci    path = target;
21113498266Sopenharmony_ci    path_len = target_len;
21213498266Sopenharmony_ci
21313498266Sopenharmony_ci    /* URL parser wants 0-termination */
21413498266Sopenharmony_ci    if(target_len >= sizeof(tmp))
21513498266Sopenharmony_ci      goto out;
21613498266Sopenharmony_ci    memcpy(tmp, target, target_len);
21713498266Sopenharmony_ci    tmp[target_len] = '\0';
21813498266Sopenharmony_ci    /* See if treating TARGET as an absolute URL makes sense */
21913498266Sopenharmony_ci    if(Curl_is_absolute_url(tmp, NULL, 0, FALSE)) {
22013498266Sopenharmony_ci      int url_options;
22113498266Sopenharmony_ci
22213498266Sopenharmony_ci      url = curl_url();
22313498266Sopenharmony_ci      if(!url) {
22413498266Sopenharmony_ci        result = CURLE_OUT_OF_MEMORY;
22513498266Sopenharmony_ci        goto out;
22613498266Sopenharmony_ci      }
22713498266Sopenharmony_ci      url_options = (CURLU_NON_SUPPORT_SCHEME|
22813498266Sopenharmony_ci                     CURLU_PATH_AS_IS|
22913498266Sopenharmony_ci                     CURLU_NO_DEFAULT_PORT);
23013498266Sopenharmony_ci      if(!(options & H1_PARSE_OPT_STRICT))
23113498266Sopenharmony_ci        url_options |= CURLU_ALLOW_SPACE;
23213498266Sopenharmony_ci      uc = curl_url_set(url, CURLUPART_URL, tmp, url_options);
23313498266Sopenharmony_ci      if(uc) {
23413498266Sopenharmony_ci        goto out;
23513498266Sopenharmony_ci      }
23613498266Sopenharmony_ci    }
23713498266Sopenharmony_ci
23813498266Sopenharmony_ci    if(!url && (options & H1_PARSE_OPT_STRICT)) {
23913498266Sopenharmony_ci      /* we should have an absolute URL or have seen `/` earlier */
24013498266Sopenharmony_ci      goto out;
24113498266Sopenharmony_ci    }
24213498266Sopenharmony_ci  }
24313498266Sopenharmony_ci
24413498266Sopenharmony_ci  if(url) {
24513498266Sopenharmony_ci    result = Curl_http_req_make2(&parser->req, m, m_len, url, scheme_default);
24613498266Sopenharmony_ci  }
24713498266Sopenharmony_ci  else {
24813498266Sopenharmony_ci    if(!scheme && scheme_default) {
24913498266Sopenharmony_ci      scheme = scheme_default;
25013498266Sopenharmony_ci      scheme_len = strlen(scheme_default);
25113498266Sopenharmony_ci    }
25213498266Sopenharmony_ci    result = Curl_http_req_make(&parser->req, m, m_len, scheme, scheme_len,
25313498266Sopenharmony_ci                                authority, authority_len, path, path_len);
25413498266Sopenharmony_ci  }
25513498266Sopenharmony_ci
25613498266Sopenharmony_ciout:
25713498266Sopenharmony_ci  curl_url_cleanup(url);
25813498266Sopenharmony_ci  return result;
25913498266Sopenharmony_ci}
26013498266Sopenharmony_ci
26113498266Sopenharmony_cissize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
26213498266Sopenharmony_ci                               const char *buf, size_t buflen,
26313498266Sopenharmony_ci                               const char *scheme_default, int options,
26413498266Sopenharmony_ci                               CURLcode *err)
26513498266Sopenharmony_ci{
26613498266Sopenharmony_ci  ssize_t nread = 0, n;
26713498266Sopenharmony_ci
26813498266Sopenharmony_ci  *err = CURLE_OK;
26913498266Sopenharmony_ci  while(!parser->done) {
27013498266Sopenharmony_ci    n = next_line(parser, buf, buflen, options, err);
27113498266Sopenharmony_ci    if(n < 0) {
27213498266Sopenharmony_ci      if(*err != CURLE_AGAIN) {
27313498266Sopenharmony_ci        nread = -1;
27413498266Sopenharmony_ci      }
27513498266Sopenharmony_ci      *err = CURLE_OK;
27613498266Sopenharmony_ci      goto out;
27713498266Sopenharmony_ci    }
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci    /* Consume this line */
28013498266Sopenharmony_ci    nread += (size_t)n;
28113498266Sopenharmony_ci    buf += (size_t)n;
28213498266Sopenharmony_ci    buflen -= (size_t)n;
28313498266Sopenharmony_ci
28413498266Sopenharmony_ci    if(!parser->line) {
28513498266Sopenharmony_ci      /* consumed bytes, but line not complete */
28613498266Sopenharmony_ci      if(!buflen)
28713498266Sopenharmony_ci        goto out;
28813498266Sopenharmony_ci    }
28913498266Sopenharmony_ci    else if(!parser->req) {
29013498266Sopenharmony_ci      *err = start_req(parser, scheme_default, options);
29113498266Sopenharmony_ci      if(*err) {
29213498266Sopenharmony_ci        nread = -1;
29313498266Sopenharmony_ci        goto out;
29413498266Sopenharmony_ci      }
29513498266Sopenharmony_ci    }
29613498266Sopenharmony_ci    else if(parser->line_len == 0) {
29713498266Sopenharmony_ci      /* last, empty line, we are finished */
29813498266Sopenharmony_ci      if(!parser->req) {
29913498266Sopenharmony_ci        *err = CURLE_URL_MALFORMAT;
30013498266Sopenharmony_ci        nread = -1;
30113498266Sopenharmony_ci        goto out;
30213498266Sopenharmony_ci      }
30313498266Sopenharmony_ci      parser->done = TRUE;
30413498266Sopenharmony_ci      Curl_dyn_reset(&parser->scratch);
30513498266Sopenharmony_ci      /* last chance adjustments */
30613498266Sopenharmony_ci    }
30713498266Sopenharmony_ci    else {
30813498266Sopenharmony_ci      *err = Curl_dynhds_h1_add_line(&parser->req->headers,
30913498266Sopenharmony_ci                                     parser->line, parser->line_len);
31013498266Sopenharmony_ci      if(*err) {
31113498266Sopenharmony_ci        nread = -1;
31213498266Sopenharmony_ci        goto out;
31313498266Sopenharmony_ci      }
31413498266Sopenharmony_ci    }
31513498266Sopenharmony_ci  }
31613498266Sopenharmony_ci
31713498266Sopenharmony_ciout:
31813498266Sopenharmony_ci  return nread;
31913498266Sopenharmony_ci}
32013498266Sopenharmony_ci
32113498266Sopenharmony_ciCURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
32213498266Sopenharmony_ci                                struct dynbuf *dbuf)
32313498266Sopenharmony_ci{
32413498266Sopenharmony_ci  CURLcode result;
32513498266Sopenharmony_ci
32613498266Sopenharmony_ci  result = Curl_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
32713498266Sopenharmony_ci                         req->method,
32813498266Sopenharmony_ci                         req->scheme? req->scheme : "",
32913498266Sopenharmony_ci                         req->scheme? "://" : "",
33013498266Sopenharmony_ci                         req->authority? req->authority : "",
33113498266Sopenharmony_ci                         req->path? req->path : "",
33213498266Sopenharmony_ci                         http_minor);
33313498266Sopenharmony_ci  if(result)
33413498266Sopenharmony_ci    goto out;
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci  result = Curl_dynhds_h1_dprint(&req->headers, dbuf);
33713498266Sopenharmony_ci  if(result)
33813498266Sopenharmony_ci    goto out;
33913498266Sopenharmony_ci
34013498266Sopenharmony_ci  result = Curl_dyn_addn(dbuf, STRCONST("\r\n"));
34113498266Sopenharmony_ci
34213498266Sopenharmony_ciout:
34313498266Sopenharmony_ci  return result;
34413498266Sopenharmony_ci}
34513498266Sopenharmony_ci
34613498266Sopenharmony_ci#endif /* !CURL_DISABLE_HTTP */
347