17
17
from cStringIO import StringIO
20
from SimpleHTTPServer import SimpleHTTPRequestHandler
33
28
from bzrlib.smart import protocol
34
from bzrlib.tests import http_server
37
class HTTPServerWithSmarts(http_server.HttpServer):
29
from bzrlib.tests import TestCaseWithTransport
30
from bzrlib.tests.HttpServer import (
32
TestingHTTPRequestHandler,
34
from bzrlib.transport import (
39
class WallRequestHandler(TestingHTTPRequestHandler):
40
"""Whatever request comes in, close the connection"""
42
def handle_one_request(self):
43
"""Handle a single HTTP request, by abruptly closing the connection"""
44
self.close_connection = 1
47
class BadStatusRequestHandler(TestingHTTPRequestHandler):
48
"""Whatever request comes in, returns a bad status"""
50
def parse_request(self):
51
"""Fakes handling a single HTTP request, returns a bad status"""
52
ignored = TestingHTTPRequestHandler.parse_request(self)
54
self.send_response(0, "Bad status")
56
except socket.error, e:
57
# We don't want to pollute the test results with
58
# spurious server errors while test succeed. In our
59
# case, it may occur that the test has already read
60
# the 'Bad Status' and closed the socket while we are
61
# still trying to send some headers... So the test is
62
# ok, but if we raise the exception, the output is
63
# dirty. So we don't raise, but we close the
64
# connection, just to be safe :)
65
spurious = [errno.EPIPE,
69
if (len(e.args) > 0) and (e.args[0] in spurious):
70
self.close_connection = 1
77
class InvalidStatusRequestHandler(TestingHTTPRequestHandler):
78
"""Whatever request comes in, returns am invalid status"""
80
def parse_request(self):
81
"""Fakes handling a single HTTP request, returns a bad status"""
82
ignored = TestingHTTPRequestHandler.parse_request(self)
83
self.wfile.write("Invalid status line\r\n")
87
class BadProtocolRequestHandler(TestingHTTPRequestHandler):
88
"""Whatever request comes in, returns a bad protocol version"""
90
def parse_request(self):
91
"""Fakes handling a single HTTP request, returns a bad status"""
92
ignored = TestingHTTPRequestHandler.parse_request(self)
93
# Returns an invalid protocol version, but curl just
94
# ignores it and those cannot be tested.
95
self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
97
'Look at my protocol version'))
101
class ForbiddenRequestHandler(TestingHTTPRequestHandler):
102
"""Whatever request comes in, returns a 403 code"""
104
def parse_request(self):
105
"""Handle a single HTTP request, by replying we cannot handle it"""
106
ignored = TestingHTTPRequestHandler.parse_request(self)
111
class HTTPServerWithSmarts(HttpServer):
38
112
"""HTTPServerWithSmarts extends the HttpServer with POST methods that will
39
113
trigger a smart server to execute with a transport rooted at the rootdir of
43
def __init__(self, protocol_version=None):
44
http_server.HttpServer.__init__(self, SmartRequestHandler,
45
protocol_version=protocol_version)
48
class SmartRequestHandler(http_server.TestingHTTPRequestHandler):
118
HttpServer.__init__(self, SmartRequestHandler)
121
class SmartRequestHandler(TestingHTTPRequestHandler):
49
122
"""Extend TestingHTTPRequestHandler to support smart client POSTs."""
51
124
def do_POST(self):
52
125
"""Hand the request off to a smart server instance."""
53
126
self.send_response(200)
54
127
self.send_header("Content-type", "application/octet-stream")
55
t = transport.get_transport(self.server.test_case_server._home_dir)
128
transport = get_transport(self.server.test_case_server._home_dir)
56
129
# TODO: We might like to support streaming responses. 1.0 allows no
57
130
# Content-length in this case, so for integrity we should perform our
58
131
# own chunking within the stream.
77
150
self.wfile.write(out_buffer.getvalue())
80
class TestCaseWithWebserver(tests.TestCaseWithTransport):
153
class LimitedRangeRequestHandler(TestingHTTPRequestHandler):
154
"""Errors out when range specifiers exceed the limit"""
156
def get_multiple_ranges(self, file, file_size, ranges):
157
"""Refuses the multiple ranges request"""
158
tcs = self.server.test_case_server
159
if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
161
# Emulate apache behavior
162
self.send_error(400, "Bad Request")
164
return TestingHTTPRequestHandler.get_multiple_ranges(self, file,
168
class LimitedRangeHTTPServer(HttpServer):
169
"""An HttpServer erroring out on requests with too much range specifiers"""
171
def __init__(self, request_handler=LimitedRangeRequestHandler,
173
HttpServer.__init__(self, request_handler)
174
self.range_limit = range_limit
177
class SingleRangeRequestHandler(TestingHTTPRequestHandler):
178
"""Always reply to range request as if they were single.
180
Don't be explicit about it, just to annoy the clients.
183
def get_multiple_ranges(self, file, file_size, ranges):
184
"""Answer as if it was a single range request and ignores the rest"""
185
(start, end) = ranges[0]
186
return self.get_single_range(file, file_size, start, end)
189
class SingleOnlyRangeRequestHandler(TestingHTTPRequestHandler):
190
"""Only reply to simple range requests, errors out on multiple"""
192
def get_multiple_ranges(self, file, file_size, ranges):
193
"""Refuses the multiple ranges request"""
196
self.send_error(416, "Requested range not satisfiable")
198
(start, end) = ranges[0]
199
return self.get_single_range(file, file_size, start, end)
202
class NoRangeRequestHandler(TestingHTTPRequestHandler):
203
"""Ignore range requests without notice"""
206
# Update the statistics
207
self.server.test_case_server.GET_request_nb += 1
208
# Just bypass the range handling done by TestingHTTPRequestHandler
209
return SimpleHTTPRequestHandler.do_GET(self)
212
class TestCaseWithWebserver(TestCaseWithTransport):
81
213
"""A support class that provides readonly urls that are http://.
83
215
This is done by forcing the readonly server to be an http
116
248
return self.__secondary_server
119
class ProxyServer(http_server.HttpServer):
251
class ProxyServer(HttpServer):
120
252
"""A proxy test server for http transports."""
122
254
proxy_requests = True
125
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
257
class RedirectRequestHandler(TestingHTTPRequestHandler):
126
258
"""Redirect all request to the specified server"""
128
260
def parse_request(self):
129
261
"""Redirect a single HTTP request to another host"""
130
valid = http_server.TestingHTTPRequestHandler.parse_request(self)
262
valid = TestingHTTPRequestHandler.parse_request(self)
132
264
tcs = self.server.test_case_server
133
265
code, target = tcs.is_redirected(self.path)
148
class HTTPServerRedirecting(http_server.HttpServer):
278
class HTTPServerRedirecting(HttpServer):
149
279
"""An HttpServer redirecting to another server """
151
def __init__(self, request_handler=RedirectRequestHandler,
152
protocol_version=None):
153
http_server.HttpServer.__init__(self, request_handler,
154
protocol_version=protocol_version)
281
def __init__(self, request_handler=RedirectRequestHandler):
282
HttpServer.__init__(self, request_handler)
155
283
# redirections is a list of tuples (source, target, code)
156
284
# - source is a regexp for the paths requested
157
285
# - target is a replacement for re.sub describing where
410
532
class HTTPBasicAuthServer(HTTPAuthServer):
411
533
"""An HTTP server requiring basic authentication"""
413
def __init__(self, protocol_version=None):
414
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
415
protocol_version=protocol_version)
536
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
416
537
self.init_http_auth()
419
540
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
420
541
"""An HTTP server requiring digest authentication"""
422
def __init__(self, protocol_version=None):
423
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
424
protocol_version=protocol_version)
544
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
425
545
self.init_http_auth()
428
548
class ProxyBasicAuthServer(ProxyAuthServer):
429
549
"""A proxy server requiring basic authentication"""
431
def __init__(self, protocol_version=None):
432
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
433
protocol_version=protocol_version)
552
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
434
553
self.init_proxy_auth()
437
556
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
438
557
"""A proxy server requiring basic authentication"""
440
def __init__(self, protocol_version=None):
441
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
442
protocol_version=protocol_version)
560
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
443
561
self.init_proxy_auth()