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 SingleRangeRequestHandler(TestingHTTPRequestHandler):
154
"""Always reply to range request as if they were single.
156
Don't be explicit about it, just to annoy the clients.
159
def get_multiple_ranges(self, file, file_size, ranges):
160
"""Answer as if it was a single range request and ignores the rest"""
161
(start, end) = ranges[0]
162
return self.get_single_range(file, file_size, start, end)
165
class SingleOnlyRangeRequestHandler(TestingHTTPRequestHandler):
166
"""Only reply to simple range requests, errors out on multiple"""
168
def get_multiple_ranges(self, file, file_size, ranges):
169
"""Refuses the multiple ranges request"""
172
self.send_error(416, "Requested range not satisfiable")
174
(start, end) = ranges[0]
175
return self.get_single_range(file, file_size, start, end)
178
class NoRangeRequestHandler(TestingHTTPRequestHandler):
179
"""Ignore range requests without notice"""
181
# Just bypass the range handling done by TestingHTTPRequestHandler
182
do_GET = SimpleHTTPRequestHandler.do_GET
185
class TestCaseWithWebserver(TestCaseWithTransport):
80
class TestCaseWithWebserver(tests.TestCaseWithTransport):
186
81
"""A support class that provides readonly urls that are http://.
188
83
This is done by forcing the readonly server to be an http
221
116
return self.__secondary_server
224
class ProxyServer(HttpServer):
119
class ProxyServer(http_server.HttpServer):
225
120
"""A proxy test server for http transports."""
227
122
proxy_requests = True
230
class RedirectRequestHandler(TestingHTTPRequestHandler):
125
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
231
126
"""Redirect all request to the specified server"""
233
128
def parse_request(self):
234
129
"""Redirect a single HTTP request to another host"""
235
valid = TestingHTTPRequestHandler.parse_request(self)
130
valid = http_server.TestingHTTPRequestHandler.parse_request(self)
237
132
tcs = self.server.test_case_server
238
133
code, target = tcs.is_redirected(self.path)
251
class HTTPServerRedirecting(HttpServer):
148
class HTTPServerRedirecting(http_server.HttpServer):
252
149
"""An HttpServer redirecting to another server """
254
def __init__(self, request_handler=RedirectRequestHandler):
255
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)
256
155
# redirections is a list of tuples (source, target, code)
257
156
# - source is a regexp for the paths requested
258
157
# - target is a replacement for re.sub describing where
393
292
def send_header_auth_reqed(self):
394
293
tcs = self.server.test_case_server
395
294
header = 'Digest realm="%s", ' % tcs.auth_realm
396
header += 'nonce="%s", algorithm=%s, qop=auth' % (tcs.auth_nonce, 'MD5')
295
header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
397
297
self.send_header(tcs.auth_header_sent,header)
400
class AuthServer(HttpServer):
300
class AuthServer(http_server.HttpServer):
401
301
"""Extends HttpServer with a dictionary of passwords.
403
303
This is used as a base class for various schemes which should
506
410
class HTTPBasicAuthServer(HTTPAuthServer):
507
411
"""An HTTP server requiring basic authentication"""
510
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
413
def __init__(self, protocol_version=None):
414
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
415
protocol_version=protocol_version)
511
416
self.init_http_auth()
514
419
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
515
420
"""An HTTP server requiring digest authentication"""
518
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
422
def __init__(self, protocol_version=None):
423
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
424
protocol_version=protocol_version)
519
425
self.init_http_auth()
522
428
class ProxyBasicAuthServer(ProxyAuthServer):
523
429
"""A proxy server requiring basic authentication"""
526
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
431
def __init__(self, protocol_version=None):
432
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
433
protocol_version=protocol_version)
527
434
self.init_proxy_auth()
530
437
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
531
438
"""A proxy server requiring basic authentication"""
534
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
440
def __init__(self, protocol_version=None):
441
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
442
protocol_version=protocol_version)
535
443
self.init_proxy_auth()