quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

test_05_errors.py (5926B)


      1 #!/usr/bin/env python3
      2 # -*- coding: utf-8 -*-
      3 #***************************************************************************
      4 #                                  _   _ ____  _
      5 #  Project                     ___| | | |  _ \| |
      6 #                             / __| | | | |_) | |
      7 #                            | (__| |_| |  _ <| |___
      8 #                             \___|\___/|_| \_\_____|
      9 #
     10 # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
     11 #
     12 # This software is licensed as described in the file COPYING, which
     13 # you should have received as part of this distribution. The terms
     14 # are also available at https://curl.se/docs/copyright.html.
     15 #
     16 # You may opt to use, copy, modify, merge, publish, distribute and/or sell
     17 # copies of the Software, and permit persons to whom the Software is
     18 # furnished to do so, under the terms of the COPYING file.
     19 #
     20 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     21 # KIND, either express or implied.
     22 #
     23 # SPDX-License-Identifier: curl
     24 #
     25 ###########################################################################
     26 #
     27 import logging
     28 import pytest
     29 
     30 from testenv import Env, CurlClient
     31 
     32 
     33 log = logging.getLogger(__name__)
     34 
     35 
     36 @pytest.mark.skipif(condition=not Env.httpd_is_at_least('2.4.55'),
     37                     reason=f"httpd version too old for this: {Env.httpd_version()}")
     38 class TestErrors:
     39 
     40     # download 1 file, check that we get CURLE_PARTIAL_FILE
     41     @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
     42     def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto):
     43         if proto == 'h3' and not env.have_h3():
     44             pytest.skip("h3 not supported")
     45         if proto == 'h3' and env.curl_uses_lib('msh3'):
     46             pytest.skip("msh3 stalls here")
     47         count = 1
     48         curl = CurlClient(env=env)
     49         urln = f'https://{env.authority_for(env.domain1, proto)}' \
     50             f'/curltest/tweak?id=[0-{count - 1}]'\
     51             '&chunks=3&chunk_size=16000&body_error=reset'
     52         r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[
     53             '--retry', '0'
     54         ])
     55         r.check_exit_code(False)
     56         invalid_stats = []
     57         for idx, s in enumerate(r.stats):
     58             if 'exitcode' not in s or s['exitcode'] not in [18, 56, 92, 95]:
     59                 invalid_stats.append(f'request {idx} exit with {s["exitcode"]}')
     60         assert len(invalid_stats) == 0, f'failed: {invalid_stats}'
     61 
     62     # download files, check that we get CURLE_PARTIAL_FILE for all
     63     @pytest.mark.parametrize("proto", ['h2', 'h3'])
     64     def test_05_02_partial_20(self, env: Env, httpd, nghttpx, proto):
     65         if proto == 'h3' and not env.have_h3():
     66             pytest.skip("h3 not supported")
     67         if proto == 'h3' and env.curl_uses_ossl_quic():
     68             pytest.skip("openssl-quic is flaky in yielding proper error codes")
     69         if proto == 'h3' and env.curl_uses_lib('msh3'):
     70             pytest.skip("msh3 stalls here")
     71         count = 20
     72         curl = CurlClient(env=env)
     73         urln = f'https://{env.authority_for(env.domain1, proto)}' \
     74             f'/curltest/tweak?id=[0-{count - 1}]'\
     75             '&chunks=5&chunk_size=16000&body_error=reset'
     76         r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[
     77             '--retry', '0', '--parallel',
     78         ])
     79         r.check_exit_code(False)
     80         assert len(r.stats) == count, f'did not get all stats: {r}'
     81         invalid_stats = []
     82         for idx, s in enumerate(r.stats):
     83             if 'exitcode' not in s or s['exitcode'] not in [18, 55, 56, 92, 95]:
     84                 invalid_stats.append(f'request {idx} exit with {s["exitcode"]}\n{s}')
     85         assert len(invalid_stats) == 0, f'failed: {invalid_stats}'
     86 
     87     # access a resource that, on h2, RST the stream with HTTP_1_1_REQUIRED
     88     def test_05_03_required(self, env: Env, httpd, nghttpx):
     89         curl = CurlClient(env=env)
     90         proto = 'http/1.1'
     91         urln = f'https://{env.authority_for(env.domain1, proto)}/curltest/1_1'
     92         r = curl.http_download(urls=[urln], alpn_proto=proto)
     93         r.check_exit_code(0)
     94         r.check_response(http_status=200, count=1)
     95         proto = 'h2'
     96         urln = f'https://{env.authority_for(env.domain1, proto)}/curltest/1_1'
     97         r = curl.http_download(urls=[urln], alpn_proto=proto)
     98         r.check_exit_code(0)
     99         r.check_response(http_status=200, count=1)
    100         # check that we did a downgrade
    101         assert r.stats[0]['http_version'] == '1.1', r.dump_logs()
    102 
    103     # On the URL used here, Apache is doing an "unclean" TLS shutdown,
    104     # meaning it sends no shutdown notice and just closes TCP.
    105     # The HTTP response delivers a body without Content-Length. We expect:
    106     # - http/1.0 to fail since it relies on a clean connection close to
    107     #   detect the end of the body
    108     # - http/1.1 to work since it will used "chunked" transfer encoding
    109     #   and stop receiving when that signals the end
    110     # - h2 to work since it will signal the end of the response before
    111     #   and not see the "unclean" close either
    112     @pytest.mark.parametrize("proto", ['http/1.0', 'http/1.1', 'h2'])
    113     def test_05_04_unclean_tls_shutdown(self, env: Env, httpd, nghttpx, proto):
    114         if proto == 'h3' and not env.have_h3():
    115             pytest.skip("h3 not supported")
    116         count = 10 if proto == 'h2' else 1
    117         curl = CurlClient(env=env)
    118         url = f'https://{env.authority_for(env.domain1, proto)}'\
    119             f'/curltest/shutdown_unclean?id=[0-{count-1}]&chunks=4'
    120         r = curl.http_download(urls=[url], alpn_proto=proto, extra_args=[
    121             '--parallel', '--trace-config', 'ssl'
    122         ])
    123         if proto == 'http/1.0':
    124             # we are inconsistent if we fail or not in missing TLS shutdown
    125             # openssl code ignore such errors intentionally in non-debug builds
    126             r.check_exit_code(56)
    127         else:
    128             r.check_exit_code(0)
    129             r.check_response(http_status=200, count=count)