Skip to content

Commit b49af62

Browse files
committed
optimize receive
1 parent e883e42 commit b49af62

File tree

8 files changed

+71
-48
lines changed

8 files changed

+71
-48
lines changed

src/__init__.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
# encoding: utf-8
22

3-
__all__ = ['Application', 'WebServer', 'Request', 'Response', 'request']
3+
__all__ = ["Application", "WebServer", "Request", "Response", "request"]
44

55
from .request import Request
66
from .server import WebServer
77
from .response import Response
8-
from .proxy import Proxy, request
8+
from .proxy import request
99
from .application import Application
1010

1111

12-
if __name__ == '__main__':
12+
if __name__ == "__main__":
1313
app = Application()
1414

15-
16-
@app.route('/')
15+
@app.route("/")
1716
def index():
18-
return 'Hello World'
19-
17+
return "Hello World"
2018

2119
WebServer().run(app)

src/application.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99

1010
class Method:
11-
GET = 'GET'
12-
POST = 'POST'
13-
PUT = 'PUT'
14-
DELETE = 'DELETE'
11+
GET = "GET"
12+
POST = "POST"
13+
PUT = "PUT"
14+
DELETE = "DELETE"
1515

1616

1717
class ViewFunction:
@@ -23,9 +23,15 @@ def __init__(self, methods, function):
2323
class Application:
2424
def __init__(self):
2525
self.url_func_map = {}
26-
self.http_404 = Response('<html><body>Not Found 404</body></html>', status_code=404)
27-
self.http_405 = Response('<html><body>Method Not Allowed</body></html>', status_code=405)
28-
self.http_500 = Response('<html><body>Server Internal Error</body></html>', status_code=500)
26+
self.http_404 = Response(
27+
"<html><body>Not Found 404</body></html>", status_code=404
28+
)
29+
self.http_405 = Response(
30+
"<html><body>Method Not Allowed</body></html>", status_code=405
31+
)
32+
self.http_500 = Response(
33+
"<html><body>Server Internal Error</body></html>", status_code=500
34+
)
2935

3036
def route(self, path, methods=None):
3137
if methods is None:
@@ -56,13 +62,16 @@ def __call__(self):
5662
if isinstance(response, str):
5763
response = Response(response)
5864
elif isinstance(response, dict):
59-
response = Response(dict2str(response), {HeaderKey.CONTENT_TYPE: ContentTypeCharset.JSON})
65+
response = Response(
66+
dict2str(response),
67+
{HeaderKey.CONTENT_TYPE: ContentTypeCharset.JSON},
68+
)
6069
if not isinstance(response, Response):
61-
raise TypeError('Should return Response or dict or str')
70+
raise TypeError("Should return Response or dict or str")
6271
except Exception as e:
63-
print('Server Internal Error', e)
72+
print("Server Internal Error", e)
6473
response = self.http_500
6574
return response.get_str()
6675

67-
def run(self, host='', port=8000):
76+
def run(self, host="", port=8000):
6877
WebServer(host=host, port=port).run(self)

src/constants.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
MAX_SIZE = 1024 * 4
2-
DEFAULT_ENCODE = 'utf-8'
2+
DEFAULT_ENCODE = "utf-8"
33

44

55
class HeaderKey:
6-
CONTENT_TYPE = 'Content-Type'
7-
CONTENT_LENGTH = 'Content-Length'
6+
CONTENT_TYPE = "Content-Type"
7+
CONTENT_LENGTH = "Content-Length"
88

99

1010
class ContentType:
11-
JSON = 'application/json'
12-
HTML = 'text/html'
11+
JSON = "application/json"
12+
HTML = "text/html"
1313

1414

1515
class ContentTypeCharset:
16-
UTF8_CHARSET = f';charset={DEFAULT_ENCODE}'
16+
UTF8_CHARSET = f";charset={DEFAULT_ENCODE}"
1717
JSON = ContentType.JSON + UTF8_CHARSET
1818
HTML = ContentType.HTML + UTF8_CHARSET

src/proxy.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ def __init__(self, local, attribute):
77
self.attribute = attribute
88

99
def __getattribute__(self, item):
10-
local = super().__getattribute__('local')
11-
attribute = super().__getattribute__('attribute')
10+
local = super().__getattribute__("local")
11+
attribute = super().__getattribute__("attribute")
1212
obj = getattr(local, attribute)
1313
return getattr(obj, item)
1414

1515

1616
http_local = threading.local()
17-
request = Proxy(http_local, 'request')
17+
request = Proxy(http_local, "request")

src/request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def __init__(self, method, path, protocol_version, headers, body, ip_port):
1818
self.args = parse_qs(split.query)
1919

2020
def str2dict(self):
21-
if self.headers.get(HeaderKey.CONTENT_TYPE, '').startswith(ContentType.JSON):
21+
if self.headers.get(HeaderKey.CONTENT_TYPE, "").startswith(ContentType.JSON):
2222
return json.loads(self.body.decode(DEFAULT_ENCODE))
2323

2424
def __repr__(self):
25-
return '<Request: %s>' % self.__dict__
25+
return "<Request: %s>" % self.__dict__

src/response.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ def __init__(self, body, headers=None, status_code=200):
1010
self.status_code = status_code
1111

1212
def get_str(self):
13-
response_line = f'{http_local.request.protocol_version} {self.status_code} OK\r\n'
13+
response_line = (
14+
f"{http_local.request.protocol_version} {self.status_code} OK\r\n"
15+
)
1416
if isinstance(self.body, dict):
1517
self.body = dict2str(self.body)
1618
self.headers[HeaderKey.CONTENT_TYPE] = ContentTypeCharset.JSON
17-
response_headers = ''
19+
response_headers = ""
1820
for k, v in self.headers.items():
19-
response_headers += f'{k}: {v}\r\n'
20-
return response_line + response_headers + '\r\n' + self.body
21+
response_headers += f"{k}: {v}\r\n"
22+
return response_line + response_headers + "\r\n" + self.body

src/server.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99

1010

1111
class WebServer:
12-
def __init__(self, *, host='', port=8000, backlog=512):
12+
def __init__(self, *, host="", port=8000, backlog=512):
1313
self.host = host
1414
self.port = port
1515
self.backlog = backlog
1616
self.address = (host, port)
1717

1818
def run(self, application):
19-
print('web server start...')
19+
print("web server start...")
2020
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2121
_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
2222
_socket.bind(self.address)
23-
print('web server bind address:', self.address)
23+
print("web server bind address:", self.address)
2424
_socket.listen(self.backlog)
2525
with ThreadPoolExecutor() as pool:
2626
while True:
@@ -30,39 +30,49 @@ def run(self, application):
3030
def service_client(self, _socket, ip_port, application):
3131
try:
3232
content = self.receive(_socket, ip_port)
33-
http_local.request = content
3433
except Exception as e:
35-
print(f'parse error', e)
34+
print(f"parse error", e)
3635
else:
37-
response = application()
38-
_socket.send(response.encode(DEFAULT_ENCODE))
36+
if content:
37+
http_local.request = content
38+
response = application()
39+
_socket.send(response.encode(DEFAULT_ENCODE))
3940
finally:
4041
_socket.close()
4142

4243
@classmethod
4344
def receive(cls, _socket, ip_port):
45+
_socket.settimeout(0)
4446
content = b""
4547

46-
while data := _socket.recv(MAX_SIZE):
47-
content += data
48-
if len(data) < MAX_SIZE:
48+
while True:
49+
try:
50+
chunk = _socket.recv(MAX_SIZE)
51+
except BlockingIOError:
4952
break
53+
else:
54+
content += chunk
55+
if len(chunk) < MAX_SIZE:
56+
break
57+
if not content:
58+
return
5059

5160
try:
5261
method, path, protocol_version, headers, body = cls.parse(content)
5362
except Exception as e:
5463
logging.exception(e)
64+
logging.error(content)
5565
raise e
5666
else:
5767
return Request(method, path, protocol_version, headers, body, ip_port)
5868

5969
@staticmethod
6070
def parse(content):
61-
line_header, body = content.split(b'\r\n\r\n')
62-
line, *headers_raw = line_header.split(b'\r\n')
71+
line_header, body = content.split(b"\r\n\r\n")
72+
line, *headers_raw = line_header.split(b"\r\n")
6373
headers = IgnoreCaseDict()
6474
for header in headers_raw:
65-
k, v = header.decode(DEFAULT_ENCODE).split(':', maxsplit=1)
75+
k, v = header.decode(DEFAULT_ENCODE).split(":", maxsplit=1)
6676
headers[k] = v.strip()
67-
method, path, protocol_version = line.decode(DEFAULT_ENCODE).split(' ')
77+
method, path, protocol_version = line.decode(DEFAULT_ENCODE).split(" ")
6878
return method, path, protocol_version, headers, body

src/utility.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,8 @@ def __str__(self):
3030

3131

3232
def dict2str(obj, ensure_ascii=False):
33-
return json.dumps(obj, ensure_ascii=ensure_ascii, default=lambda o: dict(o) if isinstance(o, IgnoreCaseDict) else o)
33+
return json.dumps(
34+
obj,
35+
ensure_ascii=ensure_ascii,
36+
default=lambda o: dict(o) if isinstance(o, IgnoreCaseDict) else o,
37+
)

0 commit comments

Comments
 (0)