}
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;
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;
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;
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;
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);
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;
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;
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) {
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) {
###########################################################################
#
import logging
+import re
import pytest
from testenv import Env
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)
struct tcase {
const char **input;
const char *default_scheme;
+ const char *custom_method;
const char *method;
const char *scheme;
const char *authority;
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");
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[] = {
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[] = {
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[] = {
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[] = {
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[] = {
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);
parse_success(&TEST4a);
parse_success(&TEST5a);
parse_success(&TEST6a);
+ parse_success(&TEST7a);
+ parse_success(&TEST7b);
#endif
UNITTEST_END_SIMPLE