mirror of https://gitlab.com/tildes/tildes.git
Deimos
6 years ago
2 changed files with 100 additions and 92 deletions
@ -0,0 +1,96 @@ |
|||||
|
# Copyright (c) 2019 Tildes contributors <code@tildes.net> |
||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
|
||||
|
"""Contains Pyramid "tweens", used to insert additional logic into request-handling.""" |
||||
|
|
||||
|
from time import time |
||||
|
from typing import Callable |
||||
|
|
||||
|
from prometheus_client import Histogram |
||||
|
from pyramid.registry import Registry |
||||
|
from pyramid.request import Request |
||||
|
from pyramid.response import Response |
||||
|
|
||||
|
|
||||
|
def http_method_tween_factory(handler: Callable, registry: Registry) -> Callable: |
||||
|
# pylint: disable=unused-argument |
||||
|
"""Return a tween function that can override the request's HTTP method.""" |
||||
|
|
||||
|
def method_override_tween(request: Request) -> Response: |
||||
|
"""Override HTTP method with one specified in header.""" |
||||
|
valid_overrides_by_method = {"POST": ["DELETE", "PATCH", "PUT"]} |
||||
|
|
||||
|
original_method = request.method.upper() |
||||
|
valid_overrides = valid_overrides_by_method.get(original_method, []) |
||||
|
override = request.headers.get("X-HTTP-Method-Override", "").upper() |
||||
|
|
||||
|
if override in valid_overrides: |
||||
|
request.method = override |
||||
|
|
||||
|
return handler(request) |
||||
|
|
||||
|
return method_override_tween |
||||
|
|
||||
|
|
||||
|
def metrics_tween_factory(handler: Callable, registry: Registry) -> Callable: |
||||
|
# pylint: disable=unused-argument |
||||
|
"""Return a tween function that gathers metrics for Prometheus.""" |
||||
|
|
||||
|
request_histogram = Histogram( |
||||
|
"tildes_pyramid_requests_seconds", |
||||
|
"Request processing times", |
||||
|
labelnames=["route", "status_code", "method", "logged_in"], |
||||
|
) |
||||
|
|
||||
|
def metrics_tween(request: Request) -> Response: |
||||
|
"""Gather metrics for each request.""" |
||||
|
start_time = time() |
||||
|
response = handler(request) |
||||
|
duration = time() - start_time |
||||
|
|
||||
|
# ignore requests for invalid addresses |
||||
|
if not request.matched_route: |
||||
|
return response |
||||
|
|
||||
|
request_histogram.labels( |
||||
|
route=request.matched_route.name, |
||||
|
status_code=response.status_code, |
||||
|
method=request.method, |
||||
|
logged_in=str(bool(request.user)).lower(), |
||||
|
).observe(duration) |
||||
|
|
||||
|
return response |
||||
|
|
||||
|
return metrics_tween |
||||
|
|
||||
|
|
||||
|
def theme_cookie_tween_factory(handler: Callable, registry: Registry) -> Callable: |
||||
|
# pylint: disable=unused-argument |
||||
|
"""Return a tween function that sets the theme cookie.""" |
||||
|
|
||||
|
def theme_cookie_tween(request: Request) -> Response: |
||||
|
"""Set the theme cookie if needed (currently always, see comment below).""" |
||||
|
response = handler(request) |
||||
|
|
||||
|
current_theme = request.cookies.get("theme", "") |
||||
|
if not current_theme and request.user: |
||||
|
current_theme = request.user.theme_default |
||||
|
|
||||
|
# Currently, we want to always set the theme cookie. This is because we |
||||
|
# recently started setting the domain on this cookie (to be able to apply the |
||||
|
# user's theme to the Blog/Docs sites), and all older cookies won't have a |
||||
|
# domain set. This will basically let us convert the old no-domain cookies to |
||||
|
# new ones. After a decent amount of time (maybe sometime in April 2019), we |
||||
|
# can change this to only set the cookie when it's not already present and the |
||||
|
# user has a default theme set (so their default theme will work for Blog/Docs). |
||||
|
if current_theme: |
||||
|
response.set_cookie( |
||||
|
"theme", |
||||
|
current_theme, |
||||
|
max_age=315360000, |
||||
|
secure=True, |
||||
|
domain="." + request.domain, |
||||
|
) |
||||
|
return response |
||||
|
|
||||
|
return theme_cookie_tween |
Write
Preview
Loading…
Cancel
Save
Reference in new issue