Skip to content

Commit 6e2e7bc

Browse files
authored
Merge pull request #16 from d1618033/issue-15
Add raw field
2 parents 4be4848 + 4040170 commit 6e2e7bc

5 files changed

Lines changed: 70 additions & 4 deletions

File tree

flask_loopback/flask_loopback.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
from contextlib import contextmanager
33

44
import requests
5+
import six
56
from requests.cookies import MockRequest
7+
from urllib3 import HTTPResponse
68

79
from . import dispatch
810
from ._compat import httplib, iteritems, gzip_decompress
@@ -23,6 +25,29 @@ def __init__(self, request, code):
2325
self.response.request = request
2426

2527

28+
class _IOReader(six.BytesIO):
29+
"""A reader that makes a BytesIO look like a HTTPResponse.
30+
A HTTPResponse will return an empty string when you read from it after
31+
the socket has been closed. A BytesIO will raise a ValueError. For
32+
compatibility we want to do the same thing a HTTPResponse does.
33+
"""
34+
35+
def read(self, *args, **kwargs): # pylint: disable=arguments-differ
36+
if self.closed:
37+
return six.b('')
38+
39+
# not a new style object in python 2
40+
result = six.BytesIO.read(self, *args, **kwargs)
41+
42+
# when using resp.iter_content(None) it'll go through a different
43+
# request path in urllib3. This path checks whether the object is
44+
# marked closed instead of the return value. see gh124.
45+
if result == six.b(''):
46+
self.close()
47+
48+
return result
49+
50+
2651
class FlaskLoopback(object):
2752

2853
def __init__(self, flask_app):
@@ -72,7 +97,7 @@ def handle_request(self, session, url, request):
7297
with ExitStack() as stack:
7398
for handler in self._request_context_handlers:
7499
try:
75-
stack.enter_context(handler(request))
100+
stack.enter_context(handler(request)) # pylint: disable=no-member
76101
except CustomHTTPResponse as e:
77102
return e.response
78103

@@ -89,8 +114,16 @@ def handle_request(self, session, url, request):
89114
resp_data = resp.get_data()
90115
if resp.headers.get('content-encoding') == 'gzip':
91116
resp_data = gzip_decompress(resp_data)
92-
returned._content = resp_data # pylint: disable=protected-access
117+
returned._content = resp_data # pylint: disable=protected-access
93118
returned.headers.update(resp.headers.items())
119+
returned.raw = HTTPResponse(
120+
status=returned.status_code,
121+
reason=returned.reason,
122+
headers=returned.headers,
123+
body=_IOReader(resp_data) or _IOReader(six.b('')),
124+
decode_content=False,
125+
preload_content=False,
126+
)
94127
self._extract_cookies(session, request, resp, returned)
95128
return returned
96129

@@ -122,8 +155,9 @@ def get_all(self, name, default=None):
122155

123156
_hostname = None
124157

158+
125159
def _get_hostname():
126-
global _hostname # pylint: disable=global-statement
160+
global _hostname # pylint: disable=global-statement
127161
if _hostname is None:
128162
_hostname = socket.getfqdn()
129163
return _hostname

tests/conftest.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import hashlib
2+
import os
23

34
import pytest
4-
from flask import Flask, g, jsonify, request, Response
5+
from flask import Flask, g, jsonify, request, Response, send_file
56
from flask_loopback import FlaskLoopback
67
from flask_loopback._compat import gzip_compress
78
from urlobject import URLObject as URL
@@ -65,6 +66,14 @@ def compressed(): # pylint: disable=unused-variable
6566
orig = "uncompressed!".encode("utf-8")
6667
return Response(gzip_compress(orig), headers={"Content-Encoding": "gzip"})
6768

69+
@returned.route("/files/binary")
70+
def binary_file(): # pylint: disable=unused-variable
71+
return send_file(os.path.join(os.path.dirname(__file__), "files", "binary"))
72+
73+
@returned.route("/files/text")
74+
def text_file(): # pylint: disable=unused-variable
75+
return send_file(os.path.join(os.path.dirname(__file__), "files", "text"))
76+
6877
@returned.before_request
6978
def before(): # pylint: disable=unused-variable
7079
g.cookies = []

tests/files/binary

1.25 KB
Binary file not shown.

tests/files/text

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello world

tests/test_download_file.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import os
2+
3+
import pytest
4+
import requests
5+
6+
7+
def read_response_using_iter_content(resp):
8+
return b''.join([chunk for chunk in resp.iter_content(chunk_size=1024)])
9+
10+
11+
def read_response_using_content(resp):
12+
return resp.content
13+
14+
15+
@pytest.mark.parametrize("read_response", [read_response_using_content, read_response_using_iter_content])
16+
@pytest.mark.parametrize("file_type", ["binary", "text"])
17+
def test_download_file(active_app, url, read_response, file_type): # pylint: disable=unused-argument
18+
resp = requests.get(url.add_path("files").add_path(file_type))
19+
resp.raise_for_status()
20+
result = read_response(resp)
21+
with open(os.path.join(os.path.dirname(__file__), 'files', file_type), 'rb') as f:
22+
assert result == f.read()

0 commit comments

Comments
 (0)