-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcp_server.py
148 lines (118 loc) · 4.55 KB
/
tcp_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
148
import socket
import sys
import traceback
from threading import Thread
class Route():
def __init__(self):
self.routes = {}
def route(self, route_str):
def decorator(f):
self.routes[route_str] = f
return f
return decorator
def serve(self, path):
view_function = self.routes.get(path)
if view_function:
return view_function()
else:
raise ValueError('Route "{}"" has not been registered'.format(path))
app = Route()
@app.route('/hello')
def hello():
msg = '<html><body><h1>Hello World</h1><p>More content here</p></body></html>'
return msg
@app.route('/test')
def test():
msg = '<html><body><h1>This is a test</h1><p>More content here</p></body></html>'
return msg
@app.route('/something')
def something():
msg = '<html><body><h1>This is something</h1><p>More content here</p></body></html>'
return msg
def main():
start_server()
def start_server():
host = "127.0.0.1"
port = 8888
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state,
# without waiting for its natural timeout to expire
soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Socket created")
try:
soc.bind((host, port))
except:
print("Bind failed. Error : " + str(sys.exc_info()))
sys.exit()
soc.listen(5) # queue up to 5 requests
print("Socket now listening at {}".format(port))
# infinite loop- do not reset for every requests
while True:
connection, address = soc.accept()
ip, port = str(address[0]), str(address[1])
print("Connected with " + ip + ":" + port)
try:
Thread(target=client_thread, args=(connection, ip, port)).start()
except:
print("Thread did not start.")
traceback.print_exc()
soc.close()
def client_thread(connection, ip, port, max_buffer_size = 2048):
is_active = True
while is_active:
client_input = receive_input(connection, max_buffer_size)
response_headers = {
'Content-Type': 'text/html; encoding=utf8',
'Connection': 'close',
}
response_headers_raw = ''.join('%s: %s\r\n' % (k, v) for k, v in response_headers.items())
response_proto = 'HTTP/1.1'
response_status = '200'
bad_response = '400'
response_status_text = 'OK'
r = '%s %s %s\r\n' % (response_proto, response_status, response_status_text)
b_r = '%s %s %s\r\n' % (response_proto, bad_response, response_status_text)
if "--QUIT--" in client_input:
print("Client is requesting to quit")
connection.close()
print("Connection " + ip + ":" + port + " closed")
is_active = False
else:
print("Processed result: {}".format(client_input))
# connection.sendall("-".encode("utf8"))
if 'GET' in client_input or 'HEAD' in client_input:
print("request method:", client_input[0])
print('request header: ', client_input[1:])
path = client_input[1]
# import pdb; pdb.set_trace()
print(app.routes)
if path in app.routes:
response = 'response: 200 OK'
connection.send(r)
connection.send(response_headers_raw)
connection.send('\r\n') # to separate headers from body
connection.send(app.serve(path).encode(encoding='utf8'))
connection.send(response)
connection.close()
else:
response ='response: 400 Bad request'
connection.send(b_r)
connection.send(response_headers_raw)
connection.send('\r\n')
connection.send("<html><body><h1>Error 404 path not found</h1></body></html>".encode(encoding="utf8"))
connection.send(response)
connection.close()
def receive_input(connection, max_buffer_size):
client_input = connection.recv(max_buffer_size)
client_input_size = sys.getsizeof(client_input)
if client_input_size > max_buffer_size:
print("The input size is greater than expected {}".format(client_input_size))
decoded_input = client_input.decode("utf8").rstrip() # decode and strip end of line
result = process_input(decoded_input)
return result
def process_input(input_str):
print("Processing the input received from client")
input_var = str(input_str).split(' ')
return input_var
if __name__ == "__main__":
main()