~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/http_server.py

Merge previous attempt into current trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
30
30
import urllib
31
31
import urlparse
32
32
 
33
 
from bzrlib import transport
 
33
from bzrlib import (
 
34
    tests,
 
35
    transport,
 
36
    )
 
37
from bzrlib.tests import test_server
34
38
from bzrlib.transport import local
35
39
 
36
40
 
77
81
        connection early to avoid polluting the test results.
78
82
        """
79
83
        try:
80
 
            SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
 
84
            self._handle_one_request()
81
85
        except socket.error, e:
82
86
            # Any socket error should close the connection, but some errors are
83
87
            # due to the client closing early and we don't want to pollute test
88
92
                                     errno.ECONNABORTED, errno.EBADF)):
89
93
                raise
90
94
 
 
95
    def _handle_one_request(self):
 
96
        SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
 
97
 
91
98
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
92
99
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
93
100
 
317
324
        # the tests cases.
318
325
        self.test_case_server = test_case_server
319
326
        self._home_dir = test_case_server._home_dir
320
 
 
321
 
    def tearDown(self):
322
 
         """Called to clean-up the server.
323
 
 
324
 
         Since the server may be (surely is, even) in a blocking listen, we
325
 
         shutdown its socket before closing it.
326
 
         """
327
 
         # Note that is this executed as part of the implicit tear down in the
328
 
         # main thread while the server runs in its own thread. The clean way
329
 
         # to tear down the server is to instruct him to stop accepting
330
 
         # connections and wait for the current connection(s) to end
331
 
         # naturally. To end the connection naturally, the http transports
332
 
         # should close their socket when they do not need to talk to the
333
 
         # server anymore. This happens naturally during the garbage collection
334
 
         # phase of the test transport objetcs (the server clients), so we
335
 
         # don't have to worry about them.  So, for the server, we must tear
336
 
         # down here, from the main thread, when the test have ended.  Note
337
 
         # that since the server is in a blocking operation and since python
338
 
         # use select internally, shutting down the socket is reliable and
339
 
         # relatively clean.
340
 
         try:
341
 
             self.socket.shutdown(socket.SHUT_RDWR)
342
 
         except socket.error, e:
343
 
             # WSAENOTCONN (10057) 'Socket is not connected' is harmless on
344
 
             # windows (occurs before the first connection attempt
345
 
             # vila--20071230)
346
 
 
347
 
             # 'Socket is not connected' can also occur on OSX, with a
348
 
             # "regular" ENOTCONN (when something went wrong during test case
349
 
             # setup leading to self.setUp() *not* being called but
350
 
             # self.tearDown() still being called -- vila20081106
351
 
             if not len(e.args) or e.args[0] not in (errno.ENOTCONN, 10057):
352
 
                 raise
353
 
         # Let the server properly close the socket
354
 
         self.server_close()
355
 
 
356
 
 
357
 
class TestingHTTPServer(SocketServer.TCPServer, TestingHTTPServerMixin):
 
327
        self.serving = None
 
328
        self.is_shut_down = threading.Event()
 
329
        # We collect the sockets/threads used by the clients so we can
 
330
        # close/join them when shutting down
 
331
        self.clients = []
 
332
 
 
333
    def get_request (self):
 
334
        """Get the request and client address from the socket.
 
335
        """
 
336
        sock, addr = self._get_request()
 
337
        self.clients.append([sock, addr])
 
338
        return sock, addr
 
339
 
 
340
    def verify_request(self, request, client_address):
 
341
        """Verify the request.
 
342
 
 
343
        Return True if we should proceed with this request, False if we should
 
344
        not even touch a single byte in the socket !
 
345
        """
 
346
        return self.serving is not None and self.serving.isSet()
 
347
 
 
348
    def handle_request(self):
 
349
        request, client_address = self.get_request()
 
350
        try:
 
351
            if self.verify_request(request, client_address):
 
352
                self.process_request(request, client_address)
 
353
        except:
 
354
            if self.serving is not None and self.serving.isSet():
 
355
                self.handle_error(request, client_address)
 
356
            else:
 
357
                # Exceptions raised while we shut down are just noise, but feel
 
358
                # free to put a breakpoint here if you suspect something
 
359
                # else. Such an example is the SSL handshake: it's automatic
 
360
                # once we start processing the request but the last connection
 
361
                # will close immediately and will not be able to correctly
 
362
                # reply.
 
363
                pass
 
364
            self.close_request(request)
 
365
 
 
366
    def server_bind(self):
 
367
        # The following has been fixed in 2.5 so we need to provide it for
 
368
        # older python versions.
 
369
        if sys.version < (2, 5):
 
370
            self.server_address = self.socket.getsockname()
 
371
 
 
372
    def serve(self, started):
 
373
        self.serving  = threading.Event()
 
374
        self.serving.set()
 
375
        self.is_shut_down.clear()
 
376
        if 'threads' in tests.selftest_debug_flags:
 
377
            print 'Starting %r' % (self.server_address,)
 
378
        # We are listening and ready to accept connections
 
379
        started.set()
 
380
        while self.serving.isSet():
 
381
            if 'threads' in tests.selftest_debug_flags:
 
382
                print 'Accepting on %r' % (self.server_address,)
 
383
            # Really a connection but the python framework is generic and
 
384
            # call them requests
 
385
            self.handle_request()
 
386
        if 'threads' in tests.selftest_debug_flags:
 
387
            print 'Closing  %r' % (self.server_address,)
 
388
        # Let's close the listening socket
 
389
        self.server_close()
 
390
        if 'threads' in tests.selftest_debug_flags:
 
391
            print 'Closed   %r' % (self.server_address,)
 
392
        self.is_shut_down.set()
 
393
 
 
394
    def connect_socket(self):
 
395
        err = socket.error('getaddrinfo returns an empty list')
 
396
        for res in socket.getaddrinfo(*self.server_address):
 
397
            af, socktype, proto, canonname, sa = res
 
398
            sock = None
 
399
            try:
 
400
                sock = socket.socket(af, socktype, proto)
 
401
                sock.connect(sa)
 
402
                return sock
 
403
 
 
404
            except socket.error, err:
 
405
                # 'err' is now the most recent error
 
406
                if sock is not None:
 
407
                    sock.close()
 
408
        raise err
 
409
 
 
410
    def join_thread(self, thread, timeout=2):
 
411
        thread.join(timeout)
 
412
        if thread.isAlive():
 
413
            # The timeout expired without joining the thread, the thread is
 
414
            # therefore stucked and that's a failure as far as the test is
 
415
            # concerned. We used to hang here.
 
416
            raise AssertionError('thread %s hung' % (thread.name,))
 
417
 
 
418
    def shutdown(self):
 
419
        """Stops the serve() loop.
 
420
 
 
421
        Blocks until the loop has finished. This must be called while serve()
 
422
        is running in another thread, or it will deadlock.
 
423
        """
 
424
        if self.serving is None:
 
425
            # If the server wasn't properly started, there is nothing to
 
426
            # shutdown.
 
427
            return
 
428
        # As soon as we stop serving, no more connection are accepted except
 
429
        # one to get out of the blocking listen.
 
430
        self.serving.clear()
 
431
        # The server is listening for a last connection, let's give it:
 
432
        last_conn = None
 
433
        try:
 
434
            last_conn = self.connect_socket()
 
435
        except socket.error, e:
 
436
            # But ignore connection errors as the point is to unblock the
 
437
            # server thread, it may happen that it's not blocked or even not
 
438
            # started (when something went wrong during test case setup
 
439
            # leading to self.setUp() *not* being called but self.tearDown()
 
440
            # still being called)
 
441
            pass
 
442
        # We don't have to wait for the server to shut down to start shutting
 
443
        # down the clients, so let's start now.
 
444
        for c in self.clients:
 
445
            self.shutdown_client(c)
 
446
        self.clients = []
 
447
        # Now we wait for the thread running serve() to finish
 
448
        self.is_shut_down.wait()
 
449
        if last_conn is not None:
 
450
            # Close the last connection without trying to use it. The server
 
451
            # will not process a single byte on that socket to avoid
 
452
            # complications (SSL starts with a handshake for example).
 
453
            last_conn.close()
 
454
 
 
455
    def shutdown_client(self, client):
 
456
        sock, addr = client[:2]
 
457
        self.shutdown_client_socket(sock)
 
458
 
 
459
    def shutdown_client_socket(self, sock):
 
460
        """Properly shutdown a client socket.
 
461
 
 
462
        Under some circumstances (as in bug #383920), we need to force the
 
463
        shutdown as python delays it until gc occur otherwise and the client
 
464
        may hang.
 
465
 
 
466
        This should be called only when no other thread is trying to use the
 
467
        socket.
 
468
        """
 
469
        try:
 
470
            # The request process has been completed, the thread is about to
 
471
            # die, let's shutdown the socket if we can.
 
472
            sock.shutdown(socket.SHUT_RDWR)
 
473
        except (socket.error, select.error), e:
 
474
            if e[0] in (errno.EBADF, errno.ENOTCONN):
 
475
                # Right, the socket is already down
 
476
                pass
 
477
            else:
 
478
                raise
 
479
 
 
480
 
 
481
class TestingHTTPServer(TestingHTTPServerMixin, SocketServer.TCPServer):
358
482
 
359
483
    def __init__(self, server_address, request_handler_class,
360
484
                 test_case_server):
362
486
        SocketServer.TCPServer.__init__(self, server_address,
363
487
                                        request_handler_class)
364
488
 
365
 
 
366
 
class TestingThreadingHTTPServer(SocketServer.ThreadingTCPServer,
367
 
                                 TestingHTTPServerMixin):
 
489
    def _get_request (self):
 
490
        return SocketServer.TCPServer.get_request(self)
 
491
 
 
492
    def server_bind(self):
 
493
        SocketServer.TCPServer.server_bind(self)
 
494
        TestingHTTPServerMixin.server_bind(self)
 
495
 
 
496
 
 
497
class TestingThreadingHTTPServer(TestingHTTPServerMixin,
 
498
                                 SocketServer.ThreadingTCPServer,
 
499
                                 ):
368
500
    """A threading HTTP test server for HTTP 1.1.
369
501
 
370
502
    Since tests can initiate several concurrent connections to the same http
382
514
        # lying around.
383
515
        self.daemon_threads = True
384
516
 
385
 
    def process_request_thread(self, request, client_address):
 
517
    def _get_request (self):
 
518
        return SocketServer.ThreadingTCPServer.get_request(self)
 
519
 
 
520
    def process_request_thread(self, started, request, client_address):
 
521
        if 'threads' in tests.selftest_debug_flags:
 
522
            print 'Processing: %s' % (threading.currentThread().name,)
 
523
        started.set()
386
524
        SocketServer.ThreadingTCPServer.process_request_thread(
387
525
            self, request, client_address)
388
 
        # Under some circumstances (as in bug #383920), we need to force the
389
 
        # shutdown as python delays it until gc occur otherwise and the client
390
 
        # may hang.
391
 
        try:
392
 
            # The request process has been completed, the thread is about to
393
 
            # die, let's shutdown the socket if we can.
394
 
            request.shutdown(socket.SHUT_RDWR)
395
 
        except (socket.error, select.error), e:
396
 
            if e[0] in (errno.EBADF, errno.ENOTCONN):
397
 
                # Right, the socket is already down
398
 
                pass
399
 
            else:
400
 
                raise
 
526
        # Shutdown the socket as soon as possible, the thread will be joined
 
527
        # later if needed during server shutdown thread.
 
528
        self.shutdown_client_socket(request)
 
529
 
 
530
    def process_request(self, request, client_address):
 
531
        """Start a new thread to process the request."""
 
532
        client = self.clients.pop()
 
533
        started = threading.Event()
 
534
        t = threading.Thread(target = self.process_request_thread,
 
535
                             args = (started, request, client_address))
 
536
        t.name = '%s -> %s' % (client_address, self.server_address)
 
537
        client.append(t)
 
538
        self.clients.append(client)
 
539
        if self.daemon_threads:
 
540
            t.setDaemon (1)
 
541
        t.start()
 
542
        started.wait()
 
543
 
 
544
    def shutdown_client(self, client):
 
545
        TestingHTTPServerMixin.shutdown_client(self, client)
 
546
        if len(client) == 3:
 
547
            # The thread has been created only if the request is processed but
 
548
            # after the connection is inited. This could happne when the server
 
549
            # is shut down.
 
550
            sock, addr, thread = client
 
551
            if 'threads' in tests.selftest_debug_flags:
 
552
                print 'Try    joining: %s' % (thread.name,)
 
553
            self.join_thread(thread)
 
554
 
 
555
    def server_bind(self):
 
556
        SocketServer.ThreadingTCPServer.server_bind(self)
 
557
        TestingHTTPServerMixin.server_bind(self)
401
558
 
402
559
 
403
560
class HttpServer(transport.Server):
462
619
                raise httplib.UnknownProtocol(proto_vers)
463
620
            else:
464
621
                self._httpd = self.create_httpd(serv_cls, rhandler)
465
 
            host, self.port = self._httpd.socket.getsockname()
 
622
            # Ensure we get the right port and an updated host if needed
 
623
            self.host, self.port = self._httpd.server_address
466
624
        return self._httpd
467
625
 
468
 
    def _http_start(self):
 
626
    def _http_start(self, started):
469
627
        """Server thread main entry point. """
470
 
        self._http_running = False
 
628
        server = None
471
629
        try:
472
 
            try:
473
 
                httpd = self._get_httpd()
474
 
                self._http_base_url = '%s://%s:%s/' % (self._url_protocol,
475
 
                                                       self.host, self.port)
476
 
                self._http_running = True
477
 
            except:
478
 
                # Whatever goes wrong, we save the exception for the main
479
 
                # thread. Note that since we are running in a thread, no signal
480
 
                # can be received, so we don't care about KeyboardInterrupt.
481
 
                self._http_exception = sys.exc_info()
482
 
        finally:
483
 
            # Release the lock or the main thread will block and the whole
484
 
            # process will hang.
485
 
            self._http_starting.release()
 
630
            server = self._get_httpd()
 
631
            self._http_base_url = '%s://%s:%s/' % (self._url_protocol,
 
632
                                                   self.host, self.port)
 
633
        except:
 
634
            # Whatever goes wrong, we save the exception for the main
 
635
            # thread. Note that since we are running in a thread, no signal
 
636
            # can be received, so we don't care about KeyboardInterrupt.
 
637
            self._http_exception = sys.exc_info()
486
638
 
487
 
        # From now on, exceptions are taken care of by the
488
 
        # SocketServer.BaseServer or the request handler.
489
 
        while self._http_running:
490
 
            try:
491
 
                # Really an HTTP connection but the python framework is generic
492
 
                # and call them requests
493
 
                httpd.handle_request()
494
 
            except socket.timeout:
495
 
                pass
496
 
            except (socket.error, select.error), e:
497
 
               if e[0] == errno.EBADF:
498
 
                   # Starting with python-2.6, handle_request may raise socket
499
 
                   # or select exceptions when the server is shut down (as we
500
 
                   # do).
501
 
                   pass
502
 
               else:
503
 
                   raise
 
639
        if server is not None:
 
640
            # From now on, exceptions are taken care of by the
 
641
            # SocketServer.BaseServer or the request handler.
 
642
            server.serve(started)
 
643
        if not started.isSet():
 
644
            # Hmm, something went wrong, but we can release the caller anyway
 
645
            started.set()
504
646
 
505
647
    def _get_remote_url(self, path):
506
648
        path_parts = path.split(os.path.sep)
518
660
        """Capture Server log output."""
519
661
        self.logs.append(format % args)
520
662
 
521
 
    def setUp(self, backing_transport_server=None):
522
 
        """See bzrlib.transport.Server.setUp.
 
663
    def start_server(self, backing_transport_server=None):
 
664
        """See bzrlib.transport.Server.start_server.
523
665
 
524
666
        :param backing_transport_server: The transport that requests over this
525
667
            protocol should be forwarded to. Note that this is currently not
527
669
        """
528
670
        # XXX: TODO: make the server back onto vfs_server rather than local
529
671
        # disk.
530
 
        if not (backing_transport_server is None or \
531
 
                isinstance(backing_transport_server, local.LocalURLServer)):
 
672
        if not (backing_transport_server is None
 
673
                or isinstance(backing_transport_server,
 
674
                              test_server.LocalURLServer)):
532
675
            raise AssertionError(
533
 
                "HTTPServer currently assumes local transport, got %s" % \
 
676
                "HTTPServer currently assumes local transport, got %s" %
534
677
                backing_transport_server)
535
678
        self._home_dir = os.getcwdu()
536
679
        self._local_path_parts = self._home_dir.split(os.path.sep)
537
680
        self._http_base_url = None
538
681
 
539
682
        # Create the server thread
540
 
        self._http_starting = threading.Lock()
541
 
        self._http_starting.acquire()
542
 
        self._http_thread = threading.Thread(target=self._http_start)
 
683
        started = threading.Event()
 
684
        self._http_thread = threading.Thread(target=self._http_start,
 
685
                                             args = (started,))
543
686
        self._http_thread.setDaemon(True)
544
687
        self._http_exception = None
545
688
        self._http_thread.start()
546
 
 
547
689
        # Wait for the server thread to start (i.e release the lock)
548
 
        self._http_starting.acquire()
 
690
        started.wait()
 
691
        self._http_thread.name = self._http_base_url
 
692
        if 'threads' in tests.selftest_debug_flags:
 
693
            print 'Thread started: %s' % (self._http_thread.name,)
 
694
 
549
695
 
550
696
        if self._http_exception is not None:
551
697
            # Something went wrong during server start
552
698
            exc_class, exc_value, exc_tb = self._http_exception
553
699
            raise exc_class, exc_value, exc_tb
554
 
        self._http_starting.release()
555
700
        self.logs = []
556
701
 
557
 
    def tearDown(self):
 
702
    def stop_server(self):
558
703
        """See bzrlib.transport.Server.tearDown."""
559
 
        self._httpd.tearDown()
560
 
        self._http_running = False
561
 
        # We don't need to 'self._http_thread.join()' here since the thread is
562
 
        # a daemonic one and will be garbage collected anyway. Joining just
563
 
        # slows us down for no added benefit.
 
704
        self._httpd.shutdown()
 
705
        if 'threads' in tests.selftest_debug_flags:
 
706
            print 'Try    joining: %s' % (self._http_thread.name,)
 
707
        self._httpd.join_thread(self._http_thread)
 
708
        if 'threads' in tests.selftest_debug_flags:
 
709
            print 'Thread  joined: %s' % (self._http_thread.name,)
564
710
 
565
711
    def get_url(self):
566
712
        """See bzrlib.transport.Server.get_url."""