mirror of
https://github.com/kennethreitz/httpbin.git
synced 2026-06-05 14:50:17 +00:00
bugfix: JSON-safe encoding of raw binary data/files
Encoding of the received files and/or body data was failing in case of raw binary data being POSTed/PUT/etc. (The exact trigger was actually a non-UTF-8 data stream, since Flask's `jsonify`, i.e. `simplejson.dumps` assumes UTF-8.) Binary data in a JSON response are now encoded as "data" URLs, according to RFC 2397. "Data" URL scheme was chosen for its simplicity and clarity. MIME type is included. Plain text is passed thru unmodified, as before. Also, tests demonstrating the bug/fix added to `test_httpbin.py`.
This commit is contained in:
@@ -14,4 +14,5 @@ Patches and Suggestions
|
||||
- Andrey Petrov
|
||||
- Lispython
|
||||
- Kyle Conroy
|
||||
- Flavio Percoco
|
||||
- Flavio Percoco
|
||||
- Radomir Stevanovic (http://github.com/randomir)
|
||||
+25
-3
@@ -7,7 +7,8 @@ httpbin.helpers
|
||||
This module provides helper functions for httpbin.
|
||||
"""
|
||||
|
||||
import json
|
||||
import base64
|
||||
import simplejson as json
|
||||
from hashlib import md5
|
||||
from werkzeug.http import parse_authorization_header
|
||||
|
||||
@@ -62,13 +63,34 @@ ANGRY_ASCII ="""
|
||||
YOU SHOUDN'T BE HERE
|
||||
"""
|
||||
|
||||
|
||||
def json_safe(string, content_type='application/octet-stream'):
|
||||
"""Returns JSON-safe version of `string`.
|
||||
|
||||
If `string` is a Unicode string or a valid UTF-8, it is returned unmodified,
|
||||
as it can safely be encoded to JSON string.
|
||||
|
||||
If `string` contains raw/binary data, it is Base64-encoded, formatted and
|
||||
returned according to "data" URL scheme (RFC2397). Since JSON is not
|
||||
suitable for binary data, some additional encoding was necessary; "data"
|
||||
URL scheme was chosen for its simplicity.
|
||||
"""
|
||||
|
||||
try:
|
||||
_encoded = json.dumps(string)
|
||||
return string
|
||||
except ValueError:
|
||||
return ''.join(['data:%s;base64,' % content_type,
|
||||
base64.b64encode(string)])
|
||||
|
||||
|
||||
def get_files():
|
||||
"""Returns files dict from request context."""
|
||||
|
||||
files = dict()
|
||||
|
||||
for k, v in request.files.items():
|
||||
files[k] = v.read()
|
||||
files[k] = json_safe(v.read(), request.files[k].content_type)
|
||||
|
||||
return files
|
||||
|
||||
@@ -120,7 +142,7 @@ def get_dict(*keys, **extras):
|
||||
url=request.url,
|
||||
args=request.args,
|
||||
form=form,
|
||||
data=data,
|
||||
data=json_safe(data),
|
||||
origin=request.remote_addr,
|
||||
headers=get_headers(),
|
||||
files=get_files(),
|
||||
|
||||
@@ -25,6 +25,22 @@ class HttpbinTestCase(unittest.TestCase):
|
||||
content = response.data.decode('utf-8')
|
||||
self.assertEquals(greeting, content)
|
||||
|
||||
def test_post_binary(self):
|
||||
response = self.app.post('/post',
|
||||
data='\x01\x02\x03\x81\x82\x83',
|
||||
content_type='application/octet-stream')
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def test_post_file_text(self):
|
||||
with open('httpbin/core.py') as f:
|
||||
response = self.app.post('/post', data={"file": f})
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def test_post_file_binary(self):
|
||||
with open('httpbin/core.pyc') as f:
|
||||
response = self.app.post('/post', data={"file": f})
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user