diff --git a/mesop/cli/cli.py b/mesop/cli/cli.py index 9149ab257..3c4b9fb66 100644 --- a/mesop/cli/cli.py +++ b/mesop/cli/cli.py @@ -23,6 +23,7 @@ from mesop.server.logging import log_startup from mesop.server.server import configure_flask_app from mesop.server.static_file_serving import configure_static_file_serving +from mesop.utils.host_util import get_default_host from mesop.utils.runfiles import get_runfile_location FLAGS = flags.FLAGS @@ -152,7 +153,7 @@ def main(argv: Sequence[str]): log_startup(port=port()) logging.getLogger("werkzeug").setLevel(logging.WARN) - flask_app.run(host="::", port=port(), use_reloader=False) + flask_app.run(host=get_default_host(), port=port(), use_reloader=False) if __name__ == "__main__": diff --git a/mesop/colab/colab_run.py b/mesop/colab/colab_run.py index 6d7273916..5dadadec7 100644 --- a/mesop/colab/colab_run.py +++ b/mesop/colab/colab_run.py @@ -8,6 +8,7 @@ from mesop.server.logging import log_startup from mesop.server.server import configure_flask_app from mesop.server.static_file_serving import configure_static_file_serving +from mesop.utils.host_util import get_default_host def colab_run(*, port: int = 32123, prod_mode: bool = False): @@ -43,7 +44,7 @@ def colab_run(*, port: int = 32123, prod_mode: bool = False): log_startup(port=port) def run_flask_app(): - flask_app.run(host="::", port=port, use_reloader=False) + flask_app.run(host=get_default_host(), port=port, use_reloader=False) # Launch Flask in background thread so we don't hog up the main thread # for regular Colab usage. diff --git a/mesop/server/wsgi_app.py b/mesop/server/wsgi_app.py index b9f95003c..979c443eb 100644 --- a/mesop/server/wsgi_app.py +++ b/mesop/server/wsgi_app.py @@ -10,6 +10,7 @@ from mesop.server.logging import log_startup from mesop.server.server import configure_flask_app from mesop.server.static_file_serving import configure_static_file_serving +from mesop.utils.host_util import get_default_host class App: @@ -20,7 +21,9 @@ def __init__(self, flask_app: Flask): def run(self): log_startup(port=port()) - self._flask_app.run(host="::", port=port(), use_reloader=False) + self._flask_app.run( + host=get_default_host(), port=port(), use_reloader=False + ) def create_app( diff --git a/mesop/utils/host_util.py b/mesop/utils/host_util.py new file mode 100644 index 000000000..a23f95661 --- /dev/null +++ b/mesop/utils/host_util.py @@ -0,0 +1,42 @@ +import socket + + +def get_default_host() -> str: + """ + Returns the default host (which is externally accessible) + based on the availability of IPv6. + """ + if has_ipv6(): + return "::" + return "0.0.0.0" + + +# Context: https://github.com/urllib3/urllib3/pull/611 +def has_ipv6(): + """Returns True if the system can bind an IPv6 address.""" + # First, check if Python was compiled with IPv6 support + if not socket.has_ipv6: + return False + + sock = None + try: + # Attempt to create an IPv6 socket + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + + # Try to bind to the IPv6 unspecified address + # Note: cannot use IPv6 loopback address: "::1" + # because it doesn't work on Colab, likely due to + # some networking configuration w/ the container. + # + # Using port 0 lets the OS choose an available port + sock.bind(("::", 0)) + + # If we get here, the bind was successful + return True + except Exception: + # An exception means IPv6 is not supported or cannot be used + return False + finally: + # Ensure the socket is closed, even if an exception occurred + if sock: + sock.close()