18d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#!/usr/bin/env python
28d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
38d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# Copyright 2010 Google Inc.
48d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
58d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# Licensed under the Apache License, Version 2.0 (the "License");
68d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# you may not use this file except in compliance with the License.
78d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# You may obtain a copy of the License at
88d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
98d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#     http://www.apache.org/licenses/LICENSE-2.0
108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# Unless required by applicable law or agreed to in writing, software
128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# distributed under the License is distributed on an "AS IS" BASIS,
138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# See the License for the specific language governing permissions and
158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# limitations under the License.
168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi#
178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi"""Handlers for remote services.
198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi KandoiThis module contains classes that may be used to build a service
218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoion top of the App Engine Webapp framework.
228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi KandoiThe services request handler can be configured to handle requests in a number
248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiof different request formats.  All different request formats must have a way
258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoito map the request to the service handlers defined request message.Message
268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass.  The handler can also send a response in any format that can be mapped
278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom the response message.Message class.
288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi KandoiParticipants in an RPC:
308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  There are four classes involved with the life cycle of an RPC.
328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Service factory: A user-defined service factory that is responsible for
348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      instantiating an RPC service.  The methods intended for use as RPC
358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      methods must be decorated by the 'remote' decorator.
368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    RPCMapper: Responsible for determining whether or not a specific request
388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      matches a particular RPC format and translating between the actual
398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      request/response and the underlying message types.  A single instance of
408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      an RPCMapper sub-class is required per service configuration.  Each
418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      mapper must be usable across multiple requests.
428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ServiceHandler: A webapp.RequestHandler sub-class that responds to the
448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      webapp framework.  It mediates between the RPCMapper and service
458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      implementation class during a request.  As determined by the Webapp
468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      framework, a new ServiceHandler instance is created to handle each
478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      user request.  A handler is never used to handle more than one request.
488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ServiceHandlerFactory: A class that is responsible for creating new,
508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      properly configured ServiceHandler instance for each request.  The
518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      factory is configured by providing it with a set of RPCMapper instances.
528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      When the Webapp framework invokes the service handler, the handler
538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      creates a new service class instance.  The service class instance is
548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      provided with a reference to the handler.  A single instance of an
558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      RPCMapper sub-class is required to configure each service.  Each mapper
568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      instance must be usable across multiple requests.
578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi KandoiRPC mappers:
598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  RPC mappers translate between a single HTTP based RPC protocol and the
618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  underlying service implementation.  Each RPC mapper must configured
628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  with the following information to determine if it is an appropriate
638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  mapper for a given request:
648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    http_methods: Set of HTTP methods supported by handler.
668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    content_types: Set of supported content types.
688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    default_content_type: Default content type for handler responses.
708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Built-in mapper implementations:
728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    URLEncodedRPCMapper: Matches requests that are compatible with post
748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      forms with the 'application/x-www-form-urlencoded' content-type
758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      (this content type is the default if none is specified.  It
768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      translates post parameters into request parameters.
778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ProtobufRPCMapper: Matches requests that are compatible with post
798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      forms with the 'application/x-google-protobuf' content-type.  It
808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      reads the contents of a binary post request.
818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi KandoiPublic Exceptions:
838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Error: Base class for service handler errors.
848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  ServiceConfigurationError: Raised when a service not correctly configured.
858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  RequestError: Raised by RPC mappers when there is an error in its request
868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    or request format.
878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  ResponseError: Raised by RPC mappers when there is an error in its response.
888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi"""
898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport six
908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi__author__ = 'rafek@google.com (Rafe Kaplan)'
928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport six.moves.http_client
948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiimport logging
958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .google_imports import webapp
978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .google_imports import webapp_util
988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import messages
998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import protobuf
1008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import protojson
1018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import protourlencode
1028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import registry
1038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import remote
1048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom .. import util
1058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoifrom . import forms
1068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi__all__ = [
1088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'Error',
1098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'RequestError',
1108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'ResponseError',
1118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'ServiceConfigurationError',
1128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'DEFAULT_REGISTRY_PATH',
1148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'ProtobufRPCMapper',
1168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'RPCMapper',
1178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'ServiceHandler',
1188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'ServiceHandlerFactory',
1198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'URLEncodedRPCMapper',
1208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'JSONRPCMapper',
1218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'service_mapping',
1228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    'run_services',
1238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi]
1248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass Error(Exception):
1278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Base class for all errors in service handlers module."""
1288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass ServiceConfigurationError(Error):
1318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """When service configuration is incorrect."""
1328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass RequestError(Error):
1358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Error occurred when building request."""
1368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass ResponseError(Error):
1398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Error occurred when building response."""
1408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_URLENCODED_CONTENT_TYPE = protourlencode.CONTENT_TYPE
1438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_PROTOBUF_CONTENT_TYPE = protobuf.CONTENT_TYPE
1448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_JSON_CONTENT_TYPE = protojson.CONTENT_TYPE
1458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_EXTRA_JSON_CONTENT_TYPES = ['application/x-javascript',
1478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                             'text/javascript',
1488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                             'text/x-javascript',
1498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                             'text/x-json',
1508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                             'text/json',
1518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                            ]
1528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# The whole method pattern is an optional regex.  It contains a single
1548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# group used for mapping to the query parameter.  This is passed to the
1558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# parameters of 'get' and 'post' on the ServiceHandler.
1568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi_METHOD_PATTERN = r'(?:\.([^?]*))?'
1578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi KandoiDEFAULT_REGISTRY_PATH = forms.DEFAULT_REGISTRY_PATH
1598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass RPCMapper(object):
1628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Interface to mediate between request and service object.
1638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Request mappers are implemented to support various types of
1658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  RPC protocols.  It is responsible for identifying whether a
1668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  given request matches a particular protocol, resolve the remote
1678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  method to invoke and mediate between the request and appropriate
1688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  protocol messages for the remote method.
1698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
1708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @util.positional(4)
1728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __init__(self,
1738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi               http_methods,
1748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi               default_content_type,
1758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi               protocol,
1768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi               content_types=None):
1778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Constructor.
1788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
1808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      http_methods: Set of HTTP methods supported by mapper.
1818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      default_content_type: Default content type supported by mapper.
1828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      protocol: The protocol implementation.  Must implement encode_message and
1838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        decode_message.
1848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      content_types: Set of additionally supported content types.
1858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
1868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__http_methods = frozenset(http_methods)
1878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__default_content_type = default_content_type
1888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__protocol = protocol
1898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if content_types is None:
1918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      content_types = []
1928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__content_types = frozenset([self.__default_content_type] +
1938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                     content_types)
1948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @property
1968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def http_methods(self):
1978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return self.__http_methods
1988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
1998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @property
2008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def default_content_type(self):
2018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return self.__default_content_type
2028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @property
2048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def content_types(self):
2058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return self.__content_types
2068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def build_request(self, handler, request_type):
2088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Build request message based on request.
2098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Each request mapper implementation is responsible for converting a
2118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    request to an appropriate message instance.
2128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
2148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      handler: RequestHandler instance that is servicing request.
2158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        Must be initialized with request object and been previously determined
2168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        to matching the protocol of the RPCMapper.
2178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      request_type: Message type to build.
2188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
2208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      Instance of request_type populated by protocol buffer in request body.
2218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Raises:
2238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      RequestError if the mapper implementation is not able to correctly
2248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      convert the request to the appropriate message.
2258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
2268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    try:
2278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return self.__protocol.decode_message(request_type, handler.request.body)
2288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    except (messages.ValidationError, messages.DecodeError) as err:
2298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      raise RequestError('Unable to parse request content: %s' % err)
2308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def build_response(self, handler, response, pad_string=False):
2328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Build response based on service object response message.
2338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Each request mapper implementation is responsible for converting a
2358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response message to an appropriate handler response.
2368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
2388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      handler: RequestHandler instance that is servicing request.
2398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        Must be initialized with request object and been previously determined
2408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        to matching the protocol of the RPCMapper.
2418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      response: Response message as returned from the service object.
2428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Raises:
2448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      ResponseError if the mapper implementation is not able to correctly
2458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      convert the message to an appropriate response.
2468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
2478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    try:
2488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      encoded_message = self.__protocol.encode_message(response)
2498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    except messages.ValidationError as err:
2508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      raise ResponseError('Unable to encode message: %s' % err)
2518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
2528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      handler.response.headers['Content-Type'] = self.default_content_type
2538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      handler.response.out.write(encoded_message)
2548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass ServiceHandlerFactory(object):
2578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Factory class used for instantiating new service handlers.
2588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Normally a handler class is passed directly to the webapp framework
2608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  so that it can be simply instantiated to handle a single request.
2618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  The service handler, however, must be configured with additional
2628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  information so that it knows how to instantiate a service object.
2638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  This class acts the same as a normal RequestHandler class by
2648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  overriding the __call__ method to correctly configures a ServiceHandler
2658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  instance with a new service object.
2668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  The factory must also provide a set of RPCMapper instances which
2688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  examine a request to determine what protocol is being used and mediates
2698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  between the request and the service object.
2708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  The mapping of a service handler must have a single group indicating the
2728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  part of the URL path that maps to the request method.  This group must
2738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  exist but can be optional for the request (the group may be followed by
2748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  '?' in the regular expression matching the request).
2758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Usage:
2778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    stock_factory = ServiceHandlerFactory(StockService)
2798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ... configure stock_factory by adding RPCMapper instances ...
2808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    application = webapp.WSGIApplication(
2828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        [stock_factory.mapping('/stocks')])
2838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Default usage:
2858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    application = webapp.WSGIApplication(
2878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        [ServiceHandlerFactory.default(StockService).mapping('/stocks')])
2888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
2898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __init__(self, service_factory):
2918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Constructor.
2928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
2938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
2948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service_factory: Service factory to instantiate and provide to
2958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        service handler.
2968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
2978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__service_factory = service_factory
2988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__request_mappers = []
2998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def all_request_mappers(self):
3018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Get all request mappers.
3028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
3048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      Iterator of all request mappers used by this service factory.
3058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
3068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return iter(self.__request_mappers)
3078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def add_request_mapper(self, mapper):
3098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Add request mapper to end of request mapper list."""
3108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__request_mappers.append(mapper)
3118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __call__(self):
3138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Construct a new service handler instance."""
3148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return ServiceHandler(self, self.__service_factory())
3158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @property
3178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def service_factory(self):
3188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Service factory associated with this factory."""
3198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return self.__service_factory
3208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @staticmethod
3228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __check_path(path):
3238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Check a path parameter.
3248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Make sure a provided path parameter is compatible with the
3268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    webapp URL mapping.
3278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
3298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      path: Path to check.  This is a plain path, not a regular expression.
3308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Raises:
3328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      ValueError if path does not start with /, path ends with /.
3338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
3348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if path.endswith('/'):
3358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      raise ValueError('Path %s must not end with /.' % path)
3368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def mapping(self, path):
3388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Convenience method to map service to application.
3398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
3418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      path: Path to map service to.  It must be a simple path
3428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        with a leading / and no trailing /.
3438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
3458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      Mapping from service URL to service handler factory.
3468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
3478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__check_path(path)
3488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    service_url_pattern = r'(%s)%s' % (path, _METHOD_PATTERN)
3508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return service_url_pattern, self
3528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @classmethod
3548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def default(cls, service_factory, parameter_prefix=''):
3558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Convenience method to map default factory configuration to application.
3568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Creates a standardized default service factory configuration that pre-maps
3588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    the URL encoded protocol handler to the factory.
3598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
3618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service_factory: Service factory to instantiate and provide to
3628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        service handler.
3638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      method_parameter: The name of the form parameter used to determine the
3648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        method to invoke used by the URLEncodedRPCMapper.  If None, no
3658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        parameter is used and the mapper will only match against the form
3668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        path-name.  Defaults to 'method'.
3678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      parameter_prefix: If provided, all the parameters in the form are
3688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        expected to begin with that prefix by the URLEncodedRPCMapper.
3698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
3718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      Mapping from service URL to service handler factory.
3728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
3738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    factory = cls(service_factory)
3748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    factory.add_request_mapper(ProtobufRPCMapper())
3768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    factory.add_request_mapper(JSONRPCMapper())
3778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return factory
3798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass ServiceHandler(webapp.RequestHandler):
3828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Web handler for RPC service.
3838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Overridden methods:
3858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    get: All requests handled by 'handle' method.  HTTP method stored in
3868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      attribute.  Takes remote_method parameter as derived from the URL mapping.
3878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    post: All requests handled by 'handle' method.  HTTP method stored in
3888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      attribute.  Takes remote_method parameter as derived from the URL mapping.
3898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    redirect: Not implemented for this service handler.
3908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  New methods:
3928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    handle: Handle request for both GET and POST.
3938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
3948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Attributes (in addition to attributes in RequestHandler):
3958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    service: Service instance associated with request being handled.
3968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    method: Method of request.  Used by RPCMapper to determine match.
3978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    remote_method: Sub-path as provided to the 'get' and 'post' methods.
3988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
3998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __init__(self, factory, service):
4018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Constructor.
4028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
4048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      factory: Instance of ServiceFactory used for constructing new service
4058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        instances used for handling requests.
4068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service: Service instance used for handling RPC.
4078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
4088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__factory = factory
4098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__service = service
4108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @property
4128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def service(self):
4138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return self.__service
4148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __show_info(self, service_path, remote_method):
4168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.headers['content-type'] = 'text/plain; charset=utf-8'
4178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response_message = []
4188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if remote_method:
4198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      response_message.append('%s.%s is a ProtoRPC method.\n\n' %(
4208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        service_path, remote_method))
4218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
4228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      response_message.append('%s is a ProtoRPC service.\n\n' % service_path)
4238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    definition_name_function = getattr(self.__service, 'definition_name', None)
4248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if definition_name_function:
4258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      definition_name = definition_name_function()
4268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
4278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      definition_name = '%s.%s' % (self.__service.__module__,
4288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                   self.__service.__class__.__name__)
4298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response_message.append('Service %s\n\n' % definition_name)
4318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response_message.append('More about ProtoRPC: ')
4328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response_message.append('http://code.google.com/p/google-protorpc\n')
4348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.out.write(util.pad_string(''.join(response_message)))
4358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def get(self, service_path, remote_method):
4378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Handler method for GET requests.
4388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
4408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service_path: Service path derived from request URL.
4418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      remote_method: Sub-path after service path has been matched.
4428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
4438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.handle('GET', service_path, remote_method)
4448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def post(self, service_path, remote_method):
4468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Handler method for POST requests.
4478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
4498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service_path: Service path derived from request URL.
4508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      remote_method: Sub-path after service path has been matched.
4518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
4528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.handle('POST', service_path, remote_method)
4538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def redirect(self, uri, permanent=False):
4558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Not supported for services."""
4568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    raise NotImplementedError('Services do not currently support redirection.')
4578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __send_error(self,
4598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                   http_code,
4608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                   status_state,
4618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                   error_message,
4628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                   mapper,
4638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                   error_name=None):
4648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    status = remote.RpcStatus(state=status_state,
4658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                              error_message=error_message,
4668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                              error_name=error_name)
4678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    mapper.build_response(self, status)
4688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.headers['content-type'] = mapper.default_content_type
4698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    logging.error(error_message)
4718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response_content = self.response.out.getvalue()
4728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    padding = ' ' * max(0, 512 - len(response_content))
4738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.out.write(padding)
4748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.set_status(http_code, error_message)
4768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __send_simple_error(self, code, message, pad=True):
4788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Send error to caller without embedded message."""
4798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.headers['content-type'] = 'text/plain; charset=utf-8'
4808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    logging.error(message)
4818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.set_status(code, message)
4828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    response_message = six.moves.http_client.responses.get(code, 'Unknown Error')
4848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if pad:
4858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      response_message = util.pad_string(response_message)
4868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.out.write(response_message)
4878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __get_content_type(self):
4898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    content_type = self.request.headers.get('content-type', None)
4908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if not content_type:
4918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      content_type = self.request.environ.get('HTTP_CONTENT_TYPE', None)
4928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if not content_type:
4938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return None
4948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Lop off parameters from the end (for example content-encoding)
4968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return content_type.split(';', 1)[0].lower()
4978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
4988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __headers(self, content_type):
4998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    for name in self.request.headers:
5008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      name = name.lower()
5018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      if name == 'content-type':
5028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        value = content_type
5038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      elif name == 'content-length':
5048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        value = str(len(self.request.body))
5058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      else:
5068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        value = self.request.headers.get(name, '')
5078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      yield name, value
5088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def handle(self, http_method, service_path, remote_method):
5108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Handle a service request.
5118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    The handle method will handle either a GET or POST response.
5138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    It is up to the individual mappers from the handler factory to determine
5148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    which request methods they can service.
5158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    If the protocol is not recognized, the request does not provide a correct
5178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    request for that protocol or the service object does not support the
5188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    requested RPC method, will return error code 400 in the response.
5198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
5218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      http_method: HTTP method of request.
5228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service_path: Service path derived from request URL.
5238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      remote_method: Sub-path after service path has been matched.
5248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
5258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.response.headers['x-content-type-options'] = 'nosniff'
5268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if not remote_method and http_method == 'GET':
5278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      # Special case a normal get request, presumably via a browser.
5288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      self.error(405)
5298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      self.__show_info(service_path, remote_method)
5308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return
5318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    content_type = self.__get_content_type()
5338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Provide server state to the service.  If the service object does not have
5358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # an "initialize_request_state" method, will not attempt to assign state.
5368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    try:
5378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      state_initializer = self.service.initialize_request_state
5388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    except AttributeError:
5398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      pass
5408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
5418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      server_port = self.request.environ.get('SERVER_PORT', None)
5428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      if server_port:
5438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        server_port = int(server_port)
5448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      request_state = remote.HttpRequestState(
5468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          remote_host=self.request.environ.get('REMOTE_HOST', None),
5478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          remote_address=self.request.environ.get('REMOTE_ADDR', None),
5488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          server_host=self.request.environ.get('SERVER_HOST', None),
5498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          server_port=server_port,
5508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          http_method=http_method,
5518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          service_path=service_path,
5528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          headers=list(self.__headers(content_type)))
5538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      state_initializer(request_state)
5548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if not content_type:
5568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      self.__send_simple_error(400, 'Invalid RPC request: missing content-type')
5578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return
5588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Search for mapper to mediate request.
5608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    for mapper in self.__factory.all_request_mappers():
5618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      if content_type in mapper.content_types:
5628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        break
5638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
5648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      if http_method == 'GET':
5658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        self.error(six.moves.http_client.UNSUPPORTED_MEDIA_TYPE)
5668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        self.__show_info(service_path, remote_method)
5678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      else:
5688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        self.__send_simple_error(six.moves.http_client.UNSUPPORTED_MEDIA_TYPE,
5698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                 'Unsupported content-type: %s' % content_type)
5708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return
5718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    try:
5738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      if http_method not in mapper.http_methods:
5748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        if http_method == 'GET':
5758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          self.error(six.moves.http_client.METHOD_NOT_ALLOWED)
5768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          self.__show_info(service_path, remote_method)
5778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        else:
5788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          self.__send_simple_error(six.moves.http_client.METHOD_NOT_ALLOWED,
5798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                   'Unsupported HTTP method: %s' % http_method)
5808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return
5818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      try:
5838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        try:
5848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          method = getattr(self.service, remote_method)
5858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          method_info = method.remote
5868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        except AttributeError as err:
5878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          self.__send_error(
5888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          400, remote.RpcState.METHOD_NOT_FOUND_ERROR,
5898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            'Unrecognized RPC method: %s' % remote_method,
5908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi            mapper)
5918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi          return
5928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
5938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        request = mapper.build_request(self, method_info.request_type)
5948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      except (RequestError, messages.DecodeError) as err:
5958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        self.__send_error(400,
5968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          remote.RpcState.REQUEST_ERROR,
5978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          'Error parsing ProtoRPC request (%s)' % err,
5988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          mapper)
5998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return
6008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      try:
6028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        response = method(request)
6038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      except remote.ApplicationError as err:
6048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        self.__send_error(400,
6058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          remote.RpcState.APPLICATION_ERROR,
6068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          err.message,
6078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          mapper,
6088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                          err.error_name)
6098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        return
6108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      mapper.build_response(self, response)
6128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    except Exception as err:
6138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      logging.error('An unexpected error occured when handling RPC: %s',
6148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                    err, exc_info=1)
6158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      self.__send_error(500,
6178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                        remote.RpcState.SERVER_ERROR,
6188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                        'Internal Server Error',
6198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                        mapper)
6208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      return
6218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi# TODO(rafek): Support tag-id only forms.
6248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass URLEncodedRPCMapper(RPCMapper):
6258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Request mapper for application/x-www-form-urlencoded forms.
6268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  This mapper is useful for building forms that can invoke RPC.  Many services
6288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  are also configured to work using URL encoded request information because
6298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  of its perceived ease of programming and debugging.
6308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  The mapper must be provided with at least method_parameter or
6328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  remote_method_pattern so that it is possible to determine how to determine the
6338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  requests RPC method.  If both are provided, the service will respond to both
6348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  method request types, however, only one may be present in a given request.
6358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  If both types are detected, the request will not match.
6368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
6378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __init__(self, parameter_prefix=''):
6398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Constructor.
6408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
6428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      parameter_prefix: If provided, all the parameters in the form are
6438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        expected to begin with that prefix.
6448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
6458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Private attributes:
6468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    #   __parameter_prefix: parameter prefix as provided by constructor
6478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    #     parameter.
6488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    super(URLEncodedRPCMapper, self).__init__(['POST'],
6498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                              _URLENCODED_CONTENT_TYPE,
6508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                              self)
6518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    self.__parameter_prefix = parameter_prefix
6528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def encode_message(self, message):
6548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Encode a message using parameter prefix.
6558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
6578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      message: Message to URL Encode.
6588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
6608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      URL encoded message.
6618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
6628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return protourlencode.encode_message(message,
6638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                         prefix=self.__parameter_prefix)
6648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  @property
6668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def parameter_prefix(self):
6678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Prefix all form parameters are expected to begin with."""
6688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return self.__parameter_prefix
6698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def build_request(self, handler, request_type):
6718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """Build request from URL encoded HTTP request.
6728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Constructs message from names of URL encoded parameters.  If this service
6748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    handler has a parameter prefix, parameters must begin with it or are
6758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ignored.
6768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Args:
6788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      handler: RequestHandler instance that is servicing request.
6798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      request_type: Message type to build.
6808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Returns:
6828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      Instance of request_type populated by protocol buffer in request
6838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        parameters.
6848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
6858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Raises:
6868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      RequestError if message type contains nested message field or repeated
6878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      message field.  Will raise RequestError if there are any repeated
6888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      parameters.
6898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    """
6908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    request = request_type()
6918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    builder = protourlencode.URLEncodedRequestBuilder(
6928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        request, prefix=self.__parameter_prefix)
6938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    for argument in sorted(handler.request.arguments()):
6948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      values = handler.request.get_all(argument)
6958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      try:
6968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        builder.add_parameter(argument, values)
6978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      except messages.DecodeError as err:
6988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        raise RequestError(str(err))
6998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    return request
7008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass ProtobufRPCMapper(RPCMapper):
7038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Request mapper for application/x-protobuf service requests.
7048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  This mapper will parse protocol buffer from a POST body and return the request
7068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  as a protocol buffer.
7078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
7088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __init__(self):
7108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    super(ProtobufRPCMapper, self).__init__(['POST'],
7118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                            _PROTOBUF_CONTENT_TYPE,
7128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                                            protobuf)
7138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoiclass JSONRPCMapper(RPCMapper):
7168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Request mapper for application/x-protobuf service requests.
7178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  This mapper will parse protocol buffer from a POST body and return the request
7198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  as a protocol buffer.
7208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
7218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  def __init__(self):
7238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    super(JSONRPCMapper, self).__init__(
7248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        ['POST'],
7258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        _JSON_CONTENT_TYPE,
7268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        protojson,
7278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        content_types=_EXTRA_JSON_CONTENT_TYPES)
7288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoidef service_mapping(services,
7318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                    registry_path=DEFAULT_REGISTRY_PATH):
7328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Create a services mapping for use with webapp.
7338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Creates basic default configuration and registration for ProtoRPC services.
7358d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Each service listed in the service mapping has a standard service handler
7368d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  factory created for it.
7378d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7388d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  The list of mappings can either be an explicit path to service mapping or
7398d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  just services.  If mappings are just services, they will automatically
7408d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  be mapped to their default name.  For exampel:
7418d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7428d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    package = 'my_package'
7438d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7448d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    class MyService(remote.Service):
7458d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      ...
7468d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7478d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    server_mapping([('/my_path', MyService),  # Maps to /my_path
7488d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                    MyService,                # Maps to /my_package/MyService
7498d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                   ])
7508d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7518d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Specifying a service mapping:
7528d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7538d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Normally services are mapped to URL paths by specifying a tuple
7548d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    (path, service):
7558d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      path: The path the service resides on.
7568d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service: The service class or service factory for creating new instances
7578d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        of the service.  For more information about service factories, please
7588d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        see remote.Service.new_factory.
7598d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7608d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    If no tuple is provided, and therefore no path specified, a default path
7618d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    is calculated by using the fully qualified service name using a URL path
7628d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    separator for each of its components instead of a '.'.
7638d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7648d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Args:
7658d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    services: Can be service type, service factory or string definition name of
7668d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        service being mapped or list of tuples (path, service):
7678d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      path: Path on server to map service to.
7688d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service: Service type, service factory or string definition name of
7698d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        service being mapped.
7708d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      Can also be a dict.  If so, the keys are treated as the path and values as
7718d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      the service.
7728d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    registry_path: Path to give to registry service.  Use None to disable
7738d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      registry service.
7748d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7758d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Returns:
7768d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    List of tuples defining a mapping of request handlers compatible with a
7778d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    webapp application.
7788d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7798d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Raises:
7808d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    ServiceConfigurationError when duplicate paths are provided.
7818d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
7828d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  if isinstance(services, dict):
7838d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    services = six.iteritems(services)
7848d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  mapping = []
7858d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  registry_map = {}
7868d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7878d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  if registry_path is not None:
7888d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    registry_service = registry.RegistryService.new_factory(registry_map)
7898d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    services = list(services) + [(registry_path, registry_service)]
7908d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    mapping.append((registry_path + r'/form(?:/)?',
7918d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                    forms.FormsHandler.new_factory(registry_path)))
7928d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    mapping.append((registry_path + r'/form/(.+)', forms.ResourceHandler))
7938d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
7948d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  paths = set()
7958d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  for service_item in services:
7968d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    infer_path = not isinstance(service_item, (list, tuple))
7978d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if infer_path:
7988d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service = service_item
7998d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
8008d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      service = service_item[1]
8018d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8028d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    service_class = getattr(service, 'service_class', service)
8038d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8048d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if infer_path:
8058d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      path = '/' + service_class.definition_name().replace('.', '/')
8068d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
8078d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      path = service_item[0]
8088d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8098d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    if path in paths:
8108d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      raise ServiceConfigurationError(
8118d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi        'Path %r is already defined in service mapping' % path.encode('utf-8'))
8128d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    else:
8138d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi      paths.add(path)
8148d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8158d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Create service mapping for webapp.
8168d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    new_mapping = ServiceHandlerFactory.default(service).mapping(path)
8178d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    mapping.append(new_mapping)
8188d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8198d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    # Update registry with service class.
8208d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    registry_map[path] = service_class
8218d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8228d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  return mapping
8238d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8248d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8258d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoidef run_services(services,
8268d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi                 registry_path=DEFAULT_REGISTRY_PATH):
8278d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """Handle CGI request using service mapping.
8288d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi
8298d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  Args:
8308d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi    Same as service_mapping.
8318d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  """
8328d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  mappings = service_mapping(services, registry_path=registry_path)
8338d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  application = webapp.WSGIApplication(mappings)
8348d2b206a675ec20ea07100c35df34e65ee1e45e8Ruchi Kandoi  webapp_util.run_wsgi_app(application)
835