From 2c98d04e4d4a344dc04a481b039a235678f7848e Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 12 Aug 2022 14:33:15 +0200 Subject: make Haketilo proxy web UI in terms of a Flask app --- src/hydrilla/proxy/policies/web_ui.py | 2 +- src/hydrilla/proxy/web_ui/__init__.py | 2 +- src/hydrilla/proxy/web_ui/packages.py | 44 ++++++++++++++ src/hydrilla/proxy/web_ui/repos.py | 23 +++---- src/hydrilla/proxy/web_ui/root.py | 71 +++++++++++++++++----- .../proxy/web_ui/templates/base.html.jinja | 32 ++++++++++ .../templates/packages__load_from_disk.html.jinja | 26 ++++++++ .../proxy/web_ui/templates/repos.html.jinja | 26 ++++++++ .../proxy/web_ui/templates/root.html.jinja | 26 ++++++++ src/hydrilla/url_patterns.py | 4 ++ 10 files changed, 224 insertions(+), 32 deletions(-) create mode 100644 src/hydrilla/proxy/web_ui/packages.py create mode 100644 src/hydrilla/proxy/web_ui/templates/base.html.jinja create mode 100644 src/hydrilla/proxy/web_ui/templates/packages__load_from_disk.html.jinja create mode 100644 src/hydrilla/proxy/web_ui/templates/repos.html.jinja create mode 100644 src/hydrilla/proxy/web_ui/templates/root.html.jinja diff --git a/src/hydrilla/proxy/policies/web_ui.py b/src/hydrilla/proxy/policies/web_ui.py index 5040278..6fbb4aa 100644 --- a/src/hydrilla/proxy/policies/web_ui.py +++ b/src/hydrilla/proxy/policies/web_ui.py @@ -52,7 +52,7 @@ class WebUIPolicy(base.Policy): def consume_request(self, request_info: http_messages.RequestInfo) \ -> http_messages.ProducedResponse: - return web_ui.respond(request_info, self.haketilo_state) + return web_ui.process_request(request_info, self.haketilo_state) @dc.dataclass(frozen=True, unsafe_hash=True) diff --git a/src/hydrilla/proxy/web_ui/__init__.py b/src/hydrilla/proxy/web_ui/__init__.py index ba461b1..a5dddab 100644 --- a/src/hydrilla/proxy/web_ui/__init__.py +++ b/src/hydrilla/proxy/web_ui/__init__.py @@ -4,4 +4,4 @@ # # Available under the terms of Creative Commons Zero v1.0 Universal. -from .root import respond +from .root import process_request diff --git a/src/hydrilla/proxy/web_ui/packages.py b/src/hydrilla/proxy/web_ui/packages.py new file mode 100644 index 0000000..7d67b63 --- /dev/null +++ b/src/hydrilla/proxy/web_ui/packages.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +# Proxy web UI packages loading. +# +# This file is part of Hydrilla&Haketilo. +# +# Copyright (C) 2022 Wojtek Kosior +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# +# I, Wojtek Kosior, thereby promise not to sue for violation of this +# file's license. Although I request that you do not make use this code +# in a proprietary program, I am not going to enforce this in court. + +""" +..... +""" + +# Enable using with Python 3.7. +from __future__ import annotations + +import typing as t + +import flask + + +bp = flask.Blueprint('load_packages', __package__) + +@bp.route('/packages/load_from_disk') +def load_from_disk() -> flask.Response: + html = flask.render_template('packages__load_from_disk.html.jinja') + return flask.make_response(html, 200) diff --git a/src/hydrilla/proxy/web_ui/repos.py b/src/hydrilla/proxy/web_ui/repos.py index d20791c..0f55b2a 100644 --- a/src/hydrilla/proxy/web_ui/repos.py +++ b/src/hydrilla/proxy/web_ui/repos.py @@ -33,17 +33,12 @@ from __future__ import annotations import typing as t -from ...translations import smart_gettext as _ -from .. import state as st -from .. import http_messages - -def respond( - request: http_messages.RequestInfo, - rest_of_path: t.Sequence[str], - state: st.HaketiloState -) -> http_messages.ProducedResponse: - return http_messages.ProducedResponse( - 404, - [(b'Content-Type', b'text/plain; charset=utf-8')], - _('api.repos_not_implemented').encode() - ) +import flask + + +bp = flask.Blueprint('repos', __package__) + +@bp.route('/repos') +def repos() -> flask.Response: + html = flask.render_template('repos.html.jinja') + return flask.make_response(html, 200) diff --git a/src/hydrilla/proxy/web_ui/root.py b/src/hydrilla/proxy/web_ui/root.py index dbb86c1..194251e 100644 --- a/src/hydrilla/proxy/web_ui/root.py +++ b/src/hydrilla/proxy/web_ui/root.py @@ -32,35 +32,74 @@ from __future__ import annotations import typing as t +from threading import Lock + +import jinja2 +import flask from ...translations import smart_gettext as _ from .. import state as st from .. import http_messages from . import repos +from . import packages + + +class WebUIApp(flask.Flask): + def __init__(self): + super().__init__(__name__) + + self.jinja_options = { + **self.jinja_options, + 'loader': jinja2.PackageLoader(__package__), + 'autoescape': jinja2.select_autoescape() + } + for blueprint in [repos.bp, packages.bp]: + self.register_blueprint(blueprint) -HandlerType = t.Callable[ - [http_messages.RequestInfo, t.Sequence[str], st.HaketiloState], - http_messages.ProducedResponse -] + _haketilo_state: st.HaketiloState -request_handlers: t.Mapping[str, HandlerType] = { - 'repos': repos.respond -} +# Flask app is not thread-safe and has to be accompanied by an ugly lock. This +# can cause slow requests to block other requests, so we might need a better +# workaround at some later point. +app = WebUIApp() +app_lock = Lock() -def respond( + +@app.route('/') +def respond(): + return flask.render_template('root.html.jinja') + + +def process_request( request_info: http_messages.RequestInfo, state: st.HaketiloState ) -> http_messages.ProducedResponse: - if request_info.url.path_segments == (): - raise NotImplementedError() + path = '/'.join(('', *request_info.url.path_segments)) + if (request_info.url.has_trailing_slash): + path += '/' + + with app_lock: + app._haketilo_state = state - resource, *rest_of_path = request_info.url.path_segments + flask_response = app.test_client().open( + path = path, + base_url = 'https://hkt.mitm.it', + method = request_info.method, + query_string = request_info.url.query, + headers = [*request_info.headers.items()], + data = request_info.body + ) - try: - handler = request_handlers[resource] - except KeyError: - raise NotImplementedError() + headers_bytes = [ + (key.encode(), val.encode()) + for key, val + in flask_response.headers + ] - return handler(request_info, rest_of_path, state) + return http_messages.ProducedResponse( + status_code = flask_response.status_code, + headers = headers_bytes, + body = flask_response.data + ) diff --git a/src/hydrilla/proxy/web_ui/templates/base.html.jinja b/src/hydrilla/proxy/web_ui/templates/base.html.jinja new file mode 100644 index 0000000..c6f0dcf --- /dev/null +++ b/src/hydrilla/proxy/web_ui/templates/base.html.jinja @@ -0,0 +1,32 @@ +{# +SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 + + +Proxy web UI base page template. + +This file is part of Hydrilla&Haketilo. + +Copyright (C) 2022 Wojtek Kosior + +Dual licensed under +* GNU General Public License v3.0 or later and +* Creative Commons Attribution Share Alike 4.0 International. + +You can choose to use either of these licenses or both. + + +I, Wojtek Kosior, thereby promise not to sue for violation of this +file's licenses. Although I request that you do not make use this code +in a proprietary work, I am not going to enforce this in court. +#} + + + + {% block head %} + {% block title required %}{% endblock %} - Haketilo proxy + {% endblock %} + + +
{% block main required %}{% endblock %}
+ + diff --git a/src/hydrilla/proxy/web_ui/templates/packages__load_from_disk.html.jinja b/src/hydrilla/proxy/web_ui/templates/packages__load_from_disk.html.jinja new file mode 100644 index 0000000..07ed3b3 --- /dev/null +++ b/src/hydrilla/proxy/web_ui/templates/packages__load_from_disk.html.jinja @@ -0,0 +1,26 @@ +{# +Spdx-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 + + +Proxy web UI package loading page. + +This file is part of Hydrilla&Haketilo. + +Copyright (C) 2022 Wojtek Kosior + +Dual licensed under +* GNU General Public License v3.0 or later and +* Creative Commons Attribution Share Alike 4.0 International. + +You can choose to use either of these licenses or both. + + +I, Wojtek Kosior, thereby promise not to sue for violation of this +file's licenses. Although I request that you do not make use this code +in a proprietary work, I am not going to enforce this in court. +#} +{% extends "base.html.jinja" %} +{% block title %}Load package{% endblock %} +{% block main %} + Not implemented yet :( +{% endblock %} diff --git a/src/hydrilla/proxy/web_ui/templates/repos.html.jinja b/src/hydrilla/proxy/web_ui/templates/repos.html.jinja new file mode 100644 index 0000000..59eb1bd --- /dev/null +++ b/src/hydrilla/proxy/web_ui/templates/repos.html.jinja @@ -0,0 +1,26 @@ +{# +SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 + + +Proxy web UI repos list page. + +This file is part of Hydrilla&Haketilo. + +Copyright (C) 2022 Wojtek Kosior + +Dual licensed under +* GNU General Public License v3.0 or later and +* Creative Commons Attribution Share Alike 4.0 International. + +You can choose to use either of these licenses or both. + + +I, Wojtek Kosior, thereby promise not to sue for violation of this +file's licenses. Although I request that you do not make use this code +in a proprietary work, I am not going to enforce this in court. +#} +{% extends "base.html.jinja" %} +{% block title %}Repositories{% endblock %} +{% block main %} + Not implemented yet :( +{% endblock %} diff --git a/src/hydrilla/proxy/web_ui/templates/root.html.jinja b/src/hydrilla/proxy/web_ui/templates/root.html.jinja new file mode 100644 index 0000000..f1e3500 --- /dev/null +++ b/src/hydrilla/proxy/web_ui/templates/root.html.jinja @@ -0,0 +1,26 @@ +{# +SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 + + +Proxy web UI root page. + +This file is part of Hydrilla&Haketilo. + +Copyright (C) 2022 Wojtek Kosior + +Dual licensed under +* GNU General Public License v3.0 or later and +* Creative Commons Attribution Share Alike 4.0 International. + +You can choose to use either of these licenses or both. + + +I, Wojtek Kosior, thereby promise not to sue for violation of this +file's licenses. Although I request that you do not make use this code +in a proprietary work, I am not going to enforce this in court. +#} +{% extends "base.html.jinja" %} +{% block title %}Home{% endblock %} +{% block main %} + load packages from disk +{% endblock %} diff --git a/src/hydrilla/url_patterns.py b/src/hydrilla/url_patterns.py index 0a242e3..278827a 100644 --- a/src/hydrilla/url_patterns.py +++ b/src/hydrilla/url_patterns.py @@ -55,6 +55,7 @@ class ParsedUrl: scheme: str = dc.field(hash=False, compare=False) domain_labels: tuple[str, ...] = dc.field(hash=False, compare=False) path_segments: tuple[str, ...] = dc.field(hash=False, compare=False) + query: str = dc.field(hash=False, compare=False) has_trailing_slash: bool = dc.field(hash=False, compare=False) port: int = dc.field(hash=False, compare=False) @@ -189,12 +190,15 @@ def _parse_pattern_or_url(url: str, orig_url: str, is_pattern: bool = False) \ msg = _('err.url_pattern_{}.has_frag').format(orig_url) raise HaketiloException(msg) + query = parse_result.query + return ParsedUrl( orig_url = orig_url, scheme = scheme, port = port, domain_labels = domain_labels, path_segments = path_segments, + query = query, has_trailing_slash = has_trailing_slash ) -- cgit v1.2.3