web-dev-qa-db-fra.com

Procuration à un autre service Web avec Flask

Je souhaite envoyer des requêtes proxy à mon Flask vers un autre service Web exécuté localement sur la machine. Je préfère utiliser Flask pour cela que notre version supérieure) niveau nginx afin que nous puissions réutiliser notre système d'authentification existant intégré à notre application. Plus nous pouvons conserver cette "authentification unique", mieux c'est.

Existe-t-il un module ou un autre code existant pour ce faire? Essayer de relier l'application Flask à quelque chose comme httplib ou urllib s'avère être une douleur.

45
Joe Shaw

J'ai une implémentation d'un proxy utilisant httplib dans une application basée sur Werkzeug (comme dans votre cas, j'avais besoin d'utiliser l'authentification et l'autorisation de la webapp).

Bien que les documents Flask ne précisent pas comment accéder aux en-têtes HTTP, vous pouvez utiliser request.headers (voir documentation Werkzeug ). Si vous n'avez pas besoin de modifier la réponse et que les en-têtes utilisés par l'application proxy sont prévisibles, le proxy est simple.

Notez que si vous n'avez pas besoin de modifier la réponse, vous devez utiliser le werkzeug.wsgi.wrap_file pour envelopper le flux de réponse de httplib. Cela permet de transmettre le descripteur de fichier ouvert au niveau du système d'exploitation au serveur HTTP pour des performances optimales.

9
jd.

J'ai passé beaucoup de temps à travailler sur cette même chose et j'ai finalement trouvé une solution en utilisant la bibliothèque requêtes qui semble bien fonctionner. Il gère même la configuration de plusieurs cookies dans une seule réponse, ce qui a pris un peu d'enquête pour le comprendre. Voici la fonction de vue flask:

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
64
Evan

Mon plan initial était que l'URL accessible au public ressemble à http://www.example.com/admin/myapp procuration à http://myapp.internal.example.com/. Sur cette voie mène la folie.

La plupart des applications Web, en particulier celles qui sont auto-hébergées, supposent qu'elles vont s'exécuter à la racine d'un serveur HTTP et font des choses comme référencer d'autres fichiers par chemin absolu. Pour contourner ce problème, vous devez réécrire les URL partout: en-têtes d'emplacement et fichiers HTML, JavaScript et CSS.

J'ai fait écrire un Flask proxy blueprint qui a fait cela, et même si cela fonctionnait assez bien pour la seule application Web que je voulais vraiment proxy, ce n'était pas durable. C'était un gros gâchis d'expressions régulières.

Au final, j'ai installé un nouvel hôte virtuel dans nginx et utilisé son propre proxy. Étant donné que les deux étaient à la racine de l'hôte, la réécriture d'URL était généralement inutile. (Et le peu qui était nécessaire, le module proxy de nginx a été géré.) La webapp à laquelle on procède par proxy effectue sa propre authentification, ce qui est assez bon pour l'instant.

7
Joe Shaw