mirror of
https://github.com/curl/curl.git
synced 2026-05-30 03:07:28 +03:00
h2/h3: handle methods with spaces
The parsing of the HTTP/1.1 formatted request into the h2/h3 header structures should detect CURLOPT_CUSTOMREQUEST methods and forward them correctly. Add test_01_20 to verify Fixes #19543 Reported-by: Omdahake on github Closes #19563
This commit is contained in:
parent
2459dc7a22
commit
ea105708c9
8 changed files with 80 additions and 21 deletions
23
lib/http1.c
23
lib/http1.c
|
|
@ -134,7 +134,9 @@ static ssize_t next_line(struct h1_req_parser *parser,
|
|||
}
|
||||
|
||||
static CURLcode start_req(struct h1_req_parser *parser,
|
||||
const char *scheme_default, int options)
|
||||
const char *scheme_default,
|
||||
const char *custom_method,
|
||||
int options)
|
||||
{
|
||||
const char *p, *m, *target, *hv, *scheme, *authority, *path;
|
||||
size_t m_len, target_len, hv_len, scheme_len, authority_len, path_len;
|
||||
|
|
@ -144,9 +146,15 @@ static CURLcode start_req(struct h1_req_parser *parser,
|
|||
|
||||
DEBUGASSERT(!parser->req);
|
||||
/* line must match: "METHOD TARGET HTTP_VERSION" */
|
||||
p = memchr(parser->line, ' ', parser->line_len);
|
||||
if(!p || p == parser->line)
|
||||
goto out;
|
||||
if(custom_method && custom_method[0] &&
|
||||
!strncmp(custom_method, parser->line, strlen(custom_method))) {
|
||||
p = parser->line + strlen(custom_method);
|
||||
}
|
||||
else {
|
||||
p = memchr(parser->line, ' ', parser->line_len);
|
||||
if(!p || p == parser->line)
|
||||
goto out;
|
||||
}
|
||||
|
||||
m = parser->line;
|
||||
m_len = p - parser->line;
|
||||
|
|
@ -258,8 +266,9 @@ out:
|
|||
|
||||
ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
|
||||
const char *buf, size_t buflen,
|
||||
const char *scheme_default, int options,
|
||||
CURLcode *err)
|
||||
const char *scheme_default,
|
||||
const char *custom_method,
|
||||
int options, CURLcode *err)
|
||||
{
|
||||
ssize_t nread = 0, n;
|
||||
|
||||
|
|
@ -285,7 +294,7 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
|
|||
goto out;
|
||||
}
|
||||
else if(!parser->req) {
|
||||
*err = start_req(parser, scheme_default, options);
|
||||
*err = start_req(parser, scheme_default, custom_method, options);
|
||||
if(*err) {
|
||||
nread = -1;
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ void Curl_h1_req_parse_free(struct h1_req_parser *parser);
|
|||
|
||||
ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
|
||||
const char *buf, size_t buflen,
|
||||
const char *scheme_default, int options,
|
||||
CURLcode *err);
|
||||
const char *scheme_default,
|
||||
const char *custom_method,
|
||||
int options, CURLcode *err);
|
||||
|
||||
CURLcode Curl_h1_req_dprint(const struct httpreq *req,
|
||||
struct dynbuf *dbuf);
|
||||
|
|
|
|||
|
|
@ -2248,7 +2248,10 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream,
|
|||
if(result)
|
||||
goto out;
|
||||
|
||||
rc = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result);
|
||||
rc = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL,
|
||||
!data->state.http_ignorecustom ?
|
||||
data->set.str[STRING_CUSTOMREQUEST] : NULL,
|
||||
0, &result);
|
||||
if(!curlx_sztouz(rc, &nwritten))
|
||||
goto out;
|
||||
*pnwritten = nwritten;
|
||||
|
|
|
|||
|
|
@ -1531,7 +1531,10 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result);
|
||||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL,
|
||||
!data->state.http_ignorecustom ?
|
||||
data->set.str[STRING_CUSTOMREQUEST] : NULL,
|
||||
0, &result);
|
||||
if(nwritten < 0)
|
||||
goto out;
|
||||
*pnwritten = (size_t)nwritten;
|
||||
|
|
|
|||
|
|
@ -1900,7 +1900,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
|
||||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL,
|
||||
!data->state.http_ignorecustom ?
|
||||
data->set.str[STRING_CUSTOMREQUEST] : NULL,
|
||||
0, err);
|
||||
if(nwritten < 0)
|
||||
goto out;
|
||||
if(!stream->h1.done) {
|
||||
|
|
|
|||
|
|
@ -991,7 +991,10 @@ static CURLcode h3_open_stream(struct Curl_cfilter *cf,
|
|||
Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
|
||||
|
||||
DEBUGASSERT(stream);
|
||||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, blen, NULL, 0, &result);
|
||||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, blen, NULL,
|
||||
!data->state.http_ignorecustom ?
|
||||
data->set.str[STRING_CUSTOMREQUEST] : NULL,
|
||||
0, &result);
|
||||
if(nwritten < 0)
|
||||
goto out;
|
||||
if(!stream->h1.done) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
###########################################################################
|
||||
#
|
||||
import logging
|
||||
import re
|
||||
import pytest
|
||||
|
||||
from testenv import Env
|
||||
|
|
@ -293,3 +294,24 @@ class TestBasic:
|
|||
r = curl.http_download(urls=[url1, url2], alpn_proto=proto, with_stats=True)
|
||||
assert len(r.stats) == 2
|
||||
assert r.total_connects == 2, f'{r.dump_logs()}'
|
||||
|
||||
# use a custom method containing a space
|
||||
# check that h2/h3 did send that in the :method pseudo header. #19543
|
||||
@pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs verbosecurl")
|
||||
@pytest.mark.parametrize("proto", Env.http_protos())
|
||||
def test_01_20_method_space(self, env: Env, proto, httpd):
|
||||
curl = CurlClient(env=env)
|
||||
method = 'IN SANE'
|
||||
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo'
|
||||
r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True,
|
||||
extra_args=['-X', method])
|
||||
assert len(r.stats) == 1
|
||||
if proto == 'h2' or proto == 'h3':
|
||||
r.check_response(http_status=0)
|
||||
re_m = re.compile(r'.*\[:method: ([^\]]+)\].*')
|
||||
lines = [line for line in r.trace_lines if re_m.match(line)]
|
||||
assert len(lines) == 1, f'{r.dump_logs()}'
|
||||
m = re_m.match(lines[0])
|
||||
assert m.group(1) == method, f'{r.dump_logs()}'
|
||||
else:
|
||||
r.check_response(http_status=400)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ static void check_eq(const char *s, const char *exp_s, const char *name)
|
|||
struct tcase {
|
||||
const char **input;
|
||||
const char *default_scheme;
|
||||
const char *custom_method;
|
||||
const char *method;
|
||||
const char *scheme;
|
||||
const char *authority;
|
||||
|
|
@ -74,7 +75,7 @@ static void parse_success(const struct tcase *t)
|
|||
buflen = strlen(buf);
|
||||
in_len += buflen;
|
||||
nread = Curl_h1_req_parse_read(&p, buf, buflen, t->default_scheme,
|
||||
0, &err);
|
||||
t->custom_method, 0, &err);
|
||||
if(nread < 0) {
|
||||
curl_mfprintf(stderr, "got err %d parsing: '%s'\n", err, buf);
|
||||
fail("error consuming");
|
||||
|
|
@ -122,10 +123,10 @@ static CURLcode test_unit2603(const char *arg)
|
|||
NULL,
|
||||
};
|
||||
static const struct tcase TEST1a = {
|
||||
T1_INPUT, NULL, "GET", NULL, NULL, "/path", 1, 0
|
||||
T1_INPUT, NULL, NULL, "GET", NULL, NULL, "/path", 1, 0
|
||||
};
|
||||
static const struct tcase TEST1b = {
|
||||
T1_INPUT, "https", "GET", "https", NULL, "/path", 1, 0
|
||||
T1_INPUT, "https", NULL, "GET", "https", NULL, "/path", 1, 0
|
||||
};
|
||||
|
||||
static const char *T2_INPUT[] = {
|
||||
|
|
@ -136,7 +137,7 @@ static CURLcode test_unit2603(const char *arg)
|
|||
NULL,
|
||||
};
|
||||
static const struct tcase TEST2 = {
|
||||
T2_INPUT, NULL, "GET", NULL, NULL, "/path", 1, 8
|
||||
T2_INPUT, NULL, NULL, "GET", NULL, NULL, "/path", 1, 8
|
||||
};
|
||||
|
||||
static const char *T3_INPUT[] = {
|
||||
|
|
@ -145,7 +146,7 @@ static CURLcode test_unit2603(const char *arg)
|
|||
NULL,
|
||||
};
|
||||
static const struct tcase TEST3a = {
|
||||
T3_INPUT, NULL, "GET", "ftp", "ftp.curl.se", "/xxx?a=2", 2, 0
|
||||
T3_INPUT, NULL, NULL, "GET", "ftp", "ftp.curl.se", "/xxx?a=2", 2, 0
|
||||
};
|
||||
|
||||
static const char *T4_INPUT[] = {
|
||||
|
|
@ -155,7 +156,7 @@ static CURLcode test_unit2603(const char *arg)
|
|||
NULL,
|
||||
};
|
||||
static const struct tcase TEST4a = {
|
||||
T4_INPUT, NULL, "CONNECT", NULL, "ftp.curl.se:123", NULL, 3, 2
|
||||
T4_INPUT, NULL, NULL, "CONNECT", NULL, "ftp.curl.se:123", NULL, 3, 2
|
||||
};
|
||||
|
||||
static const char *T5_INPUT[] = {
|
||||
|
|
@ -165,7 +166,7 @@ static CURLcode test_unit2603(const char *arg)
|
|||
NULL,
|
||||
};
|
||||
static const struct tcase TEST5a = {
|
||||
T5_INPUT, NULL, "OPTIONS", NULL, NULL, "*", 2, 3
|
||||
T5_INPUT, NULL, NULL, "OPTIONS", NULL, NULL, "*", 2, 3
|
||||
};
|
||||
|
||||
static const char *T6_INPUT[] = {
|
||||
|
|
@ -173,7 +174,19 @@ static CURLcode test_unit2603(const char *arg)
|
|||
NULL,
|
||||
};
|
||||
static const struct tcase TEST6a = {
|
||||
T6_INPUT, NULL, "PUT", NULL, NULL, "/path", 1, 3
|
||||
T6_INPUT, NULL, NULL, "PUT", NULL, NULL, "/path", 1, 3
|
||||
};
|
||||
|
||||
/* test a custom method with space, #19543 */
|
||||
static const char *T7_INPUT[] = {
|
||||
"IN SANE /path HTTP/1.1\r\nContent-Length: 0\r\n\r\n",
|
||||
NULL,
|
||||
};
|
||||
static const struct tcase TEST7a = {
|
||||
T7_INPUT, NULL, NULL, "IN", NULL, NULL, "SANE /path", 1, 0
|
||||
};
|
||||
static const struct tcase TEST7b = {
|
||||
T7_INPUT, NULL, "IN SANE", "IN SANE", NULL, NULL, "/path", 1, 0
|
||||
};
|
||||
|
||||
parse_success(&TEST1a);
|
||||
|
|
@ -183,6 +196,8 @@ static CURLcode test_unit2603(const char *arg)
|
|||
parse_success(&TEST4a);
|
||||
parse_success(&TEST5a);
|
||||
parse_success(&TEST6a);
|
||||
parse_success(&TEST7a);
|
||||
parse_success(&TEST7b);
|
||||
#endif
|
||||
|
||||
UNITTEST_END_SIMPLE
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue