~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2006-10-31 21:29:02 UTC
  • mfrom: (2104 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2110.
  • Revision ID: john@arbash-meinel.com-20061031212902-4b33920b90e9ce92
[merge] bzr.dev 2104

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
    get_transport,
 
44
    register_transport,
 
45
    Server,
 
46
    smart,
 
47
    Transport,
 
48
    )
43
49
from bzrlib.transport.http.response import (HttpMultipartRangeResponse,
44
50
                                            HttpRangeResponse)
45
51
from bzrlib.ui import ui_factory
119
125
    return m
120
126
 
121
127
 
122
 
class HttpTransportBase(Transport):
 
128
class HttpTransportBase(Transport, smart.SmartClientMedium):
123
129
    """Base class for http implementations.
124
130
 
125
131
    Does URL parsing, etc, but not any network IO.
238
244
        """
239
245
        raise NotImplementedError(self._get)
240
246
 
 
247
    def get_request(self):
 
248
        return SmartClientHTTPMediumRequest(self)
 
249
 
 
250
    def get_smart_medium(self):
 
251
        """See Transport.get_smart_medium.
 
252
 
 
253
        HttpTransportBase directly implements the minimal interface of
 
254
        SmartMediumClient, so this returns self.
 
255
        """
 
256
        return self
 
257
 
241
258
    def readv(self, relpath, offsets):
242
259
        """Get parts of the file at the given relative path.
243
260
 
284
301
 
285
302
        return combined
286
303
 
 
304
    def _post(self, body_bytes):
 
305
        """POST body_bytes to .bzr/smart on this transport.
 
306
        
 
307
        :returns: (response code, response body file-like object).
 
308
        """
 
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)
 
314
 
287
315
    def put_file(self, relpath, f, mode=None):
288
316
        """Copy the file-like object into the location.
289
317
 
398
426
 
399
427
        return ','.join(strings)
400
428
 
 
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,)
 
432
        return body_filelike
 
433
 
 
434
 
 
435
class SmartClientHTTPMediumRequest(smart.SmartClientMediumRequest):
 
436
    """A SmartClientMediumRequest that works with an HTTP medium."""
 
437
 
 
438
    def __init__(self, medium):
 
439
        smart.SmartClientMediumRequest.__init__(self, medium)
 
440
        self._buffer = ''
 
441
 
 
442
    def _accept_bytes(self, bytes):
 
443
        self._buffer += bytes
 
444
 
 
445
    def _finished_writing(self):
 
446
        data = self._medium.send_http_smart_request(self._buffer)
 
447
        self._response_body = data
 
448
 
 
449
    def _read_bytes(self, count):
 
450
        return self._response_body.read(count)
 
451
        
 
452
    def _finished_reading(self):
 
453
        """See SmartClientMediumRequest._finished_reading."""
 
454
        pass
 
455
        
401
456
 
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
505
560
 
506
 
    def _http_start(self):
507
 
        httpd = None
508
 
        httpd = TestingHTTPServer(('localhost', 0),
 
561
    def _get_httpd(self):
 
562
        return TestingHTTPServer(('localhost', 0),
509
563
                                  self.request_handler,
510
564
                                  self)
 
565
 
 
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()
571
628
        # etc
572
629
        return 'http://127.0.0.1:1/'
573
630
 
 
631
 
 
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
 
635
    the HTTP server.
 
636
    """
 
637
 
 
638
    def __init__(self):
 
639
        HttpServer.__init__(self, SmartRequestHandler)
 
640
 
 
641
 
 
642
class SmartRequestHandler(TestingHTTPRequestHandler):
 
643
    """Extend TestingHTTPRequestHandler to support smart client POSTs."""
 
644
 
 
645
    def do_POST(self):
 
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())))
 
670
        self.end_headers()
 
671
        self.wfile.write(out_buffer.getvalue())
 
672