diff --git a/locales/en_US/LC_MESSAGES/priviblur.po b/locales/en_US/LC_MESSAGES/priviblur.po index c0e77e4..ff88a6b 100644 --- a/locales/en_US/LC_MESSAGES/priviblur.po +++ b/locales/en_US/LC_MESSAGES/priviblur.po @@ -128,3 +128,6 @@ msgstr "Error: Unable to fetch HTTP client in time" msgid "priviblur_error_pool_timeout_error_description" msgstr "Priviblur was unable to fetch a HTTP client to request Tumblr in time. Please try again later" + +msgid "priviblur_error_invalid_internal_tumblr_redirect" +msgstr "Error: Tumblr HTTP 301 redirect points to foreign URL" \ No newline at end of file diff --git a/src/helpers/error_handlers.py b/src/helpers/error_handlers.py index 0232462..087d9e7 100644 --- a/src/helpers/error_handlers.py +++ b/src/helpers/error_handlers.py @@ -26,6 +26,7 @@ async def tumblr_error_restricted_tag(request, exception): status=403 ) + async def tumblr_error_unknown_blog(request, exception): return await sanic_ext.render( "misc/generic_error.jinja", @@ -81,3 +82,15 @@ async def error_404(request, exception): }, status=404 ) + + +async def invalid_redirect(request, exception): + return await sanic_ext.render( + "misc/generic_error.jinja", + context={ + "app": request.app, + "exception": exception, + "error_heading": request.app.ctx.translate("en", "priviblur_error_invalid_internal_tumblr_redirect"), + }, + status=502 + ) diff --git a/src/helpers/exceptions.py b/src/helpers/exceptions.py new file mode 100644 index 0000000..b1d3051 --- /dev/null +++ b/src/helpers/exceptions.py @@ -0,0 +1,2 @@ +class TumblrInvalidRedirect(Exception): + pass \ No newline at end of file diff --git a/src/helpers/helpers.py b/src/helpers/helpers.py index da50492..a367fa3 100644 --- a/src/helpers/helpers.py +++ b/src/helpers/helpers.py @@ -1,9 +1,9 @@ -import sanic - import copy import urllib.parse from typing import Sequence +import sanic + def url_handler(raw_url): """Change URLs found in posts to privacy-friendly alternatives""" url = urllib.parse.urlparse(raw_url) diff --git a/src/routes/__init__.py b/src/routes/__init__.py index cd4f8d7..cef4d0c 100644 --- a/src/routes/__init__.py +++ b/src/routes/__init__.py @@ -1,4 +1,4 @@ -from . import explore, media, search, tagged, blogs, assets, priviblur +from . import explore, media, search, tagged, blogs, assets, priviblur, miscellaneous BLUEPRINTS = ( explore.explore, @@ -7,5 +7,6 @@ tagged.tagged, blogs.blogs, assets.assets, - priviblur.priviblur + priviblur.priviblur, + miscellaneous.miscellaneous ) diff --git a/src/routes/media.py b/src/routes/media.py index 4eedb4f..4ce40f2 100644 --- a/src/routes/media.py +++ b/src/routes/media.py @@ -1,5 +1,7 @@ import sanic +from ..helpers import exceptions + media = sanic.Blueprint("TumblrMedia", url_prefix="/tblr") @@ -15,7 +17,7 @@ async def get_media(request, client, path_to_request): if location := priviblur_response_headers.get("location"): location = request.app.ctx.URL_HANDLER(location) if not location.startswith("/"): - return sanic.response.text("Media is redirecting to foreign URL", status=500) + raise exceptions.TumblrInvalidRedirect() return sanic.redirect(location) elif tumblr_response.status_code == 429: diff --git a/src/routes/miscellaneous.py b/src/routes/miscellaneous.py new file mode 100644 index 0000000..72f700a --- /dev/null +++ b/src/routes/miscellaneous.py @@ -0,0 +1,17 @@ +import urllib.parse + +import sanic +from ..helpers import exceptions + +miscellaneous = sanic.Blueprint("miscellaneous", url_prefix="/") + +@miscellaneous.get(r"/at/") +async def _at_links(request: sanic.Request, path : str): + """Redirects for at.tumblr.com links""" + response = await request.app.ctx.TumblrAtClient.head(path) + if response.status_code == 301: + location = urllib.parse.urlparse(response.headers["location"]) + if location.path.startswith("/"): + return sanic.redirect(location.path) + + raise exceptions.TumblrInvalidRedirect() \ No newline at end of file diff --git a/src/server.py b/src/server.py index 7d57273..83ac977 100644 --- a/src/server.py +++ b/src/server.py @@ -18,7 +18,7 @@ from . import routes, priviblur_extractor from . import priviblur_extractor from .config import load_config -from .helpers import setup_logging, helpers, error_handlers, pool_timeout_tracker +from .helpers import setup_logging, helpers, error_handlers, exceptions, pool_timeout_tracker from .version import VERSION, CURRENT_COMMIT from .jobs import refresh_pool @@ -64,7 +64,6 @@ @app.listener("before_server_start") async def initialize(app): - priviblur_backend = app.ctx.PRIVIBLUR_CONFIG.backend app.ctx.TumblrAPI = await priviblur_extractor.TumblrAPI.create( @@ -100,6 +99,13 @@ def create_image_client(url): tasks.append(app.add_task(refresh_pool.refresh_pool_task(app, create_image_client))) + app.ctx.TumblrAtClient = httpx.AsyncClient( + base_url="https://at.tumblr.com", + headers={"user-agent": priviblur_extractor.TumblrAPI.DEFAULT_HEADERS["user-agent"]}, + http2=True, + timeout=priviblur_backend.main_response_timeout + ) + # Add additional jinja filters and functions app.ext.environment.filters["encodepathsegment"] = functools.partial(urllib.parse.quote, safe="") @@ -182,6 +188,7 @@ async def reload_shutdown(app): app.error_handler.add(httpx.PoolTimeout, error_handlers.pool_timeout_error) app.error_handler.add(sanic.exceptions.NotFound, error_handlers.error_404) +app.error_handler.add(exceptions.TumblrInvalidRedirect, error_handlers.invalid_redirect) # Register all routes: for route in routes.BLUEPRINTS: