curl/tests/unit/unit2603.c
Stefan Eissing fc2f1e547a
http2: support HTTP/2 to forward proxies, non-tunneling
- with `--proxy-http2` allow h2 ALPN negotiation to
  forward proxies
- applies to http: requests against a https: proxy only,
  as https: requests will auto-tunnel
- adding a HTTP/1 request parser in http1.c
- removed h2h3.c
- using new request parser in nghttp2 and all h3 backends
- adding test 2603 for request parser
- adding h2 proxy test cases to test_10_*

scorecard.py: request scoring accidentally always run curl
with '-v'. Removed that, expect double numbers.

labeller: added http1.* and h2-proxy sources to detection

Closes #10967
2023-04-17 17:27:49 +02:00

191 lines
5.1 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curlcheck.h"
#include "urldata.h"
#include "http.h"
#include "http1.h"
#include "curl_log.h"
static CURLcode unit_setup(void)
{
return CURLE_OK;
}
static void unit_stop(void)
{
}
struct tcase {
const char **input;
const char *default_scheme;
const char *method;
const char *scheme;
const char *authority;
const char *path;
size_t header_count;
size_t input_remain;
};
static void check_eq(const char *s, const char *exp_s, const char *name)
{
if(s && exp_s) {
if(strcmp(s, exp_s)) {
fprintf(stderr, "expected %s: '%s' but got '%s'\n", name, exp_s, s);
fail("unexpected req component");
}
}
else if(!s && exp_s) {
fprintf(stderr, "expected %s: '%s' but got NULL\n", name, exp_s);
fail("unexpected req component");
}
else if(s && !exp_s) {
fprintf(stderr, "expected %s: NULL but got '%s'\n", name, s);
fail("unexpected req component");
}
}
static void parse_success(struct tcase *t)
{
struct h1_req_parser p;
const char *buf;
size_t buflen, i, in_len, in_consumed;
CURLcode err;
ssize_t nread;
Curl_h1_req_parse_init(&p, 1024);
in_len = in_consumed = 0;
for(i = 0; t->input[i]; ++i) {
buf = t->input[i];
buflen = strlen(buf);
in_len += buflen;
nread = Curl_h1_req_parse_read(&p, buf, buflen, t->default_scheme,
0, &err);
if(nread < 0) {
fprintf(stderr, "got err %d parsing: '%s'\n", err, buf);
fail("error consuming");
}
in_consumed += (size_t)nread;
if((size_t)nread != buflen) {
if(!p.done) {
fprintf(stderr, "only %zd/%zu consumed for: '%s'\n",
nread, buflen, buf);
fail("not all consumed");
}
}
}
fail_if(!p.done, "end not detected");
fail_if(!p.req, "not request created");
if(t->input_remain != (in_len - in_consumed)) {
fprintf(stderr, "expected %zu input bytes to remain, but got %zu\n",
t->input_remain, in_len - in_consumed);
fail("unexpected input consumption");
}
if(p.req) {
check_eq(p.req->method, t->method, "method");
check_eq(p.req->scheme, t->scheme, "scheme");
check_eq(p.req->authority, t->authority, "authority");
check_eq(p.req->path, t->path, "path");
if(Curl_dynhds_count(&p.req->headers) != t->header_count) {
fprintf(stderr, "expected %zu headers but got %zu\n", t->header_count,
Curl_dynhds_count(&p.req->headers));
fail("unexpected req header count");
}
}
Curl_h1_req_parse_free(&p);
}
static const char *T1_INPUT[] = {
"GET /path HTTP/1.1\r\nHost: test.curl.se\r\n\r\n",
NULL,
};
static struct tcase TEST1a = {
T1_INPUT, NULL, "GET", NULL, NULL, "/path", 1, 0
};
static struct tcase TEST1b = {
T1_INPUT, "https", "GET", "https", NULL, "/path", 1, 0
};
static const char *T2_INPUT[] = {
"GET /path HTT",
"P/1.1\r\nHost: te",
"st.curl.se\r\n\r",
"\n12345678",
NULL,
};
static struct tcase TEST2 = {
T2_INPUT, NULL, "GET", NULL, NULL, "/path", 1, 8
};
static const char *T3_INPUT[] = {
"GET ftp://ftp.curl.se/xxx?a=2 HTTP/1.1\r\nContent-Length: 0\r",
"\nUser-Agent: xxx\r\n\r\n",
NULL,
};
static struct tcase TEST3a = {
T3_INPUT, NULL, "GET", "ftp", "ftp.curl.se", "/xxx?a=2", 2, 0
};
static const char *T4_INPUT[] = {
"CONNECT ftp.curl.se:123 HTTP/1.1\r\nContent-Length: 0\r\n",
"User-Agent: xxx\r\n",
"nothing: \r\n\r\n\n\n",
NULL,
};
static struct tcase TEST4a = {
T4_INPUT, NULL, "CONNECT", NULL, "ftp.curl.se:123", NULL, 3, 2
};
static const char *T5_INPUT[] = {
"OPTIONS * HTTP/1.1\r\nContent-Length: 0\r\nBlabla: xxx.yyy\r",
"\n\tzzzzzz\r\n\r\n",
"123",
NULL,
};
static struct tcase TEST5a = {
T5_INPUT, NULL, "OPTIONS", NULL, NULL, "*", 2, 3
};
static const char *T6_INPUT[] = {
"PUT /path HTTP/1.1\nHost: test.curl.se\n\n123",
NULL,
};
static struct tcase TEST6a = {
T6_INPUT, NULL, "PUT", NULL, NULL, "/path", 1, 3
};
UNITTEST_START
parse_success(&TEST1a);
parse_success(&TEST1b);
parse_success(&TEST2);
parse_success(&TEST3a);
parse_success(&TEST4a);
parse_success(&TEST5a);
parse_success(&TEST6a);
UNITTEST_STOP