Repository: encode/httpx

Issue:
environ["SERVER_PORT"] can't be "None"
### Checklist

<!-- Please make sure you check all these items before submitting your bug report. -->

- [x] The bug is reproducible against the latest release and/or `master`.
- [x] There are no similar issues or pull requests to fix it yet.

### Describe the bug

https://github.com/abersheeran/a2wsgi/issues/8

```python
=================================== 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.



Discussion:

Resolving patch:
diff --git a/httpx/_transports/wsgi.py b/httpx/_transports/wsgi.py
--- a/httpx/_transports/wsgi.py
+++ b/httpx/_transports/wsgi.py
@@ -74,6 +74,9 @@ def request(
 
         scheme, host, port, full_path = url
         path, _, query = full_path.partition(b"?")
+        if port is None:
+            port = {b"http": 80, b"https": 443}[scheme]
+
         environ = {
             "wsgi.version": (1, 0),
             "wsgi.url_scheme": scheme.decode("ascii"),


Test patch:
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -116,3 +116,30 @@ def test_wsgi_generator_empty():
     response = client.get("http://www.example.org/")
     assert response.status_code == 200
     assert response.text == ""
+
+
+@pytest.mark.parametrize(
+    "url, expected_server_port",
+    [
+        pytest.param("http://www.example.org", "80", id="auto-http"),
+        pytest.param("https://www.example.org", "443", id="auto-https"),
+        pytest.param("http://www.example.org:8000", "8000", id="explicit-port"),
+    ],
+)
+def test_wsgi_server_port(url: str, expected_server_port: int):
+    """
+    SERVER_PORT is populated correctly from the requested URL.
+    """
+    hello_world_app = application_factory([b"Hello, World!"])
+    server_port: str
+
+    def app(environ, start_response):
+        nonlocal server_port
+        server_port = environ["SERVER_PORT"]
+        return hello_world_app(environ, start_response)
+
+    client = httpx.Client(app=app)
+    response = client.get(url)
+    assert response.status_code == 200
+    assert response.text == "Hello, World!"
+    assert server_port == expected_server_port