Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

environ["SERVER_PORT"] can't be "None" #1468

Closed
2 tasks done
abersheeran opened this issue Feb 16, 2021 · 1 comment · Fixed by #1469
Closed
2 tasks done

environ["SERVER_PORT"] can't be "None" #1468

abersheeran opened this issue Feb 16, 2021 · 1 comment · Fixed by #1469
Labels
bug Something isn't working

Comments

@abersheeran
Copy link
Member

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

abersheeran/a2wsgi#8

=================================== FAILURES ===================================
____________ test_convert_asgi_to_wsgi[app1-a2wsgi-ASGIMiddleware] _____________

app = <a2wsgi.asgi.ASGIMiddleware object at 0x7fced147eb80>
name = 'a2wsgi-ASGIMiddleware'

    @pytest.mark.parametrize(
        "app, name",
        [(wsgi_echo, "pure-WSGI"), (ASGIMiddleware(asgi_echo), "a2wsgi-ASGIMiddleware")],
    )
    def test_convert_asgi_to_wsgi(app, name):
        with httpx.Client(app=app, base_url="http://testserver") as client:
            start_time = time.time_ns()
            for _ in range(100):
>               client.post("/", data=b"hello world")

a2wsgi/benchmark.py:99:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.8/site-packages/httpx/_client.py:992: in post
    return self.request(
/usr/local/lib/python3.8/site-packages/httpx/_client.py:733: in request
    return self.send(
/usr/local/lib/python3.8/site-packages/httpx/_client.py:767: in send
    response = self._send_handling_auth(
/usr/local/lib/python3.8/site-packages/httpx/_client.py:805: in _send_handling_auth
    response = self._send_handling_redirects(
/usr/local/lib/python3.8/site-packages/httpx/_client.py:837: in _send_handling_redirects
    response = self._send_single_request(request, timeout)
/usr/local/lib/python3.8/site-packages/httpx/_client.py:861: in _send_single_request
    (status_code, headers, stream, ext) = transport.request(
/usr/local/lib/python3.8/site-packages/httpx/_transports/wsgi.py:113: in request
    result = _skip_leading_empty_chunks(result)
/usr/local/lib/python3.8/site-packages/httpx/_transports/wsgi.py:10: in _skip_leading_empty_chunks
    for chunk in body:
a2wsgi/a2wsgi/asgi.py:160: in __call__
    self.app(build_scope(environ), self.asgi_receive, self.asgi_send)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

environ = {'CONTENT_LENGTH': '11', 'HTTP_ACCEPT': '*/*', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_CONNECTION': 'keep-alive', ...}

    def build_scope(environ: Environ) -> Scope:
        headers = [
            (
                each[5:].lower().replace("_", "-").encode("latin1"),
                environ[each].encode("latin1"),
            )
            for each in environ.keys()
            if each.startswith("HTTP_")
        ]
        if environ.get("CONTENT_TYPE"):
            headers.append((b"content-type", environ["CONTENT_TYPE"].encode("latin1")))
        if environ.get("CONTENT_LENGTH"):
            headers.append((b"content-length", environ["CONTENT_LENGTH"].encode("latin1")))

        if environ.get("REMOTE_ADDR") and environ.get("REMOTE_PORT"):
            client = (environ.get("REMOTE_ADDR"), int(environ.get("REMOTE_PORT")))
        else:
            client = None

        return {
            "type": "http",
            "asgi": {"version": "3.0", "spec_version": "3.0"},
            "http_version": environ.get("SERVER_PROTOCOL", "http/1.0").split("/")[1],
            "method": environ["REQUEST_METHOD"],
            "scheme": environ.get("wsgi.url_scheme", "http"),
            "path": environ["PATH_INFO"].encode("latin1").decode("utf8"),
            "query_string": environ["QUERY_STRING"].encode("ascii"),
            "root_path": environ.get("SCRIPT_NAME", "").encode("latin1").decode("utf8"),
            "client": client,
>           "server": (environ["SERVER_NAME"], int(environ["SERVER_PORT"])),
            "headers": headers,
        }
E       ValueError: invalid literal for int() with base 10: 'None'

a2wsgi/a2wsgi/asgi.py:94: ValueError
=========================== short test summary info ============================
FAILED a2wsgi/benchmark.py::test_convert_asgi_to_wsgi[app1-a2wsgi-ASGIMiddleware]
==================== 1 failed, 5 passed in 95.47s (0:01:35) ====================

Expected behavior

https://www.python.org/dev/peps/pep-3333/#environ-variables

SERVER_PORT must be a valid integer value for URL splicing. In the WSGI application test, it may not have a real value, but we should give a default value, such as 80.

@florimondmanca
Copy link
Member

@abersheeran Good catch!

Indeed, in WSGITransport the url is unpacked as scheme, host, port, full_path, and port is an Optional[int], obtained from here:

httpx/httpx/_models.py

Lines 208 to 214 in 8696405

@property
def port(self) -> typing.Optional[int]:
"""
The URL port as an integer.
"""
port = self._uri_reference.port
return int(port) if port else None

So we need to handle the "default port" case in WSGITransport.

@florimondmanca florimondmanca added the bug Something isn't working label Feb 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants