in src/google/appengine/_internal/protorpc/wsgi/service.py [0:0]
def service_mapping(service_factory, service_path=r'.*', protocols=None):
"""WSGI application that handles a single ProtoRPC service mapping.
Args:
service_factory: Service factory for creating instances of service request
handlers. Either callable that takes no parameters and returns a service
instance or a service class whose constructor requires no parameters.
service_path: Regular expression for matching requests against. Requests
that do not have matching paths will cause a 404 (Not Found) response.
protocols: remote.Protocols instance that configures supported protocols
on server.
"""
service_class = getattr(service_factory, 'service_class', service_factory)
remote_methods = service_class.all_remote_methods()
path_matcher = re.compile(_REQUEST_PATH_PATTERN % service_path)
def protorpc_service_app(environ, start_response):
"""Actual WSGI application function."""
path_match = path_matcher.match(environ['PATH_INFO'])
if not path_match:
return _HTTP_NOT_FOUND(environ, start_response)
service_path = path_match.group(1)
method_name = path_match.group(2)
content_type = environ.get('CONTENT_TYPE')
if not content_type:
content_type = environ.get('HTTP_CONTENT_TYPE')
if not content_type:
return _HTTP_BAD_REQUEST(environ, start_response)
content_type = cgi.parse_header(content_type)[0]
request_method = environ['REQUEST_METHOD']
if request_method != 'POST':
content = ('%s.%s is a ProtoRPC method.\n\n'
'Service %s\n\n'
'More about ProtoRPC: '
'%s\n' %
(service_path, method_name, service_class.definition_name(),
util.PROTORPC_PROJECT_URL))
error_handler = wsgi_util.error(
six.moves.http_client.METHOD_NOT_ALLOWED,
six.moves.http_client.responses[
six.moves.http_client.METHOD_NOT_ALLOWED],
content=content,
content_type='text/plain; charset=utf-8')
return error_handler(environ, start_response)
local_protocols = protocols or remote.Protocols.get_default()
try:
protocol = local_protocols.lookup_by_content_type(content_type)
except KeyError:
return _HTTP_UNSUPPORTED_MEDIA_TYPE(environ,start_response)
def send_rpc_error(status_code, state, message, error_name=None):
"""Helper function to send an RpcStatus message as response.
Will create static error handler and begin response.
Args:
status_code: HTTP integer status code.
state: remote.RpcState enum value to send as response.
message: Helpful message to send in response.
error_name: Error name if applicable.
Returns:
List containing encoded content response using the same content-type as
the request.
"""
status = remote.RpcStatus(state=state,
error_message=message,
error_name=error_name)
encoded_status = protocol.encode_message(status)
error_handler = wsgi_util.error(
status_code,
content_type=protocol.default_content_type,
content=encoded_status)
return error_handler(environ, start_response)
method = remote_methods.get(method_name)
if not method:
return send_rpc_error(six.moves.http_client.BAD_REQUEST,
remote.RpcState.METHOD_NOT_FOUND_ERROR,
'Unrecognized RPC method: %s' % method_name)
content_length = int(environ.get('CONTENT_LENGTH') or '0')
remote_info = method.remote
try:
request = protocol.decode_message(
remote_info.request_type, environ['wsgi.input'].read(content_length))
except (messages.ValidationError, messages.DecodeError) as err:
return send_rpc_error(
six.moves.http_client.BAD_REQUEST, remote.RpcState.REQUEST_ERROR,
'Error parsing ProtoRPC request '
'(Unable to parse request content: %s)' % err)
instance = service_factory()
initialize_request_state = getattr(
instance, 'initialize_request_state', None)
if initialize_request_state:
server_port = environ.get('SERVER_PORT', None)
if server_port:
server_port = int(server_port)
headers = []
for name, value in six.iteritems(environ):
if name.startswith('HTTP_'):
headers.append((name[len('HTTP_'):].lower().replace('_', '-'), value))
request_state = remote.HttpRequestState(
remote_host=environ.get('REMOTE_HOST', None),
remote_address=environ.get('REMOTE_ADDR', None),
server_host=environ.get('SERVER_HOST', None),
server_port=server_port,
http_method=request_method,
service_path=service_path,
headers=headers)
initialize_request_state(request_state)
try:
response = method(instance, request)
encoded_response = six.ensure_binary(protocol.encode_message(response))
except remote.ApplicationError as err:
return send_rpc_error(six.moves.http_client.BAD_REQUEST,
remote.RpcState.APPLICATION_ERROR,
six.text_type(err), err.error_name)
except Exception as err:
logging.exception('Encountered unexpected error from ProtoRPC '
'method implementation: %s (%s)' %
(err.__class__.__name__, err))
return send_rpc_error(six.moves.http_client.INTERNAL_SERVER_ERROR,
remote.RpcState.SERVER_ERROR,
'Internal Server Error')
response_headers = [('content-type', content_type)]
start_response(
'%d %s' % (
six.moves.http_client.OK,
six.moves.http_client.responses[six.moves.http_client.OK],
), response_headers)
return [encoded_response]
return protorpc_service_app