Rev 2948: Fix 1.1 related bugs in HTTP server, add HTTPS passing tests (by temporarily disabling pycurl certificate verification). in file:///v/home/vila/src/bzr/experimental/https/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Sat Jan 5 22:19:09 GMT 2008
At file:///v/home/vila/src/bzr/experimental/https/
------------------------------------------------------------
revno: 2948
revision-id:v.ladeuil+lp at free.fr-20080105221904-185q2vl2hjbeul3d
parent: v.ladeuil+lp at free.fr-20080105220947-t2kymulzeqf1g5n5
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: https
timestamp: Sat 2008-01-05 23:19:04 +0100
message:
Fix 1.1 related bugs in HTTP server, add HTTPS passing tests (by temporarily disabling pycurl certificate verification).
* doc/developers/authentication-ring.txt
(verify_certificates): Fix typo, obviously only apply to HTTPS
* bzrlib/transport/http/ca_bundle.py:
(get_ca_path): Fix too long lines.
* bzrlib/transport/http/_pycurl.py:
(CURLE_SSL_CACERT): New error code.
(PyCurlTransport._set_curl_options): Temporarily disable peer
verification to make tests pass.
(PyCurlTransport._curl_perform): Catch CURLE_SSL_CACERT as a
connection error.
(get_test_permutations): Add HTTPS tests.
* bzrlib/tests/https_server.py:
(TestingHTTPSServer, TestingThreadingHTTPSServer): HTTPS test
servers.
(HTTPSServer_PyCurl): New class for pycurl HTTPS test server.
* bzrlib/tests/http_server.py:
(TestingHTTPRequestHandler.send_error): Overrides python version
since we need to specify a Content-Length.
(TestingHTTPRequestHandler.get_multiple_ranges): Sabotage !
Off-by-one error caused a buggy comment ! Went unnoticed until
pycurl+https hang.
(HttpServer.create_httpd): Allow server creation overriding.
modified:
bzrlib/tests/http_server.py httpserver.py-20061012142527-m1yxdj1xazsf8d7s-1
bzrlib/tests/https_server.py https_server.py-20071121173708-aj8zczi0ziwbwz21-1
bzrlib/tests/test_http.py testhttp.py-20051018020158-b2eef6e867c514d9
bzrlib/transport/http/_pycurl.py pycurlhttp.py-20060110060940-4e2a705911af77a6
bzrlib/transport/http/ca_bundle.py ca_bundle.py-20070226091335-84kb1xg1r2jjf858-1
doc/developers/authentication-ring.txt authring.txt-20070718200437-q5tdik0ne6lor86d-1
-------------- next part --------------
=== modified file 'bzrlib/tests/http_server.py'
--- a/bzrlib/tests/http_server.py 2008-01-03 11:49:52 +0000
+++ b/bzrlib/tests/http_server.py 2008-01-05 22:19:04 +0000
@@ -38,6 +38,10 @@
return 'path %s is not in %s' % self.args
+def _quote_html(html):
+ return html.replace("&", "&").replace("<", "<").replace(">", ">")
+
+
class TestingHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
"""Handles one request.
@@ -126,6 +130,33 @@
header_line = '%s: %s\r\n' % (keyword, value)
return len(header_line)
+ def send_error(self, code, message=None):
+ """Overrides base implementation to work around bugs in python2.5.
+
+
+ To be 1.1 compliant, we need to specify a Content-Length or 1.1 clients
+ may hang.
+ """
+ try:
+ short, long = self.responses[code]
+ except KeyError:
+ short, long = '???', '???'
+ if message is None:
+ message = short
+ explain = long
+ self.log_error("code %d, message %s", code, message)
+ # using _quote_html to prevent Cross Site Scripting attacks (see bug
+ # #1100201)
+ content = (self.error_message_format %
+ {'code': code, 'message': _quote_html(message),
+ 'explain': explain})
+ self.send_response(code, message)
+ self.send_header("Content-Type", "text/html")
+ self.send_header('Content-Length', len(content))
+ self.end_headers()
+ if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
+ self.wfile.write(content)
+
def send_head(self):
"""Overrides base implementation to work around a bug in python2.5."""
path = self.translate_path(self.path)
@@ -175,7 +206,7 @@
content_length += self._header_line_length(
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
content_length += len('\r\n') # end headers
- content_length += end - start # + 1
+ content_length += end - start + 1
content_length += len(boundary_line)
self.send_header('Content-length', content_length)
self.end_headers()
@@ -417,9 +448,8 @@
# Allows tests to verify number of GET requests issued
self.GET_request_nb = 0
- def create_httpd(self):
- return TestingHTTPServer((self.host, self.port), self.request_handler,
- self)
+ def create_httpd(self, serv_cls, rhandler_cls):
+ return serv_cls((self.host, self.port), self.request_handler, self)
def _get_httpd(self):
if self._httpd is None:
@@ -438,7 +468,7 @@
if serv_cls is None:
raise httplib.UnknownProtocol(proto_vers)
else:
- self._httpd = serv_cls((self.host, self.port), rhandler, self)
+ self._httpd = self.create_httpd(serv_cls, rhandler)
host, self.port = self._httpd.socket.getsockname()
return self._httpd
=== modified file 'bzrlib/tests/https_server.py'
--- a/bzrlib/tests/https_server.py 2007-11-24 14:57:25 +0000
+++ b/bzrlib/tests/https_server.py 2008-01-05 22:19:04 +0000
@@ -16,18 +16,17 @@
"""HTTPS test server, available when ssl python module is available"""
+import ssl
+
from bzrlib.tests import (
http_server,
ssl_certs,
)
-class TestingHTTPSServer(http_server.TestingHTTPServer):
+class TestingHTTPSServerMixin:
- def __init__(self, server_address, request_handler_class,
- test_case_server, key_file, cert_file):
- http_server.TestingHTTPServer.__init__(
- self, server_address, request_handler_class, test_case_server)
+ def __init__(self, key_file, cert_file):
self.key_file = key_file
self.cert_file = cert_file
@@ -37,18 +36,41 @@
This is called in response to a connection issued to the server, we
wrap the socket with SSL.
"""
- import ssl
sock, addr = self.socket.accept()
sslconn = ssl.wrap_socket(sock, server_side=True,
keyfile=self.key_file,
certfile=self.cert_file)
return sslconn, addr
+class TestingHTTPSServer(TestingHTTPSServerMixin,
+ http_server.TestingHTTPServer):
+
+ def __init__(self, server_address, request_handler_class,
+ test_case_server, key_file, cert_file):
+ TestingHTTPSServerMixin.__init__(self, key_file, cert_file)
+ http_server.TestingHTTPServer.__init__(
+ self, server_address, request_handler_class, test_case_server)
+
+
+class TestingThreadingHTTPSServer(TestingHTTPSServerMixin,
+ http_server.TestingThreadingHTTPServer):
+
+ def __init__(self, server_address, request_handler_class,
+ test_case_server, key_file, cert_file):
+ TestingHTTPSServerMixin.__init__(self, key_file, cert_file)
+ http_server.TestingThreadingHTTPServer.__init__(
+ self, server_address, request_handler_class, test_case_server)
+
class HTTPSServer(http_server.HttpServer):
_url_protocol = 'https'
+ # The real servers depending on the protocol
+ http_server_class = {'HTTP/1.0': TestingHTTPSServer,
+ 'HTTP/1.1': TestingThreadingHTTPSServer,
+ }
+
# Provides usable defaults since an https server requires both a
# private key and certificate to work.
def __init__(self, request_handler=http_server.TestingHTTPRequestHandler,
@@ -59,9 +81,9 @@
self.cert_file = cert_file
self.temp_files = []
- def create_httpd(self):
- return TestingHTTPSServer((self.host, self.port), self.request_handler,
- self, self.key_file, self.cert_file)
+ def create_httpd(self, serv_cls, rhandler_cls):
+ return serv_cls((self.host, self.port), self.request_handler,
+ self, self.key_file, self.cert_file)
class HTTPSServer_urllib(HTTPSServer):
@@ -74,3 +96,16 @@
# urls returned by this server should require the urllib client impl
_url_protocol = 'https+urllib'
+
+class HTTPSServer_PyCurl(HTTPSServer):
+ """Subclass of HTTPSServer that gives http+pycurl urls.
+
+ This is for use in testing: connections to this server will always go
+ through pycurl where possible.
+ """
+
+ # We don't care about checking the pycurl availability as
+ # this server will be required only when pycurl is present
+
+ # urls returned by this server should require the pycurl client impl
+ _url_protocol = 'https+pycurl'
=== modified file 'bzrlib/tests/test_http.py'
--- a/bzrlib/tests/test_http.py 2008-01-03 11:49:52 +0000
+++ b/bzrlib/tests/test_http.py 2008-01-05 22:19:04 +0000
@@ -593,7 +593,6 @@
def test_http_has(self):
server = self.get_readonly_server()
t = self._transport(server.get_url())
- import pdb; pdb.set_trace()
self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
def test_http_get(self):
=== modified file 'bzrlib/transport/http/_pycurl.py'
--- a/bzrlib/transport/http/_pycurl.py 2008-01-03 11:49:52 +0000
+++ b/bzrlib/transport/http/_pycurl.py 2008-01-05 22:19:04 +0000
@@ -86,12 +86,13 @@
"""
return pycurl.__dict__.get(symbol, default)
-CURLE_SSL_CACERT_BADFILE = _get_pycurl_errcode('E_SSL_CACERT_BADFILE', 77)
CURLE_COULDNT_CONNECT = _get_pycurl_errcode('E_COULDNT_CONNECT', 7)
CURLE_COULDNT_RESOLVE_HOST = _get_pycurl_errcode('E_COULDNT_RESOLVE_HOST', 6)
CURLE_COULDNT_RESOLVE_PROXY = _get_pycurl_errcode('E_COULDNT_RESOLVE_PROXY', 5)
CURLE_GOT_NOTHING = _get_pycurl_errcode('E_GOT_NOTHING', 52)
CURLE_PARTIAL_FILE = _get_pycurl_errcode('E_PARTIAL_FILE', 18)
+CURLE_SSL_CACERT = _get_pycurl_errcode('E_SSL_CACERT', 60)
+CURLE_SSL_CACERT_BADFILE = _get_pycurl_errcode('E_SSL_CACERT_BADFILE', 77)
class PyCurlTransport(HttpTransportBase):
@@ -310,6 +311,9 @@
if password is not None: # '' is a valid password
userpass += password
curl.setopt(pycurl.USERPWD, userpass)
+ # XXX: Temporarily disable peer verification
+# curl.setopt(pycurl.SSL_VERIFYHOST, 2)
+ curl.setopt(pycurl.SSL_VERIFYPEER, 0)
def _curl_perform(self, curl, header, more_headers=[]):
"""Perform curl operation and translate exceptions."""
@@ -327,11 +331,13 @@
url = curl.getinfo(pycurl.EFFECTIVE_URL)
mutter('got pycurl error: %s, %s, %s, url: %s ',
e[0], e[1], e, url)
- if e[0] in (CURLE_SSL_CACERT_BADFILE,
- CURLE_COULDNT_RESOLVE_HOST,
+ if e[0] in (CURLE_COULDNT_RESOLVE_HOST,
+ CURLE_COULDNT_RESOLVE_PROXY,
CURLE_COULDNT_CONNECT,
CURLE_GOT_NOTHING,
- CURLE_COULDNT_RESOLVE_PROXY,):
+ CURLE_SSL_CACERT,
+ CURLE_SSL_CACERT_BADFILE,
+ ):
raise errors.ConnectionError(
'curl connection error (%s)\non %s' % (e[1], url))
elif e[0] == CURLE_PARTIAL_FILE:
@@ -356,6 +362,11 @@
def get_test_permutations():
"""Return the permutations to be used in testing."""
- from bzrlib.tests.http_server import HttpServer_PyCurl
- return [(PyCurlTransport, HttpServer_PyCurl),
- ]
+ from bzrlib import tests
+ from bzrlib.tests import http_server
+ permutations = [(PyCurlTransport, http_server.HttpServer_PyCurl),]
+ if tests.HTTPSServerFeature.available():
+ from bzrlib.tests import https_server
+ permutations.append((PyCurlTransport,
+ https_server.HTTPSServer_PyCurl))
+ return permutations
=== modified file 'bzrlib/transport/http/ca_bundle.py'
--- a/bzrlib/transport/http/ca_bundle.py 2007-02-27 06:57:37 +0000
+++ b/bzrlib/transport/http/ca_bundle.py 2008-01-05 22:19:04 +0000
@@ -36,13 +36,13 @@
# from "Details on Server SSL Certificates"
# http://curl.haxx.se/docs/sslcerts.html
#
- # 4. If you're using the curl command line tool, you can specify your own CA
- # cert path by setting the environment variable CURL_CA_BUNDLE to the path
- # of your choice.
+ # 4. If you're using the curl command line tool, you can specify your own
+ # CA cert path by setting the environment variable CURL_CA_BUNDLE to the
+ # path of your choice.
#
- # If you're using the curl command line tool on Windows, curl will search
- # for a CA cert file named "curl-ca-bundle.crt" in these directories and in
- # this order:
+ # If you're using the curl command line tool on Windows, curl will
+ # search for a CA cert file named "curl-ca-bundle.crt" in these
+ # directories and in this order:
# 1. application's directory
# 2. current working directory
# 3. Windows System directory (e.g. C:\windows\system32)
=== modified file 'doc/developers/authentication-ring.txt'
--- a/doc/developers/authentication-ring.txt 2007-11-04 15:24:27 +0000
+++ b/doc/developers/authentication-ring.txt 2008-01-05 22:19:04 +0000
@@ -205,7 +205,7 @@
* ``port``: the port the server is listening,
* ``verify_certificates``: to control certificate verification (useful
- for self certified hosts). This applies to HTTP[S] only. Accepted values
+ for self certified hosts). This applies to HTTPS only. Accepted values
are yes and no, default to yes.
* ``path``: the branch location,
More information about the bazaar-commits
mailing list