Search code examples
pythonhttpflaskwsgigevent

Determine exact path, including presence/absence of trailing question mark on Flask + gevent.pywsgi WSGIServer server


Is there a way to determine what the path was requested to the server, including if it included a question mark? The application

from gevent import monkey
monkey.patch_all()

import gevent
from gevent.pywsgi import WSGIServer
from flask import Flask, Response, request

def root():
    return Response(
        f'full_path:{request.full_path} '
        f'path:{request.path} '
        f'query_string:{request.query_string} '
        f'url:{request.url}'
    )

app = Flask('app')
app.add_url_rule('/', view_func=root)

server = WSGIServer(('0.0.0.0', 8081), app)
server.serve_forever()

always results in

full_path:/? path:/ query_string:b'' url:http://localhost:8081/

if requesting either

http://localhost:8081/?

or

http://localhost:8081/

This may seem unimportant in a lot of cases, but I'm making an authentication flow with multiple redirections, where the user is meant to end up at the exact same URL as they started. At the moment, I can't see a way to ensure this happens with Flask + gevent WSGIServer.


This is a similar question to Flask request: determine exact path, including if there is a question mark, but the answer doesn't seem to be applicable when the WSGIServer from gevent.pywsgi is used, since request.environ has neither of the keys RAW_URI nor REQUEST_URI


Solution

  • There is a way by defining a custom handler_class / WSGIHandler and adding self.path to request.environ

    from gevent import monkey
    monkey.patch_all()
    
    import gevent
    from gevent.pywsgi import WSGIHandler, WSGIServer
    from flask import Flask, Response, request
    
    def root():
        return Response(
            f'request_line_path:{request.environ["REQUEST_LINE_PATH"]}'
        )
    
    class RequestLinePathHandler(WSGIHandler):
        def get_environ(self):
            return {
                **super().get_environ(),
                'REQUEST_LINE_PATH': self.path,
            }
    
    app = Flask('app')
    app.add_url_rule('/', view_func=root)
    
    server = WSGIServer(('0.0.0.0', 8081), app, handler_class=RequestLinePathHandler)
    server.serve_forever()
    

    so a request to http://localhost:8081/ outputs

    request_line_path:/
    

    and a request to http://localhost:8081/? outputs

    request_line_path:/?