Proxying a otro servicio web con Flask

Quiero enviar por proxy las solicitudes realizadas a mi aplicación Flask para otro servicio web que se ejecuta localmente en la máquina. Prefiero usar Flask para esto que nuestra instancia de nginx de nivel superior para poder reutilizar nuestro sistema de autenticación existente integrado en nuestra aplicación. Cuanto más podamos mantener este “inicio de sesión único”, mejor.

¿Hay un módulo existente u otro código para hacer esto? Tratar de enlazar la aplicación Flask con algo como httplib o urllib está demostrando ser un dolor.

Tengo una implementación de un proxy utilizando httplib en una aplicación basada en Werkzeug (como en su caso, necesitaba usar la autenticación y la autorización de la aplicación web).

Aunque los documentos de Flask no indican cómo acceder a los encabezados HTTP, puede usar los encabezados request.headers (consulte la documentación de Werkzeug ). Si no necesita modificar la respuesta, y los encabezados utilizados por la aplicación de proxy son predecibles, el proxy es más rápido.

Tenga en cuenta que si no necesita modificar la respuesta, debe usar el werkzeug.wsgi.wrap_file para envolver la secuencia de respuesta de httplib. Eso permite pasar el descriptor de archivo abierto a nivel del sistema operativo al servidor HTTP para un rendimiento óptimo.

Pasé mucho tiempo trabajando en esta misma cosa y, finalmente, encontré una solución utilizando la biblioteca de solicitudes que parece funcionar bien. Incluso maneja la configuración de múltiples cookies en una respuesta, lo que llevó un poco de investigación para averiguar. Aquí está la función de vista del matraz:

 from flask import request, Response import requests def _proxy(*args, **kwargs): resp = requests.request( method=request.method, url=request.url.replace(request.host_url, 'new-domain.com'), headers={key: value for (key, value) in request.headers if key != 'Host'}, data=request.get_data(), cookies=request.cookies, allow_redirects=False) excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] headers = [(name, value) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers] response = Response(resp.content, resp.status_code, headers) return response 

Mi plan original era que la URL pública fuera algo así como http://www.example.com/admin/myapp como proxy a http://myapp.internal.example.com/ . Por ese camino lleva la locura.

La mayoría de las aplicaciones web, en particular las de alojamiento propio, asumen que van a estar ejecutándose en la raíz de un servidor HTTP y hacen cosas como hacer referencia a otros archivos por una ruta absoluta. Para solucionar esto, debe volver a escribir las URL en todo el lugar: encabezados de ubicación y archivos HTML, JavaScript y CSS.

Escribí un plano de proxy de Flask que hizo esto, y aunque funcionó lo suficientemente bien para la única aplicación web que realmente quería representar, no era sostenible. Fue un gran lío de expresiones regulares.

Al final, configuré un nuevo host virtual en nginx y usé su propio proxy. Dado que ambos estaban en la raíz del host, la reescritura de URL era casi innecesaria. (Y lo poco que era necesario, se manejaba el módulo proxy de nginx). La aplicación web a la que se hace proxy tiene su propia autenticación, lo cual es suficiente por ahora.