| #!/usr/bin/python |
| # |
| # Copyright (C) 2008 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. |
| |
| """This module provides a common interface for all HTTP requests. |
| |
| HttpResponse: Represents the server's response to an HTTP request. Provides |
| an interface identical to httplib.HTTPResponse which is the response |
| expected from higher level classes which use HttpClient.request. |
| |
| GenericHttpClient: Provides an interface (superclass) for an object |
| responsible for making HTTP requests. Subclasses of this object are |
| used in AtomService and GDataService to make requests to the server. By |
| changing the http_client member object, the AtomService is able to make |
| HTTP requests using different logic (for example, when running on |
| Google App Engine, the http_client makes requests using the App Engine |
| urlfetch API). |
| """ |
| |
| |
| __author__ = 'api.jscudder (Jeff Scudder)' |
| |
| |
| import StringIO |
| |
| |
| USER_AGENT = '%s GData-Python/2.0.14' |
| |
| |
| class Error(Exception): |
| pass |
| |
| |
| class UnparsableUrlObject(Error): |
| pass |
| |
| |
| class ContentLengthRequired(Error): |
| pass |
| |
| |
| class HttpResponse(object): |
| def __init__(self, body=None, status=None, reason=None, headers=None): |
| """Constructor for an HttpResponse object. |
| |
| HttpResponse represents the server's response to an HTTP request from |
| the client. The HttpClient.request method returns a httplib.HTTPResponse |
| object and this HttpResponse class is designed to mirror the interface |
| exposed by httplib.HTTPResponse. |
| |
| Args: |
| body: A file like object, with a read() method. The body could also |
| be a string, and the constructor will wrap it so that |
| HttpResponse.read(self) will return the full string. |
| status: The HTTP status code as an int. Example: 200, 201, 404. |
| reason: The HTTP status message which follows the code. Example: |
| OK, Created, Not Found |
| headers: A dictionary containing the HTTP headers in the server's |
| response. A common header in the response is Content-Length. |
| """ |
| if body: |
| if hasattr(body, 'read'): |
| self._body = body |
| else: |
| self._body = StringIO.StringIO(body) |
| else: |
| self._body = None |
| if status is not None: |
| self.status = int(status) |
| else: |
| self.status = None |
| self.reason = reason |
| self._headers = headers or {} |
| |
| def getheader(self, name, default=None): |
| if name in self._headers: |
| return self._headers[name] |
| else: |
| return default |
| |
| def read(self, amt=None): |
| if not amt: |
| return self._body.read() |
| else: |
| return self._body.read(amt) |
| |
| |
| class GenericHttpClient(object): |
| debug = False |
| |
| def __init__(self, http_client, headers=None): |
| """ |
| |
| Args: |
| http_client: An object which provides a request method to make an HTTP |
| request. The request method in GenericHttpClient performs a |
| call-through to the contained HTTP client object. |
| headers: A dictionary containing HTTP headers which should be included |
| in every HTTP request. Common persistent headers include |
| 'User-Agent'. |
| """ |
| self.http_client = http_client |
| self.headers = headers or {} |
| |
| def request(self, operation, url, data=None, headers=None): |
| all_headers = self.headers.copy() |
| if headers: |
| all_headers.update(headers) |
| return self.http_client.request(operation, url, data=data, |
| headers=all_headers) |
| |
| def get(self, url, headers=None): |
| return self.request('GET', url, headers=headers) |
| |
| def post(self, url, data, headers=None): |
| return self.request('POST', url, data=data, headers=headers) |
| |
| def put(self, url, data, headers=None): |
| return self.request('PUT', url, data=data, headers=headers) |
| |
| def delete(self, url, headers=None): |
| return self.request('DELETE', url, headers=headers) |
| |
| |
| class GenericToken(object): |
| """Represents an Authorization token to be added to HTTP requests. |
| |
| Some Authorization headers included calculated fields (digital |
| signatures for example) which are based on the parameters of the HTTP |
| request. Therefore the token is responsible for signing the request |
| and adding the Authorization header. |
| """ |
| def perform_request(self, http_client, operation, url, data=None, |
| headers=None): |
| """For the GenericToken, no Authorization token is set.""" |
| return http_client.request(operation, url, data=data, headers=headers) |
| |
| def valid_for_scope(self, url): |
| """Tells the caller if the token authorizes access to the desired URL. |
| |
| Since the generic token doesn't add an auth header, it is not valid for |
| any scope. |
| """ |
| return False |