2828connection for each request.)
2929"""
3030
31+ import os
3132import socket
3233import string
3334import mimetools
3435
36+ try :
37+ from cStringIO import StringIO
38+ except :
39+ from StringIO import StringIO
40+
3541HTTP_VERSION = 'HTTP/1.0'
3642HTTP_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
3869class 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
159217def 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
192271if __name__ == '__main__' :
0 commit comments