Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add headers in a Flask app with unicode_literals

Adding headers with unicode_literals enabled seems to fail with Nginx, uWSGI and a simple Flask app:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from flask import Flask, make_response

app = Flask('test')

@app.route('/')
def index():
    response = make_response()
    response.status_code = 401
    response.headers = {'WWW-Authenticate': 'Basic realm="test"'} # Fail
    # response.headers = {b'WWW-Authenticate': b'Basic realm="test"'} # Succeed
    return response

if __name__ == '__main__':
    app.run(debug=True)

The app is available directly for debug purpose or through Nginx -> uWSGI -> Flask and works well.

  • When I use a browser to connect directly to the app, I've got a login dialog and the WWW-Authenticate header is correct.
  • The same request going through Nginx returns a header Transfert-Encoding: chunked and discard the WWW-Authenticate header.

Forcing bytestring (b'...') format to add the header make the app works as expected in both cases. The file is encoded in UTF-8 and there's acoding` declaration for the Python interpreter. We're using Python 2.7.3, Nginx 1.4.2 and uWSGI 1.3.

Is there any known incompatibility between Nginx or uWSGI, Flask and unicode_literals? Thanks!

edit: The problem seems to come from uWSGI ( https://github.com/unbit/uwsgi/blob/master/plugins/python/wsgi_headers.c#L116), since it only checks for PyString and not PyUnicode for Python2, if I understand this code correctly.

edit: Armin Ronacher has fixed a similar bug (https://github.com/mitsuhiko/flask/issues/758) 5 months ago, but I didn't find the commit in werkzeug git log yet. I don't know if the fix is scoped to the redirect() function or more broadly on headers handling. I'm using Werkzeug 0.9.4 and Flask 0.10.1.

like image 881
Nicolas Avatar asked Nov 12 '22 20:11

Nicolas


1 Answers

This problem is indeed due to a bug in Werkzeug. As you noticed, this is now corrected since Jun 4, 2013 (cf. the related commit on Github). You can have a bug free version of Werkzeug by using the version 0.9.5 instead of the 0.9.4.

Moreover, to troubleshoot your problem, I added app.debug = True just after the initialization of your Flask application. This allows me to got the following error in uWSGI logs:

Traceback (most recent call last):
  File "/home/afigura/.virtualenvs/stack-python2/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/afigura/.virtualenvs/stack-python2/lib/python2.7/site-packages/flask/app.py", line 1821, in wsgi_app
    return response(environ, start_response)
  File "/home/afigura/.virtualenvs/stack-python2/lib/python2.7/site-packages/werkzeug/wrappers.py", line 1201, in __call__
    start_response(status, headers)
TypeError: http header key must be a string

This corresponds to the error mentioned in the bug you found on Github.

So, you can use the following workaround to get Flask/Werkzeug working with unicode_literals:

response.headers = {b'WWW-Authenticate': 'Basic realm="test"'}

Or:

response.headers = {str('WWW-Authenticate'): 'Basic realm="test"'}

But I recommend to simply update your Werkzeug version to >=0.9.5 if you can.

Also, please note that although the headers attribute of a Flask/Werkzeug response behaves like a dictionary, it is in fact a Headers object (see Werkzeug source code). Hence, I advise you to use it as follows:

response.headers['WWW-Authenticate'] = 'Basic realm="test"'

You can see some examples about this on the Flask documentation of the function make_response.

like image 138
Alexandre Avatar answered Nov 14 '22 22:11

Alexandre