ソースを参照

feat: use local static when CND is not available

wangweimin 3 年 前
コミット
e6f60abe66

+ 5 - 0
pywebio/platform/__init__.py

@@ -170,3 +170,8 @@ try:
     from . import tornado_http
     from . import tornado_http
 except Exception:
 except Exception:
     pass
     pass
+
+try:
+    from . import aiohttp
+except Exception:
+    pass

+ 2 - 1
pywebio/platform/aiohttp.py

@@ -61,7 +61,8 @@ def _webio_handler(applications, cdn, websocket_settings, check_origin_func=_is_
 
 
             app_name = request.query.getone('app', 'index')
             app_name = request.query.getone('app', 'index')
             app = applications.get(app_name) or applications['index']
             app = applications.get(app_name) or applications['index']
-            html = render_page(app, protocol='ws', cdn=cdn)
+            no_cdn = cdn is True and request.query.getone('_pywebio_cdn', '') == 'false'
+            html = render_page(app, protocol='ws', cdn=False if no_cdn else cdn)
             return web.Response(body=html, content_type='text/html')
             return web.Response(body=html, content_type='text/html')
 
 
         ws = web.WebSocketResponse(**websocket_settings)
         ws = web.WebSocketResponse(**websocket_settings)

+ 5 - 2
pywebio/platform/fastapi.py

@@ -15,7 +15,7 @@ from starlette.websockets import WebSocketDisconnect
 from .remote_access import start_remote_access_service
 from .remote_access import start_remote_access_service
 from .tornado import open_webbrowser_on_server_started
 from .tornado import open_webbrowser_on_server_started
 from .page import make_applications, render_page
 from .page import make_applications, render_page
-from .utils import cdn_validation, OriginChecker, deserialize_binary_event
+from .utils import cdn_validation, OriginChecker, deserialize_binary_event, print_listen_address
 from ..session import CoroutineBasedSession, ThreadBasedSession, register_session_implement_for_target, Session
 from ..session import CoroutineBasedSession, ThreadBasedSession, register_session_implement_for_target, Session
 from ..session.base import get_session_info_from_headers
 from ..session.base import get_session_info_from_headers
 from ..utils import get_free_port, STATIC_PATH, iscoroutinefunction, isgeneratorfunction, strip_space
 from ..utils import get_free_port, STATIC_PATH, iscoroutinefunction, isgeneratorfunction, strip_space
@@ -41,7 +41,8 @@ def _webio_routes(applications, cdn, check_origin_func):
 
 
         app_name = request.query_params.get('app', 'index')
         app_name = request.query_params.get('app', 'index')
         app = applications.get(app_name) or applications['index']
         app = applications.get(app_name) or applications['index']
-        html = render_page(app, protocol='ws', cdn=cdn)
+        no_cdn = cdn is True and request.query_params.get('_pywebio_cdn', '') == 'false'
+        html = render_page(app, protocol='ws', cdn=False if no_cdn else cdn)
         return HTMLResponse(content=html)
         return HTMLResponse(content=html)
 
 
     async def websocket_endpoint(websocket: WebSocket):
     async def websocket_endpoint(websocket: WebSocket):
@@ -167,6 +168,8 @@ def start_server(applications, port=0, host='', cdn=True,
     if port == 0:
     if port == 0:
         port = get_free_port()
         port = get_free_port()
 
 
+    print_listen_address(host, port)
+
     if remote_access:
     if remote_access:
         start_remote_access_service(local_port=port)
         start_remote_access_service(local_port=port)
 
 

+ 6 - 1
pywebio/platform/httpbased.py

@@ -193,6 +193,11 @@ class HttpHandler:
 
 
         return context.get_response()
         return context.get_response()
 
 
+    def get_cdn(self, context):
+        if self.cdn is True and context.request_url_parameter('_pywebio_cdn', '') == 'false':
+            return False
+        return self.cdn
+
     @contextmanager
     @contextmanager
     def handle_request_context(self, context: HttpContext):
     def handle_request_context(self, context: HttpContext):
         """called when every http request"""
         """called when every http request"""
@@ -220,7 +225,7 @@ class HttpHandler:
         # 对首页HTML的请求
         # 对首页HTML的请求
         if 'webio-session-id' not in request_headers:
         if 'webio-session-id' not in request_headers:
             app = self.app_loader(context)
             app = self.app_loader(context)
-            html = render_page(app, protocol='http', cdn=self.cdn)
+            html = render_page(app, protocol='http', cdn=self.get_cdn(context))
             context.set_content(html)
             context.set_content(html)
             return context.get_response()
             return context.get_response()
 
 

+ 31 - 19
pywebio/platform/path_deploy.py

@@ -17,6 +17,7 @@ from .page import make_applications
 from ..session import register_session_implement, CoroutineBasedSession, ThreadBasedSession, Session
 from ..session import register_session_implement, CoroutineBasedSession, ThreadBasedSession, Session
 from ..utils import get_free_port, STATIC_PATH, parse_file_size
 from ..utils import get_free_port, STATIC_PATH, parse_file_size
 
 
+LOCAL_STATIC_URL = '/_pywebio_static'
 
 
 def filename_ok(f):
 def filename_ok(f):
     return not f.startswith(('.', '_'))
     return not f.startswith(('.', '_'))
@@ -183,7 +184,7 @@ def get_app_from_path(request_path, base, index, reload=False):
     return 'error', 404
     return 'error', 404
 
 
 
 
-def _path_deploy(base, port=0, host='', static_dir=None, cdn=True, max_payload_size=2 ** 20 * 200,
+def _path_deploy(base, port=0, host='', static_dir=None, max_payload_size=2 ** 20 * 200,
                  **tornado_app_settings):
                  **tornado_app_settings):
     if not host:
     if not host:
         host = '0.0.0.0'
         host = '0.0.0.0'
@@ -195,19 +196,15 @@ def _path_deploy(base, port=0, host='', static_dir=None, cdn=True, max_payload_s
 
 
     abs_base = os.path.normpath(os.path.abspath(base))
     abs_base = os.path.normpath(os.path.abspath(base))
 
 
-    cdn = cdn_validation(cdn, 'warn', stacklevel=4)  # if CDN is not available, warn user and disable CDN
-    cdn_url = '/_pywebio_static/' if not cdn else cdn
-
     register_session_implement(CoroutineBasedSession)
     register_session_implement(CoroutineBasedSession)
     register_session_implement(ThreadBasedSession)
     register_session_implement(ThreadBasedSession)
 
 
-    RequestHandler = yield cdn_url, abs_base
+    RequestHandler = yield abs_base
 
 
     handlers = []
     handlers = []
     if static_dir is not None:
     if static_dir is not None:
         handlers.append((r"/static/(.*)", StaticFileHandler, {"path": static_dir}))
         handlers.append((r"/static/(.*)", StaticFileHandler, {"path": static_dir}))
-    if not cdn:
-        handlers.append((r"/_pywebio_static/(.*)", StaticFileHandler, {"path": STATIC_PATH}))
+    handlers.append((LOCAL_STATIC_URL+r"/(.*)", StaticFileHandler, {"path": STATIC_PATH}))
     handlers.append((r"/.*", RequestHandler))
     handlers.append((r"/.*", RequestHandler))
 
 
     print_listen_address(host, port)
     print_listen_address(host, port)
@@ -254,20 +251,27 @@ def path_deploy(base, port=0, host='',
     tornado_app_settings.setdefault('websocket_max_message_size', max_payload_size)  # Backward compatible
     tornado_app_settings.setdefault('websocket_max_message_size', max_payload_size)  # Backward compatible
     tornado_app_settings['websocket_max_message_size'] = parse_file_size(tornado_app_settings['websocket_max_message_size'])
     tornado_app_settings['websocket_max_message_size'] = parse_file_size(tornado_app_settings['websocket_max_message_size'])
     gen = _path_deploy(base, port=port, host=host,
     gen = _path_deploy(base, port=port, host=host,
-                       static_dir=static_dir,
-                       cdn=cdn, debug=debug,
+                       static_dir=static_dir, debug=debug,
                        max_payload_size=max_payload_size,
                        max_payload_size=max_payload_size,
                        **tornado_app_settings)
                        **tornado_app_settings)
 
 
-    cdn_url, abs_base = next(gen)
+    cdn = cdn_validation(cdn, 'warn', stacklevel=3)  # if CDN is not available, warn user and disable CDN
+
+    abs_base = next(gen)
 
 
     index_func = {True: partial(default_index_page, base=abs_base), False: lambda p: '403 Forbidden'}.get(index, index)
     index_func = {True: partial(default_index_page, base=abs_base), False: lambda p: '403 Forbidden'}.get(index, index)
 
 
-    Handler = webio_handler(lambda: None, cdn_url, allowed_origins=allowed_origins,
+    Handler = webio_handler(lambda: None, cdn=cdn, allowed_origins=allowed_origins,
                             check_origin=check_origin, reconnect_timeout=reconnect_timeout)
                             check_origin=check_origin, reconnect_timeout=reconnect_timeout)
 
 
     class WSHandler(Handler):
     class WSHandler(Handler):
 
 
+        def get_cdn(self):
+            _cdn = super().get_cdn()
+            if not _cdn:
+                return LOCAL_STATIC_URL
+            return _cdn
+
         def get_app(self):
         def get_app(self):
             reload = self.get_query_argument('reload', None) is not None
             reload = self.get_query_argument('reload', None) is not None
             type, res = get_app_from_path(self.request.path, abs_base, index=index_func, reload=reload)
             type, res = get_app_from_path(self.request.path, abs_base, index=index_func, reload=reload)
@@ -307,12 +311,13 @@ def path_deploy_http(base, port=0, host='',
     page.MAX_PAYLOAD_SIZE = max_payload_size = parse_file_size(max_payload_size)
     page.MAX_PAYLOAD_SIZE = max_payload_size = parse_file_size(max_payload_size)
 
 
     gen = _path_deploy(base, port=port, host=host,
     gen = _path_deploy(base, port=port, host=host,
-                       static_dir=static_dir,
-                       cdn=cdn, debug=debug,
+                       static_dir=static_dir, debug=debug,
                        max_payload_size=max_payload_size,
                        max_payload_size=max_payload_size,
                        **tornado_app_settings)
                        **tornado_app_settings)
 
 
-    cdn_url, abs_base = next(gen)
+    cdn = cdn_validation(cdn, 'warn', stacklevel=3)  # if CDN is not available, warn user and disable CDN
+
+    abs_base = next(gen)
     index_func = {True: partial(default_index_page, base=abs_base), False: lambda p: '403 Forbidden'}.get(index, index)
     index_func = {True: partial(default_index_page, base=abs_base), False: lambda p: '403 Forbidden'}.get(index, index)
 
 
     def get_app(context: TornadoHttpContext):
     def get_app(context: TornadoHttpContext):
@@ -327,11 +332,18 @@ def path_deploy_http(base, port=0, host='',
         app_name = context.request_url_parameter('app', 'index')
         app_name = context.request_url_parameter('app', 'index')
         return res.get(app_name) or res['index']
         return res.get(app_name) or res['index']
 
 
-    handler = HttpHandler(app_loader=get_app, cdn=cdn_url,
-                          session_expire_seconds=session_expire_seconds,
-                          session_cleanup_interval=session_cleanup_interval,
-                          allowed_origins=allowed_origins,
-                          check_origin=check_origin)
+    class HttpPathDeployHandler(HttpHandler):
+        def get_cdn(self, context):
+            _cdn = super().get_cdn(context)
+            if not _cdn:
+                return LOCAL_STATIC_URL
+            return _cdn
+
+    handler = HttpPathDeployHandler(app_loader=get_app, cdn=cdn,
+                                    session_expire_seconds=session_expire_seconds,
+                                    session_cleanup_interval=session_cleanup_interval,
+                                    allowed_origins=allowed_origins,
+                                    check_origin=check_origin)
 
 
     class ReqHandler(tornado.web.RequestHandler):
     class ReqHandler(tornado.web.RequestHandler):
         def options(self):
         def options(self):

+ 7 - 1
pywebio/platform/tornado.py

@@ -94,6 +94,11 @@ def _webio_handler(applications=None, cdn=True, reconnect_timeout=0, check_origi
             app = applications.get(app_name) or applications['index']
             app = applications.get(app_name) or applications['index']
             return app
             return app
 
 
+        def get_cdn(self):
+            if cdn is True and self.get_query_argument('_pywebio_cdn', '') == 'false':
+                return False
+            return cdn
+
         async def get(self, *args, **kwargs) -> None:
         async def get(self, *args, **kwargs) -> None:
             # It's a simple http GET request
             # It's a simple http GET request
             if self.request.headers.get("Upgrade", "").lower() != "websocket":
             if self.request.headers.get("Upgrade", "").lower() != "websocket":
@@ -103,7 +108,8 @@ def _webio_handler(applications=None, cdn=True, reconnect_timeout=0, check_origi
                     return self.write('')
                     return self.write('')
 
 
                 app = self.get_app()
                 app = self.get_app()
-                html = render_page(app, protocol='ws', cdn=cdn)
+
+                html = render_page(app, protocol='ws', cdn=self.get_cdn())
                 return self.write(html)
                 return self.write(html)
             else:
             else:
                 await super().get()
                 await super().get()

+ 13 - 5
pywebio/platform/tpl/index.html

@@ -41,11 +41,6 @@
     </div>
     </div>
 </div>
 </div>
 
 
-
-<footer class="footer">
-    Powered by <a href="https://www.pyweb.io/" target="_blank">PyWebIO</a>
-</footer>
-
 <script src="{{ base_url }}js/mustache.min.js"></script>  <!--template system-->
 <script src="{{ base_url }}js/mustache.min.js"></script>  <!--template system-->
 <script src="{{ base_url }}js/codemirror.min.js"></script>  <!--code textarea editor-->
 <script src="{{ base_url }}js/codemirror.min.js"></script>  <!--code textarea editor-->
 <script src="{{ base_url }}codemirror/addons.js"></script> <!--codemirror addons: matchbrackets, python mode, active line, auto refresh, mode/meta and loadmode -->
 <script src="{{ base_url }}codemirror/addons.js"></script> <!--codemirror addons: matchbrackets, python mode, active line, auto refresh, mode/meta and loadmode -->
@@ -70,6 +65,15 @@
 <script src="{{ base_url }}js/require.min.js"></script> <!-- JS module loader -->
 <script src="{{ base_url }}js/require.min.js"></script> <!-- JS module loader -->
 
 
 <script>
 <script>
+    if (window.WebIO === undefined) { // resource load failed
+        let url = new URL(window.location.href);
+        if (url.searchParams.get('_pywebio_cdn') === 'false') {
+            alert("Failed to load resource")
+        } else {
+            url.searchParams.set('_pywebio_cdn', 'false');
+            window.location.href = url.href;
+        }
+    }
 
 
     require.config({
     require.config({
         paths: {
         paths: {
@@ -101,5 +105,9 @@
 {% if js_code %}
 {% if js_code %}
 <script>{% raw js_code %}</script>
 <script>{% raw js_code %}</script>
 {% end %}
 {% end %}
+
+<footer class="footer">
+    Powered by <a href="https://www.pyweb.io/" target="_blank">PyWebIO</a>
+</footer>
 </body>
 </body>
 </html>
 </html>