39
39
TransportError, ConnectionError, InvalidURL)
40
40
from bzrlib.branch import Branch
41
41
from bzrlib.trace import mutter
42
from bzrlib.transport import Transport, register_transport, Server
42
from bzrlib.transport import (
43
49
from bzrlib.transport.http.response import (HttpMultipartRangeResponse,
45
51
from bzrlib.ui import ui_factory
122
class HttpTransportBase(Transport):
128
class HttpTransportBase(Transport, smart.SmartClientMedium):
123
129
"""Base class for http implementations.
125
131
Does URL parsing, etc, but not any network IO.
239
245
raise NotImplementedError(self._get)
247
def get_request(self):
248
return SmartClientHTTPMediumRequest(self)
250
def get_smart_medium(self):
251
"""See Transport.get_smart_medium.
253
HttpTransportBase directly implements the minimal interface of
254
SmartMediumClient, so this returns self.
241
258
def readv(self, relpath, offsets):
242
259
"""Get parts of the file at the given relative path.
304
def _post(self, body_bytes):
305
"""POST body_bytes to .bzr/smart on this transport.
307
:returns: (response code, response body file-like object).
309
# TODO: Requiring all the body_bytes to be available at the beginning of
310
# the POST may require large client buffers. It would be nice to have
311
# an interface that allows streaming via POST when possible (and
312
# degrades to a local buffer when not).
313
raise NotImplementedError(self._post)
287
315
def put_file(self, relpath, f, mode=None):
288
316
"""Copy the file-like object into the location.
399
427
return ','.join(strings)
429
def send_http_smart_request(self, bytes):
430
code, body_filelike = self._post(bytes)
431
assert code == 200, 'unexpected HTTP response code %r' % (code,)
435
class SmartClientHTTPMediumRequest(smart.SmartClientMediumRequest):
436
"""A SmartClientMediumRequest that works with an HTTP medium."""
438
def __init__(self, medium):
439
smart.SmartClientMediumRequest.__init__(self, medium)
442
def _accept_bytes(self, bytes):
443
self._buffer += bytes
445
def _finished_writing(self):
446
data = self._medium.send_http_smart_request(self._buffer)
447
self._response_body = data
449
def _read_bytes(self, count):
450
return self._response_body.read(count)
452
def _finished_reading(self):
453
"""See SmartClientMediumRequest._finished_reading."""
402
457
#---------------- test server facilities ----------------
403
458
# TODO: load these only when running tests
503
558
Server.__init__(self)
504
559
self.request_handler = request_handler
506
def _http_start(self):
508
httpd = TestingHTTPServer(('localhost', 0),
561
def _get_httpd(self):
562
return TestingHTTPServer(('localhost', 0),
509
563
self.request_handler,
566
def _http_start(self):
567
httpd = self._get_httpd()
511
568
host, port = httpd.socket.getsockname()
512
569
self._http_base_url = '%s://localhost:%s/' % (self._url_protocol, port)
513
570
self._http_starting.release()
572
629
return 'http://127.0.0.1:1/'
632
class HTTPServerWithSmarts(HttpServer):
633
"""HTTPServerWithSmarts extends the HttpServer with POST methods that will
634
trigger a smart server to execute with a transport rooted at the rootdir of
639
HttpServer.__init__(self, SmartRequestHandler)
642
class SmartRequestHandler(TestingHTTPRequestHandler):
643
"""Extend TestingHTTPRequestHandler to support smart client POSTs."""
646
"""Hand the request off to a smart server instance."""
647
self.send_response(200)
648
self.send_header("Content-type", "application/octet-stream")
649
transport = get_transport(self.server.test_case._home_dir)
650
# TODO: We might like to support streaming responses. 1.0 allows no
651
# Content-length in this case, so for integrity we should perform our
652
# own chunking within the stream.
653
# 1.1 allows chunked responses, and in this case we could chunk using
654
# the HTTP chunking as this will allow HTTP persistence safely, even if
655
# we have to stop early due to error, but we would also have to use the
656
# HTTP trailer facility which may not be widely available.
657
out_buffer = StringIO()
658
smart_protocol_request = smart.SmartServerRequestProtocolOne(
659
transport, out_buffer.write)
660
# if this fails, we should return 400 bad request, but failure is
661
# failure for now - RBC 20060919
662
data_length = int(self.headers['Content-Length'])
663
# Perhaps there should be a SmartServerHTTPMedium that takes care of
664
# feeding the bytes in the http request to the smart_protocol_request,
665
# but for now it's simpler to just feed the bytes directly.
666
smart_protocol_request.accept_bytes(self.rfile.read(data_length))
667
assert smart_protocol_request.next_read_size() == 0, (
668
"not finished reading, but all data sent to protocol.")
669
self.send_header("Content-Length", str(len(out_buffer.getvalue())))
671
self.wfile.write(out_buffer.getvalue())