Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 09c8b6c

Browse files
committed
OpenSSL support. This is based on patches for a version of SSLeay by
Brian E Gallew, which were improved and adapted to OpenSSL 0.9.4 by Laszlo Kovacs of HP. Both have kindly given permission to include the patches in the Python distribution. Final formatting by GvR.
1 parent 5274c33 commit 09c8b6c

3 files changed

Lines changed: 439 additions & 19 deletions

File tree

Lib/httplib.py

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,61 @@
2828
connection for each request.)
2929
"""
3030

31+
import os
3132
import socket
3233
import string
3334
import mimetools
3435

36+
try:
37+
from cStringIO import StringIO
38+
except:
39+
from StringIO import StringIO
40+
3541
HTTP_VERSION = 'HTTP/1.0'
3642
HTTP_PORT = 80
43+
HTTPS_PORT = 443
44+
45+
class FakeSocket:
46+
def __init__(self, sock, ssl):
47+
self.__sock = sock
48+
self.__ssl = ssl
49+
return
50+
51+
def makefile(self, mode): # hopefully, never have to write
52+
msgbuf = ""
53+
while 1:
54+
try:
55+
msgbuf = msgbuf + self.__ssl.read()
56+
except socket.sslerror, msg:
57+
break
58+
return StringIO(msgbuf)
59+
60+
def send(self, stuff, flags = 0):
61+
return self.__ssl.write(stuff)
62+
63+
def recv(self, len = 1024, flags = 0):
64+
return self.__ssl.read(len)
65+
66+
def __getattr__(self, attr):
67+
return getattr(self.__sock, attr)
3768

3869
class HTTP:
3970
"""This class manages a connection to an HTTP server."""
40-
41-
def __init__(self, host = '', port = 0):
71+
72+
def __init__(self, host = '', port = 0, **x509):
4273
"""Initialize a new instance.
4374
4475
If specified, `host' is the name of the remote host to which
4576
to connect. If specified, `port' specifies the port to which
4677
to connect. By default, httplib.HTTP_PORT is used.
4778
4879
"""
80+
self.key_file = x509.get('key_file')
81+
self.cert_file = x509.get('cert_file')
4982
self.debuglevel = 0
5083
self.file = None
5184
if host: self.connect(host, port)
52-
85+
5386
def set_debuglevel(self, debuglevel):
5487
"""Set the debug output level.
5588
@@ -58,10 +91,10 @@ def set_debuglevel(self, debuglevel):
5891
5992
"""
6093
self.debuglevel = debuglevel
61-
94+
6295
def connect(self, host, port = 0):
6396
"""Connect to a host on a given port.
64-
97+
6598
Note: This method is automatically invoked by __init__,
6699
if a host is specified during instantiation.
67100
@@ -77,12 +110,12 @@ def connect(self, host, port = 0):
77110
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
78111
if self.debuglevel > 0: print 'connect:', (host, port)
79112
self.sock.connect(host, port)
80-
113+
81114
def send(self, str):
82115
"""Send `str' to the server."""
83116
if self.debuglevel > 0: print 'send:', `str`
84117
self.sock.send(str)
85-
118+
86119
def putrequest(self, request, selector):
87120
"""Send a request to the server.
88121
@@ -94,7 +127,7 @@ def putrequest(self, request, selector):
94127
if not selector: selector = '/'
95128
str = '%s %s %s\r\n' % (request, selector, HTTP_VERSION)
96129
self.send(str)
97-
130+
98131
def putheader(self, header, *args):
99132
"""Send a request header line to the server.
100133
@@ -103,14 +136,14 @@ def putheader(self, header, *args):
103136
"""
104137
str = '%s: %s\r\n' % (header, string.joinfields(args,'\r\n\t'))
105138
self.send(str)
106-
139+
107140
def endheaders(self):
108141
"""Indicate that the last header line has been sent to the server."""
109142
self.send('\r\n')
110-
143+
111144
def getreply(self):
112145
"""Get a reply from the server.
113-
146+
114147
Returns a tuple consisting of:
115148
- server response code (e.g. '200' if all goes well)
116149
- server response string corresponding to response code
@@ -136,7 +169,7 @@ def getreply(self):
136169
errmsg = string.strip(msg)
137170
self.headers = mimetools.Message(self.file, 0)
138171
return errcode, errmsg, self.headers
139-
172+
140173
def getfile(self):
141174
"""Get a file object from which to receive data from the HTTP server.
142175
@@ -145,7 +178,7 @@ def getfile(self):
145178
146179
"""
147180
return self.file
148-
181+
149182
def close(self):
150183
"""Close the connection to the HTTP server."""
151184
if self.file:
@@ -155,6 +188,31 @@ def close(self):
155188
self.sock.close()
156189
self.sock = None
157190

191+
if hasattr(socket, "ssl"):
192+
class HTTPS(HTTP):
193+
"""This class allows communication via SSL."""
194+
195+
def connect(self, host, port = 0):
196+
"""Connect to a host on a given port.
197+
198+
Note: This method is automatically invoked by __init__,
199+
if a host is specified during instantiation.
200+
201+
"""
202+
if not port:
203+
i = string.find(host, ':')
204+
if i >= 0:
205+
host, port = host[:i], host[i+1:]
206+
try: port = string.atoi(port)
207+
except string.atoi_error:
208+
raise socket.error, "nonnumeric port"
209+
if not port: port = HTTPS_PORT
210+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
211+
if self.debuglevel > 0: print 'connect:', (host, port)
212+
sock.connect(host, port)
213+
ssl = socket.ssl(sock, self.key_file, self.cert_file)
214+
self.sock = FakeSocket(sock, ssl)
215+
158216

159217
def test():
160218
"""Test this module.
@@ -170,6 +228,7 @@ def test():
170228
dl = 0
171229
for o, a in opts:
172230
if o == '-d': dl = dl + 1
231+
print "testing HTTP..."
173232
host = 'www.python.org'
174233
selector = '/'
175234
if args[0:]: host = args[0]
@@ -187,6 +246,26 @@ def test():
187246
for header in headers.headers: print string.strip(header)
188247
print
189248
print h.getfile().read()
249+
if hasattr(socket, "ssl"):
250+
print "-"*40
251+
print "testing HTTPS..."
252+
host = 'synergy.as.cmu.edu'
253+
selector = '/~geek/'
254+
if args[0:]: host = args[0]
255+
if args[1:]: selector = args[1]
256+
h = HTTPS()
257+
h.set_debuglevel(dl)
258+
h.connect(host)
259+
h.putrequest('GET', selector)
260+
h.endheaders()
261+
errcode, errmsg, headers = h.getreply()
262+
print 'errcode =', errcode
263+
print 'errmsg =', errmsg
264+
print
265+
if headers:
266+
for header in headers.headers: print string.strip(header)
267+
print
268+
print h.getfile().read()
190269

191270

192271
if __name__ == '__main__':

Lib/urllib.py

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import sys
2828

2929

30-
__version__ = '1.11' # XXX This version is not always updated :-(
30+
__version__ = '1.12' # XXX This version is not always updated :-(
3131

3232
MAXFTPCACHE = 10 # Trim the ftp cache beyond this size
3333

@@ -81,11 +81,13 @@ class URLopener:
8181
__tempfiles = None
8282

8383
# Constructor
84-
def __init__(self, proxies=None):
84+
def __init__(self, proxies=None, **x509):
8585
if proxies is None:
8686
proxies = getproxies()
8787
assert hasattr(proxies, 'has_key'), "proxies must be a mapping"
8888
self.proxies = proxies
89+
self.key_file = x509.get('key_file')
90+
self.cert_file = x509.get('cert_file')
8991
server_version = "Python-urllib/%s" % __version__
9092
self.addheaders = [('User-agent', server_version)]
9193
self.__tempfiles = []
@@ -144,6 +146,7 @@ def open(self, fullurl, data=None):
144146
host, selector = splithost(proxy)
145147
url = (host, fullurl) # Signal special case to open_*()
146148
name = 'open_' + type
149+
self.type = type
147150
if '-' in name:
148151
# replace - with _
149152
name = string.join(string.split(name, '-'), '_')
@@ -294,6 +297,42 @@ def http_error_default(self, url, fp, errcode, errmsg, headers):
294297
fp.close()
295298
raise IOError, ('http error', errcode, errmsg, headers)
296299

300+
# Use HTTPS protocol
301+
if hasattr(socket, "ssl"):
302+
def open_https(self, url):
303+
import httplib
304+
if type(url) is type(""):
305+
host, selector = splithost(url)
306+
user_passwd, host = splituser(host)
307+
else:
308+
host, selector = url
309+
urltype, rest = splittype(selector)
310+
if string.lower(urltype) == 'https':
311+
realhost, rest = splithost(rest)
312+
user_passwd, realhost = splituser(realhost)
313+
if user_passwd:
314+
selector = "%s://%s%s" % (urltype, realhost, rest)
315+
print "proxy via https:", host, selector
316+
if not host: raise IOError, ('https error', 'no host given')
317+
if user_passwd:
318+
import base64
319+
auth = string.strip(base64.encodestring(user_passwd))
320+
else:
321+
auth = None
322+
h = httplib.HTTPS(host, 0,
323+
key_file=self.key_file,
324+
cert_file=self.cert_file)
325+
h.putrequest('GET', selector)
326+
if auth: h.putheader('Authorization: Basic %s' % auth)
327+
for args in self.addheaders: apply(h.putheader, args)
328+
h.endheaders()
329+
errcode, errmsg, headers = h.getreply()
330+
fp = h.getfile()
331+
if errcode == 200:
332+
return addinfourl(fp, headers, url)
333+
else:
334+
return self.http_error(url, fp, errcode, errmsg, headers)
335+
297336
# Use Gopher protocol
298337
def open_gopher(self, url):
299338
import gopherlib
@@ -477,7 +516,8 @@ def http_error_401(self, url, fp, errcode, errmsg, headers,
477516
if match:
478517
scheme, realm = match.groups()
479518
if string.lower(scheme) == 'basic':
480-
return self.retry_http_basic_auth(url, realm, data)
519+
name = 'retry_' + self.type + '_basic_auth'
520+
return getattr(self,name)(url, realm)
481521

482522
def retry_http_basic_auth(self, url, realm, data):
483523
host, selector = splithost(url)
@@ -488,6 +528,16 @@ def retry_http_basic_auth(self, url, realm, data):
488528
host = user + ':' + passwd + '@' + host
489529
newurl = 'http://' + host + selector
490530
return self.open(newurl, data)
531+
532+
def retry_https_basic_auth(self, url, realm):
533+
host, selector = splithost(url)
534+
i = string.find(host, '@') + 1
535+
host = host[i:]
536+
user, passwd = self.get_user_passwd(host, realm, i)
537+
if not (user or passwd): return None
538+
host = user + ':' + passwd + '@' + host
539+
newurl = '//' + host + selector
540+
return self.open_https(newurl)
491541

492542
def get_user_passwd(self, host, realm, clear_cache = 0):
493543
key = realm + '@' + string.lower(host)
@@ -630,8 +680,8 @@ def __init__(self, fp):
630680
self.fp = fp
631681
self.read = self.fp.read
632682
self.readline = self.fp.readline
633-
self.readlines = self.fp.readlines
634-
self.fileno = self.fp.fileno
683+
if hasattr(self.fp, "readlines"): self.readlines = self.fp.readlines
684+
if hasattr(self.fp, "fileno"): self.fileno = self.fp.fileno
635685
def __repr__(self):
636686
return '<%s at %s whose fp = %s>' % (self.__class__.__name__,
637687
`id(self)`, `self.fp`)
@@ -1015,6 +1065,8 @@ def test(args=[]):
10151065
## 'gopher://gopher.micro.umn.edu/1/',
10161066
'http://www.python.org/index.html',
10171067
]
1068+
if hasattr(URLopener, "open_https"):
1069+
args.append('https://synergy.as.cmu.edu/~geek/')
10181070
try:
10191071
for url in args:
10201072
print '-'*10, url, '-'*10

0 commit comments

Comments
 (0)