-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.py
157 lines (125 loc) · 3.88 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
148
149
150
151
152
153
154
155
156
import sys
import threading
import logging
import Queue
import zlib
from socket import *
from os import kill, getpid
from request import Request
import util
def monitorQuit():
'''
Watches for user inputting 'exit' on terminal to kill server
'''
while 1:
sent = raw_input()
if sent == 'exit':
kill(getpid(), 9)
def create_socket(hostname, port):
'''
Returns a socket object listening on http://@hostname:@port
'''
# Create the socket
try:
sock = socket(AF_INET, SOCK_STREAM) # Create the socket with TCP protocol
except error as msg:
print "Error: could not create socket. Exiting...\n{}".format(msg)
logging.error('Could not create socket. Terminated server.')
sys.exit(1)
# Bind to host and port
try:
sock.bind((hostname, port)) # Bind the port to the socket.
sock.listen(20) # Listen on the given port, and the size of queue is 20.
except error as msg:
print "Error: could not bind or listen. Exiting...\n{}".format(msg)
logging.error('Could not bind or listen. Terminated server.')
sock.close()
# If the socket cannot be opened, quit the program.
if sock is None:
print "Error: cannot open socket. Exiting...\n"
logging.error('Could open socket. Terminated server.')
sys.exit(1)
return sock
def main():
'''
Sets up the socket for listening at host and port.
Also creates a thread monitoring for program termination.
'''
logging.basicConfig(filename='server.log',level=logging.DEBUG,format='%(asctime)s %(message)s')
hostname = 'localhost'
port = 9002
if len(sys.argv) >= 2:
port = int(sys.argv[1])
# Monitor thread will wait for the 'quit' signal
monitor = threading.Thread(target=monitorQuit, args=[])
monitor.start()
sock = create_socket(hostname, port)
print 'Server is listening on http://{}:{}'.format(hostname, port)
logging.info('Server has started on http://{}:{}'.format(hostname, port))
# Create and run dispatcher threads
for i in range(10):
Dispatcher(i, sock).start()
logging.info('Dispatcher Thread-%d created' % i)
# Create and run worker threads
for i in range(10):
Worker(i).start()
logging.info('Worker Thread-%d created' % i)
request_queue = Queue.Queue() # Synchronized queue
class Dispatcher(threading.Thread):
'''
Worker thread takes a request and puts it into the request queue
'''
def __init__(self, id, sock):
threading.Thread.__init__(self)
self.setName('Dispatcher-%d' % id)
self.sock = sock
def run(self):
'''
Insert client connections into the queue
'''
global request_queue
# Keep accepting client connections and put client sockets into request queue
while 1:
client, addr = self.sock.accept()
request_queue.put((client, addr))
print self.getName(), 'dispatched connection from', addr
class Worker(threading.Thread):
'''
Get a request from the queue and serve it
'''
def __init__(self, id):
threading.Thread.__init__(self)
self.setName('Worker-%d' % id)
def run(self):
'''
Serve the request and pass it back to the client
'''
global request_queue
# Get a client connection from the queue and serve its request
while 1:
client_conn = request_queue.get()
self.serve(client_conn[0], *client_conn[1])
print self.getName(), 'served request from', client_conn[1]
request_queue.task_done()
def serve(self, client_sock, host, port):
'''
Receives request from client socket and returns response back to the socket
'''
recv = client_sock.recv(2048)
recv = recv.split()
'''
Request method - recv[0]
URL path - recv[1]
HTTP Version - recv[2]
'''
method, filename, http_version = recv[:3]
# Check for malformed request by checking request type and HTTP version
if recv == [] or method not in util.req_methods \
or http_version not in ['HTTP/1.1', 'HTTP/1.0']:
sock.close()
else:
r = Request(filename, method, host, port)
client_sock.sendall(r.do_request())
client_sock.close()
if __name__ == '__main__':
main()