bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 2052.3.2
by John Arbash Meinel Change Copyright .. by Canonical to Copyright ... Canonical | 1 | # Copyright (C) 2005 Canonical Ltd
 | 
| 1887.1.1
by Adeodato Simó Do not separate paragraphs in the copyright statement with blank lines, | 2 | #
 | 
| 1185.1.18
by Robert Collins Lalo Martins remotebranch patch | 3 | # This program is free software; you can redistribute it and/or modify
 | 
| 4 | # it under the terms of the GNU General Public License as published by
 | |
| 5 | # the Free Software Foundation; either version 2 of the License, or
 | |
| 6 | # (at your option) any later version.
 | |
| 1887.1.1
by Adeodato Simó Do not separate paragraphs in the copyright statement with blank lines, | 7 | #
 | 
| 1185.1.18
by Robert Collins Lalo Martins remotebranch patch | 8 | # This program is distributed in the hope that it will be useful,
 | 
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| 11 | # GNU General Public License for more details.
 | |
| 1887.1.1
by Adeodato Simó Do not separate paragraphs in the copyright statement with blank lines, | 12 | #
 | 
| 1185.1.18
by Robert Collins Lalo Martins remotebranch patch | 13 | # You should have received a copy of the GNU General Public License
 | 
| 14 | # along with this program; if not, write to the Free Software
 | |
| 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| 16 | ||
| 2004.1.28
by v.ladeuil+lp at free Merge bzr.dev. Including http modifications by "smart" related code | 17 | from cStringIO import StringIO | 
| 2004.1.25
by v.ladeuil+lp at free Shuffle http related test code. Hopefully it ends up at the right place :) | 18 | import errno | 
| 2004.1.29
by v.ladeuil+lp at free New tests for http range requests handling. | 19 | from SimpleHTTPServer import SimpleHTTPRequestHandler | 
| 2164.2.29
by Vincent Ladeuil Test the http redirection at the request level even if it's not | 20 | import re | 
| 2004.1.25
by v.ladeuil+lp at free Shuffle http related test code. Hopefully it ends up at the right place :) | 21 | import socket | 
| 2213.1.1
by v.ladeuil+lp at free Workaround SimpleHTTPRequestHandler.translate_path limitation in | 22 | import urlparse | 
| 1530.1.14
by Robert Collins Remove duplicate web server from HTTPTestUtil. | 23 | |
| 1534.4.50
by Robert Collins Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running. | 24 | from bzrlib.tests import TestCaseWithTransport | 
| 2004.1.25
by v.ladeuil+lp at free Shuffle http related test code. Hopefully it ends up at the right place :) | 25 | from bzrlib.tests.HttpServer import ( | 
| 26 | HttpServer, | |
| 27 | TestingHTTPRequestHandler, | |
| 28 |     )
 | |
| 2004.1.28
by v.ladeuil+lp at free Merge bzr.dev. Including http modifications by "smart" related code | 29 | from bzrlib.transport import ( | 
| 30 | get_transport, | |
| 31 |     )
 | |
| 2400.1.3
by Andrew Bennetts Split smart transport code into several separate modules. | 32 | from bzrlib.smart import protocol | 
| 2004.1.25
by v.ladeuil+lp at free Shuffle http related test code. Hopefully it ends up at the right place :) | 33 | |
| 34 | ||
| 35 | class WallRequestHandler(TestingHTTPRequestHandler): | |
| 36 | """Whatever request comes in, close the connection""" | |
| 37 | ||
| 38 | def handle_one_request(self): | |
| 39 | """Handle a single HTTP request, by abruptly closing the connection""" | |
| 40 | self.close_connection = 1 | |
| 41 | ||
| 42 | ||
| 43 | class BadStatusRequestHandler(TestingHTTPRequestHandler): | |
| 44 | """Whatever request comes in, returns a bad status""" | |
| 45 | ||
| 46 | def parse_request(self): | |
| 47 | """Fakes handling a single HTTP request, returns a bad status""" | |
| 48 | ignored = TestingHTTPRequestHandler.parse_request(self) | |
| 49 | try: | |
| 50 | self.send_response(0, "Bad status") | |
| 51 | self.end_headers() | |
| 52 | except socket.error, e: | |
| 2158.2.1
by v.ladeuil+lp at free Windows tests cleanup. | 53 |             # We don't want to pollute the test results with
 | 
| 54 |             # spurious server errors while test succeed. In our
 | |
| 2188.1.1
by Aaron Bentley Windows tests cleanup. (Vincent Ladeuil) | 55 |             # case, it may occur that the test has already read
 | 
| 2158.2.1
by v.ladeuil+lp at free Windows tests cleanup. | 56 |             # the 'Bad Status' and closed the socket while we are
 | 
| 57 |             # still trying to send some headers... So the test is
 | |
| 2188.1.1
by Aaron Bentley Windows tests cleanup. (Vincent Ladeuil) | 58 |             # ok, but if we raise the exception, the output is
 | 
| 2158.2.1
by v.ladeuil+lp at free Windows tests cleanup. | 59 |             # dirty. So we don't raise, but we close the
 | 
| 60 |             # connection, just to be safe :)
 | |
| 61 | spurious = [errno.EPIPE, | |
| 62 | errno.ECONNRESET, | |
| 63 | errno.ECONNABORTED, | |
| 64 |                         ]
 | |
| 65 | if (len(e.args) > 0) and (e.args[0] in spurious): | |
| 2004.1.25
by v.ladeuil+lp at free Shuffle http related test code. Hopefully it ends up at the right place :) | 66 | self.close_connection = 1 | 
| 67 |                 pass
 | |
| 68 | else: | |
| 69 |                 raise
 | |
| 70 | return False | |
| 71 | ||
| 72 | ||
| 73 | class InvalidStatusRequestHandler(TestingHTTPRequestHandler): | |
| 74 | """Whatever request comes in, returns am invalid status""" | |
| 75 | ||
| 76 | def parse_request(self): | |
| 77 | """Fakes handling a single HTTP request, returns a bad status""" | |
| 78 | ignored = TestingHTTPRequestHandler.parse_request(self) | |
| 79 | self.wfile.write("Invalid status line\r\n") | |
| 80 | return False | |
| 81 | ||
| 82 | ||
| 83 | class BadProtocolRequestHandler(TestingHTTPRequestHandler): | |
| 84 | """Whatever request comes in, returns a bad protocol version""" | |
| 85 | ||
| 86 | def parse_request(self): | |
| 87 | """Fakes handling a single HTTP request, returns a bad status""" | |
| 88 | ignored = TestingHTTPRequestHandler.parse_request(self) | |
| 89 |         # Returns an invalid protocol version, but curl just
 | |
| 90 |         # ignores it and those cannot be tested.
 | |
| 91 | self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0', | |
| 92 | 404, | |
| 93 | 'Look at my protocol version')) | |
| 94 | return False | |
| 1534.4.50
by Robert Collins Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running. | 95 | |
| 96 | ||
| 2004.1.27
by v.ladeuil+lp at free Fix bug #57644 by issuing an explicit error message. | 97 | class ForbiddenRequestHandler(TestingHTTPRequestHandler): | 
| 98 | """Whatever request comes in, returns a 403 code""" | |
| 99 | ||
| 100 | def parse_request(self): | |
| 101 | """Handle a single HTTP request, by replying we cannot handle it""" | |
| 102 | ignored = TestingHTTPRequestHandler.parse_request(self) | |
| 103 | self.send_error(403) | |
| 104 | return False | |
| 105 | ||
| 106 | ||
| 2004.1.28
by v.ladeuil+lp at free Merge bzr.dev. Including http modifications by "smart" related code | 107 | class HTTPServerWithSmarts(HttpServer): | 
| 108 | """HTTPServerWithSmarts extends the HttpServer with POST methods that will | |
| 109 |     trigger a smart server to execute with a transport rooted at the rootdir of
 | |
| 110 |     the HTTP server.
 | |
| 111 |     """
 | |
| 112 | ||
| 113 | def __init__(self): | |
| 114 | HttpServer.__init__(self, SmartRequestHandler) | |
| 115 | ||
| 116 | ||
| 117 | class SmartRequestHandler(TestingHTTPRequestHandler): | |
| 118 | """Extend TestingHTTPRequestHandler to support smart client POSTs.""" | |
| 119 | ||
| 120 | def do_POST(self): | |
| 121 | """Hand the request off to a smart server instance.""" | |
| 122 | self.send_response(200) | |
| 123 | self.send_header("Content-type", "application/octet-stream") | |
| 2164.2.28
by Vincent Ladeuil TestingHTTPServer.test_case_server renamed from test_case to avoid confusions. | 124 | transport = get_transport(self.server.test_case_server._home_dir) | 
| 2004.1.28
by v.ladeuil+lp at free Merge bzr.dev. Including http modifications by "smart" related code | 125 |         # TODO: We might like to support streaming responses.  1.0 allows no
 | 
| 126 |         # Content-length in this case, so for integrity we should perform our
 | |
| 127 |         # own chunking within the stream.
 | |
| 128 |         # 1.1 allows chunked responses, and in this case we could chunk using
 | |
| 129 |         # the HTTP chunking as this will allow HTTP persistence safely, even if
 | |
| 130 |         # we have to stop early due to error, but we would also have to use the
 | |
| 131 |         # HTTP trailer facility which may not be widely available.
 | |
| 132 | out_buffer = StringIO() | |
| 2400.1.3
by Andrew Bennetts Split smart transport code into several separate modules. | 133 | smart_protocol_request = protocol.SmartServerRequestProtocolOne( | 
| 2004.1.28
by v.ladeuil+lp at free Merge bzr.dev. Including http modifications by "smart" related code | 134 | transport, out_buffer.write) | 
| 135 |         # if this fails, we should return 400 bad request, but failure is
 | |
| 136 |         # failure for now - RBC 20060919
 | |
| 137 | data_length = int(self.headers['Content-Length']) | |
| 138 |         # Perhaps there should be a SmartServerHTTPMedium that takes care of
 | |
| 139 |         # feeding the bytes in the http request to the smart_protocol_request,
 | |
| 140 |         # but for now it's simpler to just feed the bytes directly.
 | |
| 141 | smart_protocol_request.accept_bytes(self.rfile.read(data_length)) | |
| 142 | assert smart_protocol_request.next_read_size() == 0, ( | |
| 143 | "not finished reading, but all data sent to protocol.") | |
| 144 | self.send_header("Content-Length", str(len(out_buffer.getvalue()))) | |
| 145 | self.end_headers() | |
| 146 | self.wfile.write(out_buffer.getvalue()) | |
| 147 | ||
| 148 | ||
| 2004.1.29
by v.ladeuil+lp at free New tests for http range requests handling. | 149 | class SingleRangeRequestHandler(TestingHTTPRequestHandler): | 
| 150 | """Always reply to range request as if they were single. | |
| 151 | ||
| 152 |     Don't be explicit about it, just to annoy the clients.
 | |
| 153 |     """
 | |
| 154 | ||
| 155 | def get_multiple_ranges(self, file, file_size, ranges): | |
| 156 | """Answer as if it was a single range request and ignores the rest""" | |
| 157 | (start, end) = ranges[0] | |
| 158 | return self.get_single_range(file, file_size, start, end) | |
| 159 | ||
| 160 | ||
| 161 | class NoRangeRequestHandler(TestingHTTPRequestHandler): | |
| 162 | """Ignore range requests without notice""" | |
| 163 | ||
| 164 |     # Just bypass the range handling done by TestingHTTPRequestHandler
 | |
| 165 | do_GET = SimpleHTTPRequestHandler.do_GET | |
| 166 | ||
| 167 | ||
| 1534.4.50
by Robert Collins Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running. | 168 | class TestCaseWithWebserver(TestCaseWithTransport): | 
| 169 | """A support class that provides readonly urls that are http://. | |
| 170 | ||
| 2004.3.3
by vila Better (but still incomplete) design for bogus servers. | 171 |     This is done by forcing the readonly server to be an http
 | 
| 172 |     one. This will currently fail if the primary transport is not
 | |
| 173 |     backed by regular disk files.
 | |
| 1185.1.18
by Robert Collins Lalo Martins remotebranch patch | 174 |     """
 | 
| 175 | def setUp(self): | |
| 1530.1.14
by Robert Collins Remove duplicate web server from HTTPTestUtil. | 176 | super(TestCaseWithWebserver, self).setUp() | 
| 2004.1.25
by v.ladeuil+lp at free Shuffle http related test code. Hopefully it ends up at the right place :) | 177 | self.transport_readonly_server = HttpServer | 
| 2167.3.5
by v.ladeuil+lp at free Tests for proxies, covering #74759. | 178 | |
| 179 | ||
| 180 | class TestCaseWithTwoWebservers(TestCaseWithWebserver): | |
| 2164.2.13
by v.ladeuil+lp at free Add tests for redirection. Preserve transport decorations. | 181 | """A support class providing readonly urls on two servers that are http://. | 
| 2167.3.5
by v.ladeuil+lp at free Tests for proxies, covering #74759. | 182 | |
| 2164.2.25
by Vincent Ladeuil Fix typos noticed by Aaron. | 183 |     We set up two webservers to allows various tests involving
 | 
| 2167.3.5
by v.ladeuil+lp at free Tests for proxies, covering #74759. | 184 |     proxies or redirections from one server to the other.
 | 
| 185 |     """
 | |
| 186 | def setUp(self): | |
| 187 | super(TestCaseWithTwoWebservers, self).setUp() | |
| 188 | self.transport_secondary_server = HttpServer | |
| 189 | self.__secondary_server = None | |
| 190 | ||
| 191 | def create_transport_secondary_server(self): | |
| 192 | """Create a transport server from class defined at init. | |
| 193 | ||
| 194 |         This is mostly a hook for daughter classes.
 | |
| 195 |         """
 | |
| 196 | return self.transport_secondary_server() | |
| 197 | ||
| 198 | def get_secondary_server(self): | |
| 199 | """Get the server instance for the secondary transport.""" | |
| 200 | if self.__secondary_server is None: | |
| 201 | self.__secondary_server = self.create_transport_secondary_server() | |
| 202 | self.__secondary_server.setUp() | |
| 203 | self.addCleanup(self.__secondary_server.tearDown) | |
| 204 | return self.__secondary_server | |
| 205 | ||
| 206 | ||
| 207 | class FakeProxyRequestHandler(TestingHTTPRequestHandler): | |
| 208 | """Append a '-proxied' suffix to file served""" | |
| 209 | ||
| 210 | def translate_path(self, path): | |
| 2213.1.1
by v.ladeuil+lp at free Workaround SimpleHTTPRequestHandler.translate_path limitation in | 211 |         # We need to act as a proxy and accept absolute urls,
 | 
| 212 |         # which SimpleHTTPRequestHandler (grand parent) is not
 | |
| 213 |         # ready for. So we just drop the protocol://host:port
 | |
| 214 |         # part in front of the request-url (because we know we
 | |
| 215 |         # would not forward the request to *another* proxy).
 | |
| 216 | ||
| 217 |         # So we do what SimpleHTTPRequestHandler.translate_path
 | |
| 218 |         # do beginning with python 2.4.3: abandon query
 | |
| 219 |         # parameters, scheme, host port, etc (which ensure we
 | |
| 220 |         # provide the right behaviour on all python versions).
 | |
| 221 | path = urlparse.urlparse(path)[2] | |
| 222 |         # And now, we can apply *our* trick to proxy files
 | |
| 223 | self.path += '-proxied' | |
| 224 |         # An finally we leave our mother class do whatever it
 | |
| 225 |         # wants with the path
 | |
| 226 | return TestingHTTPRequestHandler.translate_path(self, path) | |
| 227 | ||
| 2167.3.5
by v.ladeuil+lp at free Tests for proxies, covering #74759. | 228 | |
| 2164.2.13
by v.ladeuil+lp at free Add tests for redirection. Preserve transport decorations. | 229 | class RedirectRequestHandler(TestingHTTPRequestHandler): | 
| 230 | """Redirect all request to the specified server""" | |
| 231 | ||
| 232 | def parse_request(self): | |
| 233 | """Redirect a single HTTP request to another host""" | |
| 234 | valid = TestingHTTPRequestHandler.parse_request(self) | |
| 235 | if valid: | |
| 2164.2.29
by Vincent Ladeuil Test the http redirection at the request level even if it's not | 236 | tcs = self.server.test_case_server | 
| 237 | code, target = tcs.is_redirected(self.path) | |
| 238 | if code is not None and target is not None: | |
| 239 |                 # Redirect as instructed
 | |
| 240 | self.send_response(code) | |
| 2164.2.16
by Vincent Ladeuil Add tests. | 241 | self.send_header('Location', target) | 
| 242 | self.end_headers() | |
| 243 | return False # The job is done | |
| 2164.2.29
by Vincent Ladeuil Test the http redirection at the request level even if it's not | 244 | else: | 
| 245 |                 # We leave the parent class serve the request
 | |
| 246 |                 pass
 | |
| 2164.2.13
by v.ladeuil+lp at free Add tests for redirection. Preserve transport decorations. | 247 | return valid | 
| 248 | ||
| 249 | ||
| 250 | class HTTPServerRedirecting(HttpServer): | |
| 251 | """An HttpServer redirecting to another server """ | |
| 252 | ||
| 2164.2.16
by Vincent Ladeuil Add tests. | 253 | def __init__(self, request_handler=RedirectRequestHandler): | 
| 2164.2.13
by v.ladeuil+lp at free Add tests for redirection. Preserve transport decorations. | 254 | HttpServer.__init__(self, request_handler) | 
| 2164.2.29
by Vincent Ladeuil Test the http redirection at the request level even if it's not | 255 |         # redirections is a list of tuples (source, target, code)
 | 
| 256 |         # - source is a regexp for the paths requested
 | |
| 257 |         # - target is a replacement for re.sub describing where
 | |
| 258 |         #   the request will be redirected
 | |
| 259 |         # - code is the http error code associated to the
 | |
| 260 |         #   redirection (301 permanent, 302 temporarry, etc
 | |
| 261 | self.redirections = [] | |
| 262 | ||
| 263 | def redirect_to(self, host, port): | |
| 264 | """Redirect all requests to a specific host:port""" | |
| 265 | self.redirections = [('(.*)', | |
| 266 | r'http://%s:%s\1' % (host, port) , | |
| 267 | 301)] | |
| 268 | ||
| 269 | def is_redirected(self, path): | |
| 270 | """Is the path redirected by this server. | |
| 271 | ||
| 272 |         :param path: the requested relative path
 | |
| 273 | ||
| 274 |         :returns: a tuple (code, target) if a matching
 | |
| 275 |              redirection is found, (None, None) otherwise.
 | |
| 276 |         """
 | |
| 277 | code = None | |
| 278 | target = None | |
| 279 | for (rsource, rtarget, rcode) in self.redirections: | |
| 280 | target, match = re.subn(rsource, rtarget, path) | |
| 281 | if match: | |
| 282 | code = rcode | |
| 283 | break # The first match wins | |
| 284 | else: | |
| 285 | target = None | |
| 286 | return code, target | |
| 2164.2.13
by v.ladeuil+lp at free Add tests for redirection. Preserve transport decorations. | 287 | |
| 2167.3.5
by v.ladeuil+lp at free Tests for proxies, covering #74759. | 288 | |
| 2164.2.22
by Vincent Ladeuil Take Aaron's review comments into account. | 289 | class TestCaseWithRedirectedWebserver(TestCaseWithTwoWebservers): | 
| 290 | """A support class providing redirections from one server to another. | |
| 291 | ||
| 2164.2.25
by Vincent Ladeuil Fix typos noticed by Aaron. | 292 |    We set up two webservers to allows various tests involving
 | 
| 2164.2.22
by Vincent Ladeuil Take Aaron's review comments into account. | 293 |    redirections.
 | 
| 294 |    The 'old' server is redirected to the 'new' server.
 | |
| 295 |    """
 | |
| 296 | ||
| 297 | def create_transport_secondary_server(self): | |
| 298 | """Create the secondary server redirecting to the primary server""" | |
| 299 | new = self.get_readonly_server() | |
| 300 | redirecting = HTTPServerRedirecting() | |
| 301 | redirecting.redirect_to(new.host, new.port) | |
| 302 | return redirecting | |
| 303 | ||
| 304 | def setUp(self): | |
| 305 | super(TestCaseWithRedirectedWebserver, self).setUp() | |
| 306 |        # The redirections will point to the new server
 | |
| 307 | self.new_server = self.get_readonly_server() | |
| 308 |        # The requests to the old server will be redirected
 | |
| 309 | self.old_server = self.get_secondary_server() | |
| 310 | ||
| 311 |