uhttp-server 2.3.0__tar.gz → 2.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/PKG-INFO +1 -1
- uhttp_server-2.3.1/tests/test_respond_falsy.py +106 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/uhttp/server.py +15 -2
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/uhttp_server.egg-info/PKG-INFO +1 -1
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/uhttp_server.egg-info/SOURCES.txt +1 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/.github/workflows/micropython.yml +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/.github/workflows/publish.yml +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/.github/workflows/tests.yml +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/.gitignore +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/LICENSE +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/README.md +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/examples/client_with_server.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/examples/http_to_https_redirect.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/examples/https_server.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/pyproject.toml +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/setup.cfg +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/__init__.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_100_continue.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_concurrent_connections.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_content_length_security.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_data_parsing.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_double_response.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_eagain.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_error_handling.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_event_mode.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_ipv6.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_keepalive.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_keepalive_basic.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_keepalive_http10.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_keepalive_limits.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_keepalive_simple.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_mpy_integration.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_mpy_websocket.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_multipart.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_pipelining.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_respond_file.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_respond_file_race.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_ssl.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_utils.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_websocket.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/tests/test_websocket_utils.py +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/uhttp_server.egg-info/dependency_links.txt +0 -0
- {uhttp_server-2.3.0 → uhttp_server-2.3.1}/uhttp_server.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Test responding with falsy values (empty list, empty dict, 0, empty string, empty bytes)
|
|
4
|
+
"""
|
|
5
|
+
import unittest
|
|
6
|
+
import socket
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
import json
|
|
10
|
+
from uhttp import server as uhttp_server
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestRespondFalsy(unittest.TestCase):
|
|
14
|
+
"""Test that respond() correctly handles falsy but valid data values"""
|
|
15
|
+
|
|
16
|
+
server = None
|
|
17
|
+
server_thread = None
|
|
18
|
+
response_data = None
|
|
19
|
+
PORT = 9997
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def setUpClass(cls):
|
|
23
|
+
cls.server = uhttp_server.HttpServer(port=cls.PORT)
|
|
24
|
+
|
|
25
|
+
def run_server():
|
|
26
|
+
try:
|
|
27
|
+
while cls.server:
|
|
28
|
+
client = cls.server.wait(timeout=0.1)
|
|
29
|
+
if client:
|
|
30
|
+
client.respond(cls.response_data)
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
cls.server_thread = threading.Thread(target=run_server, daemon=True)
|
|
35
|
+
cls.server_thread.start()
|
|
36
|
+
time.sleep(0.5)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def tearDownClass(cls):
|
|
40
|
+
if cls.server:
|
|
41
|
+
cls.server.close()
|
|
42
|
+
cls.server = None
|
|
43
|
+
|
|
44
|
+
def get_response(self):
|
|
45
|
+
"""Send GET and return (headers_str, body_bytes)"""
|
|
46
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
47
|
+
sock.settimeout(2.0)
|
|
48
|
+
sock.connect(('localhost', self.PORT))
|
|
49
|
+
sock.sendall(b"GET / HTTP/1.0\r\nHost: localhost\r\n\r\n")
|
|
50
|
+
response = b""
|
|
51
|
+
while True:
|
|
52
|
+
chunk = sock.recv(4096)
|
|
53
|
+
if not chunk:
|
|
54
|
+
break
|
|
55
|
+
response += chunk
|
|
56
|
+
sock.close()
|
|
57
|
+
parts = response.split(b"\r\n\r\n", 1)
|
|
58
|
+
headers = parts[0].decode()
|
|
59
|
+
body = parts[1] if len(parts) > 1 else b""
|
|
60
|
+
return headers, body
|
|
61
|
+
|
|
62
|
+
def test_respond_empty_list(self):
|
|
63
|
+
"""respond([]) should return JSON '[]'"""
|
|
64
|
+
TestRespondFalsy.response_data = []
|
|
65
|
+
headers, body = self.get_response()
|
|
66
|
+
self.assertIn('200', headers)
|
|
67
|
+
self.assertEqual(json.loads(body), [])
|
|
68
|
+
|
|
69
|
+
def test_respond_empty_dict(self):
|
|
70
|
+
"""respond({}) should return JSON '{}'"""
|
|
71
|
+
TestRespondFalsy.response_data = {}
|
|
72
|
+
headers, body = self.get_response()
|
|
73
|
+
self.assertIn('200', headers)
|
|
74
|
+
self.assertEqual(json.loads(body), {})
|
|
75
|
+
|
|
76
|
+
def test_respond_zero(self):
|
|
77
|
+
"""respond(0) should return JSON '0'"""
|
|
78
|
+
TestRespondFalsy.response_data = 0
|
|
79
|
+
headers, body = self.get_response()
|
|
80
|
+
self.assertIn('200', headers)
|
|
81
|
+
self.assertEqual(json.loads(body), 0)
|
|
82
|
+
|
|
83
|
+
def test_respond_empty_string(self):
|
|
84
|
+
"""respond('') should return empty body"""
|
|
85
|
+
TestRespondFalsy.response_data = ''
|
|
86
|
+
headers, body = self.get_response()
|
|
87
|
+
self.assertIn('200', headers)
|
|
88
|
+
self.assertEqual(body, b'')
|
|
89
|
+
|
|
90
|
+
def test_respond_empty_bytes(self):
|
|
91
|
+
"""respond(b'') should return empty body"""
|
|
92
|
+
TestRespondFalsy.response_data = b''
|
|
93
|
+
headers, body = self.get_response()
|
|
94
|
+
self.assertIn('200', headers)
|
|
95
|
+
self.assertEqual(body, b'')
|
|
96
|
+
|
|
97
|
+
def test_respond_none(self):
|
|
98
|
+
"""respond(None) should return no body"""
|
|
99
|
+
TestRespondFalsy.response_data = None
|
|
100
|
+
headers, body = self.get_response()
|
|
101
|
+
self.assertIn('200', headers)
|
|
102
|
+
self.assertEqual(body, b'')
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == '__main__':
|
|
106
|
+
unittest.main()
|
|
@@ -60,6 +60,19 @@ CONTENT_TYPE_MAP = {
|
|
|
60
60
|
'webp': 'image/webp',
|
|
61
61
|
'ico': 'image/x-icon',
|
|
62
62
|
'bmp': 'image/bmp',
|
|
63
|
+
'js': 'application/javascript',
|
|
64
|
+
'css': 'text/css',
|
|
65
|
+
'json': 'application/json',
|
|
66
|
+
'xml': 'text/xml',
|
|
67
|
+
'txt': 'text/plain',
|
|
68
|
+
'woff': 'font/woff',
|
|
69
|
+
'woff2': 'font/woff2',
|
|
70
|
+
'ttf': 'font/ttf',
|
|
71
|
+
'mp3': 'audio/mpeg',
|
|
72
|
+
'mp4': 'video/mp4',
|
|
73
|
+
'wav': 'audio/wav',
|
|
74
|
+
'pdf': 'application/pdf',
|
|
75
|
+
'zip': 'application/zip',
|
|
63
76
|
}
|
|
64
77
|
METHODS = (
|
|
65
78
|
'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST',
|
|
@@ -1447,12 +1460,12 @@ class HttpConnection(_WsFrameMixin):
|
|
|
1447
1460
|
if self._socket is None:
|
|
1448
1461
|
return
|
|
1449
1462
|
headers = self._prepare_response(headers)
|
|
1450
|
-
if data:
|
|
1463
|
+
if data is not None:
|
|
1451
1464
|
data = encode_response_data(headers, data)
|
|
1452
1465
|
|
|
1453
1466
|
header = self._build_response_header(status, headers=headers, cookies=cookies)
|
|
1454
1467
|
try:
|
|
1455
|
-
if data:
|
|
1468
|
+
if data is not None:
|
|
1456
1469
|
header_bytes = header.encode('ascii') if isinstance(header, str) else header
|
|
1457
1470
|
self._send(header_bytes + data)
|
|
1458
1471
|
else:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|