New cfilter HTTP-CONNECT for h3/h2/http1.1 eyeballing.
- filter is installed when `--http3` in the tool is used (or
the equivalent CURLOPT_ done in the library)
- starts a QUIC/HTTP/3 connect right away. Should that not
succeed after 100ms (subject to change), a parallel attempt
is started for HTTP/2 and HTTP/1.1 via TCP
- both attempts are subject to IPv6/IPv4 eyeballing, same
as happens for other connections
- tie timeout to the ip-version HAPPY_EYEBALLS_TIMEOUT
- use a `soft` timeout at half the value. When the soft timeout
expires, the HTTPS-CONNECT filter checks if the QUIC filter
has received any data from the server. If not, it will start
the HTTP/2 attempt.
HTTP/3(ngtcp2) improvements.
- setting call_data in all cfilter calls similar to http/2 and vtls filters
for use in callback where no stream data is available.
- returning CURLE_PARTIAL_FILE for prematurely terminated transfers
- enabling pytest test_05 for h3
- shifting functionality to "connect" UDP sockets from ngtcp2
implementation into the udp socket cfilter. Because unconnected
UDP sockets are weird. For example they error when adding to a
pollset.
HTTP/3(quiche) improvements.
- fixed upload bug in quiche implementation, now passes 251 and pytest
- error codes on stream RESET
- improved debug logs
- handling of DRAIN during connect
- limiting pending event queue
HTTP/2 cfilter improvements.
- use LOG_CF macros for dynamic logging in debug build
- fix CURLcode on RST streams to be CURLE_PARTIAL_FILE
- enable pytest test_05 for h2
- fix upload pytests and improve parallel transfer performance.
GOAWAY handling for ngtcp2/quiche
- during connect, when the remote server refuses to accept new connections
and closes immediately (so the local conn goes into DRAIN phase), the
connection is torn down and a another attempt is made after a short grace
period.
This is the behaviour observed with nghttpx when we tell it to shut
down gracefully. Tested in pytest test_03_02.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
- new tests/tests-httpd/scorecard.py for testing h3/h2 protocol implementation.
Invoke:
python3 tests/tests-httpd/scorecard.py --help
for usage.
Improvements on gathering connect statistics and socket access.
- new CF_CTRL_CONN_REPORT_STATS cfilter control for having cfilters
report connection statistics. This is triggered when the connection
has completely connected.
- new void Curl_pgrsTimeWas(..) method to report a timer update with
a timestamp of when it happend. This allows for updating timers
"later", e.g. a connect statistic after full connectivity has been
reached.
- in case of HTTP eyeballing, the previous changes will update
statistics only from the filter chain that "won" the eyeballing.
- new cfilter query CF_QUERY_SOCKET for retrieving the socket used
by a filter chain.
Added methods Curl_conn_cf_get_socket() and Curl_conn_get_socket()
for convenient use of this query.
- Change VTLS backend to query their sub-filters for the socket when
checks during the handshake are made.
HTTP/3 documentation on how https eyeballing works.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
Scorecard with Caddy.
- configure can be run with `--with-test-caddy=path` to specify which caddy to use for testing
- tests/tests-httpd/scorecard.py now measures download speeds with caddy
pytest improvements
- adding Makfile to clean gen dir
- adding nghttpx rundir creation on start
- checking httpd version 2.4.55 for test_05 cases where it is needed. Skipping with message if too old.
- catch exception when checking for caddy existance on system.
Closes #10349
|
||
|---|---|---|
| .. | ||
| testenv | ||
| .gitignore | ||
| config.ini.in | ||
| conftest.py | ||
| Makefile.am | ||
| README.md | ||
| scorecard.py | ||
| test_01_basic.py | ||
| test_02_download.py | ||
| test_03_goaway.py | ||
| test_04_stuttered.py | ||
| test_05_errors.py | ||
| test_06_eyeballs.py | ||
| test_07_upload.py | ||
The curl HTTPD Test Suite
This is an additional test suite using a combination of Apache httpd and nghttpx servers to perform various tests beyond the capabilities of the standard curl test suite.
Usage
The test cases and necessary files are in tests/httpd. You can invoke pytest from there or from the top level curl checkout and it will find all tests.
curl> pytest
platform darwin -- Python 3.9.15, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/sei/projects/curl
collected 5 items
tests/httpd/test_01_basic.py .....
Pytest takes arguments. -v increases its verbosity and can be used several times. -k <expr> can be used to run only matching test cases. The expr can be something resembling a python test or just a string that needs to match test cases in their names.
curl> pytest -vv -k test_01_02
runs all test cases that have test_01_02 in their name. This does not have to be the start of the name.
Depending on your setup, some test cases may be skipped and appear as s in the output. If you run pytest verbose, it will also give you the reason for skipping.
Prerequisites
You will need:
- a recent Python, the
cryptographymodule and, of course,pytest - a apache httpd development version. On Debian/Ubuntu, the package
apache2-devhas this. - a local
curlproject build - optionally, a
nghttpxwith HTTP/3 enabled or h3 test cases will be skipped.
Configuration
Via curl's configure script you may specify:
--with-test-nghttpx=<path-of-nghttpx>if you have nghttpx to use somewhere outside your$PATH.--with-test-httpd=<httpd-install-path>if you have an Apache httpd installed somewhere else. On Debian/Ubuntu it will otherwise look into/usr/binand/usr/sbinto find those.
Usage Tips
Several test cases are parameterized, for example with the HTTP version to use. If you want to run a test with a particular protocol only, use a command line like:
curl> pytest -k "test_02_06 and h2"
Several test cases can be repeated, they all have the repeat parameter. To make this work, you have to start pytest in the test directory itself (for some unknown reason). Like in:
curl/tests/tests-httpd> pytest -k "test_02_06 and h2" --repeat=100
which then runs this test case a hundred times. In case of flaky tests, you can make pytest stop on the first one with:
curl/tests/tests-httpd> pytest -k "test_02_06 and h2" --repeat=100 --maxfail=1
which allow you to inspect output and log files for the failed run. Speaking of log files, the verbosity of pytest is also used to collect curl trace output. If you specify -v three times, the curl command is started with --trace:
curl/tests/tests-httpd> pytest -vvv -k "test_02_06 and h2" --repeat=100 --maxfail=1
all of curl's output and trace file are found in tests/tests-httpd/gen/curl.
Writing Tests
There is a lot of pytest documentation with examples. No use in repeating that here. Assuming you are somewhat familiar with it, it is useful how this general test suite is setup. Especially if you want to add test cases.
Servers
In conftest.py 3 "fixtures" are defined that are used by all test cases:
env: the test environment. It is an instance of classtestenv/env.py:Env. It holds all information about paths, availability of features (HTTP/3!), port numbers to use, domains and SSL certificates for those.httpd: the Apache httpd instance, configured and started, then stopped at the end of the test suite. It has sites configured for the domains fromenv. It also loads a local modulemod_curltest?and makes it available in certain locations. (more on mod_curltest below).nghttpx: an instance of nghttpx that provides HTTP/3 support.nghttpxproxies those requests to thehttpdserver. In a direct mapping, so you may access all the resources under the same path as with HTTP/2. Only the port number used for HTTP/3 requests will be different.
pytest manages these fixture so that they are created once and terminated before exit. This means you can Ctrl-C a running pytest and the server will shutdown. Only when you brutally chop its head off, might there be servers left
behind.
Test Cases
Tests making use of these fixtures have them in their parameter list. This tells pytest that a particular test needs them, so it has to create them. Since one can invoke pytest for just a single test, it is important that a test references the ones it needs.
All test cases start with test_ in their name. We use a double number scheme to group them. This makes it ease to run only specific tests and also give a short mnemonic to communicate trouble with others in the project. Otherwise you are free to name test cases as you think fitting.
Tests are grouped thematically in a file with a single Python test class. This is convenient if you need a special "fixture" for several tests. "fixtures" can have "class" scope.
There is a curl helper class that knows how to invoke curl and interpret its output. Among other things, it does add the local CA to the command line, so that SSL connections to the test servers are verified. Nothing prevents anyone from running curl directly, for specific uses not covered by the CurlClient class.
mod_curltest
The module source code is found in testenv/mod_curltest. It is compiled using the apxs command, commonly provided via the apache2-dev package. Compilation is quick and done once at the start of a test run.
The module adds 2 "handlers" to the Apache server (right now). Handler are pieces of code that receive HTTP requests and generate the response. Those handlers are:
curltest-echo: hooked up on the path/curltest/echo. This one echoes a request and copies all data from the request body to the response body. Useful for simulating upload and checking that the data arrived as intended.curltest-tweak: hooked up on the path/curltest/tweak. This handler is more of a Swiss army knife. It interprets parameters from the URL query string to drive its behavior.status=nnn: generate a response with HTTP status codennn.chunks=n: generatenchunks of data in the response body, defaults to 3.chunk_size=nnn: each chunk should containnnnbytes of data. Maximum is 16KB right now.chunkd_delay=duration: waitdurationtime between writing chunksdelay=duration: waitdurationtime to send the response headersbody_error=(timeout|reset): produce an error after the first chunk in the response bodyid=str: addstrin the response headerrequest-id
duration values are integers, optionally followed by a unit. Units are:
d: days (probably not useful here)h: hoursmi: minutess: seconds (the default)ms: milliseconds
As you can see, mod_curltest's tweak handler allow to simulate many kinds of responses. An example of its use is test_03_01 where responses are delayed using chunk_delay. This gives the response a defined duration and the test uses that to reload httpd in the middle of the first request. A graceful reload in httpd lets ongoing requests finish, but will close the connection afterwards and tear down the serving process. The following request need then to open a new connection. This is verified by the test case.