File m2crypto-0.16-m2urllib2.patch of Package m2crypto
diff -urN m2crypto/M2Crypto/__init__.py m2crypto-0.16/M2Crypto/__init__.py
--- m2crypto/M2Crypto/__init__.py 2006-06-12 19:36:19.000000000 +0200
+++ m2crypto-0.16/M2Crypto/__init__.py 2006-10-20 16:04:57.000000000 +0200
@@ -33,6 +33,7 @@
import m2urllib
# Backwards compatibility.
urllib2 = m2urllib
+import m2urllib2
import ftpslib
import httpslib
import m2xmlrpclib
diff -urN m2crypto/M2Crypto/m2urllib2.py m2crypto-0.16/M2Crypto/m2urllib2.py
--- m2crypto/M2Crypto/m2urllib2.py 1970-01-01 01:00:00.000000000 +0100
+++ m2crypto-0.16/M2Crypto/m2urllib2.py 2006-10-20 16:04:57.000000000 +0200
@@ -0,0 +1,123 @@
+"""
+M2Crypto enhancement to Python's urllib2 for handling
+'https' url's.
+
+Code from urllib2 is Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006
+Python Software Foundation; All Rights Reserved
+
+Summary of changes:
+ * Add the SSL context to the https connection when performing https_open.
+ * Add the M2Crypto HTTPSHandler when building a default opener.
+"""
+
+from urllib2 import *
+
+import SSL
+import httpslib
+
+class HTTPSHandler(AbstractHTTPHandler):
+ def __init__(self, ssl_context = None):
+ AbstractHTTPHandler.__init__(self)
+
+ if ssl_context is not None:
+ self.ctx = ssl_context
+ else:
+ self.ctx = SSL.Context()
+
+ # Copied from urllib2, so we can set the ssl context.
+ def https_open(self, req):
+ """Return an addinfourl object for the request, using http_class.
+
+ http_class must implement the HTTPConnection API from httplib.
+ The addinfourl return value is a file-like object. It also
+ has methods and attributes including:
+ - info(): return a mimetools.Message object for the headers
+ - geturl(): return the original request URL
+ - code: HTTP status code
+ """
+ host = req.get_host()
+ if not host:
+ raise URLError('no host given')
+
+ # Our change: add the ssl context.
+ h = httpslib.HTTPSConnection(host = host, ssl_context = self.ctx)
+ # End our change
+ h.set_debuglevel(self._debuglevel)
+
+ headers = dict(req.headers)
+ headers.update(req.unredirected_hdrs)
+ # We want to make an HTTP/1.1 request, but the addinfourl
+ # class isn't prepared to deal with a persistent connection.
+ # It will try to read all remaining data from the socket,
+ # which will block while the server waits for the next request.
+ # So make sure the connection gets closed after the (only)
+ # request.
+ headers["Connection"] = "close"
+ try:
+ h.request(req.get_method(), req.get_selector(), req.data, headers)
+ r = h.getresponse()
+ except socket.error, err: # XXX what error?
+ raise URLError(err)
+
+ # Pick apart the HTTPResponse object to get the addinfourl
+ # object initialized properly.
+
+ # Wrap the HTTPResponse object in socket's file object adapter
+ # for Windows. That adapter calls recv(), so delegate recv()
+ # to read(). This weird wrapping allows the returned object to
+ # have readline() and readlines() methods.
+
+ # XXX It might be better to extract the read buffering code
+ # out of socket._fileobject() and into a base class.
+
+ r.recv = r.read
+ fp = socket._fileobject(r)
+
+ resp = addinfourl(fp, r.msg, req.get_full_url())
+ resp.code = r.status
+ resp.msg = r.reason
+ return resp
+
+
+ https_request = AbstractHTTPHandler.do_request_
+
+
+# Copied from urllib2 with modifications for ssl
+def build_opener(ssl_context = None, *handlers):
+ """Create an opener object from a list of handlers.
+
+ The opener will use several default handlers, including support
+ for HTTP and FTP.
+
+ If any of the handlers passed as arguments are subclasses of the
+ default handlers, the default handlers will not be used.
+ """
+
+ opener = OpenerDirector()
+ default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
+ HTTPDefaultErrorHandler, HTTPRedirectHandler,
+ FTPHandler, FileHandler, HTTPErrorProcessor]
+ skip = []
+ for klass in default_classes:
+ for check in handlers:
+ if inspect.isclass(check):
+ if issubclass(check, klass):
+ skip.append(klass)
+ elif isinstance(check, klass):
+ skip.append(klass)
+ for klass in skip:
+ default_classes.remove(klass)
+
+ for klass in default_classes:
+ opener.add_handler(klass())
+
+ # Add the HTTPS handler with ssl_context
+ if HTTPSHandler not in skip:
+ opener.add_handler(HTTPSHandler(ssl_context))
+
+
+ for h in handlers:
+ if inspect.isclass(h):
+ h = h()
+ opener.add_handler(h)
+ return opener
diff -urN m2crypto/tests/test_ssl.py m2crypto-0.16/tests/test_ssl.py
--- m2crypto/tests/test_ssl.py 2006-05-25 00:54:03.000000000 +0200
+++ m2crypto-0.16/tests/test_ssl.py 2006-10-20 16:04:57.000000000 +0200
@@ -556,6 +556,54 @@
self.stop_server(pid)
self.failIf(string.find(data, 's_server -quiet -www') == -1)
+ # XXX Don't actually know how to use m2urllib safely!
+ #def test_urllib_safe_context(self):
+ #def test_urllib_safe_context_fail(self):
+
+ def test_urllib2(self):
+ pid = self.start_server(self.args)
+ try:
+ from M2Crypto import m2urllib2
+ opener = m2urllib2.build_opener()
+ opener.addheaders = [('Connection', 'close')]
+ u = opener.open('https://%s:%s/' % (srv_host, srv_port))
+ data = u.read()
+ u.close()
+ finally:
+ self.stop_server(pid)
+ self.failIf(string.find(data, 's_server -quiet -www') == -1)
+
+ def test_urllib2_secure_context(self):
+ pid = self.start_server(self.args)
+ try:
+ ctx = SSL.Context()
+ ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, 9)
+ ctx.load_verify_locations('ca.pem')
+
+ from M2Crypto import m2urllib2
+ opener = m2urllib2.build_opener(ctx)
+ opener.addheaders = [('Connection', 'close')]
+ u = opener.open('https://%s:%s/' % (srv_host, srv_port))
+ data = u.read()
+ u.close()
+ finally:
+ self.stop_server(pid)
+ self.failIf(string.find(data, 's_server -quiet -www') == -1)
+
+ def test_urllib2_secure_context_fail(self):
+ pid = self.start_server(self.args)
+ try:
+ ctx = SSL.Context()
+ ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, 9)
+ ctx.load_verify_locations('server.pem')
+
+ from M2Crypto import m2urllib2
+ opener = m2urllib2.build_opener(ctx)
+ opener.addheaders = [('Connection', 'close')]
+ self.assertRaises(SSL.SSLError, opener.open, 'https://%s:%s/' % (srv_host, srv_port))
+ finally:
+ self.stop_server(pid)
+
def test_blocking0(self):
pid = self.start_server(self.args)
try:
@@ -597,6 +645,8 @@
from twisted.internet import reactor
import M2Crypto.SSL.TwistedProtocolWrapper as wrapper
except ImportError:
+ import warnings
+ warnings.warn('Skipping twisted wrapper test because twisted not found')
return
class EchoClient(LineReceiver):