Merge pull request #1403 from harshanarayana/fix/GIT-1398-Http_Response_Content_Length_Mismatch

Fix Content-Length Mismatch while using json and ujson
This commit is contained in:
7 2018-11-10 00:14:03 +08:00 committed by GitHub
commit 4cb40f2042
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 13 deletions

View File

@ -1,3 +1,4 @@
from functools import partial
from mimetypes import guess_type from mimetypes import guess_type
from os import path from os import path
from urllib.parse import quote_plus from urllib.parse import quote_plus
@ -12,7 +13,11 @@ from sanic.helpers import STATUS_CODES, has_message_body, remove_entity_headers
try: try:
from ujson import dumps as json_dumps from ujson import dumps as json_dumps
except BaseException: except BaseException:
from json import dumps as json_dumps from json import dumps
# This is done in order to ensure that the JSON response is
# kept consistent across both ujson and inbuilt json usage.
json_dumps = partial(dumps, separators=(",", ":"))
class BaseHTTPResponse: class BaseHTTPResponse:

View File

@ -1,20 +1,19 @@
import sys
import asyncio import asyncio
import inspect import inspect
import os import os
from aiofiles import os as async_os
from mimetypes import guess_type from mimetypes import guess_type
from random import choice
from unittest.mock import MagicMock
from urllib.parse import unquote from urllib.parse import unquote
import pytest import pytest
from random import choice from aiofiles import os as async_os
from sanic.response import ( from sanic.response import (
HTTPResponse, stream, StreamingHTTPResponse, file, file_stream, json HTTPResponse, stream, StreamingHTTPResponse, file, file_stream, json
) )
from sanic.server import HttpProtocol from sanic.server import HttpProtocol
from sanic.testing import HOST, PORT from sanic.testing import HOST, PORT
from unittest.mock import MagicMock
JSON_DATA = {'ok': True} JSON_DATA = {'ok': True}
@ -38,7 +37,6 @@ async def sample_streaming_fn(response):
def test_method_not_allowed(app): def test_method_not_allowed(app):
@app.get('/') @app.get('/')
async def test_get(request): async def test_get(request):
return response.json({'hello': 'world'}) return response.json({'hello': 'world'})
@ -55,12 +53,12 @@ def test_method_not_allowed(app):
request, response = app.test_client.head('/') request, response = app.test_client.head('/')
assert response.status == 405 assert response.status == 405
assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) assert set(response.headers['Allow'].split(', ')) == {'GET', 'POST'}
assert response.headers['Content-Length'] == '0' assert response.headers['Content-Length'] == '0'
request, response = app.test_client.patch('/') request, response = app.test_client.patch('/')
assert response.status == 405 assert response.status == 405
assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) assert set(response.headers['Allow'].split(', ')) == {'GET', 'POST'}
assert response.headers['Content-Length'] == '0' assert response.headers['Content-Length'] == '0'
@ -74,19 +72,62 @@ def test_response_header(app):
'CONTENT-TYPE': 'application/json' 'CONTENT-TYPE': 'application/json'
}) })
is_windows = sys.platform in ['win32', 'cygwin']
request, response = app.test_client.get('/') request, response = app.test_client.get('/')
assert dict(response.headers) == { assert dict(response.headers) == {
'Connection': 'keep-alive', 'Connection': 'keep-alive',
'Keep-Alive': str(app.config.KEEP_ALIVE_TIMEOUT), 'Keep-Alive': str(app.config.KEEP_ALIVE_TIMEOUT),
# response body contains an extra \r at the end if its windows 'Content-Length': '11',
# TODO: this is the only place this difference shows up in our tests
# we should figure out a way to unify testing on both platforms
'Content-Length': '12' if is_windows else '11',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
def test_response_content_length(app):
@app.get("/response_with_space")
async def response_with_space(request):
return json({
"message": "Data",
"details": "Some Details"
}, headers={
'CONTENT-TYPE': 'application/json'
})
@app.get("/response_without_space")
async def response_without_space(request):
return json({
"message":"Data",
"details":"Some Details"
}, headers={
'CONTENT-TYPE': 'application/json'
})
_, response = app.test_client.get("/response_with_space")
content_length_for_response_with_space = response.headers.get("Content-Length")
_, response = app.test_client.get("/response_without_space")
content_length_for_response_without_space = response.headers.get("Content-Length")
assert content_length_for_response_with_space == content_length_for_response_without_space
assert content_length_for_response_with_space == '43'
def test_response_content_length_with_different_data_types(app):
@app.get("/")
async def get_data_with_different_types(request):
# Indentation issues in the Response is intentional. Please do not fix
return json({
'bool': True,
'none': None,
'string':'string',
'number': -1},
headers={
'CONTENT-TYPE': 'application/json'
})
_, response = app.test_client.get("/")
assert response.headers.get("Content-Length") == '55'
@pytest.fixture @pytest.fixture
def json_app(app): def json_app(app):