From: Stefan Eissing Date: Thu, 16 May 2024 15:53:19 +0000 (+0200) Subject: pytest: add DELETE tests, check server version X-Git-Tag: curl-8_8_0~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dad8c1e3057b4cff0b2a1fd24f0013f8b87a0e6f;p=thirdparty%2Fcurl.git pytest: add DELETE tests, check server version - add tests for DELETE working - check apache version in keepalive test - fix some comments Closes #13679 --- diff --git a/tests/http/test_03_goaway.py b/tests/http/test_03_goaway.py index e0319b5595..8cb917ccf3 100644 --- a/tests/http/test_03_goaway.py +++ b/tests/http/test_03_goaway.py @@ -122,10 +122,13 @@ class TestGoAway: def long_run(): curl = CurlClient(env=env) # send 10 chunks of 1024 bytes in a response body with 100ms delay in between + # pause 2 seconds between requests urln = f'https://{env.authority_for(env.domain1, proto)}' \ f'/curltest/tweak?id=[0-{count - 1}]'\ '&chunks=10&chunk_size=1024&chunk_delay=100ms' - self.r = curl.http_download(urls=[urln], alpn_proto=proto) + self.r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ + '--rate', '30/m', + ]) t = Thread(target=long_run) t.start() @@ -136,7 +139,7 @@ class TestGoAway: t.join() r: ExecResult = self.r r.check_response(count=count, http_status=200, connect_count=2) - # reload will shut down the connection gracefully with GOAWAY + # reload will shut down the connection gracefully # we expect to see a second connection opened afterwards for idx, s in enumerate(r.stats): if s['num_connects'] > 0: diff --git a/tests/http/test_12_reuse.py b/tests/http/test_12_reuse.py index 83bfadfe4a..5896e19619 100644 --- a/tests/http/test_12_reuse.py +++ b/tests/http/test_12_reuse.py @@ -59,6 +59,8 @@ class TestReuse: delta = 5 assert (count/2 - delta) < r.total_connects < (count/2 + delta) + @pytest.mark.skipif(condition=Env.httpd_is_at_least('2.5.0'), + reason=f"httpd 2.5+ handles KeepAlives different") @pytest.mark.parametrize("proto", ['http/1.1']) def test_12_02_h1_conn_timeout(self, env: Env, httpd, nghttpx, repeat, proto): diff --git a/tests/http/test_18_methods.py b/tests/http/test_18_methods.py new file mode 100644 index 0000000000..fca3ee2d8d --- /dev/null +++ b/tests/http/test_18_methods.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# +import difflib +import filecmp +import logging +import os +from datetime import timedelta +import pytest + +from testenv import Env, CurlClient, LocalClient + + +log = logging.getLogger(__name__) + + +class TestMethods: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env, httpd, nghttpx): + if env.have_h3(): + nghttpx.start_if_needed() + httpd.clear_extra_configs() + httpd.reload() + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env, httpd): + indir = httpd.docs_dir + env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + + # download 1 file + @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) + def test_18_01_delete(self, env: Env, httpd, nghttpx, repeat, proto): + if proto == 'h3' and not env.have_h3(): + pytest.skip("h3 not supported") + count = 1 + curl = CurlClient(env=env) + url = f'https://{env.authority_for(env.domain1, proto)}/curltest/tweak?id=[0-{count-1}]' + r = curl.http_delete(urls=[url], alpn_proto=proto) + r.check_stats(count=count, http_status=204, exitcode=0) + + # make HTTP/2 in the server send + # - HEADER frame with 204 and eos=0 + # - 10ms later DATA frame length=0 and eos=1 + # should be accepted + def test_18_02_delete_h2_special(self, env: Env, httpd, nghttpx, repeat): + proto = 'h2' + count = 1 + curl = CurlClient(env=env) + url = f'https://{env.authority_for(env.domain1, proto)}/curltest/tweak?id=[0-{count-1}]'\ + '&chunks=1&chunk_size=0&chunk_delay=10ms' + r = curl.http_delete(urls=[url], alpn_proto=proto) + r.check_stats(count=count, http_status=204, exitcode=0) + diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 2a8dbe09f4..ee365cfa16 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -474,6 +474,25 @@ class CurlClient: with_headers=with_headers, with_profile=with_profile) + def http_delete(self, urls: List[str], + alpn_proto: Optional[str] = None, + with_stats: bool = True, + with_profile: bool = False, + extra_args: Optional[List[str]] = None): + if extra_args is None: + extra_args = [] + extra_args.extend([ + '-X', 'DELETE', '-o', '/dev/null', + ]) + if with_stats: + extra_args.extend([ + '-w', '%{json}\\n' + ]) + return self._raw(urls, alpn_proto=alpn_proto, options=extra_args, + with_stats=with_stats, + with_headers=False, + with_profile=with_profile) + def http_put(self, urls: List[str], data=None, fdata=None, alpn_proto: Optional[str] = None, with_stats: bool = True, diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index 4443e0021e..abe97a3c0f 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -302,7 +302,11 @@ static int curltest_tweak_handler(request_rec *r) if(strcmp(r->handler, "curltest-tweak")) { return DECLINED; } - if(r->method_number != M_GET && r->method_number != M_POST) { + if(r->method_number == M_DELETE) { + http_status = 204; + chunks = 0; + } + else if(r->method_number != M_GET && r->method_number != M_POST) { return DECLINED; }