fix content length mismatch in windows and other platform
The current implementation of `sanic` attempts to make use of `ujson` if it's available in the system and if not, it will default to the inbuilt `json` module provided by python. The current implementation of `ujson` does not provide a mechanism to provide a custom `seperators` parameter as part of the `dumps` method invocation and the default behavior of the module is to strip all the spaces around seperators such as `:` and `,`. This leads to an inconsistency in the response length when the response is generated using the `ujson` and in built `json` module provided by python. To maintain the consistency, this commit overrides the default behavior of the `dumps` method provided by the `json` module to add a `seperators` argument that will strip the white spaces around these character like the default behavior of `ujson` This addresses the issue referenced in #1398 Signed-off-by: Harsha Narayana <harsha2k4@gmail.com>
This commit is contained in:
parent
92b73a6f4f
commit
d4d1df03c9
|
@ -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:
|
||||||
|
|
|
@ -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,15 +72,11 @@ 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',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user