1
2
3
4 """
5 Remoting client implementation.
6
7 @author: U{Nick Joyce<mailto:nick@boxdesign.co.uk>}
8
9 @since: 0.1.0
10 """
11
12 import httplib, urlparse
13
14 import pyamf
15 from pyamf import remoting, logging
16
17
18
19 DEFAULT_CLIENT_TYPE = pyamf.ClientTypes.Flash6
20
21 HTTP_OK = 200
22
24 if args == (tuple(),):
25 return []
26 else:
27 return [x for x in args]
28
30 """
31 Serves as a proxy for calling a service method.
32
33 @ivar service: The parent service.
34 @type service: L{ServiceProxy}
35 @ivar name: The name of the method.
36 @type name: C{str} or C{None}
37
38 @see: L{ServiceProxy.__getattr__}
39 """
40
42 self.service = service
43 self.name = name
44
46 """
47 Inform the proxied service that this function has been called.
48 """
49 return self.service._call(self, *args)
50
52 """
53 Returns the full service name, including the method name if there is
54 one.
55 """
56 service_name = str(self.service)
57
58 if self.name is not None:
59 service_name = '%s.%s' % (service_name, self.name)
60
61 return service_name
62
64 """
65 Serves as a service object proxy for RPC calls. Generates
66 L{ServiceMethodProxy} objects for method calls.
67
68 @see: L{RequestWrapper} for more info.
69
70 @ivar _gw: The parent gateway
71 @type _gw: L{RemotingService}
72 @ivar _name: The name of the service
73 @type _name: C{str}
74 @ivar _auto_execute: If set to C{True}, when a service method is called,
75 the AMF request is immediately sent to the remote gateway and a
76 response is returned. If set to C{False}, a L{RequestWrapper} is
77 returned, waiting for the underlying gateway to fire the
78 L{execute<RemotingService.execute>} method.
79 """
80
81 - def __init__(self, gw, name, auto_execute=True):
82 self._gw = gw
83 self._name = name
84 self._auto_execute = auto_execute
85
88
89 - def _call(self, method_proxy, *args):
90 """
91 Executed when a L{ServiceMethodProxy} is called. Adds a request to the
92 underlying gateway. If C{_auto_execute} is set to C{True}, then the
93 request is immediately called on the remote gateway.
94 """
95
96 request = self._gw.addRequest(method_proxy, *args)
97
98 if self._auto_execute:
99 response = self._gw.execute_single(request)
100
101
102 return response.body
103
104 return request
105
107 """
108 This allows services to be 'called' without a method name.
109 """
110 return self._call(ServiceMethodProxy(self, None), *args)
111
113 """
114 Returns a string representation of the name of the service.
115 """
116 return self._name
117
119 """
120 A container object that wraps a service method request.
121
122 @ivar gw: The underlying gateway.
123 @type gw: L{RemotingService}
124 @ivar id: The id of the request.
125 @type id: C{str}
126 @ivar service: The service proxy.
127 @type service: L{ServiceProxy}
128 @ivar args: The args used to invoke the call.
129 @type args: C{list}
130 """
131
132 - def __init__(self, gw, id_, service, *args):
133 self.gw = gw
134 self.id = id_
135 self.service = service
136 self.args = args
137
140
151
153 """
154 Returns the result of the called remote request. If the request has not
155 yet been called, an exception is raised.
156 """
157 if not hasattr(self, '_result'):
158 raise AttributeError, "'RequestWrapper' object has no attribute 'result'"
159
160 return self._result
161
164
165 result = property(_get_result, _set_result)
166
168 """
169 Acts as a client for AMF calls.
170
171 @ivar url: The url of the remote gateway. Accepts C{http} or C{https} as schemes.
172 @type url: C{str}
173 @ivar requests: The list of pending requests to process.
174 @type requests: C{list}
175 @ivar request_number: A unique identifier for an tracking the number of
176 requests.
177 @ivar amf_version: The AMF version to use.
178 See L{ENCODING_TYPES<pyamf.ENCODING_TYPES>}.
179 @type amf_version: C{int}
180 @ivar client_type: The client type. See L{ClientTypes<pyamf.ClientTypes>}.
181 @ivar connection: The underlying connection to the remoting server.
182 @type connection: C{httplib.HTTPConnection} or C{httplib.HTTPSConnection}
183 @ivar headers: A list of persistent headers to send with each request.
184 @type headers: L{HeaderCollection<pyamf.remoting.HeaderCollection>}
185 """
186
198
200 self.url = urlparse.urlparse(url)
201 self._root_url = urlparse.urlunparse(['', ''] + list(self.url[2:]))
202
203 port = None
204 hostname = None
205
206 if hasattr(self.url, 'port'):
207 if self.url.port is not None:
208 port = self.url.port
209 else:
210 if ':' not in self.url[1]:
211 hostname = self.url[1]
212 port = None
213 else:
214 sp = self.url[1].split(':')
215
216 hostname, port = sp[0], sp[1]
217 port = int(port)
218
219 if hostname is None:
220 if hasattr(self.url, 'hostname'):
221 hostname = self.url.hostname
222
223 if self.url[0] == 'http':
224 if port is None:
225 port = httplib.HTTP_PORT
226
227 self.connection = httplib.HTTPConnection(hostname, port)
228 elif self.url[0] == 'https':
229 if port is None:
230 port = httplib.HTTPS_PORT
231
232 self.connection = httplib.HTTPSConnection(hostname, port)
233 else:
234 raise ValueError, 'Unknown scheme'
235
236 self.logger.debug('creating connection to %s://%s:%s' % (self.url[0], hostname, port))
237
244
246 """
247 Returns a L{ServiceProxy} for the supplied name. Sets up an object that
248 can have method calls made to it that build the AMF requests.
249
250 @raise TypeError: C{string} type required for C{name}.
251 @rtype: L{ServiceProxy}
252 """
253 if not isinstance(name, basestring):
254 raise TypeError, 'string type required'
255
256 return ServiceProxy(self, name, auto_execute)
257
259 """
260 Gets a request based on the id.
261
262 @raise LookupError: Request not found.
263 """
264 for request in self.requests:
265 if request.id == id_:
266 return request
267
268 raise LookupError, "request %s not found" % id_
269
271 """
272 Adds a request to be sent to the remoting gateway.
273 """
274 wrapper = RequestWrapper(self, '/%d' % self.request_number,
275 service, *args)
276
277 self.request_number += 1
278 self.requests.append(wrapper)
279 self.logger.debug('adding request %s%r' % (wrapper.service, args))
280
281 return wrapper
282
284 """
285 Removes a request from the pending request list.
286
287 @raise LookupError: Request not found.
288 """
289 if isinstance(service, RequestWrapper):
290 del self.requests[self.requests.index(service)]
291
292 return
293
294 for request in self.requests:
295 if request.service == service and request.args == args:
296 del self.requests[self.requests.index(request)]
297
298 return
299
300 raise LookupError, "request not found"
301
303 """
304 Builds an AMF request L{envelope<pyamf.remoting.Envelope>} from a
305 supplied list of requests.
306
307 @param requests: List of requests
308 @type requests: C{list}
309 @rtype: L{Envelope<pyamf.remoting.Envelope>}
310 """
311 envelope = remoting.Envelope(self.amf_version, self.client_type)
312
313 for request in requests:
314 service = request.service
315 args = list(request.args)
316
317 envelope[request.id] = remoting.Request(str(service), args)
318
319 envelope.headers = self.headers
320
321 return envelope
322
324 """
325 Builds, sends and handles the response to a single request, returning
326 the response.
327
328 @param request:
329 @type request:
330 @rtype:
331 """
332 self.logger.debug('executing single request')
333 body = remoting.encode(self.getAMFRequest([request]))
334
335 self.logger.debug('sending POST request to %s' % self._root_url)
336 self.connection.request('POST', self._root_url, body.getvalue())
337
338 envelope = self._getResponse()
339 self.removeRequest(request)
340
341 return envelope[request.id]
342
344 """
345 Builds, sends and handles the responses to all requests listed in
346 C{self.requests}.
347 """
348 body = remoting.encode(self.getAMFRequest(self.requests))
349
350 self.connection.request('POST', self._root_url, body.getvalue())
351
352 envelope = self._getResponse()
353
354 for response in envelope:
355 request = self.getRequest(response[0])
356 response = response[1]
357
358 request.setResponse(response)
359
360 self.removeRequest(request)
361
420
422 """
423 Sets authentication credentials for accessing the remote gateway.
424 """
425 self.addHeader('Credentials', dict(userid=unicode(username),
426 password=unicode(password)), True)
427