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