1 | import urlparse, types |
---|
2 | from ZSI.TCcompound import ComplexType, Struct |
---|
3 | from ZSI import client |
---|
4 | import ZSI |
---|
5 | |
---|
6 | from ZSI import _copyright, _seqtypes, ParsedSoap, SoapWriter, TC, ZSI_SCHEMA_URI,\ |
---|
7 | EvaluateException, FaultFromFaultMessage, _child_elements, _attrs,\ |
---|
8 | _get_idstr, FaultException, WSActionException |
---|
9 | from ZSI.auth import AUTH |
---|
10 | from ZSI.TC import AnyElement, AnyType, String, TypeCode, _get_global_element_declaration,\ |
---|
11 | _get_type_definition |
---|
12 | import base64, httplib, Cookie, time |
---|
13 | from ZSI.address import Address |
---|
14 | |
---|
15 | import urllib, urllib2 |
---|
16 | |
---|
17 | |
---|
18 | class URLlib2Binding(client.Binding): |
---|
19 | def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None, |
---|
20 | endPointReference=None, **kw): |
---|
21 | '''Send a message. If url is None, use the value from the |
---|
22 | constructor (else error). obj is the object (data) to send. |
---|
23 | Data may be described with a requesttypecode keyword, or a |
---|
24 | requestclass keyword; default is the class's typecode (if |
---|
25 | there is one), else Any. |
---|
26 | |
---|
27 | Optional WS-Address Keywords |
---|
28 | wsaction -- WS-Address Action, goes in SOAP Header. |
---|
29 | endPointReference -- set by calling party, must be an |
---|
30 | EndPointReference type instance. |
---|
31 | |
---|
32 | ''' |
---|
33 | url = url or self.url |
---|
34 | # Get the TC for the obj. |
---|
35 | if kw.has_key('requesttypecode'): |
---|
36 | tc = kw['requesttypecode'] |
---|
37 | elif kw.has_key('requestclass'): |
---|
38 | tc = kw['requestclass'].typecode |
---|
39 | elif type(obj) == types.InstanceType: |
---|
40 | tc = getattr(obj.__class__, 'typecode') |
---|
41 | if tc is None: tc = TC.Any(opname, aslist=1) |
---|
42 | else: |
---|
43 | tc = TC.Any(opname, aslist=1) |
---|
44 | |
---|
45 | endPointReference = endPointReference or self.endPointReference |
---|
46 | |
---|
47 | # Serialize the object. |
---|
48 | d = {} |
---|
49 | |
---|
50 | d.update(self.nsdict) |
---|
51 | d.update(nsdict) |
---|
52 | |
---|
53 | useWSAddress = self.wsAddressURI is not None |
---|
54 | sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass, |
---|
55 | encodingStyle=kw.get('encodingStyle'),) |
---|
56 | if kw.has_key('_args'): |
---|
57 | sw.serialize(kw['_args'], tc) |
---|
58 | else: |
---|
59 | sw.serialize(obj, tc) |
---|
60 | |
---|
61 | # Determine the SOAP auth element. SOAP:Header element |
---|
62 | if self.auth_style & AUTH.zsibasic: |
---|
63 | sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass), |
---|
64 | _AuthHeader.typecode) |
---|
65 | |
---|
66 | # Serialize WS-Address |
---|
67 | if useWSAddress is True: |
---|
68 | if self.soapaction and wsaction.strip('\'"') != self.soapaction: |
---|
69 | raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\ |
---|
70 | %(self.soapaction,wsaction) |
---|
71 | self.address = Address(url, self.wsAddressURI) |
---|
72 | self.address.setRequest(endPointReference, wsaction) |
---|
73 | self.address.serialize(sw) |
---|
74 | |
---|
75 | # WS-Security Signature Handler |
---|
76 | if self.sig_handler is not None: |
---|
77 | self.sig_handler.sign(sw) |
---|
78 | soapdata = str(sw) |
---|
79 | |
---|
80 | scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url) |
---|
81 | |
---|
82 | # self.transport httplib.HTTPConnection derived class set-up removed |
---|
83 | # from HERE - this now handled by urllib2.urlopen() |
---|
84 | self.SendSOAPData(soapdata, url, soapaction, **kw) |
---|
85 | |
---|
86 | def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw): |
---|
87 | # Tracing? |
---|
88 | if self.trace: |
---|
89 | print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:" |
---|
90 | print >>self.trace, soapdata |
---|
91 | |
---|
92 | # Create a request |
---|
93 | req = urllib2.Request(url, data=soapdata) |
---|
94 | |
---|
95 | req.add_header("Content-length", "%d" % len(soapdata)) |
---|
96 | req.add_header("Content-type", 'text/xml; charset=utf-8') |
---|
97 | |
---|
98 | # TODO: equivalent method for cookies using urllib2 |
---|
99 | #self.__addcookies() |
---|
100 | |
---|
101 | for header,value in headers.items(): |
---|
102 | req.add_header(header, value) |
---|
103 | |
---|
104 | SOAPActionValue = '"%s"' % (soapaction or self.soapaction) |
---|
105 | req.add_header("SOAPAction", SOAPActionValue) |
---|
106 | |
---|
107 | # client.Binding has Authentication handler set-up code here - |
---|
108 | # urllib2.HTTPBasicAuthHandler can do this instead? |
---|
109 | |
---|
110 | for header,value in self.user_headers: |
---|
111 | req.add_header(header, value) |
---|
112 | |
---|
113 | # Check for custom urllib2 handler class |
---|
114 | if 'urlHandler' in kw: |
---|
115 | if not isinstance(kw['urlHandler'], urllib2.BaseHandler): |
---|
116 | raise TypeError, \ |
---|
117 | "URL Handler class %s must be derived from urllib2.BaseHandler" %\ |
---|
118 | kw['urlHandler'] |
---|
119 | |
---|
120 | # Make an opener and make it the default so that urllib2.urlopen |
---|
121 | # will use it |
---|
122 | urlOpener = urllib2.build_opener(kw['urlHandler']) |
---|
123 | urllib2.install_opener(urlOpener) |
---|
124 | |
---|
125 | # Send request [and receive response all in one (!) - implications |
---|
126 | # for client.Binding architecture + functionality??] |
---|
127 | self.response = urllib2.urlopen(req) |
---|
128 | |
---|
129 | # Clear prior receive state. |
---|
130 | self.data, self.ps = None, None |
---|
131 | |
---|
132 | |
---|
133 | def ReceiveRaw(self, **kw): |
---|
134 | '''Read a server reply, unconverted to any format and return it. |
---|
135 | ''' |
---|
136 | if self.data: return self.data |
---|
137 | trace = self.trace |
---|
138 | |
---|
139 | if hasattr(self, 'response') and self.response is not None: |
---|
140 | self.reply_code, self.reply_msg, self.reply_headers, self.data = \ |
---|
141 | self.response.code, self.response.msg, self.response.headers,\ |
---|
142 | self.response.read() |
---|
143 | |
---|
144 | # Reset response for next call |
---|
145 | self.response = None |
---|
146 | if trace: |
---|
147 | print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:" |
---|
148 | for i in (self.reply_code, self.reply_msg,): |
---|
149 | print >>trace, str(i) |
---|
150 | print >>trace, "-------" |
---|
151 | print >>trace, str(self.reply_headers) |
---|
152 | print >>trace, self.data |
---|
153 | |
---|
154 | return self.data |
---|
155 | |
---|
156 | # else Send didn't use SendSOAPData... |
---|
157 | while 1: |
---|
158 | response = self.h.getresponse() |
---|
159 | self.reply_code, self.reply_msg, self.reply_headers, self.data = \ |
---|
160 | response.status, response.reason, response.msg, response.read() |
---|
161 | if trace: |
---|
162 | print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:" |
---|
163 | for i in (self.reply_code, self.reply_msg,): |
---|
164 | print >>trace, str(i) |
---|
165 | print >>trace, "-------" |
---|
166 | print >>trace, str(self.reply_headers) |
---|
167 | print >>trace, self.data |
---|
168 | saved = None |
---|
169 | for d in response.msg.getallmatchingheaders('set-cookie'): |
---|
170 | if d[0] in [ ' ', '\t' ]: |
---|
171 | saved += d.strip() |
---|
172 | else: |
---|
173 | if saved: self.cookies.load(saved) |
---|
174 | saved = d.strip() |
---|
175 | if saved: self.cookies.load(saved) |
---|
176 | if response.status == 401: |
---|
177 | if not callable(self.http_callbacks.get(response.status,None)): |
---|
178 | raise RuntimeError, 'HTTP Digest Authorization Failed' |
---|
179 | self.http_callbacks[response.status](response) |
---|
180 | continue |
---|
181 | if response.status != 100: break |
---|
182 | |
---|
183 | # The httplib doesn't understand the HTTP continuation header. |
---|
184 | # Horrible internals hack to patch things up. |
---|
185 | self.h._HTTPConnection__state = httplib._CS_REQ_SENT |
---|
186 | self.h._HTTPConnection__response = None |
---|
187 | return self.data |
---|