diff --git a/HISTORY.rst b/HISTORY.rst index 171ab8d5..58b77160 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -28,9 +28,13 @@ PlatformIO Core 5 - Disabled automatic removal of unnecessary development platform packages (`issue #3708 `_, `issue #3770 `_) - Fixed an issue when unnecessary packages were removed in ``update --dry-run`` mode (`issue #3809 `_) -* **Miscellaneous** +* **PlatformIO Home** - Significantly speedup PlatformIO Home loading time by migrating to native Python 3 Asynchronous I/O + - Added a new ``--session-id`` option to `pio home `__ command that helps to keep PlatformIO Home isolated from other instances and protect from 3rd party access (`issue #3397 `_) + +* **Miscellaneous** + - Improved listing of `multicast DNS services `_ - Check for debugging server's "ready_pattern" in "stderr" - Fixed a "UnicodeDecodeError: 'utf-8' codec can't decode byte" when using J-Link for firmware uploading on Linux (`issue #3804 `_) diff --git a/docs b/docs index ee815b1b..24f57666 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit ee815b1b4214d10f10635f9c9116e4017751184f +Subproject commit 24f57666602e68e53904ec3600e5f011d55b3aba diff --git a/platformio/__init__.py b/platformio/__init__.py index 26485ade..7b9eaeb6 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -47,7 +47,7 @@ __pioremote_endpoint__ = "ssl:host=remote.platformio.org:port=4413" __default_requests_timeout__ = (10, None) # (connect, read) __core_packages__ = { - "contrib-piohome": "~3.3.1", + "contrib-piohome": "~3.3.2", "contrib-pysite": "~2.%d%d.0" % (sys.version_info.major, sys.version_info.minor), "tool-unity": "~1.20500.0", "tool-scons": "~2.20501.7" if sys.version_info.major == 2 else "~4.40100.0", diff --git a/platformio/commands/home/command.py b/platformio/commands/home/command.py index 28cbfef4..2973bdd2 100644 --- a/platformio/commands/home/command.py +++ b/platformio/commands/home/command.py @@ -40,7 +40,14 @@ from platformio.compat import ensure_python3 "are connected. Default is 0 which means never auto shutdown" ), ) -def cli(port, host, no_open, shutdown_timeout): +@click.option( + "--session-id", + help=( + "A unique session identifier to keep PIO Home isolated from other instances " + "and protect from 3rd party access" + ), +) +def cli(port, host, no_open, shutdown_timeout, session_id): ensure_python3() # Ensure PIO Home mimetypes are known @@ -52,7 +59,11 @@ def cli(port, host, no_open, shutdown_timeout): if host == "__do_not_start__": return - home_url = "http://%s:%d" % (host, port) + home_url = "http://%s:%d%s" % ( + host, + port, + ("/session/%s/" % session_id) if session_id else "/", + ) click.echo( "\n".join( [ @@ -61,7 +72,7 @@ def cli(port, host, no_open, shutdown_timeout): " /\\-_--\\ PlatformIO Home", "/ \\_-__\\", "|[]| [] | %s" % home_url, - "|__|____|______________%s" % ("_" * len(host)), + "|__|____|__%s" % ("_" * len(home_url)), ] ) ) diff --git a/platformio/commands/home/run.py b/platformio/commands/home/run.py index 585c9268..6e93cc2b 100644 --- a/platformio/commands/home/run.py +++ b/platformio/commands/home/run.py @@ -13,6 +13,7 @@ # limitations under the License. import os +from urllib.parse import urlparse import click import uvicorn @@ -21,6 +22,7 @@ from starlette.middleware import Middleware from starlette.responses import PlainTextResponse from starlette.routing import Mount, Route, WebSocketRoute from starlette.staticfiles import StaticFiles +from starlette.status import HTTP_403_FORBIDDEN from platformio.commands.home.rpc.handlers.account import AccountRPC from platformio.commands.home.rpc.handlers.app import AppRPC @@ -51,6 +53,12 @@ async def shutdown_server(_=None): return PlainTextResponse("Server has been shutdown!") +async def protected_page(_): + return PlainTextResponse( + "Protected PlatformIO Home session", status_code=HTTP_403_FORBIDDEN + ) + + def run_server(host, port, no_open, shutdown_timeout, home_url): contrib_dir = get_core_package_dir("contrib-piohome") if not os.path.isdir(contrib_dir): @@ -65,14 +73,19 @@ def run_server(host, port, no_open, shutdown_timeout, home_url): ws_rpc_factory.addHandler(PIOCoreRPC(), namespace="core") ws_rpc_factory.addHandler(ProjectRPC(), namespace="project") + path = urlparse(home_url).path + routes = [ + WebSocketRoute(path + "wsrpc", ws_rpc_factory, name="wsrpc"), + Route(path + "__shutdown__", shutdown_server, methods=["POST"]), + Mount(path, StaticFiles(directory=contrib_dir, html=True), name="static"), + ] + if path != "/": + routes.append(Route("/", protected_page)) + uvicorn.run( Starlette( middleware=[Middleware(ShutdownMiddleware)], - routes=[ - WebSocketRoute("/wsrpc", ws_rpc_factory, name="wsrpc"), - Route("/__shutdown__", shutdown_server, methods=["POST"]), - Mount("/", StaticFiles(directory=contrib_dir, html=True)), - ], + routes=routes, on_startup=[ lambda: click.echo( "PIO Home has been started. Press Ctrl+C to shutdown."