~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http.py

Merged mailine

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
"""Implementation of Transport over http.
17
17
"""
18
18
 
19
 
from bzrlib.transport import Transport, register_transport
20
 
from bzrlib.errors import (TransportNotPossible, NoSuchFile, 
21
 
                           TransportError, ConnectionError)
22
19
import os, errno
23
20
from cStringIO import StringIO
24
21
import urllib, urllib2
25
22
import urlparse
 
23
from warnings import warn
26
24
 
 
25
from bzrlib.transport import Transport, Server
 
26
from bzrlib.errors import (TransportNotPossible, NoSuchFile, 
 
27
                           TransportError, ConnectionError)
27
28
from bzrlib.errors import BzrError, BzrCheckError
28
29
from bzrlib.branch import Branch
29
30
from bzrlib.trace import mutter
79
80
    def __init__(self, base):
80
81
        """Set the base path where files will be stored."""
81
82
        assert base.startswith('http://') or base.startswith('https://')
 
83
        if base[-1] != '/':
 
84
            base = base + '/'
82
85
        super(HttpTransport, self).__init__(base)
83
86
        # In the future we might actually connect to the remote host
84
87
        # rather than using get_url
196
199
                             % (self.abspath(relpath), str(e)),
197
200
                             orig_error=e)
198
201
 
199
 
    def put(self, relpath, f):
 
202
    def put(self, relpath, f, mode=None):
200
203
        """Copy the file-like or string object into the location.
201
204
 
202
205
        :param relpath: Location to put the contents, relative to base.
204
207
        """
205
208
        raise TransportNotPossible('http PUT not supported')
206
209
 
207
 
    def mkdir(self, relpath):
 
210
    def mkdir(self, relpath, mode=None):
208
211
        """Create a directory at the given path."""
209
212
        raise TransportNotPossible('http does not support mkdir()')
210
213
 
218
221
        """Copy the item at rel_from to the location at rel_to"""
219
222
        raise TransportNotPossible('http does not support copy()')
220
223
 
221
 
    def copy_to(self, relpaths, other, pb=None):
 
224
    def copy_to(self, relpaths, other, mode=None, pb=None):
222
225
        """Copy a set of entries from self into another Transport.
223
226
 
224
227
        :param relpaths: A list/generator of entries to be copied.
232
235
        if isinstance(other, HttpTransport):
233
236
            raise TransportNotPossible('http cannot be the target of copy_to()')
234
237
        else:
235
 
            return super(HttpTransport, self).copy_to(relpaths, other, pb=pb)
 
238
            return super(HttpTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
236
239
 
237
240
    def move(self, rel_from, rel_to):
238
241
        """Move the item at rel_from to the location at rel_to"""
242
245
        """Delete the item at relpath"""
243
246
        raise TransportNotPossible('http does not support delete()')
244
247
 
 
248
    def is_readonly(self):
 
249
        """See Transport.is_readonly."""
 
250
        return True
 
251
 
245
252
    def listable(self):
246
253
        """See Transport.listable."""
247
254
        return False
271
278
        :return: A lock object, which should be passed to Transport.unlock()
272
279
        """
273
280
        raise TransportNotPossible('http does not support lock_write()')
 
281
 
 
282
 
 
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):
 
347
    """A test server for http transports."""
 
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
        self.logs.append(args[3])
 
398
 
 
399
    def setUp(self):
 
400
        """See bzrlib.transport.Server.setUp."""
 
401
        self._home_dir = os.getcwdu()
 
402
        self._local_path_parts = self._home_dir.split(os.path.sep)
 
403
        self._http_starting = threading.Lock()
 
404
        self._http_starting.acquire()
 
405
        self._http_running = True
 
406
        self._http_base_url = None
 
407
        self._http_thread = threading.Thread(target=self._http_start)
 
408
        self._http_thread.setDaemon(True)
 
409
        self._http_thread.start()
 
410
        self._http_proxy = os.environ.get("http_proxy")
 
411
        if self._http_proxy is not None:
 
412
            del os.environ["http_proxy"]
 
413
        self.logs = []
 
414
 
 
415
    def tearDown(self):
 
416
        """See bzrlib.transport.Server.tearDown."""
 
417
        self._http_running = False
 
418
        self._http_thread.join()
 
419
        if self._http_proxy is not None:
 
420
            import os
 
421
            os.environ["http_proxy"] = self._http_proxy
 
422
 
 
423
    def get_url(self):
 
424
        """See bzrlib.transport.Server.get_url."""
 
425
        return self._get_remote_url(self._home_dir)
 
426
        
 
427
    def get_bogus_url(self):
 
428
        """See bzrlib.transport.Server.get_bogus_url."""
 
429
        return 'http://jasldkjsalkdjalksjdkljasd'
 
430
 
 
431
 
 
432
def get_test_permutations():
 
433
    """Return the permutations to be used in testing."""
 
434
    warn("There are no HTTPS transport provider tests yet.")
 
435
    return [(HttpTransport, HttpServer),
 
436
            ]