Repository: encode/uvicorn

Issue:
Built address of server in logs is wrong in case of a ipv6 host
Prompted by the below issue, we are missing the brackets around the host in case of a ipv6 host, the below should be 
`http://[::]:8000`

```
❯ uvicorn app:app --host="::"
INFO:     Started server process [15903]
INFO:     Waiting for application startup.
INFO:     ASGI 'lifespan' protocol appears unsupported.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://:::8000 (Press CTRL+C to quit)
INFO:     ::1:35892 - "GET / HTTP/1.1" 200 OK
INFO:     ::1:35892 - "GET /favicon.ico HTTP/1.1" 200 OK
```

_Originally posted by @euri10 in https://github.com/encode/uvicorn/issues/782#issuecomment-700578964_


Discussion:

Resolving patch:
diff --git a/uvicorn/config.py b/uvicorn/config.py
--- a/uvicorn/config.py
+++ b/uvicorn/config.py
@@ -7,6 +7,7 @@
 import socket
 import ssl
 import sys
+from enum import Enum
 from typing import List, Tuple
 
 import click
@@ -112,6 +113,27 @@ def create_ssl_context(certfile, keyfile, ssl_version, cert_reqs, ca_certs, ciph
     return ctx
 
 
+class _IPKind(Enum):
+    IPv4 = "IPv4"
+    IPv6 = "IPv6"
+
+
+def _get_server_start_message(
+    host_ip_version: _IPKind = _IPKind.IPv4,
+) -> Tuple[str, str]:
+    if host_ip_version is _IPKind.IPv6:
+        ip_repr = "%s://[%s]:%d"
+    else:
+        ip_repr = "%s://%s:%d"
+    message = f"Uvicorn running on {ip_repr} (Press CTRL+C to quit)"
+    color_message = (
+        "Uvicorn running on "
+        + click.style(ip_repr, bold=True)
+        + " (Press CTRL+C to quit)"
+    )
+    return message, color_message
+
+
 class Config:
     def __init__(
         self,
@@ -335,7 +357,10 @@ def setup_event_loop(self):
             loop_setup()
 
     def bind_socket(self):
-        sock = socket.socket()
+        family, sockettype, proto, canonname, sockaddr = socket.getaddrinfo(
+            self.host, self.port, type=socket.SOCK_STREAM
+        )[0]
+        sock = socket.socket(family=family, type=sockettype)
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         try:
             sock.bind((self.host, self.port))
@@ -344,12 +369,10 @@ def bind_socket(self):
             sys.exit(1)
         sock.set_inheritable(True)
 
-        message = "Uvicorn running on %s://%s:%d (Press CTRL+C to quit)"
-        color_message = (
-            "Uvicorn running on "
-            + click.style("%s://%s:%d", bold=True)
-            + " (Press CTRL+C to quit)"
-        )
+        if family == socket.AddressFamily.AF_INET6:
+            message, color_message = _get_server_start_message(_IPKind.IPv6)
+        else:
+            message, color_message = _get_server_start_message(_IPKind.IPv4)
         protocol_name = "https" if self.is_ssl else "http"
         logger.info(
             message,
diff --git a/uvicorn/main.py b/uvicorn/main.py
--- a/uvicorn/main.py
+++ b/uvicorn/main.py
@@ -10,6 +10,7 @@
 import time
 import typing
 from email.utils import formatdate
+from ipaddress import IPv6Address, ip_address
 
 import click
 
@@ -24,6 +25,8 @@
     SSL_PROTOCOL_VERSION,
     WS_PROTOCOLS,
     Config,
+    _get_server_start_message,
+    _IPKind,
 )
 from uvicorn.supervisors import ChangeReload, Multiprocess
 
@@ -500,12 +503,11 @@ async def startup(self, sockets=None):
             if port == 0:
                 port = server.sockets[0].getsockname()[1]
             protocol_name = "https" if config.ssl else "http"
-            message = "Uvicorn running on %s://%s:%d (Press CTRL+C to quit)"
-            color_message = (
-                "Uvicorn running on "
-                + click.style("%s://%s:%d", bold=True)
-                + " (Press CTRL+C to quit)"
-            )
+            if isinstance(ip_address(config.host), IPv6Address):
+                message, color_message = _get_server_start_message(_IPKind.IPv6)
+            else:
+                message, color_message = _get_server_start_message(_IPKind.IPv4)
+
             logger.info(
                 message,
                 protocol_name,


Test patch: