Skip to content

Commit e16bc0d

Browse files
authored
Merge branch 'adafruit:main' into connection-manager
2 parents 66fb48c + 178dbc3 commit e16bc0d

File tree

7 files changed

+225
-55
lines changed

7 files changed

+225
-55
lines changed

adafruit_requests.py

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,16 @@ def _send(socket: SocketType, data: bytes):
399399
raise OSError(errno.EIO)
400400
total_sent += sent
401401

402+
def _send_as_bytes(self, socket: SocketType, data: str):
403+
return self._send(socket, bytes(data, "utf-8"))
404+
405+
def _send_header(self, socket, header, value):
406+
self._send_as_bytes(socket, header)
407+
self._send(socket, b": ")
408+
self._send_as_bytes(socket, value)
409+
self._send(socket, b"\r\n")
410+
411+
# pylint: disable=too-many-arguments
402412
def _send_request(
403413
self,
404414
socket: SocketType,
@@ -409,40 +419,51 @@ def _send_request(
409419
data: Any,
410420
json: Any,
411421
):
412-
# pylint: disable=too-many-arguments
413-
self._send(socket, bytes(method, "utf-8"))
414-
self._send(socket, b" /")
415-
self._send(socket, bytes(path, "utf-8"))
416-
self._send(socket, b" HTTP/1.1\r\n")
417-
if "Host" not in headers:
418-
self._send(socket, b"Host: ")
419-
self._send(socket, bytes(host, "utf-8"))
420-
self._send(socket, b"\r\n")
421-
if "User-Agent" not in headers:
422-
self._send(socket, b"User-Agent: Adafruit CircuitPython\r\n")
423-
# Iterate over keys to avoid tuple alloc
424-
for k in headers:
425-
self._send(socket, k.encode())
426-
self._send(socket, b": ")
427-
self._send(socket, headers[k].encode())
428-
self._send(socket, b"\r\n")
422+
# Convert data
423+
content_type_header = None
424+
425+
# If json is sent, set content type header and convert to string
429426
if json is not None:
430427
assert data is None
428+
content_type_header = "application/json"
431429
data = json_module.dumps(json)
432-
self._send(socket, b"Content-Type: application/json\r\n")
433-
if data:
434-
if isinstance(data, dict):
435-
self._send(
436-
socket, b"Content-Type: application/x-www-form-urlencoded\r\n"
437-
)
438-
_post_data = ""
439-
for k in data:
440-
_post_data = "{}&{}={}".format(_post_data, k, data[k])
441-
data = _post_data[1:]
442-
if isinstance(data, str):
443-
data = bytes(data, "utf-8")
444-
self._send(socket, b"Content-Length: %d\r\n" % len(data))
430+
431+
# If data is sent and it's a dict, set content type header and convert to string
432+
if data and isinstance(data, dict):
433+
content_type_header = "application/x-www-form-urlencoded"
434+
_post_data = ""
435+
for k in data:
436+
_post_data = "{}&{}={}".format(_post_data, k, data[k])
437+
# remove first "&" from concatenation
438+
data = _post_data[1:]
439+
440+
# Convert str data to bytes
441+
if data and isinstance(data, str):
442+
data = bytes(data, "utf-8")
443+
444+
self._send_as_bytes(socket, method)
445+
self._send(socket, b" /")
446+
self._send_as_bytes(socket, path)
447+
self._send(socket, b" HTTP/1.1\r\n")
448+
449+
# create lower-case supplied header list
450+
supplied_headers = {header.lower() for header in headers}
451+
452+
# Send headers
453+
if not "host" in supplied_headers:
454+
self._send_header(socket, "Host", host)
455+
if not "user-agent" in supplied_headers:
456+
self._send_header(socket, "User-Agent", "Adafruit CircuitPython")
457+
if content_type_header and not "content-type" in supplied_headers:
458+
self._send_header(socket, "Content-Type", content_type_header)
459+
if data and not "content-length" in supplied_headers:
460+
self._send_header(socket, "Content-Length", str(len(data)))
461+
# Iterate over keys to avoid tuple alloc
462+
for header in headers:
463+
self._send_header(socket, header, headers[header])
445464
self._send(socket, b"\r\n")
465+
466+
# Send data
446467
if data:
447468
self._send(socket, bytes(data))
448469

tests/chunk_test.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def do_test_get_text(
6363
)
6464
sock.send.assert_has_calls(
6565
[
66-
mock.call(b"Host: "),
66+
mock.call(b"Host"),
67+
mock.call(b": "),
6768
mock.call(b"wifitest.adafruit.com"),
6869
]
6970
)
@@ -105,7 +106,8 @@ def do_test_close_flush(
105106
)
106107
sock.send.assert_has_calls(
107108
[
108-
mock.call(b"Host: "),
109+
mock.call(b"Host"),
110+
mock.call(b": "),
109111
mock.call(b"wifitest.adafruit.com"),
110112
]
111113
)
@@ -146,7 +148,8 @@ def do_test_get_text_extra_space(
146148
)
147149
sock.send.assert_has_calls(
148150
[
149-
mock.call(b"Host: "),
151+
mock.call(b"Host"),
152+
mock.call(b": "),
150153
mock.call(b"wifitest.adafruit.com"),
151154
]
152155
)

tests/concurrent_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ def test_second_connect_fails_memoryerror():
4040

4141
sock.send.assert_has_calls(
4242
[
43-
mock.call(b"Host: "),
43+
mock.call(b"Host"),
44+
mock.call(b": "),
4445
mock.call(b"wifitest.adafruit.com"),
4546
mock.call(b"\r\n"),
4647
]
@@ -82,7 +83,8 @@ def test_second_connect_fails_oserror():
8283

8384
sock.send.assert_has_calls(
8485
[
85-
mock.call(b"Host: "),
86+
mock.call(b"Host"),
87+
mock.call(b": "),
8688
mock.call(b"wifitest.adafruit.com"),
8789
mock.call(b"\r\n"),
8890
]

tests/header_test.py

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,75 @@
1212
RESPONSE_HEADERS = b"HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"
1313

1414

15-
def test_json():
15+
def test_host():
16+
pool = mocket.MocketPool()
17+
pool.getaddrinfo.return_value = ((None, None, None, None, (IP, 80)),)
18+
sock = mocket.Mocket(RESPONSE_HEADERS)
19+
pool.socket.return_value = sock
20+
sent = []
21+
22+
def _send(data):
23+
sent.append(data) # pylint: disable=no-member
24+
return len(data)
25+
26+
sock.send.side_effect = _send
27+
28+
requests_session = adafruit_requests.Session(pool)
29+
headers = {}
30+
requests_session.get("http://" + HOST + "/get", headers=headers)
31+
32+
sock.connect.assert_called_once_with((IP, 80))
33+
sent = b"".join(sent)
34+
assert b"Host: httpbin.org\r\n" in sent
35+
36+
37+
def test_host_replace():
38+
pool = mocket.MocketPool()
39+
pool.getaddrinfo.return_value = ((None, None, None, None, (IP, 80)),)
40+
sock = mocket.Mocket(RESPONSE_HEADERS)
41+
pool.socket.return_value = sock
42+
sent = []
43+
44+
def _send(data):
45+
sent.append(data) # pylint: disable=no-member
46+
return len(data)
47+
48+
sock.send.side_effect = _send
49+
50+
requests_session = adafruit_requests.Session(pool)
51+
headers = {"host": IP}
52+
requests_session.get("http://" + HOST + "/get", headers=headers)
53+
54+
sock.connect.assert_called_once_with((IP, 80))
55+
sent = b"".join(sent)
56+
assert b"host: 1.2.3.4\r\n" in sent
57+
assert b"Host: httpbin.org\r\n" not in sent
58+
assert sent.lower().count(b"host:") == 1
59+
60+
61+
def test_user_agent():
62+
pool = mocket.MocketPool()
63+
pool.getaddrinfo.return_value = ((None, None, None, None, (IP, 80)),)
64+
sock = mocket.Mocket(RESPONSE_HEADERS)
65+
pool.socket.return_value = sock
66+
sent = []
67+
68+
def _send(data):
69+
sent.append(data) # pylint: disable=no-member
70+
return len(data)
71+
72+
sock.send.side_effect = _send
73+
74+
requests_session = adafruit_requests.Session(pool)
75+
headers = {}
76+
requests_session.get("http://" + HOST + "/get", headers=headers)
77+
78+
sock.connect.assert_called_once_with((IP, 80))
79+
sent = b"".join(sent)
80+
assert b"User-Agent: Adafruit CircuitPython\r\n" in sent
81+
82+
83+
def test_user_agent_replace():
1684
pool = mocket.MocketPool()
1785
pool.getaddrinfo.return_value = ((None, None, None, None, (IP, 80)),)
1886
sock = mocket.Mocket(RESPONSE_HEADERS)
@@ -30,7 +98,55 @@ def _send(data):
3098
requests_session.get("http://" + HOST + "/get", headers=headers)
3199

32100
sock.connect.assert_called_once_with((IP, 80))
33-
sent = b"".join(sent).lower()
101+
sent = b"".join(sent)
34102
assert b"user-agent: blinka/1.0.0\r\n" in sent
35-
# The current implementation sends two user agents. Fix it, and uncomment below.
36-
# assert sent.count(b"user-agent:") == 1
103+
assert b"User-Agent: Adafruit CircuitPython\r\n" not in sent
104+
assert sent.lower().count(b"user-agent:") == 1
105+
106+
107+
def test_content_type():
108+
pool = mocket.MocketPool()
109+
pool.getaddrinfo.return_value = ((None, None, None, None, (IP, 80)),)
110+
sock = mocket.Mocket(RESPONSE_HEADERS)
111+
pool.socket.return_value = sock
112+
sent = []
113+
114+
def _send(data):
115+
sent.append(data) # pylint: disable=no-member
116+
return len(data)
117+
118+
sock.send.side_effect = _send
119+
120+
requests_session = adafruit_requests.Session(pool)
121+
headers = {}
122+
data = {"test": True}
123+
requests_session.post("http://" + HOST + "/get", data=data, headers=headers)
124+
125+
sock.connect.assert_called_once_with((IP, 80))
126+
sent = b"".join(sent)
127+
assert b"Content-Type: application/x-www-form-urlencoded\r\n" in sent
128+
129+
130+
def test_content_type_replace():
131+
pool = mocket.MocketPool()
132+
pool.getaddrinfo.return_value = ((None, None, None, None, (IP, 80)),)
133+
sock = mocket.Mocket(RESPONSE_HEADERS)
134+
pool.socket.return_value = sock
135+
sent = []
136+
137+
def _send(data):
138+
sent.append(data) # pylint: disable=no-member
139+
return len(data)
140+
141+
sock.send.side_effect = _send
142+
143+
requests_session = adafruit_requests.Session(pool)
144+
headers = {"content-type": "application/test"}
145+
data = {"test": True}
146+
requests_session.post("http://" + HOST + "/get", data=data, headers=headers)
147+
148+
sock.connect.assert_called_once_with((IP, 80))
149+
sent = b"".join(sent)
150+
assert b"content-type: application/test\r\n" in sent
151+
assert b"Content-Type: application/x-www-form-urlencoded\r\n" not in sent
152+
assert sent.lower().count(b"content-type:") == 1

tests/post_test.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def test_method():
3838
)
3939
sock.send.assert_has_calls(
4040
[
41-
mock.call(b"Host: "),
41+
mock.call(b"Host"),
42+
mock.call(b": "),
4243
mock.call(b"httpbin.org"),
4344
]
4445
)
@@ -64,10 +65,18 @@ def test_form():
6465
pool.socket.return_value = sock
6566

6667
requests_session = adafruit_requests.Session(pool)
67-
data = {"Date": "July 25, 2019"}
68+
data = {"Date": "July 25, 2019", "Time": "12:00"}
6869
requests_session.post("http://" + HOST + "/post", data=data)
6970
sock.connect.assert_called_once_with((IP, 80))
70-
sock.send.assert_called_with(b"Date=July 25, 2019")
71+
sock.send.assert_has_calls(
72+
[
73+
mock.call(b"Content-Type"),
74+
mock.call(b": "),
75+
mock.call(b"application/x-www-form-urlencoded"),
76+
mock.call(b"\r\n"),
77+
]
78+
)
79+
sock.send.assert_called_with(b"Date=July 25, 2019&Time=12:00")
7180

7281

7382
def test_json():
@@ -77,7 +86,15 @@ def test_json():
7786
pool.socket.return_value = sock
7887

7988
requests_session = adafruit_requests.Session(pool)
80-
json_data = {"Date": "July 25, 2019"}
89+
json_data = {"Date": "July 25, 2019", "Time": "12:00"}
8190
requests_session.post("http://" + HOST + "/post", json=json_data)
8291
sock.connect.assert_called_once_with((IP, 80))
83-
sock.send.assert_called_with(b'{"Date": "July 25, 2019"}')
92+
sock.send.assert_has_calls(
93+
[
94+
mock.call(b"Content-Type"),
95+
mock.call(b": "),
96+
mock.call(b"application/json"),
97+
mock.call(b"\r\n"),
98+
]
99+
)
100+
sock.send.assert_called_with(b'{"Date": "July 25, 2019", "Time": "12:00"}')

tests/protocol_test.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ def test_get_https_text():
4949
)
5050
sock.send.assert_has_calls(
5151
[
52-
mock.call(b"Host: "),
52+
mock.call(b"Host"),
53+
mock.call(b": "),
5354
mock.call(b"wifitest.adafruit.com"),
5455
]
5556
)
@@ -80,7 +81,8 @@ def test_get_http_text():
8081
)
8182
sock.send.assert_has_calls(
8283
[
83-
mock.call(b"Host: "),
84+
mock.call(b"Host"),
85+
mock.call(b": "),
8486
mock.call(b"wifitest.adafruit.com"),
8587
]
8688
)
@@ -109,7 +111,8 @@ def test_get_close():
109111
)
110112
sock.send.assert_has_calls(
111113
[
112-
mock.call(b"Host: "),
114+
mock.call(b"Host"),
115+
mock.call(b": "),
113116
mock.call(b"wifitest.adafruit.com"),
114117
]
115118
)

0 commit comments

Comments
 (0)