Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
progress towards a working requester and connection class to authenti…
…cate and connect to the api in general. none of this works i'd bet... work in progress...
- Loading branch information
Showing
4 changed files
with
237 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# -*- encoding: utf-8 -*- | ||
__author__ = "Andrew Regner <andrew@aregner.com>" | ||
|
||
""" See COPYING for license information. """ | ||
|
||
__version__ = "0.1" | ||
user_agent = "python-clouddb/%s" % __version__ | ||
us_authurl = 'https://auth.api.rackspacecloud.com/v1.1' | ||
uk_authurl = 'https://lon.auth.api.rackspacecloud.com/v1.1' | ||
db_management_host = '%s.databases.api.rackspacecloud.com' | ||
default_authurl = us_authurl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# -*- encoding: utf-8 -*- | ||
__author__ = "Andrew Regner <andrew@aregner.com>" | ||
|
||
""" | ||
exception classes | ||
See COPYING for license information. | ||
""" | ||
|
||
class Error(StandardError): | ||
""" | ||
Base class for all errors and exceptions | ||
""" | ||
pass | ||
|
||
|
||
class ResponseError(Error): | ||
""" | ||
Raised when the remote service returns an error. | ||
""" | ||
def __init__(self, status, reason): | ||
self.status = status | ||
self.reason = reason | ||
Error.__init__(self) | ||
|
||
def __str__(self): | ||
return '%d: %s' % (self.status, self.reason) | ||
|
||
def __repr__(self): | ||
return '%d: %s' % (self.status, self.reason) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# -*- encoding: utf-8 -*- | ||
__author__ = "Andrew Regner <andrew@aregner.com>" | ||
|
||
""" | ||
Generic module to make HTTP(S) requests to a server. | ||
This code is licensed under the BSD license. See COPYING for more details. | ||
""" | ||
|
||
from httplib import HTTPSConnection | ||
from urllib import urlencode | ||
|
||
try: | ||
from json import loads as json_loads | ||
except ImportError: | ||
from simplejson import loads as json_loads | ||
|
||
import errors | ||
|
||
class Requester(object): | ||
""" | ||
This class is used internally by the clouddb project to make requests to an | ||
HTTPS server in a convient way. | ||
""" | ||
|
||
def __init__(self, host, **kwargs): | ||
""" | ||
""" | ||
self.host = host | ||
self.debug = int(kwargs.get('debug', 0)) | ||
|
||
self.base_path = "/" | ||
self.base_headers = {} | ||
|
||
self._client_setup() | ||
self._authenticate() | ||
|
||
def _setup(self): | ||
""" | ||
""" | ||
self.client = HTTPSConnection(self.host) | ||
self.client.set_debuglevel(self.debug) | ||
|
||
def _authenticate(self): | ||
""" | ||
""" | ||
pass | ||
|
||
def set_base_header(self, header, value): | ||
""" | ||
""" | ||
|
||
# set multiple headers | ||
if type(header) in (dict,) and len(header) > 0: | ||
for k, v in header.items(): | ||
self.set_header(k, v) | ||
|
||
# set this one header, from a list or strings | ||
else: | ||
if type(header) in (tuple, list) and len(header) >= 2: | ||
header, value = header[0], header[1] | ||
|
||
self.base_headers[header.title()] = value | ||
|
||
def get_base_headers(self): | ||
""" | ||
""" | ||
return self.headers | ||
|
||
def delete_base_header(self, header): | ||
""" | ||
""" | ||
if header in self.headers: | ||
del self.base_headers[header.title()] | ||
|
||
def _build_path(self, path): | ||
""" | ||
""" | ||
if type(path) in (tuple, list): | ||
path = "/".join(path) | ||
return path.strip('/') | ||
|
||
def set_base_path(self, path): | ||
""" | ||
""" | ||
self.base_path = self._build_path(path) | ||
|
||
def get_base_path(self): | ||
""" | ||
""" | ||
return self.base_path | ||
|
||
def request(self, method='GET', path=None, data=None, headers=None, args=None): | ||
""" | ||
""" | ||
method = method.upper() | ||
|
||
# build the full path to request | ||
path = '/' + self.get_base_path() + '/' + self._build_path(path) | ||
|
||
# check that we aren't trying to use data when we can't | ||
if data and method not in ('POST', 'PUT'): | ||
raise BadRequest("%s requests cannot contain data" % method) | ||
|
||
# merge base headers and supplied headers | ||
headers = self.base_headers.update(headers) | ||
|
||
# encode data if needed | ||
if data and type(data) != str: | ||
data = json_dumps(data) | ||
headers['Content-type'] = "application/json" | ||
|
||
# don't a type when there is no content | ||
if not data: | ||
del headers['Content-type'] | ||
|
||
# append url arguments if given | ||
if args: | ||
path = path + '?' + urlencode(args) | ||
|
||
# this is how we make a request | ||
def make_request(): | ||
self.client.request(method, path, data, headers) | ||
return self.client.getresponse() | ||
|
||
try: | ||
# first try... | ||
response = make_request() | ||
except (socket.error, IOError, HTTPException): | ||
# maybe we just lost the socket, try again | ||
self._setup() | ||
response = make_request() | ||
|
||
# maybe we need to authenticate again | ||
if response.status == 401: | ||
self._authenticate() | ||
response = make_request() | ||
|
||
return response | ||
|
||
def get(self, path, data=None, headers=None, args=None): | ||
""" | ||
""" | ||
r = self.request('GET', path, data, headers, args) | ||
|
||
if r.status < 200 or r.status > 299: | ||
# TODO : this is probably throwing away some error information | ||
r.read() | ||
raise errors.ResponseError(r.status, r.reason) | ||
|
||
read_output = r.read() | ||
|
||
if r.getheader('content-type', 'text/plain') == "application/json": | ||
return json_loads(read_output) | ||
else: | ||
return read_output | ||
|
||
|
||
class BadRequest(Exception): | ||
pass |