~bzr-pqm/bzr/bzr.dev

1185.11.19 by John Arbash Meinel
Testing put and append, also testing agaist file-like objects as well as strings.
1
# Copyright (C) 2005 Canonical Ltd
2
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.
7
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.
12
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
"""Implementation of Transport over http.
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
17
"""
18
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
19
import os, errno
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
20
from cStringIO import StringIO
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
21
import urllib, urllib2
1185.11.6 by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently.
22
import urlparse
1530.1.11 by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports.
23
from warnings import warn
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
24
1530.1.3 by Robert Collins
transport implementations now tested consistently.
25
from bzrlib.transport import Transport, Server
26
from bzrlib.errors import (TransportNotPossible, NoSuchFile, 
27
                           TransportError, ConnectionError)
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
28
from bzrlib.errors import BzrError, BzrCheckError
1393.2.3 by John Arbash Meinel
Fixing typos, updating stores, getting tests to pass.
29
from bzrlib.branch import Branch
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
30
from bzrlib.trace import mutter
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
31
32
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
33
def extract_auth(url, password_manager):
34
    """
35
    Extract auth parameters from am HTTP/HTTPS url and add them to the given
36
    password manager.  Return the url, minus those auth parameters (which
37
    confuse urllib2).
38
    """
39
    assert url.startswith('http://') or url.startswith('https://')
40
    scheme, host = url.split('//', 1)
41
    if '/' in host:
42
        host, path = host.split('/', 1)
43
        path = '/' + path
44
    else:
45
        path = ''
46
    port = ''
47
    if '@' in host:
48
        auth, host = host.split('@', 1)
49
        if ':' in auth:
50
            username, password = auth.split(':', 1)
51
        else:
52
            username, password = auth, None
53
        if ':' in host:
54
            host, port = host.split(':', 1)
55
            port = ':' + port
56
        # FIXME: if password isn't given, should we ask for it?
57
        if password is not None:
58
            username = urllib.unquote(username)
59
            password = urllib.unquote(password)
60
            password_manager.add_password(None, host, username, password)
61
    url = scheme + '//' + host + port + path
62
    return url
63
    
1185.11.14 by John Arbash Meinel
Working on getting tests to run. TestFetch only works if named runTest
64
def get_url(url):
65
    import urllib2
66
    mutter("get_url %s" % url)
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
67
    manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
68
    url = extract_auth(url, manager)
69
    auth_handler = urllib2.HTTPBasicAuthHandler(manager)
70
    opener = urllib2.build_opener(auth_handler)
71
    url_f = opener.open(url)
1185.11.14 by John Arbash Meinel
Working on getting tests to run. TestFetch only works if named runTest
72
    return url_f
73
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
74
class HttpTransport(Transport):
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
75
    """This is the transport agent for http:// access.
76
    
77
    TODO: Implement pipelined versions of all of the *_multi() functions.
78
    """
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
79
80
    def __init__(self, base):
81
        """Set the base path where files will be stored."""
82
        assert base.startswith('http://') or base.startswith('https://')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
83
        if base[-1] != '/':
84
            base = base + '/'
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
85
        super(HttpTransport, self).__init__(base)
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
86
        # In the future we might actually connect to the remote host
87
        # rather than using get_url
88
        # self._connection = None
1185.11.6 by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently.
89
        (self._proto, self._host,
90
            self._path, self._parameters,
91
            self._query, self._fragment) = urlparse.urlparse(self.base)
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
92
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
93
    def should_cache(self):
94
        """Return True if the data pulled across should be cached locally.
95
        """
96
        return True
97
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
98
    def clone(self, offset=None):
99
        """Return a new HttpTransport with root at self.base + offset
100
        For now HttpTransport does not actually connect, so just return
101
        a new HttpTransport object.
102
        """
103
        if offset is None:
104
            return HttpTransport(self.base)
105
        else:
106
            return HttpTransport(self.abspath(offset))
107
108
    def abspath(self, relpath):
109
        """Return the full url to the given relative path.
110
        This can be supplied with a string or a list
111
        """
1469 by Robert Collins
Change Transport.* to work with URL's.
112
        assert isinstance(relpath, basestring)
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
113
        if isinstance(relpath, basestring):
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
114
            relpath_parts = relpath.split('/')
115
        else:
116
            # TODO: Don't call this with an array - no magic interfaces
117
            relpath_parts = relpath[:]
118
        if len(relpath_parts) > 1:
119
            if relpath_parts[0] == '':
120
                raise ValueError("path %r within branch %r seems to be absolute"
121
                                 % (relpath, self._path))
122
            if relpath_parts[-1] == '':
123
                raise ValueError("path %r within branch %r seems to be a directory"
124
                                 % (relpath, self._path))
1185.11.6 by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently.
125
        basepath = self._path.split('/')
1185.11.8 by John Arbash Meinel
Fixed typo
126
        if len(basepath) > 0 and basepath[-1] == '':
1185.11.6 by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently.
127
            basepath = basepath[:-1]
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
128
        for p in relpath_parts:
1185.11.6 by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently.
129
            if p == '..':
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
130
                if len(basepath) == 0:
1185.11.7 by John Arbash Meinel
HttpTransport just returns root when parent is requested.
131
                    # In most filesystems, a request for the parent
132
                    # of root, just returns root.
133
                    continue
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
134
                basepath.pop()
135
            elif p == '.' or p == '':
1185.11.6 by John Arbash Meinel
Made HttpTransport handle a request for a parent directory differently.
136
                continue # No-op
137
            else:
138
                basepath.append(p)
139
        # Possibly, we could use urlparse.urljoin() here, but
140
        # I'm concerned about when it chooses to strip the last
141
        # portion of the path, and when it doesn't.
142
        path = '/'.join(basepath)
1185.11.9 by John Arbash Meinel
Most tests pass, some problems with unavailable socket recv
143
        return urlparse.urlunparse((self._proto,
144
                self._host, path, '', '', ''))
907.1.24 by John Arbash Meinel
Remote functionality work.
145
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
146
    def has(self, relpath):
907.1.35 by John Arbash Meinel
updating TODO for http_transport.
147
        """Does the target location exist?
148
149
        TODO: HttpTransport.has() should use a HEAD request,
150
        not a full GET request.
1185.11.15 by John Arbash Meinel
Got HttpTransport tests to pass. Check for EAGAIN, pass permit_failure around, etc
151
152
        TODO: This should be changed so that we don't use
153
        urllib2 and get an exception, the code path would be
154
        cleaner if we just do an http HEAD request, and parse
155
        the return code.
907.1.35 by John Arbash Meinel
updating TODO for http_transport.
156
        """
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
157
        path = relpath
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
158
        try:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
159
            path = self.abspath(relpath)
160
            f = get_url(path)
1185.11.15 by John Arbash Meinel
Got HttpTransport tests to pass. Check for EAGAIN, pass permit_failure around, etc
161
            # Without the read and then close()
162
            # we tend to have busy sockets.
163
            f.read()
164
            f.close()
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
165
            return True
1468 by Robert Collins
The HTTP transport would return NoSuchFile inappropriately.
166
        except urllib2.URLError, e:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
167
            mutter('url error code: %s for has url: %r', e.code, path)
1468 by Robert Collins
The HTTP transport would return NoSuchFile inappropriately.
168
            if e.code == 404:
169
                return False
170
            raise
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
171
        except IOError, e:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
172
            mutter('io error: %s %s for has url: %r', 
173
                e.errno, errno.errorcode.get(e.errno), path)
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
174
            if e.errno == errno.ENOENT:
175
                return False
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
176
            raise TransportError(orig_error=e)
907.1.55 by John Arbash Meinel
Adding pipelined http support.
177
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
178
    def get(self, relpath, decode=False):
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
179
        """Get the file at the given relative path.
180
181
        :param relpath: The relative path to the file
182
        """
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
183
        path = relpath
907.1.57 by John Arbash Meinel
Trying to get pipelined http library working + tests.
184
        try:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
185
            path = self.abspath(relpath)
186
            return get_url(path)
1185.25.4 by Aaron Bentley
Switched to specific error; not all URLErrors have a .code member
187
        except urllib2.HTTPError, e:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
188
            mutter('url error code: %s for has url: %r', e.code, path)
1468 by Robert Collins
The HTTP transport would return NoSuchFile inappropriately.
189
            if e.code == 404:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
190
                raise NoSuchFile(path, extra=e)
1468 by Robert Collins
The HTTP transport would return NoSuchFile inappropriately.
191
            raise
192
        except (BzrError, IOError), e:
1185.50.15 by John Arbash Meinel
Added some extra logging when we get http exceptions.
193
            if hasattr(e, 'errno'):
194
                mutter('io error: %s %s for has url: %r', 
195
                    e.errno, errno.errorcode.get(e.errno), path)
196
                if e.errno == errno.ENOENT:
197
                    raise NoSuchFile(path, extra=e)
1185.35.31 by Aaron Bentley
Throw ConnectionError instead of NoSuchFile except when we get a 404
198
            raise ConnectionError(msg = "Error retrieving %s: %s" 
1185.16.2 by Martin Pool
- try to get better reporting of http errors
199
                             % (self.abspath(relpath), str(e)),
1428 by Robert Collins
report the url location on failed http requests
200
                             orig_error=e)
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
201
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
202
    def put(self, relpath, f, mode=None):
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
203
        """Copy the file-like or string object into the location.
204
205
        :param relpath: Location to put the contents, relative to base.
206
        :param f:       File-like or string object.
207
        """
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
208
        raise TransportNotPossible('http PUT not supported')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
209
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
210
    def mkdir(self, relpath, mode=None):
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
211
        """Create a directory at the given path."""
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
212
        raise TransportNotPossible('http does not support mkdir()')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
213
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
214
    def append(self, relpath, f):
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
215
        """Append the text in the file-like object into the final
216
        location.
217
        """
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
218
        raise TransportNotPossible('http does not support append()')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
219
220
    def copy(self, rel_from, rel_to):
221
        """Copy the item at rel_from to the location at rel_to"""
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
222
        raise TransportNotPossible('http does not support copy()')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
223
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
224
    def copy_to(self, relpaths, other, mode=None, pb=None):
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
225
        """Copy a set of entries from self into another Transport.
226
227
        :param relpaths: A list/generator of entries to be copied.
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
228
229
        TODO: if other is LocalTransport, is it possible to
230
              do better than put(get())?
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
231
        """
907.1.29 by John Arbash Meinel
Fixing small bug in HttpTransport.copy_to
232
        # At this point HttpTransport might be able to check and see if
233
        # the remote location is the same, and rather than download, and
234
        # then upload, it could just issue a remote copy_this command.
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
235
        if isinstance(other, HttpTransport):
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
236
            raise TransportNotPossible('http cannot be the target of copy_to()')
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
237
        else:
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
238
            return super(HttpTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
907.1.28 by John Arbash Meinel
Added pb to function that were missing, implemented a basic double-dispatch copy_to function.
239
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
240
    def move(self, rel_from, rel_to):
241
        """Move the item at rel_from to the location at rel_to"""
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
242
        raise TransportNotPossible('http does not support move()')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
243
244
    def delete(self, relpath):
245
        """Delete the item at relpath"""
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
246
        raise TransportNotPossible('http does not support delete()')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
247
1530.1.3 by Robert Collins
transport implementations now tested consistently.
248
    def is_readonly(self):
249
        """See Transport.is_readonly."""
250
        return True
251
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
252
    def listable(self):
253
        """See Transport.listable."""
254
        return False
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
255
256
    def stat(self, relpath):
257
        """Return the stat information for a file.
258
        """
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
259
        raise TransportNotPossible('http does not support stat()')
907.1.21 by John Arbash Meinel
Adding http transport as a valid transport protocol.
260
907.1.24 by John Arbash Meinel
Remote functionality work.
261
    def lock_read(self, relpath):
262
        """Lock the given file for shared (read) access.
263
        :return: A lock object, which should be passed to Transport.unlock()
264
        """
265
        # The old RemoteBranch ignore lock for reading, so we will
266
        # continue that tradition and return a bogus lock object.
267
        class BogusLock(object):
268
            def __init__(self, path):
269
                self.path = path
270
            def unlock(self):
271
                pass
272
        return BogusLock(relpath)
273
274
    def lock_write(self, relpath):
275
        """Lock the given file for exclusive (write) access.
276
        WARNING: many transports do not support this, so trying avoid using it
277
278
        :return: A lock object, which should be passed to Transport.unlock()
279
        """
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
280
        raise TransportNotPossible('http does not support lock_write()')
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
281
282
1530.1.3 by Robert Collins
transport implementations now tested consistently.
283
#---------------- test server facilities ----------------
284
import BaseHTTPServer, SimpleHTTPServer, socket, time
285
import threading
286
287
288
class WebserverNotAvailable(Exception):
289
    pass
290
291
292
class BadWebserverPath(ValueError):
293
    def __str__(self):
294
        return 'path %s is not in %s' % self.args
295
296
297
class TestingHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
298
299
    def log_message(self, format, *args):
300
        self.server.test_case.log("webserver - %s - - [%s] %s",
301
                                  self.address_string(),
302
                                  self.log_date_time_string(),
303
                                  format%args)
304
305
    def handle_one_request(self):
306
        """Handle a single HTTP request.
307
308
        You normally don't need to override this method; see the class
309
        __doc__ string for information on how to handle specific HTTP
310
        commands such as GET and POST.
311
312
        """
313
        for i in xrange(1,11): # Don't try more than 10 times
314
            try:
315
                self.raw_requestline = self.rfile.readline()
316
            except socket.error, e:
317
                if e.args[0] in (errno.EAGAIN, errno.EWOULDBLOCK):
318
                    # omitted for now because some tests look at the log of
319
                    # the server and expect to see no errors.  see recent
320
                    # email thread. -- mbp 20051021. 
321
                    ## self.log_message('EAGAIN (%d) while reading from raw_requestline' % i)
322
                    time.sleep(0.01)
323
                    continue
324
                raise
325
            else:
326
                break
327
        if not self.raw_requestline:
328
            self.close_connection = 1
329
            return
330
        if not self.parse_request(): # An error code has been sent, just exit
331
            return
332
        mname = 'do_' + self.command
333
        if not hasattr(self, mname):
334
            self.send_error(501, "Unsupported method (%r)" % self.command)
335
            return
336
        method = getattr(self, mname)
337
        method()
338
339
class TestingHTTPServer(BaseHTTPServer.HTTPServer):
340
    def __init__(self, server_address, RequestHandlerClass, test_case):
341
        BaseHTTPServer.HTTPServer.__init__(self, server_address,
342
                                                RequestHandlerClass)
343
        self.test_case = test_case
344
345
346
class HttpServer(Server):
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
347
    """A test server for http transports."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
348
349
    _HTTP_PORTS = range(13000, 0x8000)
350
351
    def _http_start(self):
352
        httpd = None
353
        for port in self._HTTP_PORTS:
354
            try:
355
                httpd = TestingHTTPServer(('localhost', port),
356
                                          TestingHTTPRequestHandler,
357
                                          self)
358
            except socket.error, e:
359
                if e.args[0] == errno.EADDRINUSE:
360
                    continue
361
                print >>sys.stderr, "Cannot run webserver :-("
362
                raise
363
            else:
364
                break
365
366
        if httpd is None:
367
            raise WebserverNotAvailable("Cannot run webserver :-( "
368
                                        "no free ports in range %s..%s" %
369
                                        (_HTTP_PORTS[0], _HTTP_PORTS[-1]))
370
371
        self._http_base_url = 'http://localhost:%s/' % port
372
        self._http_starting.release()
373
        httpd.socket.settimeout(0.1)
374
375
        while self._http_running:
376
            try:
377
                httpd.handle_request()
378
            except socket.timeout:
379
                pass
380
381
    def _get_remote_url(self, path):
382
        path_parts = path.split(os.path.sep)
383
        if os.path.isabs(path):
384
            if path_parts[:len(self._local_path_parts)] != \
385
                   self._local_path_parts:
386
                raise BadWebserverPath(path, self.test_dir)
387
            remote_path = '/'.join(path_parts[len(self._local_path_parts):])
388
        else:
389
            remote_path = '/'.join(path_parts)
390
391
        self._http_starting.acquire()
392
        self._http_starting.release()
393
        return self._http_base_url + remote_path
394
395
    def log(self, *args, **kwargs):
396
        """Capture Server log output."""
397
398
    def setUp(self):
399
        """See bzrlib.transport.Server.setUp."""
400
        self._home_dir = os.getcwdu()
401
        self._local_path_parts = self._home_dir.split(os.path.sep)
402
        self._http_starting = threading.Lock()
403
        self._http_starting.acquire()
404
        self._http_running = True
405
        self._http_base_url = None
406
        self._http_thread = threading.Thread(target=self._http_start)
407
        self._http_thread.setDaemon(True)
408
        self._http_thread.start()
409
        self._http_proxy = os.environ.get("http_proxy")
410
        if self._http_proxy is not None:
411
            del os.environ["http_proxy"]
412
413
    def tearDown(self):
414
        """See bzrlib.transport.Server.tearDown."""
415
        self._http_running = False
416
        self._http_thread.join()
417
        if self._http_proxy is not None:
418
            import os
419
            os.environ["http_proxy"] = self._http_proxy
420
421
    def get_url(self):
422
        """See bzrlib.transport.Server.get_url."""
423
        return self._get_remote_url(self._home_dir)
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
424
        
425
    def get_bogus_url(self):
426
        """See bzrlib.transport.Server.get_bogus_url."""
427
        return 'http://jasldkjsalkdjalksjdkljasd'
428
1530.1.11 by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports.
429
430
def get_test_permutations():
431
    """Return the permutations to be used in testing."""
432
    warn("There are no HTTPS transport provider tests yet.")
433
    return [(HttpTransport, HttpServer),
434
            ]