| Index: tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/webapp/service_handlers.py
|
| diff --git a/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/webapp/service_handlers.py b/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/webapp/service_handlers.py
|
| deleted file mode 100755
|
| index a574b2b4e08016c317e3b62168edcc9347264222..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/gsutilz/third_party/protorpc/protorpc/webapp/service_handlers.py
|
| +++ /dev/null
|
| @@ -1,834 +0,0 @@
|
| -#!/usr/bin/env python
|
| -#
|
| -# Copyright 2010 Google Inc.
|
| -#
|
| -# Licensed under the Apache License, Version 2.0 (the "License");
|
| -# you may not use this file except in compliance with the License.
|
| -# You may obtain a copy of the License at
|
| -#
|
| -# http://www.apache.org/licenses/LICENSE-2.0
|
| -#
|
| -# Unless required by applicable law or agreed to in writing, software
|
| -# distributed under the License is distributed on an "AS IS" BASIS,
|
| -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -# See the License for the specific language governing permissions and
|
| -# limitations under the License.
|
| -#
|
| -
|
| -"""Handlers for remote services.
|
| -
|
| -This module contains classes that may be used to build a service
|
| -on top of the App Engine Webapp framework.
|
| -
|
| -The services request handler can be configured to handle requests in a number
|
| -of different request formats. All different request formats must have a way
|
| -to map the request to the service handlers defined request message.Message
|
| -class. The handler can also send a response in any format that can be mapped
|
| -from the response message.Message class.
|
| -
|
| -Participants in an RPC:
|
| -
|
| - There are four classes involved with the life cycle of an RPC.
|
| -
|
| - Service factory: A user-defined service factory that is responsible for
|
| - instantiating an RPC service. The methods intended for use as RPC
|
| - methods must be decorated by the 'remote' decorator.
|
| -
|
| - RPCMapper: Responsible for determining whether or not a specific request
|
| - matches a particular RPC format and translating between the actual
|
| - request/response and the underlying message types. A single instance of
|
| - an RPCMapper sub-class is required per service configuration. Each
|
| - mapper must be usable across multiple requests.
|
| -
|
| - ServiceHandler: A webapp.RequestHandler sub-class that responds to the
|
| - webapp framework. It mediates between the RPCMapper and service
|
| - implementation class during a request. As determined by the Webapp
|
| - framework, a new ServiceHandler instance is created to handle each
|
| - user request. A handler is never used to handle more than one request.
|
| -
|
| - ServiceHandlerFactory: A class that is responsible for creating new,
|
| - properly configured ServiceHandler instance for each request. The
|
| - factory is configured by providing it with a set of RPCMapper instances.
|
| - When the Webapp framework invokes the service handler, the handler
|
| - creates a new service class instance. The service class instance is
|
| - provided with a reference to the handler. A single instance of an
|
| - RPCMapper sub-class is required to configure each service. Each mapper
|
| - instance must be usable across multiple requests.
|
| -
|
| -RPC mappers:
|
| -
|
| - RPC mappers translate between a single HTTP based RPC protocol and the
|
| - underlying service implementation. Each RPC mapper must configured
|
| - with the following information to determine if it is an appropriate
|
| - mapper for a given request:
|
| -
|
| - http_methods: Set of HTTP methods supported by handler.
|
| -
|
| - content_types: Set of supported content types.
|
| -
|
| - default_content_type: Default content type for handler responses.
|
| -
|
| - Built-in mapper implementations:
|
| -
|
| - URLEncodedRPCMapper: Matches requests that are compatible with post
|
| - forms with the 'application/x-www-form-urlencoded' content-type
|
| - (this content type is the default if none is specified. It
|
| - translates post parameters into request parameters.
|
| -
|
| - ProtobufRPCMapper: Matches requests that are compatible with post
|
| - forms with the 'application/x-google-protobuf' content-type. It
|
| - reads the contents of a binary post request.
|
| -
|
| -Public Exceptions:
|
| - Error: Base class for service handler errors.
|
| - ServiceConfigurationError: Raised when a service not correctly configured.
|
| - RequestError: Raised by RPC mappers when there is an error in its request
|
| - or request format.
|
| - ResponseError: Raised by RPC mappers when there is an error in its response.
|
| -"""
|
| -import six
|
| -
|
| -__author__ = 'rafek@google.com (Rafe Kaplan)'
|
| -
|
| -import six.moves.http_client
|
| -import logging
|
| -
|
| -from .google_imports import webapp
|
| -from .google_imports import webapp_util
|
| -from .. import messages
|
| -from .. import protobuf
|
| -from .. import protojson
|
| -from .. import protourlencode
|
| -from .. import registry
|
| -from .. import remote
|
| -from .. import util
|
| -from . import forms
|
| -
|
| -__all__ = [
|
| - 'Error',
|
| - 'RequestError',
|
| - 'ResponseError',
|
| - 'ServiceConfigurationError',
|
| -
|
| - 'DEFAULT_REGISTRY_PATH',
|
| -
|
| - 'ProtobufRPCMapper',
|
| - 'RPCMapper',
|
| - 'ServiceHandler',
|
| - 'ServiceHandlerFactory',
|
| - 'URLEncodedRPCMapper',
|
| - 'JSONRPCMapper',
|
| - 'service_mapping',
|
| - 'run_services',
|
| -]
|
| -
|
| -
|
| -class Error(Exception):
|
| - """Base class for all errors in service handlers module."""
|
| -
|
| -
|
| -class ServiceConfigurationError(Error):
|
| - """When service configuration is incorrect."""
|
| -
|
| -
|
| -class RequestError(Error):
|
| - """Error occurred when building request."""
|
| -
|
| -
|
| -class ResponseError(Error):
|
| - """Error occurred when building response."""
|
| -
|
| -
|
| -_URLENCODED_CONTENT_TYPE = protourlencode.CONTENT_TYPE
|
| -_PROTOBUF_CONTENT_TYPE = protobuf.CONTENT_TYPE
|
| -_JSON_CONTENT_TYPE = protojson.CONTENT_TYPE
|
| -
|
| -_EXTRA_JSON_CONTENT_TYPES = ['application/x-javascript',
|
| - 'text/javascript',
|
| - 'text/x-javascript',
|
| - 'text/x-json',
|
| - 'text/json',
|
| - ]
|
| -
|
| -# The whole method pattern is an optional regex. It contains a single
|
| -# group used for mapping to the query parameter. This is passed to the
|
| -# parameters of 'get' and 'post' on the ServiceHandler.
|
| -_METHOD_PATTERN = r'(?:\.([^?]*))?'
|
| -
|
| -DEFAULT_REGISTRY_PATH = forms.DEFAULT_REGISTRY_PATH
|
| -
|
| -
|
| -class RPCMapper(object):
|
| - """Interface to mediate between request and service object.
|
| -
|
| - Request mappers are implemented to support various types of
|
| - RPC protocols. It is responsible for identifying whether a
|
| - given request matches a particular protocol, resolve the remote
|
| - method to invoke and mediate between the request and appropriate
|
| - protocol messages for the remote method.
|
| - """
|
| -
|
| - @util.positional(4)
|
| - def __init__(self,
|
| - http_methods,
|
| - default_content_type,
|
| - protocol,
|
| - content_types=None):
|
| - """Constructor.
|
| -
|
| - Args:
|
| - http_methods: Set of HTTP methods supported by mapper.
|
| - default_content_type: Default content type supported by mapper.
|
| - protocol: The protocol implementation. Must implement encode_message and
|
| - decode_message.
|
| - content_types: Set of additionally supported content types.
|
| - """
|
| - self.__http_methods = frozenset(http_methods)
|
| - self.__default_content_type = default_content_type
|
| - self.__protocol = protocol
|
| -
|
| - if content_types is None:
|
| - content_types = []
|
| - self.__content_types = frozenset([self.__default_content_type] +
|
| - content_types)
|
| -
|
| - @property
|
| - def http_methods(self):
|
| - return self.__http_methods
|
| -
|
| - @property
|
| - def default_content_type(self):
|
| - return self.__default_content_type
|
| -
|
| - @property
|
| - def content_types(self):
|
| - return self.__content_types
|
| -
|
| - def build_request(self, handler, request_type):
|
| - """Build request message based on request.
|
| -
|
| - Each request mapper implementation is responsible for converting a
|
| - request to an appropriate message instance.
|
| -
|
| - Args:
|
| - handler: RequestHandler instance that is servicing request.
|
| - Must be initialized with request object and been previously determined
|
| - to matching the protocol of the RPCMapper.
|
| - request_type: Message type to build.
|
| -
|
| - Returns:
|
| - Instance of request_type populated by protocol buffer in request body.
|
| -
|
| - Raises:
|
| - RequestError if the mapper implementation is not able to correctly
|
| - convert the request to the appropriate message.
|
| - """
|
| - try:
|
| - return self.__protocol.decode_message(request_type, handler.request.body)
|
| - except (messages.ValidationError, messages.DecodeError) as err:
|
| - raise RequestError('Unable to parse request content: %s' % err)
|
| -
|
| - def build_response(self, handler, response, pad_string=False):
|
| - """Build response based on service object response message.
|
| -
|
| - Each request mapper implementation is responsible for converting a
|
| - response message to an appropriate handler response.
|
| -
|
| - Args:
|
| - handler: RequestHandler instance that is servicing request.
|
| - Must be initialized with request object and been previously determined
|
| - to matching the protocol of the RPCMapper.
|
| - response: Response message as returned from the service object.
|
| -
|
| - Raises:
|
| - ResponseError if the mapper implementation is not able to correctly
|
| - convert the message to an appropriate response.
|
| - """
|
| - try:
|
| - encoded_message = self.__protocol.encode_message(response)
|
| - except messages.ValidationError as err:
|
| - raise ResponseError('Unable to encode message: %s' % err)
|
| - else:
|
| - handler.response.headers['Content-Type'] = self.default_content_type
|
| - handler.response.out.write(encoded_message)
|
| -
|
| -
|
| -class ServiceHandlerFactory(object):
|
| - """Factory class used for instantiating new service handlers.
|
| -
|
| - Normally a handler class is passed directly to the webapp framework
|
| - so that it can be simply instantiated to handle a single request.
|
| - The service handler, however, must be configured with additional
|
| - information so that it knows how to instantiate a service object.
|
| - This class acts the same as a normal RequestHandler class by
|
| - overriding the __call__ method to correctly configures a ServiceHandler
|
| - instance with a new service object.
|
| -
|
| - The factory must also provide a set of RPCMapper instances which
|
| - examine a request to determine what protocol is being used and mediates
|
| - between the request and the service object.
|
| -
|
| - The mapping of a service handler must have a single group indicating the
|
| - part of the URL path that maps to the request method. This group must
|
| - exist but can be optional for the request (the group may be followed by
|
| - '?' in the regular expression matching the request).
|
| -
|
| - Usage:
|
| -
|
| - stock_factory = ServiceHandlerFactory(StockService)
|
| - ... configure stock_factory by adding RPCMapper instances ...
|
| -
|
| - application = webapp.WSGIApplication(
|
| - [stock_factory.mapping('/stocks')])
|
| -
|
| - Default usage:
|
| -
|
| - application = webapp.WSGIApplication(
|
| - [ServiceHandlerFactory.default(StockService).mapping('/stocks')])
|
| - """
|
| -
|
| - def __init__(self, service_factory):
|
| - """Constructor.
|
| -
|
| - Args:
|
| - service_factory: Service factory to instantiate and provide to
|
| - service handler.
|
| - """
|
| - self.__service_factory = service_factory
|
| - self.__request_mappers = []
|
| -
|
| - def all_request_mappers(self):
|
| - """Get all request mappers.
|
| -
|
| - Returns:
|
| - Iterator of all request mappers used by this service factory.
|
| - """
|
| - return iter(self.__request_mappers)
|
| -
|
| - def add_request_mapper(self, mapper):
|
| - """Add request mapper to end of request mapper list."""
|
| - self.__request_mappers.append(mapper)
|
| -
|
| - def __call__(self):
|
| - """Construct a new service handler instance."""
|
| - return ServiceHandler(self, self.__service_factory())
|
| -
|
| - @property
|
| - def service_factory(self):
|
| - """Service factory associated with this factory."""
|
| - return self.__service_factory
|
| -
|
| - @staticmethod
|
| - def __check_path(path):
|
| - """Check a path parameter.
|
| -
|
| - Make sure a provided path parameter is compatible with the
|
| - webapp URL mapping.
|
| -
|
| - Args:
|
| - path: Path to check. This is a plain path, not a regular expression.
|
| -
|
| - Raises:
|
| - ValueError if path does not start with /, path ends with /.
|
| - """
|
| - if path.endswith('/'):
|
| - raise ValueError('Path %s must not end with /.' % path)
|
| -
|
| - def mapping(self, path):
|
| - """Convenience method to map service to application.
|
| -
|
| - Args:
|
| - path: Path to map service to. It must be a simple path
|
| - with a leading / and no trailing /.
|
| -
|
| - Returns:
|
| - Mapping from service URL to service handler factory.
|
| - """
|
| - self.__check_path(path)
|
| -
|
| - service_url_pattern = r'(%s)%s' % (path, _METHOD_PATTERN)
|
| -
|
| - return service_url_pattern, self
|
| -
|
| - @classmethod
|
| - def default(cls, service_factory, parameter_prefix=''):
|
| - """Convenience method to map default factory configuration to application.
|
| -
|
| - Creates a standardized default service factory configuration that pre-maps
|
| - the URL encoded protocol handler to the factory.
|
| -
|
| - Args:
|
| - service_factory: Service factory to instantiate and provide to
|
| - service handler.
|
| - method_parameter: The name of the form parameter used to determine the
|
| - method to invoke used by the URLEncodedRPCMapper. If None, no
|
| - parameter is used and the mapper will only match against the form
|
| - path-name. Defaults to 'method'.
|
| - parameter_prefix: If provided, all the parameters in the form are
|
| - expected to begin with that prefix by the URLEncodedRPCMapper.
|
| -
|
| - Returns:
|
| - Mapping from service URL to service handler factory.
|
| - """
|
| - factory = cls(service_factory)
|
| -
|
| - factory.add_request_mapper(ProtobufRPCMapper())
|
| - factory.add_request_mapper(JSONRPCMapper())
|
| -
|
| - return factory
|
| -
|
| -
|
| -class ServiceHandler(webapp.RequestHandler):
|
| - """Web handler for RPC service.
|
| -
|
| - Overridden methods:
|
| - get: All requests handled by 'handle' method. HTTP method stored in
|
| - attribute. Takes remote_method parameter as derived from the URL mapping.
|
| - post: All requests handled by 'handle' method. HTTP method stored in
|
| - attribute. Takes remote_method parameter as derived from the URL mapping.
|
| - redirect: Not implemented for this service handler.
|
| -
|
| - New methods:
|
| - handle: Handle request for both GET and POST.
|
| -
|
| - Attributes (in addition to attributes in RequestHandler):
|
| - service: Service instance associated with request being handled.
|
| - method: Method of request. Used by RPCMapper to determine match.
|
| - remote_method: Sub-path as provided to the 'get' and 'post' methods.
|
| - """
|
| -
|
| - def __init__(self, factory, service):
|
| - """Constructor.
|
| -
|
| - Args:
|
| - factory: Instance of ServiceFactory used for constructing new service
|
| - instances used for handling requests.
|
| - service: Service instance used for handling RPC.
|
| - """
|
| - self.__factory = factory
|
| - self.__service = service
|
| -
|
| - @property
|
| - def service(self):
|
| - return self.__service
|
| -
|
| - def __show_info(self, service_path, remote_method):
|
| - self.response.headers['content-type'] = 'text/plain; charset=utf-8'
|
| - response_message = []
|
| - if remote_method:
|
| - response_message.append('%s.%s is a ProtoRPC method.\n\n' %(
|
| - service_path, remote_method))
|
| - else:
|
| - response_message.append('%s is a ProtoRPC service.\n\n' % service_path)
|
| - definition_name_function = getattr(self.__service, 'definition_name', None)
|
| - if definition_name_function:
|
| - definition_name = definition_name_function()
|
| - else:
|
| - definition_name = '%s.%s' % (self.__service.__module__,
|
| - self.__service.__class__.__name__)
|
| -
|
| - response_message.append('Service %s\n\n' % definition_name)
|
| - response_message.append('More about ProtoRPC: ')
|
| -
|
| - response_message.append('http://code.google.com/p/google-protorpc\n')
|
| - self.response.out.write(util.pad_string(''.join(response_message)))
|
| -
|
| - def get(self, service_path, remote_method):
|
| - """Handler method for GET requests.
|
| -
|
| - Args:
|
| - service_path: Service path derived from request URL.
|
| - remote_method: Sub-path after service path has been matched.
|
| - """
|
| - self.handle('GET', service_path, remote_method)
|
| -
|
| - def post(self, service_path, remote_method):
|
| - """Handler method for POST requests.
|
| -
|
| - Args:
|
| - service_path: Service path derived from request URL.
|
| - remote_method: Sub-path after service path has been matched.
|
| - """
|
| - self.handle('POST', service_path, remote_method)
|
| -
|
| - def redirect(self, uri, permanent=False):
|
| - """Not supported for services."""
|
| - raise NotImplementedError('Services do not currently support redirection.')
|
| -
|
| - def __send_error(self,
|
| - http_code,
|
| - status_state,
|
| - error_message,
|
| - mapper,
|
| - error_name=None):
|
| - status = remote.RpcStatus(state=status_state,
|
| - error_message=error_message,
|
| - error_name=error_name)
|
| - mapper.build_response(self, status)
|
| - self.response.headers['content-type'] = mapper.default_content_type
|
| -
|
| - logging.error(error_message)
|
| - response_content = self.response.out.getvalue()
|
| - padding = ' ' * max(0, 512 - len(response_content))
|
| - self.response.out.write(padding)
|
| -
|
| - self.response.set_status(http_code, error_message)
|
| -
|
| - def __send_simple_error(self, code, message, pad=True):
|
| - """Send error to caller without embedded message."""
|
| - self.response.headers['content-type'] = 'text/plain; charset=utf-8'
|
| - logging.error(message)
|
| - self.response.set_status(code, message)
|
| -
|
| - response_message = six.moves.http_client.responses.get(code, 'Unknown Error')
|
| - if pad:
|
| - response_message = util.pad_string(response_message)
|
| - self.response.out.write(response_message)
|
| -
|
| - def __get_content_type(self):
|
| - content_type = self.request.headers.get('content-type', None)
|
| - if not content_type:
|
| - content_type = self.request.environ.get('HTTP_CONTENT_TYPE', None)
|
| - if not content_type:
|
| - return None
|
| -
|
| - # Lop off parameters from the end (for example content-encoding)
|
| - return content_type.split(';', 1)[0].lower()
|
| -
|
| - def __headers(self, content_type):
|
| - for name in self.request.headers:
|
| - name = name.lower()
|
| - if name == 'content-type':
|
| - value = content_type
|
| - elif name == 'content-length':
|
| - value = str(len(self.request.body))
|
| - else:
|
| - value = self.request.headers.get(name, '')
|
| - yield name, value
|
| -
|
| - def handle(self, http_method, service_path, remote_method):
|
| - """Handle a service request.
|
| -
|
| - The handle method will handle either a GET or POST response.
|
| - It is up to the individual mappers from the handler factory to determine
|
| - which request methods they can service.
|
| -
|
| - If the protocol is not recognized, the request does not provide a correct
|
| - request for that protocol or the service object does not support the
|
| - requested RPC method, will return error code 400 in the response.
|
| -
|
| - Args:
|
| - http_method: HTTP method of request.
|
| - service_path: Service path derived from request URL.
|
| - remote_method: Sub-path after service path has been matched.
|
| - """
|
| - self.response.headers['x-content-type-options'] = 'nosniff'
|
| - if not remote_method and http_method == 'GET':
|
| - # Special case a normal get request, presumably via a browser.
|
| - self.error(405)
|
| - self.__show_info(service_path, remote_method)
|
| - return
|
| -
|
| - content_type = self.__get_content_type()
|
| -
|
| - # Provide server state to the service. If the service object does not have
|
| - # an "initialize_request_state" method, will not attempt to assign state.
|
| - try:
|
| - state_initializer = self.service.initialize_request_state
|
| - except AttributeError:
|
| - pass
|
| - else:
|
| - server_port = self.request.environ.get('SERVER_PORT', None)
|
| - if server_port:
|
| - server_port = int(server_port)
|
| -
|
| - request_state = remote.HttpRequestState(
|
| - remote_host=self.request.environ.get('REMOTE_HOST', None),
|
| - remote_address=self.request.environ.get('REMOTE_ADDR', None),
|
| - server_host=self.request.environ.get('SERVER_HOST', None),
|
| - server_port=server_port,
|
| - http_method=http_method,
|
| - service_path=service_path,
|
| - headers=list(self.__headers(content_type)))
|
| - state_initializer(request_state)
|
| -
|
| - if not content_type:
|
| - self.__send_simple_error(400, 'Invalid RPC request: missing content-type')
|
| - return
|
| -
|
| - # Search for mapper to mediate request.
|
| - for mapper in self.__factory.all_request_mappers():
|
| - if content_type in mapper.content_types:
|
| - break
|
| - else:
|
| - if http_method == 'GET':
|
| - self.error(six.moves.http_client.UNSUPPORTED_MEDIA_TYPE)
|
| - self.__show_info(service_path, remote_method)
|
| - else:
|
| - self.__send_simple_error(six.moves.http_client.UNSUPPORTED_MEDIA_TYPE,
|
| - 'Unsupported content-type: %s' % content_type)
|
| - return
|
| -
|
| - try:
|
| - if http_method not in mapper.http_methods:
|
| - if http_method == 'GET':
|
| - self.error(six.moves.http_client.METHOD_NOT_ALLOWED)
|
| - self.__show_info(service_path, remote_method)
|
| - else:
|
| - self.__send_simple_error(six.moves.http_client.METHOD_NOT_ALLOWED,
|
| - 'Unsupported HTTP method: %s' % http_method)
|
| - return
|
| -
|
| - try:
|
| - try:
|
| - method = getattr(self.service, remote_method)
|
| - method_info = method.remote
|
| - except AttributeError as err:
|
| - self.__send_error(
|
| - 400, remote.RpcState.METHOD_NOT_FOUND_ERROR,
|
| - 'Unrecognized RPC method: %s' % remote_method,
|
| - mapper)
|
| - return
|
| -
|
| - request = mapper.build_request(self, method_info.request_type)
|
| - except (RequestError, messages.DecodeError) as err:
|
| - self.__send_error(400,
|
| - remote.RpcState.REQUEST_ERROR,
|
| - 'Error parsing ProtoRPC request (%s)' % err,
|
| - mapper)
|
| - return
|
| -
|
| - try:
|
| - response = method(request)
|
| - except remote.ApplicationError as err:
|
| - self.__send_error(400,
|
| - remote.RpcState.APPLICATION_ERROR,
|
| - err.message,
|
| - mapper,
|
| - err.error_name)
|
| - return
|
| -
|
| - mapper.build_response(self, response)
|
| - except Exception as err:
|
| - logging.error('An unexpected error occured when handling RPC: %s',
|
| - err, exc_info=1)
|
| -
|
| - self.__send_error(500,
|
| - remote.RpcState.SERVER_ERROR,
|
| - 'Internal Server Error',
|
| - mapper)
|
| - return
|
| -
|
| -
|
| -# TODO(rafek): Support tag-id only forms.
|
| -class URLEncodedRPCMapper(RPCMapper):
|
| - """Request mapper for application/x-www-form-urlencoded forms.
|
| -
|
| - This mapper is useful for building forms that can invoke RPC. Many services
|
| - are also configured to work using URL encoded request information because
|
| - of its perceived ease of programming and debugging.
|
| -
|
| - The mapper must be provided with at least method_parameter or
|
| - remote_method_pattern so that it is possible to determine how to determine the
|
| - requests RPC method. If both are provided, the service will respond to both
|
| - method request types, however, only one may be present in a given request.
|
| - If both types are detected, the request will not match.
|
| - """
|
| -
|
| - def __init__(self, parameter_prefix=''):
|
| - """Constructor.
|
| -
|
| - Args:
|
| - parameter_prefix: If provided, all the parameters in the form are
|
| - expected to begin with that prefix.
|
| - """
|
| - # Private attributes:
|
| - # __parameter_prefix: parameter prefix as provided by constructor
|
| - # parameter.
|
| - super(URLEncodedRPCMapper, self).__init__(['POST'],
|
| - _URLENCODED_CONTENT_TYPE,
|
| - self)
|
| - self.__parameter_prefix = parameter_prefix
|
| -
|
| - def encode_message(self, message):
|
| - """Encode a message using parameter prefix.
|
| -
|
| - Args:
|
| - message: Message to URL Encode.
|
| -
|
| - Returns:
|
| - URL encoded message.
|
| - """
|
| - return protourlencode.encode_message(message,
|
| - prefix=self.__parameter_prefix)
|
| -
|
| - @property
|
| - def parameter_prefix(self):
|
| - """Prefix all form parameters are expected to begin with."""
|
| - return self.__parameter_prefix
|
| -
|
| - def build_request(self, handler, request_type):
|
| - """Build request from URL encoded HTTP request.
|
| -
|
| - Constructs message from names of URL encoded parameters. If this service
|
| - handler has a parameter prefix, parameters must begin with it or are
|
| - ignored.
|
| -
|
| - Args:
|
| - handler: RequestHandler instance that is servicing request.
|
| - request_type: Message type to build.
|
| -
|
| - Returns:
|
| - Instance of request_type populated by protocol buffer in request
|
| - parameters.
|
| -
|
| - Raises:
|
| - RequestError if message type contains nested message field or repeated
|
| - message field. Will raise RequestError if there are any repeated
|
| - parameters.
|
| - """
|
| - request = request_type()
|
| - builder = protourlencode.URLEncodedRequestBuilder(
|
| - request, prefix=self.__parameter_prefix)
|
| - for argument in sorted(handler.request.arguments()):
|
| - values = handler.request.get_all(argument)
|
| - try:
|
| - builder.add_parameter(argument, values)
|
| - except messages.DecodeError as err:
|
| - raise RequestError(str(err))
|
| - return request
|
| -
|
| -
|
| -class ProtobufRPCMapper(RPCMapper):
|
| - """Request mapper for application/x-protobuf service requests.
|
| -
|
| - This mapper will parse protocol buffer from a POST body and return the request
|
| - as a protocol buffer.
|
| - """
|
| -
|
| - def __init__(self):
|
| - super(ProtobufRPCMapper, self).__init__(['POST'],
|
| - _PROTOBUF_CONTENT_TYPE,
|
| - protobuf)
|
| -
|
| -
|
| -class JSONRPCMapper(RPCMapper):
|
| - """Request mapper for application/x-protobuf service requests.
|
| -
|
| - This mapper will parse protocol buffer from a POST body and return the request
|
| - as a protocol buffer.
|
| - """
|
| -
|
| - def __init__(self):
|
| - super(JSONRPCMapper, self).__init__(
|
| - ['POST'],
|
| - _JSON_CONTENT_TYPE,
|
| - protojson,
|
| - content_types=_EXTRA_JSON_CONTENT_TYPES)
|
| -
|
| -
|
| -def service_mapping(services,
|
| - registry_path=DEFAULT_REGISTRY_PATH):
|
| - """Create a services mapping for use with webapp.
|
| -
|
| - Creates basic default configuration and registration for ProtoRPC services.
|
| - Each service listed in the service mapping has a standard service handler
|
| - factory created for it.
|
| -
|
| - The list of mappings can either be an explicit path to service mapping or
|
| - just services. If mappings are just services, they will automatically
|
| - be mapped to their default name. For exampel:
|
| -
|
| - package = 'my_package'
|
| -
|
| - class MyService(remote.Service):
|
| - ...
|
| -
|
| - server_mapping([('/my_path', MyService), # Maps to /my_path
|
| - MyService, # Maps to /my_package/MyService
|
| - ])
|
| -
|
| - Specifying a service mapping:
|
| -
|
| - Normally services are mapped to URL paths by specifying a tuple
|
| - (path, service):
|
| - path: The path the service resides on.
|
| - service: The service class or service factory for creating new instances
|
| - of the service. For more information about service factories, please
|
| - see remote.Service.new_factory.
|
| -
|
| - If no tuple is provided, and therefore no path specified, a default path
|
| - is calculated by using the fully qualified service name using a URL path
|
| - separator for each of its components instead of a '.'.
|
| -
|
| - Args:
|
| - services: Can be service type, service factory or string definition name of
|
| - service being mapped or list of tuples (path, service):
|
| - path: Path on server to map service to.
|
| - service: Service type, service factory or string definition name of
|
| - service being mapped.
|
| - Can also be a dict. If so, the keys are treated as the path and values as
|
| - the service.
|
| - registry_path: Path to give to registry service. Use None to disable
|
| - registry service.
|
| -
|
| - Returns:
|
| - List of tuples defining a mapping of request handlers compatible with a
|
| - webapp application.
|
| -
|
| - Raises:
|
| - ServiceConfigurationError when duplicate paths are provided.
|
| - """
|
| - if isinstance(services, dict):
|
| - services = six.iteritems(services)
|
| - mapping = []
|
| - registry_map = {}
|
| -
|
| - if registry_path is not None:
|
| - registry_service = registry.RegistryService.new_factory(registry_map)
|
| - services = list(services) + [(registry_path, registry_service)]
|
| - mapping.append((registry_path + r'/form(?:/)?',
|
| - forms.FormsHandler.new_factory(registry_path)))
|
| - mapping.append((registry_path + r'/form/(.+)', forms.ResourceHandler))
|
| -
|
| - paths = set()
|
| - for service_item in services:
|
| - infer_path = not isinstance(service_item, (list, tuple))
|
| - if infer_path:
|
| - service = service_item
|
| - else:
|
| - service = service_item[1]
|
| -
|
| - service_class = getattr(service, 'service_class', service)
|
| -
|
| - if infer_path:
|
| - path = '/' + service_class.definition_name().replace('.', '/')
|
| - else:
|
| - path = service_item[0]
|
| -
|
| - if path in paths:
|
| - raise ServiceConfigurationError(
|
| - 'Path %r is already defined in service mapping' % path.encode('utf-8'))
|
| - else:
|
| - paths.add(path)
|
| -
|
| - # Create service mapping for webapp.
|
| - new_mapping = ServiceHandlerFactory.default(service).mapping(path)
|
| - mapping.append(new_mapping)
|
| -
|
| - # Update registry with service class.
|
| - registry_map[path] = service_class
|
| -
|
| - return mapping
|
| -
|
| -
|
| -def run_services(services,
|
| - registry_path=DEFAULT_REGISTRY_PATH):
|
| - """Handle CGI request using service mapping.
|
| -
|
| - Args:
|
| - Same as service_mapping.
|
| - """
|
| - mappings = service_mapping(services, registry_path=registry_path)
|
| - application = webapp.WSGIApplication(mappings)
|
| - webapp_util.run_wsgi_app(application)
|
|
|