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
tcs = self.server.test_case_server
169
tcs.GET_request_nb += 1
170
return TestingHTTPRequestHandler.do_GET(self)
173
class LimitedRangeHTTPServer(HttpServer):
174
"""An HttpServer erroring out on requests with too much range specifiers"""
176
def __init__(self, request_handler=LimitedRangeRequestHandler,
178
HttpServer.__init__(self, request_handler)
179
self.range_limit = range_limit
180
self.GET_request_nb = 0
183
class SingleRangeRequestHandler(TestingHTTPRequestHandler):
184
"""Always reply to range request as if they were single.
186
Don't be explicit about it, just to annoy the clients.
189
def get_multiple_ranges(self, file, file_size, ranges):
190
"""Answer as if it was a single range request and ignores the rest"""
191
(start, end) = ranges[0]
192
return self.get_single_range(file, file_size, start, end)
195
class SingleOnlyRangeRequestHandler(TestingHTTPRequestHandler):
196
"""Only reply to simple range requests, errors out on multiple"""
198
def get_multiple_ranges(self, file, file_size, ranges):
199
"""Refuses the multiple ranges request"""
202
self.send_error(416, "Requested range not satisfiable")
204
(start, end) = ranges[0]
205
return self.get_single_range(file, file_size, start, end)
208
class NoRangeRequestHandler(TestingHTTPRequestHandler):
209
"""Ignore range requests without notice"""
211
# Just bypass the range handling done by TestingHTTPRequestHandler
212
do_GET = SimpleHTTPRequestHandler.do_GET
215
class TestCaseWithWebserver(TestCaseWithTransport):
81
216
"""A support class that provides readonly urls that are http://.
83
218
This is done by forcing the readonly server to be an http
116
251
return self.__secondary_server
119
class ProxyServer(http_server.HttpServer):
254
class ProxyServer(HttpServer):
120
255
"""A proxy test server for http transports."""
122
257
proxy_requests = True
125
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
260
class RedirectRequestHandler(TestingHTTPRequestHandler):
126
261
"""Redirect all request to the specified server"""
128
263
def parse_request(self):
129
264
"""Redirect a single HTTP request to another host"""
130
valid = http_server.TestingHTTPRequestHandler.parse_request(self)
265
valid = TestingHTTPRequestHandler.parse_request(self)
132
267
tcs = self.server.test_case_server
133
268
code, target = tcs.is_redirected(self.path)
148
class HTTPServerRedirecting(http_server.HttpServer):
281
class HTTPServerRedirecting(HttpServer):
149
282
"""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)
284
def __init__(self, request_handler=RedirectRequestHandler):
285
HttpServer.__init__(self, request_handler)
155
286
# redirections is a list of tuples (source, target, code)
156
287
# - source is a regexp for the paths requested
157
288
# - target is a replacement for re.sub describing where
410
535
class HTTPBasicAuthServer(HTTPAuthServer):
411
536
"""An HTTP server requiring basic authentication"""
413
def __init__(self, protocol_version=None):
414
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
415
protocol_version=protocol_version)
539
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
416
540
self.init_http_auth()
419
543
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
420
544
"""An HTTP server requiring digest authentication"""
422
def __init__(self, protocol_version=None):
423
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
424
protocol_version=protocol_version)
547
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
425
548
self.init_http_auth()
428
551
class ProxyBasicAuthServer(ProxyAuthServer):
429
552
"""A proxy server requiring basic authentication"""
431
def __init__(self, protocol_version=None):
432
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
433
protocol_version=protocol_version)
555
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
434
556
self.init_proxy_auth()
437
559
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
438
560
"""A proxy server requiring basic authentication"""
440
def __init__(self, protocol_version=None):
441
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
442
protocol_version=protocol_version)
563
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
443
564
self.init_proxy_auth()