This repository was archived by the owner on Apr 19, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathapi_request.py
More file actions
128 lines (109 loc) · 4.58 KB
/
Copy pathapi_request.py
File metadata and controls
128 lines (109 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# 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 cgi
import copy
import json
import logging
import urllib
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.
"""
_API_PREFIX = '/_ah/api/'
def __init__(self, environ):
"""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 self.path.startswith(self._API_PREFIX):
raise ValueError('Invalid request path: %s' % self.path)
self.path = self.path[len(self._API_PREFIX):]
if self.query:
self.parameters = cgi.parse_qs(self.query, keep_blank_values=True)
else:
self.parameters = {}
self.body_json = json.loads(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 _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