| #!/usr/bin/env python |
| # |
| # Copyright 2007 Google Inc. |
| # |
| # 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. |
| # |
| """Handles requests GCS API requests. |
| |
| Includes a WSGI application that forwards Google Cloud Stroage API requests |
| to the local emulation layer. |
| """ |
| |
| |
| import httplib |
| import logging |
| import webob |
| |
| from google.appengine.ext.cloudstorage import stub_dispatcher |
| from google.appengine.tools.devappserver2 import wsgi_server |
| |
| # Regex for all requests routed through this module. |
| GCS_URL_PATTERN = '_ah/gcs/(.+)' |
| |
| HTTP_308_STATUS_MESSAGE = 'Resume Incomplete' |
| |
| |
| class Application(object): |
| """A WSGI application that forwards GCS requests to stub.""" |
| |
| def __call__(self, environ, start_response): |
| """Handles WSGI requests. |
| |
| Args: |
| environ: An environ dict for the current request as defined in PEP-333. |
| start_response: A function with semantics defined in PEP-333. |
| |
| Returns: |
| An iterable over strings containing the body of the HTTP response. |
| """ |
| request = webob.Request(environ) |
| |
| try: |
| result = stub_dispatcher.dispatch(request.method, request.headers, |
| request.url, request.body) |
| except ValueError as e: |
| status_message = httplib.responses.get(e.args[1], '') |
| start_response('%d %s' % (e.args[1], status_message), []) |
| return [e.args[0]] |
| |
| # The metadata headers must be convereted from unicode to string. |
| headers = [] |
| for k, v in result.headers.iteritems(): |
| headers.append((str(k), str(v))) |
| |
| # GCS uses non-standard HTTP 308 status code. |
| status_code = result.status_code |
| if status_code == 308: |
| status_message = HTTP_308_STATUS_MESSAGE |
| else: |
| status_message = httplib.responses.get(status_code, '') |
| |
| start_response('%d %s' % (status_code, status_message), headers) |
| |
| return [result.content] |
| |
| |
| class GCSServer(Application, wsgi_server.WsgiServer): |
| """Serves API calls over HTTP.""" |
| |
| def __init__(self, host, port): |
| self._host = host |
| super(GCSServer, self).__init__((host, port), self) |
| |
| def start(self): |
| """Start the API Server.""" |
| super(GCSServer, self).start() |
| logging.info('Starting Google Cloud Storage server at: http://%s:%d', |
| self._host, self.port) |