import tornado from tornado.httpclient import AsyncHTTPClient class V1RedirectHandler(tornado.web.RequestHandler): """Transparently proxies requests from the old API to the new one, returning whatever the v2 endpoint returns, for endpoints with no breaking changes.""" SUPPORTED_METHODS = ("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS") # Methods where an HTTP body isn't valid _BODYLESS_METHODS = {"GET", "HEAD", "OPTIONS"} async def _proxy(self, path): new_url = f"{self.request.protocol}://{self.request.host}/api/v2/{path}" if self.request.query: new_url += "?" + self.request.query client = AsyncHTTPClient() try: response = await client.fetch( new_url, method=self.request.method, headers=self.request.headers, body=None if self.request.method in self._BODYLESS_METHODS else (self.request.body or b""), raise_error=False, follow_redirects=False, request_timeout=10.0, ) except Exception as e: raise tornado.web.HTTPError(502, reason=str(e)) self.set_status(response.code, response.reason) for name, value in response.headers.get_all(): # Let Tornado recompute these for the outgoing response if name.lower() not in ("content-length", "transfer-encoding", "connection"): self.add_header(name, value) if response.body: self.write(response.body) self.finish() async def get(self, path): await self._proxy(path) async def post(self, path): await self._proxy(path) async def put(self, path): await self._proxy(path) async def delete(self, path): await self._proxy(path) async def patch(self, path): await self._proxy(path)