Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(579)

Side by Side Diff: third_party/google-endpoints/endpoints/util.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2016 Google Inc. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """Helper utilities for the endpoints package."""
16
17 # pylint: disable=g-bad-name
18
19 import cStringIO
20 import json
21 import os
22 import wsgiref.headers
23
24 from google.appengine.api import app_identity
25
26 from google.appengine.api.modules import modules
27
28
29 class StartResponseProxy(object):
30 """Proxy for the typical WSGI start_response object."""
31
32 def __init__(self):
33 self.call_context = {}
34 self.body_buffer = cStringIO.StringIO()
35
36 def __enter__(self):
37 return self
38
39 def __exit__(self, exc_type, exc_value, traceback):
40 # Close out the cStringIO.StringIO buffer to prevent memory leakage.
41 if self.body_buffer:
42 self.body_buffer.close()
43
44 def Proxy(self, status, headers, exc_info=None):
45 """Save args, defer start_response until response body is parsed.
46
47 Create output buffer for body to be written into.
48 Note: this is not quite WSGI compliant: The body should come back as an
49 iterator returned from calling service_app() but instead, StartResponse
50 returns a writer that will be later called to output the body.
51 See google/appengine/ext/webapp/__init__.py::Response.wsgi_write()
52 write = start_response('%d %s' % self.__status, self.__wsgi_headers)
53 write(body)
54
55 Args:
56 status: Http status to be sent with this response
57 headers: Http headers to be sent with this response
58 exc_info: Exception info to be displayed for this response
59 Returns:
60 callable that takes as an argument the body content
61 """
62 self.call_context['status'] = status
63 self.call_context['headers'] = headers
64 self.call_context['exc_info'] = exc_info
65
66 return self.body_buffer.write
67
68 @property
69 def response_body(self):
70 return self.body_buffer.getvalue()
71
72 @property
73 def response_headers(self):
74 return self.call_context.get('headers')
75
76 @property
77 def response_status(self):
78 return self.call_context.get('status')
79
80 @property
81 def response_exc_info(self):
82 return self.call_context.get('exc_info')
83
84
85 def send_wsgi_not_found_response(start_response, cors_handler=None):
86 return send_wsgi_response('404 Not Found', [('Content-Type', 'text/plain')],
87 'Not Found', start_response,
88 cors_handler=cors_handler)
89
90
91 def send_wsgi_error_response(message, start_response, cors_handler=None):
92 body = json.dumps({'error': {'message': message}})
93 return send_wsgi_response('500', [('Content-Type', 'application/json')], body,
94 start_response, cors_handler=cors_handler)
95
96
97 def send_wsgi_rejected_response(rejection_error, start_response,
98 cors_handler=None):
99 body = rejection_error.to_json()
100 return send_wsgi_response('400', [('Content-Type', 'application/json')], body,
101 start_response, cors_handler=cors_handler)
102
103
104 def send_wsgi_redirect_response(redirect_location, start_response,
105 cors_handler=None):
106 return send_wsgi_response('302', [('Location', redirect_location)], '',
107 start_response, cors_handler=cors_handler)
108
109
110 def send_wsgi_no_content_response(start_response, cors_handler=None):
111 return send_wsgi_response('204 No Content', [], '', start_response,
112 cors_handler)
113
114
115 def send_wsgi_response(status, headers, content, start_response,
116 cors_handler=None):
117 """Dump reformatted response to CGI start_response.
118
119 This calls start_response and returns the response body.
120
121 Args:
122 status: A string containing the HTTP status code to send.
123 headers: A list of (header, value) tuples, the headers to send in the
124 response.
125 content: A string containing the body content to write.
126 start_response: A function with semantics defined in PEP-333.
127 cors_handler: A handler to process CORS request headers and update the
128 headers in the response. Or this can be None, to bypass CORS checks.
129
130 Returns:
131 A string containing the response body.
132 """
133 if cors_handler:
134 cors_handler.update_headers(headers)
135
136 # Update content length.
137 content_len = len(content) if content else 0
138 headers = [(header, value) for header, value in headers
139 if header.lower() != 'content-length']
140 headers.append(('Content-Length', '%s' % content_len))
141
142 start_response(status, headers)
143 return content
144
145
146 def get_headers_from_environ(environ):
147 """Get a wsgiref.headers.Headers object with headers from the environment.
148
149 Headers in environ are prefixed with 'HTTP_', are all uppercase, and have
150 had dashes replaced with underscores. This strips the HTTP_ prefix and
151 changes underscores back to dashes before adding them to the returned set
152 of headers.
153
154 Args:
155 environ: An environ dict for the request as defined in PEP-333.
156
157 Returns:
158 A wsgiref.headers.Headers object that's been filled in with any HTTP
159 headers found in environ.
160 """
161 headers = wsgiref.headers.Headers([])
162 for header, value in environ.iteritems():
163 if header.startswith('HTTP_'):
164 headers[header[5:].replace('_', '-')] = value
165 # Content-Type is special; it does not start with 'HTTP_'.
166 if 'CONTENT_TYPE' in environ:
167 headers['CONTENT-TYPE'] = environ['CONTENT_TYPE']
168 return headers
169
170
171 def put_headers_in_environ(headers, environ):
172 """Given a list of headers, put them into environ based on PEP-333.
173
174 This converts headers to uppercase, prefixes them with 'HTTP_', and
175 converts dashes to underscores before adding them to the environ dict.
176
177 Args:
178 headers: A list of (header, value) tuples. The HTTP headers to add to the
179 environment.
180 environ: An environ dict for the request as defined in PEP-333.
181 """
182 for key, value in headers:
183 environ['HTTP_%s' % key.upper().replace('-', '_')] = value
184
185
186 def is_running_on_app_engine():
187 return os.environ.get('GAE_MODULE_NAME') is not None
188
189
190 def is_running_on_devserver():
191 return os.environ.get('SERVER_SOFTWARE', '').startswith('Development/')
192
193
194 def is_running_on_localhost():
195 return os.environ.get('SERVER_NAME') == 'localhost'
196
197
198 def get_app_hostname():
199 """Return hostname of a running Endpoints service.
200
201 Returns hostname of an running Endpoints API. It can be 1) "localhost:PORT"
202 if running on development server, or 2) "app_id.appspot.com" if running on
203 external app engine prod, or "app_id.googleplex.com" if running as Google
204 first-party Endpoints API, or 4) None if not running on App Engine
205 (e.g. Tornado Endpoints API).
206
207 Returns:
208 A string representing the hostname of the service.
209 """
210 if not is_running_on_app_engine() or is_running_on_localhost():
211 return None
212
213 version = modules.get_current_version_name()
214 app_id = app_identity.get_application_id()
215
216 suffix = 'appspot.com'
217
218 if ':' in app_id:
219 tokens = app_id.split(':')
220 api_name = tokens[1]
221 if tokens[0] == 'google.com':
222 suffix = 'googleplex.com'
223 else:
224 api_name = app_id
225
226 # Check if this is the default version
227 default_version = modules.get_default_version()
228 if version == default_version:
229 return '{0}.{1}'.format(app_id, suffix)
230 else:
231 return '{0}-dot-{1}.{2}'.format(version, api_name, suffix)
232
233
234 def check_list_type(objects, allowed_type, name, allow_none=True):
235 """Verify that objects in list are of the allowed type or raise TypeError.
236
237 Args:
238 objects: The list of objects to check.
239 allowed_type: The allowed type of items in 'settings'.
240 name: Name of the list of objects, added to the exception.
241 allow_none: If set, None is also allowed.
242
243 Raises:
244 TypeError: if object is not of the allowed type.
245
246 Returns:
247 The list of objects, for convenient use in assignment.
248 """
249 if objects is None:
250 if not allow_none:
251 raise TypeError('%s is None, which is not allowed.' % name)
252 return objects
253 if not isinstance(objects, (tuple, list)):
254 raise TypeError('%s is not a list.' % name)
255 if not all(isinstance(i, allowed_type) for i in objects):
256 type_list = sorted(list(set(type(obj) for obj in objects)))
257 raise TypeError('%s contains types that don\'t match %s: %s' %
258 (name, allowed_type.__name__, type_list))
259 return objects
260
261
262 def snake_case_to_headless_camel_case(snake_string):
263 """Convert snake_case to headlessCamelCase.
264
265 Args:
266 snake_string: The string to be converted.
267 Returns:
268 The input string converted to headlessCamelCase.
269 """
270 return ''.join([snake_string.split('_')[0]] +
271 list(sub_string.capitalize()
272 for sub_string in snake_string.split('_')[1:]))
OLDNEW
« no previous file with comments | « third_party/google-endpoints/endpoints/users_id_token.py ('k') | third_party/google-endpoints/enum/LICENSE » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698