Changeset 5c7e570 in mod_gnutls


Ignore:
Timestamp:
Dec 4, 2019, 3:52:56 AM (11 months ago)
Author:
Fiona Klute <fiona.klute@…>
Branches:
master, proxy-ticket
Children:
1c2b936
Parents:
eed0c4b
Message:

https-test-client.py: Implement minimal HTTP/1.0 client

The test case 33_vhost_SNI_serveralias_missinghost checks if the
server correctly rejects requests with SNI but no "Host"
header. However, an HTTP/1.1 request without a Host header is invalid
and the server code rejects it before mod_gnutls can check the
request. This means we must use HTTP/1.0 in that test to get a useful
result.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • test/https-test-client.py

    reed0c4b r5c7e570  
    1616# limitations under the License.
    1717
     18import re
    1819import socket
    1920import subprocess
     
    185186        return conn
    186187
     188class TestRaw10(TestRequest):
     189    """This is a minimal (and likely incomplete) HTTP/1.0 test client for
     190    the one test case that strictly requires HTTP/1.0. All request
     191    parameters (method, path, headers) MUST be specified in the config
     192    file.
     193
     194    """
     195    yaml_tag = '!raw10'
     196    status_re = re.compile('^HTTP/([\d\.]+) (\d+) (.*)$')
     197
     198    def __init__(self, method, path, headers, expect):
     199        self.method = method
     200        self.path = path
     201        self.headers = headers
     202        self.expect = expect
     203
     204    def __repr__(self):
     205        return (f'{self.__class__.__name__!s}'
     206                f'(method={self.method!r}, path={self.path!r}, '
     207                f'headers={self.headers!r}, expect={self.expect!r})')
     208
     209    def run(self, command, timeout=None):
     210        req = f'{self.method} {self.path} HTTP/1.0\r\n'
     211        for name, value in self.headers.items():
     212            req = req + f'{name}: {value}\r\n'
     213        req = req + f'\r\n'
     214        proc = subprocess.Popen(command, stdout=subprocess.PIPE,
     215                                stdin=subprocess.PIPE, close_fds=True,
     216                                bufsize=0)
     217        try:
     218            # Note: errs will be empty because stderr is not captured
     219            outs, errs = proc.communicate(input=req.encode(),
     220                                          timeout=timeout)
     221        except TimeoutExpired:
     222            proc.kill()
     223            outs, errs = proc.communicate()
     224
     225        # first line of the received data must be the status
     226        status, rest = outs.decode().split('\r\n', maxsplit=1)
     227        # headers and body are separated by double newline
     228        headers, body = rest.split('\r\n\r\n', maxsplit=1)
     229        # log response for debugging
     230        print(f'{status}\n{headers}\n\n{body}')
     231
     232        m = self.status_re.match(status)
     233        if m:
     234            status_code = int(m.group(2))
     235            status_expect = self.expect.get('status')
     236            if status_expect and not status_code == status_expect:
     237                raise TestExpectationFailed('Unexpected status code: '
     238                                            f'{status}, expected '
     239                                            f'{status_expect}')
     240        else:
     241            raise TestExpectationFailed(f'Invalid status line: "{status}"')
     242
     243        if 'body' in self.expect:
     244            self._check_body(body)
     245
    187246# Override the default constructors. Pyyaml ignores default parameters
    188247# otherwise.
     
    329388                print(format_response(resp, body))
    330389                act.check_response(resp, body)
     390            elif type(act) is TestRaw10:
     391                act.run(command, conn.timeout)
    331392            else:
    332393                raise TypeError(f'Unsupported action requested: {act!r}')
Note: See TracChangeset for help on using the changeset viewer.