16
16
"""Implementation of Transport over http.
19
from bzrlib.transport import Transport, register_transport
20
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
21
TransportError, ConnectionError)
23
20
from cStringIO import StringIO
24
21
import urllib, urllib2
23
from warnings import warn
26
from bzrlib.transport import Transport, Server
27
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
28
TransportError, ConnectionError)
27
29
from bzrlib.errors import BzrError, BzrCheckError
28
30
from bzrlib.branch import Branch
29
31
from bzrlib.trace import mutter
32
from bzrlib.ui import ui_factory
32
35
def extract_auth(url, password_manager):
35
38
password manager. Return the url, minus those auth parameters (which
38
assert url.startswith('http://') or url.startswith('https://')
39
scheme, host = url.split('//', 1)
41
host, path = host.split('/', 1)
47
auth, host = host.split('@', 1)
41
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
42
assert (scheme == 'http') or (scheme == 'https')
45
auth, netloc = netloc.split('@', 1)
49
47
username, password = auth.split(':', 1)
51
49
username, password = auth, None
53
host, port = host.split(':', 1)
55
# FIXME: if password isn't given, should we ask for it?
51
host = netloc.split(':', 1)[0]
54
username = urllib.unquote(username)
56
55
if password is not None:
57
username = urllib.unquote(username)
58
56
password = urllib.unquote(password)
59
password_manager.add_password(None, host, username, password)
60
url = scheme + '//' + host + port + path
58
password = ui_factory.get_password(prompt='HTTP %(user)@%(host) password',
59
user=username, host=host)
60
password_manager.add_password(None, host, username, password)
61
url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
65
mutter("get_url %s" % url)
67
mutter("get_url %s", url)
66
68
manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
67
69
url = extract_auth(url, manager)
68
70
auth_handler = urllib2.HTTPBasicAuthHandler(manager)
69
71
opener = urllib2.build_opener(auth_handler)
70
url_f = opener.open(url)
73
request = urllib2.Request(url)
74
request.add_header('User-Agent', 'bzr/%s' % bzrlib.__version__)
75
response = opener.open(request)
73
78
class HttpTransport(Transport):
74
79
"""This is the transport agent for http:// access.
271
286
:return: A lock object, which should be passed to Transport.unlock()
273
288
raise TransportNotPossible('http does not support lock_write()')
291
#---------------- test server facilities ----------------
292
import BaseHTTPServer, SimpleHTTPServer, socket, time
296
class WebserverNotAvailable(Exception):
300
class BadWebserverPath(ValueError):
302
return 'path %s is not in %s' % self.args
305
class TestingHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
307
def log_message(self, format, *args):
308
self.server.test_case.log("webserver - %s - - [%s] %s",
309
self.address_string(),
310
self.log_date_time_string(),
313
def handle_one_request(self):
314
"""Handle a single HTTP request.
316
You normally don't need to override this method; see the class
317
__doc__ string for information on how to handle specific HTTP
318
commands such as GET and POST.
321
for i in xrange(1,11): # Don't try more than 10 times
323
self.raw_requestline = self.rfile.readline()
324
except socket.error, e:
325
if e.args[0] in (errno.EAGAIN, errno.EWOULDBLOCK):
326
# omitted for now because some tests look at the log of
327
# the server and expect to see no errors. see recent
328
# email thread. -- mbp 20051021.
329
## self.log_message('EAGAIN (%d) while reading from raw_requestline' % i)
335
if not self.raw_requestline:
336
self.close_connection = 1
338
if not self.parse_request(): # An error code has been sent, just exit
340
mname = 'do_' + self.command
341
if not hasattr(self, mname):
342
self.send_error(501, "Unsupported method (%r)" % self.command)
344
method = getattr(self, mname)
347
class TestingHTTPServer(BaseHTTPServer.HTTPServer):
348
def __init__(self, server_address, RequestHandlerClass, test_case):
349
BaseHTTPServer.HTTPServer.__init__(self, server_address,
351
self.test_case = test_case
354
class HttpServer(Server):
355
"""A test server for http transports."""
357
_HTTP_PORTS = range(13000, 0x8000)
359
def _http_start(self):
361
for port in self._HTTP_PORTS:
363
httpd = TestingHTTPServer(('localhost', port),
364
TestingHTTPRequestHandler,
366
except socket.error, e:
367
if e.args[0] == errno.EADDRINUSE:
369
print >>sys.stderr, "Cannot run webserver :-("
375
raise WebserverNotAvailable("Cannot run webserver :-( "
376
"no free ports in range %s..%s" %
377
(_HTTP_PORTS[0], _HTTP_PORTS[-1]))
379
self._http_base_url = 'http://localhost:%s/' % port
380
self._http_starting.release()
381
httpd.socket.settimeout(0.1)
383
while self._http_running:
385
httpd.handle_request()
386
except socket.timeout:
389
def _get_remote_url(self, path):
390
path_parts = path.split(os.path.sep)
391
if os.path.isabs(path):
392
if path_parts[:len(self._local_path_parts)] != \
393
self._local_path_parts:
394
raise BadWebserverPath(path, self.test_dir)
395
remote_path = '/'.join(path_parts[len(self._local_path_parts):])
397
remote_path = '/'.join(path_parts)
399
self._http_starting.acquire()
400
self._http_starting.release()
401
return self._http_base_url + remote_path
403
def log(self, *args, **kwargs):
404
"""Capture Server log output."""
405
self.logs.append(args[3])
408
"""See bzrlib.transport.Server.setUp."""
409
self._home_dir = os.getcwdu()
410
self._local_path_parts = self._home_dir.split(os.path.sep)
411
self._http_starting = threading.Lock()
412
self._http_starting.acquire()
413
self._http_running = True
414
self._http_base_url = None
415
self._http_thread = threading.Thread(target=self._http_start)
416
self._http_thread.setDaemon(True)
417
self._http_thread.start()
418
self._http_proxy = os.environ.get("http_proxy")
419
if self._http_proxy is not None:
420
del os.environ["http_proxy"]
424
"""See bzrlib.transport.Server.tearDown."""
425
self._http_running = False
426
self._http_thread.join()
427
if self._http_proxy is not None:
429
os.environ["http_proxy"] = self._http_proxy
432
"""See bzrlib.transport.Server.get_url."""
433
return self._get_remote_url(self._home_dir)
435
def get_bogus_url(self):
436
"""See bzrlib.transport.Server.get_bogus_url."""
437
return 'http://jasldkjsalkdjalksjdkljasd'
440
def get_test_permutations():
441
"""Return the permutations to be used in testing."""
442
warn("There are no HTTPS transport provider tests yet.")
443
return [(HttpTransport, HttpServer),