diff options
| -rw-r--r-- | day7/backend.py | 100 | ||||
| -rw-r--r-- | day7/db/config.db | bin | 16384 -> 16384 bytes | |||
| -rw-r--r-- | day7/http_handler.py | 2 | ||||
| -rw-r--r-- | day7/router.py | 87 |
4 files changed, 103 insertions, 86 deletions
diff --git a/day7/backend.py b/day7/backend.py index 5fef9c3..0b63883 100644 --- a/day7/backend.py +++ b/day7/backend.py @@ -1,71 +1,18 @@ -import re - from templater import render_template -from utils import parse_cookies, add_text_headers, SUCCESS, BAD_REQUEST, NOT_FOUND, HTTP_METHODS, METHOD_NOT_ALLOWED +from utils import parse_cookies, SUCCESS, BAD_REQUEST, METHOD_NOT_ALLOWED from config import TEXT_TEMPLATE_NAME, FORM_TEMPLATE_NAME import db -_router_tree = {} - - -# url_format - регулярное выражение -def route(url_format, methods=None): - if methods is None: - methods = ['GET'] - - def wrapper(func): - def inner(url, query, *args, **kwargs): - pattern = re.compile(url_format) - match = re.fullmatch(pattern, url) - - if match is None or len(match.groups()) != pattern.groups: - return BAD_REQUEST, '400 BAD REQUEST' - - return func(query, *match.groups(), *args, **kwargs) - - # Добавляем вызываемую функцию в дерево роутера. - # Благодаря этому указывать паттерн url и метод нужно указывать - # только в инициализаторе декоратора, а функция run сама разберется - # при каких условиях нужно вызвать конкретную функцию - _router_tree[url_format] = _router_tree.get(url_format, {}) - for method in methods: - _router_tree[url_format][method] = inner - - return inner - - return wrapper - - -def run(request): - res = NOT_FOUND, NOT_FOUND - - for key, value in request['cookies'].items(): - db.set_cookie(key, value) - - method, url = request['method'], request['url'] - for url_pattern in _router_tree: - if re.fullmatch(url_pattern, url) and method in _router_tree[url_pattern]: - res = _router_tree[url_pattern][method](url, request['query']) - break - - return add_text_headers(*res) - - # По заданию любой метод, кроме GET и POST должны быть запрещены на сервере. -@route('/.*', list(set(HTTP_METHODS) ^ {'GET', 'POST'})) def fallback_wrong_method(query, *args): return METHOD_NOT_ALLOWED, 'This method is not allowed' -@route('/') def index_get(query, *args): return SUCCESS, render_template(FORM_TEMPLATE_NAME, color=get_color()) -# Хотелось попробовать сделать что-то высокоуровневое с декораторами. -# Если в этом коде есть какие-то серьезные проблемы, то скажите сразу. -@route(r'/div/(\d+)/to/(\d+)/?') def divide_get(query, *args): color = get_color() @@ -74,13 +21,9 @@ def divide_get(query, *args): return SUCCESS, render_template(TEXT_TEMPLATE_NAME, text=text, color=color) except ZeroDivisionError as e: - return BAD_REQUEST, render_template( - TEXT_TEMPLATE_NAME, color=color, - text=BAD_REQUEST + ('<br>' + str(e) if db.get_config_entry('show_errors') else '') - ) + return BAD_REQUEST, get_error_template(str(e), color) -@route(r'/div/?', ['POST']) def divide_post(query, *args): color = get_color() @@ -90,19 +33,12 @@ def divide_post(query, *args): except KeyError: field = 'числитель' if 'numerator' not in query else 'знаменатель' - return BAD_REQUEST, render_template( - TEXT_TEMPLATE_NAME, color=color, - text=BAD_REQUEST + ('<br>' + f'Указан неверный {field}') - ) + return BAD_REQUEST, get_error_template(f'Указан неверный {field}', color) except (ValueError, ZeroDivisionError) as e: - return BAD_REQUEST, render_template( - TEXT_TEMPLATE_NAME, color=color, - text=BAD_REQUEST + ('<br>' + str(e) if db.get_config_entry('show_errors') else '') - ) + return BAD_REQUEST, get_error_template(str(e), color) -@route(r'/show_errors/(\d{1})/?') def show_errors_get(query, *args): color = get_color() @@ -121,7 +57,6 @@ def show_errors_get(query, *args): ) -@route(r'/show_errors/?', ['POST']) def show_errors_post(query, *args): color = get_color() @@ -140,7 +75,6 @@ def show_errors_post(query, *args): ) -@route(r'/set_cookie/=/(.*)/?') def set_cookie_get(query, *args): cookie_line = args[0] color = get_color() @@ -152,13 +86,9 @@ def set_cookie_get(query, *args): return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Cookie-файл обновлен') except ValueError as e: - return BAD_REQUEST, render_template( - TEXT_TEMPLATE_NAME, color=color, - text=BAD_REQUEST + ('<br>' + str(e) if db.get_config_entry('show_errors') else '') - ) + return BAD_REQUEST, get_error_template(str(e), color) -@route(r'/set_cookie/=/?', ['POST']) def set_cookie_post(query, *args): color = get_color() try: @@ -170,19 +100,12 @@ def set_cookie_post(query, *args): return SUCCESS, render_template(TEXT_TEMPLATE_NAME, color=color, text='Cookie-файл обновлен') except KeyError: - return BAD_REQUEST, render_template( - TEXT_TEMPLATE_NAME, color=color, - text=BAD_REQUEST + ('<br>' + f'Не указана строка с cookie') - ) + return BAD_REQUEST, get_error_template('Не указана строка с cookie', color) except ValueError as e: - return BAD_REQUEST, render_template( - TEXT_TEMPLATE_NAME, color=color, - text=BAD_REQUEST + ('<br>' + str(e) if db.get_config_entry('show_errors') else '') - ) + return BAD_REQUEST, get_error_template(str(e), color) -@route(r'/short_log/(\d{1})/?') def short_log_get(query, *args): color = get_color() @@ -201,7 +124,6 @@ def short_log_get(query, *args): ) -@route(r'/short_log/?', ['POST']) def short_log_post(query, *args): color = get_color() @@ -225,3 +147,11 @@ def get_color(): if color not in ['green', 'white']: color = 'white' return color + + +def get_error_template(text, color): + show_errors = db.get_config_entry('show_errors') + return render_template( + TEXT_TEMPLATE_NAME, color=color, + text = BAD_REQUEST + ('<br>' + text) if show_errors else '' + ) diff --git a/day7/db/config.db b/day7/db/config.db Binary files differindex 3728bc0..aba3782 100644 --- a/day7/db/config.db +++ b/day7/db/config.db diff --git a/day7/http_handler.py b/day7/http_handler.py index 6db918c..7f64f95 100644 --- a/day7/http_handler.py +++ b/day7/http_handler.py @@ -3,7 +3,7 @@ from time import strftime, gmtime import logging import db -from backend import run +from router import run from utils import ( parse_cookies, parse_query, parse_headers, validate_url, add_text_headers, format_cookies, diff --git a/day7/router.py b/day7/router.py new file mode 100644 index 0000000..b0705c3 --- /dev/null +++ b/day7/router.py @@ -0,0 +1,87 @@ +import re +from utils import NOT_FOUND, add_text_headers, HTTP_METHODS +import db + +from backend import * + + +class Route: + def __init__(self, callback, url_format, methods=None): + self.methods = ['GET'] if methods is None else methods + self.url_pattern = re.compile(url_format) + self.callback = callback + + def check_route(self, url, method): + match = self.url_pattern.fullmatch(url) + return bool(match) and len(match.groups()) == self.url_pattern.groups and method in self.methods + + def invoke_callback(self, url, method, query): + match = self.url_pattern.fullmatch(url) + groups = match.groups() + + matched = match and len(groups) == self.url_pattern.groups and method in self.methods + if not matched: + return NOT_FOUND, NOT_FOUND + + return self.callback(query, *match.groups()) + + +# url_format - регулярное выражение +def route(url_format, methods=None): + if methods is None: + methods = ['GET'] + + def wrapper(func): + def inner(url, query, *args, **kwargs): + pattern = re.compile(url_format) + match = re.fullmatch(pattern, url) + + if match is None or len(match.groups()) != pattern.groups: + return NOT_FOUND, '404 NOT FOUND' + + return func(query, *match.groups(), *args, **kwargs) + + # Добавляем вызываемую функцию в дерево роутера. + # Благодаря этому указывать паттерн url и метод нужно указывать + # только в инициализаторе декоратора, а функция run сама разберется + # при каких условиях нужно вызвать конкретную функцию + _router_tree.append(Route(func, url_format, methods)) + return inner + + return wrapper + + +def run(request): + res = NOT_FOUND, NOT_FOUND + + for key, value in request['cookies'].items(): + db.set_cookie(key, value) + + method, url = request['method'], request['url'] + + for route in _router_tree: + if route.check_route(url, method): + res = route.invoke_callback(url, method, request['query']) + break + + return add_text_headers(*res) + + +_router_tree = [ + # Пока ничего лучше не придумал + Route(fallback_wrong_method, '/.*', list(set(HTTP_METHODS) ^ {'GET', 'POST'})), + + Route(index_get, '/'), + + Route(divide_get, r'/div/(\d+)/to/(\d+)/?'), + Route(divide_post, r'/div/?', ['POST']), + + Route(show_errors_get, r'/show_errors/(\d{1})/?'), + Route(show_errors_post, r'/show_errors/?', ['POST']), + + Route(set_cookie_get, r'/set_cookie/=/(.*)/?'), + Route(set_cookie_post, r'/set_cookie/=/?', ['POST']), + + Route(short_log_get, r'/short_log/(\d{1})/?'), + Route(short_log_post, r'/short_log/?', ['POST']), +] |