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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
"""
This is a simple web-server that does very few things. It is necessary for
the downloader tests.
Here is the logic behind the initialization:
Because several instances of the test can run simultaneously on the Build
machine, we have to take this into account and not start another server if
one is already running. However, there is a chance that a server will not
terminate correctly, and will still hold the port, so we will not be able
to initialize another server.
So before initializing the server, we check if any processes are using the port
that we want to use. If we find such a process, we assume that it might be
working, and wait for about 10 seconds for it to start serving. If it does not,
we kill it.
Next, we check the name of our process and see if there are other processes
with the same name. If there are, we assume that they might start serving any
moment. So we iterate over the ones that have PID lower than ours, and wait
for them to start serving. If a process doesn't serve, we kill it.
If we have killed (or someone has) all the processes with PIDs lower than ours,
we try to start serving. If we succeed, we kill all other processes with the
same name as ours. If we don't someone else will kill us.
"""
from __future__ import print_function
from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from ResponseProvider import Payload
from ResponseProvider import ResponseProvider
from ResponseProvider import ResponseProviderMixin
from SiblingKiller import SiblingKiller
from threading import Timer
import BaseHTTPServer
import os
import socket
import threading
import traceback
try:
from tornado_handler import MainHandler
USE_TORNADO = True
except:
USE_TORNADO = False
PORT = 34568
LIFESPAN = 180.0 # timeout for the self destruction timer - how much time
# passes between the last request and the server killing
# itself
PING_TIMEOUT = 5 # Nubmer of seconds to wait for ping response
class InternalServer(HTTPServer):
def kill_me(self):
self.shutdown()
def reset_selfdestruct_timer(self):
if self.self_destruct_timer:
self.self_destruct_timer.cancel()
self.self_destruct_timer = Timer(LIFESPAN, self.kill_me)
self.self_destruct_timer.start()
def __init__(self, server_address, RequestHandlerClass,
bind_and_activate=True):
HTTPServer.__init__(self, server_address, RequestHandlerClass,
bind_and_activate=bind_and_activate)
self.self_destruct_timer = None
self.clients = 1
self.reset_selfdestruct_timer()
def suicide(self):
self.clients -= 1
if self.clients == 0:
if self.self_destruct_timer is not None:
self.self_destruct_timer.cancel()
quick_and_painless_timer = Timer(0.1, self.kill_me)
quick_and_painless_timer.start()
class TestServer:
def __init__(self):
self.may_serve = False
pid = os.getpid()
print("Init server. Pid: {}".format(pid))
self.server = None
killer = SiblingKiller(PORT, PING_TIMEOUT)
killer.kill_siblings()
if killer.allow_serving():
try:
self.init_server()
print("Started server with pid: {}".format(pid))
self.may_serve = True
except socket.error:
print("Failed to start the server: Port is in use")
except Exception as e:
print(e)
print("Failed to start serving for unknown reason")
traceback.print_exc()
else:
print("Not allowed to start serving for process: {}".format(pid))
def init_server(self):
if USE_TORNADO:
MainHandler.init_server(PORT, LIFESPAN)
else:
print("""
*************
WARNING: Using the python's built-in BaseHTTPServer!
It is all right if you run the tests on your local machine, but if you are running tests on a server,
please consider installing Tornado. It is a much more powerful web-server. Otherwise you will find
that some of your downloader tests either fail or hang.
do
sudo pip install tornado
or go to http://www.tornadoweb.org/en/stable/ for more detail.
*************
""")
self.server = InternalServer(('localhost', PORT), PostHandler)
def start_serving(self):
if not self.may_serve:
return
if USE_TORNADO:
MainHandler.start_serving()
else:
thread = threading.Thread(target=self.server.serve_forever)
thread.deamon = True
thread.start()
class PostHandler(BaseHTTPRequestHandler, ResponseProviderMixin):
def dispatch_response(self, payload):
self.send_response(payload.response_code())
for h in payload.headers():
self.send_header(h, payload.headers()[h])
self.send_header("Content-Length", payload.length())
self.end_headers()
self.wfile.write(payload.message())
def init_vars(self):
self.response_provider = ResponseProvider(self)
def do_POST(self):
self.init_vars()
self.server.reset_selfdestruct_timer()
length = int(self.headers.getheader('content-length'))
self.dispatch_response(Payload(self.rfile.read(length)))
def do_GET(self):
headers = self.prepare_headers()
self.init_vars()
self.dispatch_response(self.response_provider.response_for_url_and_headers(self.path, headers))
def prepare_headers(self):
ret = dict()
for h in self.headers:
ret[h] = self.headers.get(h)
return ret
def got_pinged(self):
self.server.clients += 1
def kill(self):
self.server.suicide()
if __name__ == '__main__':
server = TestServer()
server.start_serving()
|