| Index: third_party/google-endpoints/endpoints/api_request.py
|
| diff --git a/third_party/google-endpoints/endpoints/api_request.py b/third_party/google-endpoints/endpoints/api_request.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3fc5764c57d0d05c0044f0d4d5164d95e69eac63
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/endpoints/api_request.py
|
| @@ -0,0 +1,151 @@
|
| +# Copyright 2016 Google Inc. All Rights Reserved.
|
| +#
|
| +# Licensed under the Apache License, Version 2.0 (the "License");
|
| +# you may not use this file except in compliance with the License.
|
| +# You may obtain a copy of the License at
|
| +#
|
| +# http://www.apache.org/licenses/LICENSE-2.0
|
| +#
|
| +# Unless required by applicable law or agreed to in writing, software
|
| +# distributed under the License is distributed on an "AS IS" BASIS,
|
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +# See the License for the specific language governing permissions and
|
| +# limitations under the License.
|
| +
|
| +"""Cloud Endpoints API request-related data and functions."""
|
| +
|
| +from __future__ import with_statement
|
| +
|
| +# pylint: disable=g-bad-name
|
| +import copy
|
| +import json
|
| +import logging
|
| +import urllib
|
| +import urlparse
|
| +import zlib
|
| +
|
| +import util
|
| +
|
| +
|
| +class ApiRequest(object):
|
| + """Simple data object representing an API request.
|
| +
|
| + Parses the request from environment variables into convenient pieces
|
| + and stores them as members.
|
| + """
|
| + def __init__(self, environ, base_paths=None):
|
| + """Constructor.
|
| +
|
| + Args:
|
| + environ: An environ dict for the request as defined in PEP-333.
|
| +
|
| + Raises:
|
| + ValueError: If the path for the request is invalid.
|
| + """
|
| + self.headers = util.get_headers_from_environ(environ)
|
| + self.http_method = environ['REQUEST_METHOD']
|
| + self.url_scheme = environ['wsgi.url_scheme']
|
| + self.server = environ['SERVER_NAME']
|
| + self.port = environ['SERVER_PORT']
|
| + self.path = environ['PATH_INFO']
|
| + self.query = environ.get('QUERY_STRING')
|
| + self.body = environ['wsgi.input'].read()
|
| + if self.body and self.headers.get('CONTENT-ENCODING') == 'gzip':
|
| + # Increasing wbits to 16 + MAX_WBITS is necessary to be able to decode
|
| + # gzipped content (as opposed to zlib-encoded content).
|
| + # If there's an error in the decompression, it could be due to another
|
| + # part of the serving chain that already decompressed it without clearing
|
| + # the header. If so, just ignore it and continue.
|
| + try:
|
| + self.body = zlib.decompress(self.body, 16 + zlib.MAX_WBITS)
|
| + except zlib.error:
|
| + pass
|
| + self.source_ip = environ.get('REMOTE_ADDR')
|
| + self.relative_url = self._reconstruct_relative_url(environ)
|
| +
|
| + if not base_paths:
|
| + base_paths = set()
|
| + elif isinstance(base_paths, list):
|
| + base_paths = set(base_paths)
|
| +
|
| + # Find a base_path in the path
|
| + for base_path in base_paths:
|
| + if self.path.startswith(base_path):
|
| + self.path = self.path[len(base_path):]
|
| + self.base_path = base_path
|
| + break
|
| + else:
|
| + raise ValueError('Invalid request path: %s' % self.path)
|
| +
|
| + if self.query:
|
| + self.parameters = urlparse.parse_qs(self.query, keep_blank_values=True)
|
| + else:
|
| + self.parameters = {}
|
| + self.body_json = self._process_req_body(self.body) if self.body else {}
|
| + self.request_id = None
|
| +
|
| + # Check if it's a batch request. We'll only handle single-element batch
|
| + # requests on the dev server (and we need to handle them because that's
|
| + # what RPC and JS calls typically show up as). Pull the request out of the
|
| + # list and record the fact that we're processing a batch.
|
| + if isinstance(self.body_json, list):
|
| + if len(self.body_json) != 1:
|
| + logging.warning('Batch requests with more than 1 element aren\'t '
|
| + 'supported in devappserver2. Only the first element '
|
| + 'will be handled. Found %d elements.',
|
| + len(self.body_json))
|
| + else:
|
| + logging.info('Converting batch request to single request.')
|
| + self.body_json = self.body_json[0]
|
| + self.body = json.dumps(self.body_json)
|
| + self._is_batch = True
|
| + else:
|
| + self._is_batch = False
|
| +
|
| + def _process_req_body(self, body):
|
| + """Process the body of the HTTP request.
|
| +
|
| + If the body is valid JSON, return the JSON as a dict.
|
| + Else, convert the key=value format to a dict and return that.
|
| +
|
| + Args:
|
| + body: The body of the HTTP request.
|
| + """
|
| + try:
|
| + return json.loads(body)
|
| + except ValueError:
|
| + return urlparse.parse_qs(body, keep_blank_values=True)
|
| +
|
| + def _reconstruct_relative_url(self, environ):
|
| + """Reconstruct the relative URL of this request.
|
| +
|
| + This is based on the URL reconstruction code in Python PEP 333:
|
| + http://www.python.org/dev/peps/pep-0333/#url-reconstruction. Rebuild the
|
| + URL from the pieces available in the environment.
|
| +
|
| + Args:
|
| + environ: An environ dict for the request as defined in PEP-333.
|
| +
|
| + Returns:
|
| + The portion of the URL from the request after the server and port.
|
| + """
|
| + url = urllib.quote(environ.get('SCRIPT_NAME', ''))
|
| + url += urllib.quote(environ.get('PATH_INFO', ''))
|
| + if environ.get('QUERY_STRING'):
|
| + url += '?' + environ['QUERY_STRING']
|
| + return url
|
| +
|
| + def copy(self):
|
| + return copy.deepcopy(self)
|
| +
|
| + def is_rpc(self):
|
| + # Google's JsonRPC protocol creates a handler at /rpc for any Cloud
|
| + # Endpoints API, with api name, version, and method name being in the
|
| + # body of the request.
|
| + # If the request is sent to /rpc, we will treat it as JsonRPC.
|
| + # The client libraries for iOS's Objective C use RPC and not the REST
|
| + # versions of the API.
|
| + return self.path == 'rpc'
|
| +
|
| + def is_batch(self):
|
| + return self._is_batch
|
|
|