def addService(self, service, name=None, description=None, authenticator=None, expose_request=None, preprocessor=None): """ Adds a service to the gateway. @param service: The service to add to the gateway. @type service: C{callable}, class instance, or a module @param name: The name of the service. @type name: C{str} @raise pyamf.remoting.RemotingError: Service already exists. @raise TypeError: C{service} cannot be a scalar value. @raise TypeError: C{service} must be C{callable} or a module. """ if isinstance(service, (int, float, six.string_types)): raise TypeError("Service cannot be a scalar value") allowed_types = ( types.ModuleType, types.FunctionType, dict, types.MethodType, types.InstanceType, object, ) if not python.callable(service) and \ not isinstance(service, allowed_types): raise TypeError("Service must be a callable, module, or an object") if name is None: # TODO: include the module in the name if isinstance(service, type): name = service.__name__ elif isinstance(service, types.FunctionType): name = service.__name__ elif isinstance(service, types.ModuleType): name = service.__name__ else: name = str(service) if name in self.services: raise remoting.RemotingError("Service %s already exists" % name) self.services[name] = ServiceWrapper(service, description, authenticator, expose_request, preprocessor)
def _getResponse(self, http_request): """ Gets and handles the HTTP response from the remote gateway. """ if self.logger: self.logger.debug('Sending POST request to %s', self._root_url) try: fbh = self.opener(http_request) except urllib2.URLError, e: if self.logger: self.logger.exception('Failed request for %s', self._root_url) raise remoting.RemotingError(str(e))
def _getResponse(self): """ Gets and handles the HTTP response from the remote gateway. @raise RemotingError: HTTP Gateway reported error status. @raise RemotingError: Incorrect MIME type received. """ if self.logger: self.logger.debug('Waiting for response...') http_response = self.connection.getresponse() if self.logger: self.logger.debug('Got response status: %s' % http_response.status) self.logger.debug('Content-Type: %s' % http_response.getheader('Content-Type')) if http_response.status != HTTP_OK: if self.logger: self.logger.debug('Body: %s' % http_response.read()) if hasattr(httplib, 'responses'): raise remoting.RemotingError( "HTTP Gateway reported status %d %s" % (http_response.status, httplib.responses[http_response.status])) raise remoting.RemotingError("HTTP Gateway reported status %d" % (http_response.status, )) content_type = http_response.getheader('Content-Type') if content_type != remoting.CONTENT_TYPE: if self.logger: self.logger.debug('Body = %s' % http_response.read()) raise remoting.RemotingError( "Incorrect MIME type received. (got: %s)" % content_type) content_length = http_response.getheader('Content-Length') bytes = '' if self.logger: self.logger.debug('Content-Length: %s' % content_length) self.logger.debug('Server: %s' % http_response.getheader('Server')) if content_length in (None, ''): bytes = http_response.read() else: bytes = http_response.read(int(content_length)) if self.logger: self.logger.debug('Read %d bytes for the response' % len(bytes)) response = remoting.decode(bytes, strict=self.strict) if self.logger: self.logger.debug('Response: %s' % response) if remoting.APPEND_TO_GATEWAY_URL in response.headers: self.original_url += response.headers[ remoting.APPEND_TO_GATEWAY_URL] self._setUrl(self.original_url, self.proxy) elif remoting.REPLACE_GATEWAY_URL in response.headers: self.original_url = response.headers[remoting.REPLACE_GATEWAY_URL] self._setUrl(self.original_url, self.proxy) if remoting.REQUEST_PERSISTENT_HEADER in response.headers: data = response.headers[remoting.REQUEST_PERSISTENT_HEADER] for k, v in data.iteritems(): self.headers[k] = v http_response.close() return response
def _getResponse(self, http_request): """ Gets and handles the HTTP response from the remote gateway. """ if self.logger: self.logger.debug('Sending POST request to %s', self._root_url) try: fbh = self.opener(http_request) except URLError as e: if self.logger: self.logger.exception('Failed request for %s', self._root_url) raise remoting.RemotingError(str(e)) http_message = fbh.info() content_encoding = http_message.get('Content-Encoding') content_length = http_message.get('Content-Length') or -1 content_type = http_message.get('Content-Type') server = http_message.get('Server') if self.logger: self.logger.debug('Content-Type: %r', content_type) self.logger.debug('Content-Encoding: %r', content_encoding) self.logger.debug('Content-Length: %r', content_length) self.logger.debug('Server: %r', server) if content_type.split(';')[0].strip() != remoting.CONTENT_TYPE: if self.logger: self.logger.debug('Body = %s', fbh.read()) raise remoting.RemotingError( 'Incorrect MIME type received. (got: %s)' % (content_type, )) bytes = fbh.read(int(content_length)) if self.logger: self.logger.debug('Read %d bytes for the response', len(bytes)) if content_encoding and content_encoding.strip().lower() == 'gzip': if not GzipFile: raise remoting.RemotingError( 'Decompression of Content-Encoding: %s not available.' % (content_encoding, )) compressedstream = BytesIO(bytes) gzipper = GzipFile(fileobj=compressedstream) bytes = gzipper.read() gzipper.close() response = remoting.decode(bytes, strict=self.strict) if self.logger: self.logger.debug('Response: %s', response) if remoting.APPEND_TO_GATEWAY_URL in response.headers: self.original_url += response.headers[ remoting.APPEND_TO_GATEWAY_URL] self._setUrl(self.original_url) elif remoting.REPLACE_GATEWAY_URL in response.headers: self.original_url = response.headers[remoting.REPLACE_GATEWAY_URL] self._setUrl(self.original_url) if remoting.REQUEST_PERSISTENT_HEADER in response.headers: data = response.headers[remoting.REQUEST_PERSISTENT_HEADER] for k, v in data.items(): self.headers[k] = v return response
class RemotingService(object): """ Acts as a client for AMF calls. @ivar url: The url of the remote gateway. Accepts C{http} or C{https} as valid schemes. @type url: C{string} @ivar requests: The list of pending requests to process. @type requests: C{list} @ivar request_number: A unique identifier for tracking the number of requests. @ivar amf_version: The AMF version to use. See L{ENCODING_TYPES<pyamf.ENCODING_TYPES>}. @ivar referer: The referer, or HTTP referer, identifies the address of the client. Ignored by default. @type referer: C{string} @ivar user_agent: Contains information about the user agent (client) originating the request. See L{DEFAULT_USER_AGENT}. @type user_agent: C{string} @ivar headers: A list of persistent headers to send with each request. @type headers: L{HeaderCollection<pyamf.remoting.HeaderCollection>} @ivar http_headers: A dict of HTTP headers to apply to the underlying HTTP connection. @type http_headers: L{dict} @ivar strict: Whether to use strict AMF en/decoding or not. @ivar opener: The function used to power the connection to the remote server. Defaults to U{urllib2.urlopen<http:// docs.python.org/library/urllib2.html#urllib2.urlopen}. """ def __init__(self, url, amf_version=pyamf.AMF0, **kwargs): self.original_url = url self.amf_version = amf_version self.requests = [] self.request_number = 1 self.headers = remoting.HeaderCollection() self.http_headers = {} self.proxy_args = None self.user_agent = kwargs.pop('user_agent', DEFAULT_USER_AGENT) self.referer = kwargs.pop('referer', None) self.strict = kwargs.pop('strict', False) self.logger = kwargs.pop('logger', None) self.opener = kwargs.pop('opener', urllib2.urlopen) if kwargs: raise TypeError('Unexpected keyword arguments %r' % (kwargs,)) self._setUrl(url) def _setUrl(self, url): """ """ self.url = urlparse.urlparse(url) self._root_url = url if not self.url[0] in ('http', 'https'): raise ValueError('Unknown scheme %r' % (self.url[0],)) if self.logger: self.logger.info('Connecting to %r', self._root_url) self.logger.debug('Referer: %r', self.referer) self.logger.debug('User-Agent: %r', self.user_agent) def setProxy(self, host, type='http'): """ Set the proxy for all requests to use. @see: U{The Python Docs<http://docs.python.org/library/urllib2.html# urllib2.Request.set_proxy} """ self.proxy_args = (host, type) def addHeader(self, name, value, must_understand=False): """ Sets a persistent header to send with each request. @param name: Header name. """ self.headers[name] = value self.headers.set_required(name, must_understand) def addHTTPHeader(self, name, value): """ Adds a header to the underlying HTTP connection. """ self.http_headers[name] = value def removeHTTPHeader(self, name): """ Deletes an HTTP header. """ del self.http_headers[name] def getService(self, name, auto_execute=True): """ Returns a L{ServiceProxy} for the supplied name. Sets up an object that can have method calls made to it that build the AMF requests. @rtype: L{ServiceProxy} """ if not isinstance(name, basestring): raise TypeError('string type required') return ServiceProxy(self, name, auto_execute) def getRequest(self, id_): """ Gets a request based on the id. :raise LookupError: Request not found. """ for request in self.requests: if request.id == id_: return request raise LookupError("Request %r not found" % (id_,)) def addRequest(self, service, *args): """ Adds a request to be sent to the remoting gateway. """ wrapper = RequestWrapper(self, '/%d' % self.request_number, service, *args) self.request_number += 1 self.requests.append(wrapper) if self.logger: self.logger.debug('Adding request %s%r', wrapper.service, args) return wrapper def removeRequest(self, service, *args): """ Removes a request from the pending request list. """ if isinstance(service, RequestWrapper): if self.logger: self.logger.debug('Removing request: %s', self.requests[self.requests.index(service)]) del self.requests[self.requests.index(service)] return for request in self.requests: if request.service == service and request.args == args: if self.logger: self.logger.debug('Removing request: %s', self.requests[self.requests.index(request)]) del self.requests[self.requests.index(request)] return raise LookupError("Request not found") def getAMFRequest(self, requests): """ Builds an AMF request {LEnvelope<pyamf.remoting.Envelope>} from a supplied list of requests. """ envelope = remoting.Envelope(self.amf_version) if self.logger: self.logger.debug('AMF version: %s' % self.amf_version) for request in requests: service = request.service args = list(request.args) envelope[request.id] = remoting.Request(str(service), args) envelope.headers = self.headers return envelope def _get_execute_headers(self): headers = self.http_headers.copy() headers.update({ 'Content-Type': remoting.CONTENT_TYPE, 'User-Agent': self.user_agent }) if self.referer is not None: headers['Referer'] = self.referer return headers def execute_single(self, request): """ Builds, sends and handles the response to a single request, returning the response. """ if self.logger: self.logger.debug('Executing single request: %s', request) self.removeRequest(request) body = remoting.encode(self.getAMFRequest([request]), strict=self.strict) http_request = urllib2.Request(self._root_url, body.getvalue(), self._get_execute_headers()) if self.proxy_args: http_request.set_proxy(*self.proxy_args) envelope = self._getResponse(http_request) return envelope[request.id] def execute(self): """ Builds, sends and handles the responses to all requests listed in C{self.requests}. """ requests = self.requests[:] for r in requests: self.removeRequest(r) body = remoting.encode(self.getAMFRequest(requests), strict=self.strict) http_request = urllib2.Request(self._root_url, body.getvalue(), self._get_execute_headers()) if self.proxy_args: http_request.set_proxy(*self.proxy_args) envelope = self._getResponse(http_request) return envelope def _getResponse(self, http_request): """ Gets and handles the HTTP response from the remote gateway. """ if self.logger: self.logger.debug('Sending POST request to %s', self._root_url) try: fbh = self.opener(http_request) except urllib2.URLError, e: if self.logger: self.logger.exception('Failed request for %s', self._root_url) raise remoting.RemotingError(str(e)) http_message = fbh.info() content_encoding = http_message.getheader('Content-Encoding') content_length = http_message.getheader('Content-Length') or -1 content_type = http_message.getheader('Content-Type') server = http_message.getheader('Server') if self.logger: self.logger.debug('Content-Type: %r', content_type) self.logger.debug('Content-Encoding: %r', content_encoding) self.logger.debug('Content-Length: %r', content_length) self.logger.debug('Server: %r', server) if content_type != remoting.CONTENT_TYPE: if self.logger: self.logger.debug('Body = %s', fbh.read()) raise remoting.RemotingError('Incorrect MIME type received. ' '(got: %s)' % (content_type,)) bytes = fbh.read(int(content_length)) if self.logger: self.logger.debug('Read %d bytes for the response', len(bytes)) if content_encoding and content_encoding.strip().lower() == 'gzip': if not GzipFile: raise remoting.RemotingError( 'Decompression of Content-Encoding: %s not available.' % ( content_encoding,)) compressedstream = StringIO(bytes) gzipper = GzipFile(fileobj=compressedstream) bytes = gzipper.read() gzipper.close() response = remoting.decode(bytes, strict=self.strict) if self.logger: self.logger.debug('Response: %s', response) if remoting.APPEND_TO_GATEWAY_URL in response.headers: self.original_url += response.headers[remoting.APPEND_TO_GATEWAY_URL] self._setUrl(self.original_url) elif remoting.REPLACE_GATEWAY_URL in response.headers: self.original_url = response.headers[remoting.REPLACE_GATEWAY_URL] self._setUrl(self.original_url) if remoting.REQUEST_PERSISTENT_HEADER in response.headers: data = response.headers[remoting.REQUEST_PERSISTENT_HEADER] for k, v in data.iteritems(): self.headers[k] = v return response