-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.py
147 lines (128 loc) · 4.3 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import json
import socket
import urllib.parse
import threading
from utils import log
from routes.routes_basic import (
error,
)
from routes.routes_public import route_dict
from routes.routes_todo import route_dict as routes_todo
from routes.routes_weibo import route_dict as routes_weibo
class Request(object):
def __init__(self, raw_data):
# 只能 split 一次,因为 body 中可能有换行
header, self.body = raw_data.split('\r\n\r\n', 1)
h = header.split('\r\n')
parts = h[0].split()
self.method = parts[0]
path = parts[1]
self.path = ""
self.query = {}
self.parse_path(path)
log('请求 path 和 query', self.path, self.query)
self.headers = {}
self.cookies = {}
self.add_headers(h[1:])
log('请求 headers', self.headers)
log('请求 cookies', self.cookies)
def add_headers(self, header):
lines = header
for line in lines:
k, v = line.split(': ', 1)
self.headers[k] = v
if 'Cookie' in self.headers:
cookies = self.headers['Cookie']
parts = cookies.split('; ')
for part in parts:
k, v = part.split('=', 1)
self.cookies[k] = v
def form(self):
body = urllib.parse.unquote_plus(self.body)
log('form', self.body)
log('form', body)
args = body.split('&')
f = {}
log('args', args)
for arg in args:
if len(arg.split('=')) == 2:
k, v = arg.split('=')
f[k] = v
log('form() 字典', f)
return f
def json(self):
data = json.loads(self.body)
return data
def parse_path(self, path):
index = path.find('?')
if index == -1:
self.path = path
self.query = {}
else:
path, query_string = path.split('?', 1)
args = query_string.split('&')
query = {}
for arg in args:
k, v = arg.split('=')
query[k] = v
self.path = path
self.query = query
def request_from_connection(connection):
request = b''
buffer_size = 1024
while True:
r = connection.recv(buffer_size)
request += r
# 取到的数据长度不够 buffer_size 的时候,说明数据已经取完了。
if len(r) < buffer_size:
return request
def response_for_path(request):
"""
根据 path 调用相应的处理函数
没有处理的 path 会返回 404
"""
r = route_dict()
r.update(routes_todo())
r.update(routes_weibo())
response = r.get(request.path, error)
return response(request)
def process_connection(connection):
with connection:
r = request_from_connection(connection)
log('http 请求:<\n{}\n>'.format(r.decode()))
r = r.decode()
if len(r) > 0:
request = Request(r)
response = response_for_path(request)
index = response.find(b'Content-Type: text/html')
if index == -1:
start = response.find(b'\r\n\r\n')
gif_length = len(response[start:])
gif_header = response[:start]
log('http 响应头部: <{}> 内容长度:<{}>'.format(gif_header, gif_length))
else:
log("http 响应:<\n{}\n>".format(response.decode()))
connection.sendall(response)
else:
connection.sendall(b'')
def run(host, port):
"""
启动服务器
"""
# 初始化 socket 套路
# 使用 with 可以保证程序中断的时候正确关闭 socket 释放占用的端口
log('开始运行于', 'http://{}:{}'.format(host, port))
with socket.socket() as s:
s.bind((host, port))
s.listen()
while True:
connection, address = s.accept()
log('请求 ip 和 端口 <{}>\n'.format(address))
t = threading.Thread(target=process_connection, args=(connection,))
t.start()
if __name__ == '__main__':
config = dict(
host='localhost',
port=3000,
)
run(**config)