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