Skip to content

Commit 35b333d

Browse files
bartonipfilak-sap
authored andcommitted
Fix incorrect length when creating an HTTPResponse from FakeSocket
Due to the response_str encountering inadvertent mutation elsewhere in the code, the size slightly changes and with a large response, does not correctly get reported to HTTPResponse. We manually specify the length of the response to the size of the BytesIO buffer and this gives HTTPResponse enough room to read to the end of the response. The ODataHttpResponse.from_string method fails because in the process of converting the multipart response to and from bytes and also being passed around, some bytes are being added somewhere and causing the size of the data to no longer be equal to Content-Type. This causes HTTPResponse.begin() to seed the wrong length attribute, which causes the file pointer to not read far enough which, in turn, causes a JSON parse error. So to clean this up we just need to manually set response.length to how large the body is now after all its encoding and decoding. Signed-off-by: Jakub Filak <[email protected]>
1 parent b4ce3ea commit 35b333d

6 files changed

+60
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1414

1515
### Fixed
1616
- URL encode $filter contents - Barton Ip
17+
- JSON errors caused by invalid content length of Batch responses - Barton Ip
1718

1819
## [1.5.0]
1920

pyodata/v2/service.py

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ def makefile(self, *args, **kwargs):
134134
source = FakeSocket(data)
135135
response = HTTPResponse(source)
136136
response.begin()
137+
response.length = response.fp.__sizeof__()
137138

138139
return ODataHttpResponse(
139140
response.getheaders(),

tests/enormous_batch_response

+12
Large diffs are not rendered by default.

tests/metadata.xml

+14
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,18 @@
308308
FromRole="EmployeeRole" ToRole="AddressRole"/>
309309
</EntityType>
310310

311+
<EntityType Name="Enumeration">
312+
<Key>
313+
<PropertyRef Name="EntityType"/>
314+
<PropertyRef Name="PropertyName"/>
315+
<PropertyRef Name="RefKey"/>
316+
</Key>
317+
<Property Name="EntityType" Type="Edm.String" Nullable="false" MaxLength="40" />
318+
<Property Name="PropertyName" Type="Edm.String" Nullable="false" MaxLength="40"/>
319+
<Property Name="RefKey" Type="Edm.String" Nullable="false" MaxLength="50" />
320+
<Property Name="Value" Type="Edm.String" Nullable="false" MaxLength="255" />
321+
</EntityType>
322+
311323
<EntityType Name="Address">
312324
<Key>
313325
<PropertyRef Name="ID"/>
@@ -335,6 +347,8 @@
335347
sap:content-version="1"/>
336348

337349
<EntitySet Name="Employees" EntityType="Employee"/>
350+
<EntitySet Name="Enumerations" EntityType="Enumeration"/>
351+
338352

339353
<EntitySet Name="Addresses" EntityType="Address"/>
340354

tests/test_model_v2.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def test_edmx(schema):
3030
'CarIDPic',
3131
'Customer',
3232
'Order',
33-
'EnumTest'
33+
'EnumTest',
34+
'Enumeration'
3435
}
3536

3637
assert set((entity_set.name for entity_set in schema.entity_sets)) == {
@@ -46,7 +47,8 @@ def test_edmx(schema):
4647
'CarIDPics',
4748
'Customers',
4849
'Orders',
49-
'EnumTests'
50+
'EnumTests',
51+
'Enumerations'
5052
}
5153

5254
assert set((enum_type.name for enum_type in schema.enum_types)) == {

tests/test_service_v2.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pyodata.exceptions import PyODataException, HttpError, ExpressionError
1212
from pyodata.v2.service import EntityKey, EntityProxy, GetEntitySetFilter
1313

14-
from tests.conftest import assert_request_contains_header
14+
from tests.conftest import assert_request_contains_header, contents_of_fixtures_file
1515

1616

1717
URL_ROOT = 'http://odatapy.example.com'
@@ -1369,6 +1369,33 @@ def test_batch_request(service):
13691369
assert chset_response[0] is None # response to update request is None
13701370

13711371

1372+
@responses.activate
1373+
def test_enormous_batch_request(service):
1374+
"""Batch requests"""
1375+
1376+
# pylint: disable=redefined-outer-name
1377+
1378+
response_body = contents_of_fixtures_file('enormous_batch_response')
1379+
1380+
responses.add(
1381+
responses.POST,
1382+
'{0}/$batch'.format(URL_ROOT),
1383+
body=response_body,
1384+
content_type='multipart/mixed; boundary=16804F9C063D8720EACA19F7DFB3CD4A0',
1385+
status=202)
1386+
1387+
batch = service.create_batch()
1388+
1389+
employee_request = service.entity_sets.Enumerations.get_entities()
1390+
1391+
batch.add_request(employee_request)
1392+
1393+
response = batch.execute()
1394+
1395+
assert len(response) == 1
1396+
assert len(response[0]) == 1016
1397+
1398+
13721399
@responses.activate
13731400
def test_batch_request_failed_changeset(service):
13741401
"""Check single response for changeset"""

0 commit comments

Comments
 (0)