17
17
from cStringIO import StringIO
20
from SimpleHTTPServer import SimpleHTTPRequestHandler
28
33
from bzrlib.smart import protocol
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):
34
from bzrlib.tests import http_server
37
class HTTPServerWithSmarts(http_server.HttpServer):
112
38
"""HTTPServerWithSmarts extends the HttpServer with POST methods that will
113
39
trigger a smart server to execute with a transport rooted at the rootdir of
118
HttpServer.__init__(self, SmartRequestHandler)
121
class SmartRequestHandler(TestingHTTPRequestHandler):
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):
122
49
"""Extend TestingHTTPRequestHandler to support smart client POSTs."""
124
51
def do_POST(self):
125
52
"""Hand the request off to a smart server instance."""
126
53
self.send_response(200)
127
54
self.send_header("Content-type", "application/octet-stream")
128
transport = get_transport(self.server.test_case_server._home_dir)
55
t = transport.get_transport(self.server.test_case_server._home_dir)
129
56
# TODO: We might like to support streaming responses. 1.0 allows no
130
57
# Content-length in this case, so for integrity we should perform our
131
58
# own chunking within the stream.
150
77
self.wfile.write(out_buffer.getvalue())
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):
80
class TestCaseWithWebserver(tests.TestCaseWithTransport):
213
81
"""A support class that provides readonly urls that are http://.
215
83
This is done by forcing the readonly server to be an http
248
116
return self.__secondary_server
251
class ProxyServer(HttpServer):
119
class ProxyServer(http_server.HttpServer):
252
120
"""A proxy test server for http transports."""
254
122
proxy_requests = True
257
class RedirectRequestHandler(TestingHTTPRequestHandler):
125
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
258
126
"""Redirect all request to the specified server"""
260
128
def parse_request(self):
261
129
"""Redirect a single HTTP request to another host"""
262
valid = TestingHTTPRequestHandler.parse_request(self)
130
valid = http_server.TestingHTTPRequestHandler.parse_request(self)
264
132
tcs = self.server.test_case_server
265
133
code, target = tcs.is_redirected(self.path)
278
class HTTPServerRedirecting(HttpServer):
148
class HTTPServerRedirecting(http_server.HttpServer):
279
149
"""An HttpServer redirecting to another server """
281
def __init__(self, request_handler=RedirectRequestHandler):
282
HttpServer.__init__(self, request_handler)
151
def __init__(self, request_handler=RedirectRequestHandler,
152
protocol_version=None):
153
http_server.HttpServer.__init__(self, request_handler,
154
protocol_version=protocol_version)
283
155
# redirections is a list of tuples (source, target, code)
284
156
# - source is a regexp for the paths requested
285
157
# - target is a replacement for re.sub describing where
532
410
class HTTPBasicAuthServer(HTTPAuthServer):
533
411
"""An HTTP server requiring basic authentication"""
536
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
413
def __init__(self, protocol_version=None):
414
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
415
protocol_version=protocol_version)
537
416
self.init_http_auth()
540
419
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
541
420
"""An HTTP server requiring digest authentication"""
544
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
422
def __init__(self, protocol_version=None):
423
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
424
protocol_version=protocol_version)
545
425
self.init_http_auth()
548
428
class ProxyBasicAuthServer(ProxyAuthServer):
549
429
"""A proxy server requiring basic authentication"""
552
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
431
def __init__(self, protocol_version=None):
432
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
433
protocol_version=protocol_version)
553
434
self.init_proxy_auth()
556
437
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
557
438
"""A proxy server requiring basic authentication"""
560
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
440
def __init__(self, protocol_version=None):
441
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
442
protocol_version=protocol_version)
561
443
self.init_proxy_auth()