Permalink
Browse files

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...
1 parent f3f6e3c commit 3331bd93cc4450350a0461dcbf9dde6b949178a8 @adregner committed Apr 27, 2012
Showing with 237 additions and 2 deletions.
  1. +35 −2 clouddb/connection.py
  2. +11 −0 clouddb/consts.py
  3. +31 −0 clouddb/errors.py
  4. +160 −0 clouddb/requester.py
View
@@ -9,7 +9,10 @@
This code is licensed under the BSD license. See COPYING for more details.
"""
-import os
+from urlparse import urlparse
+
+from requester import Requester
+import consts
class Connection(object):
"""
@@ -18,7 +21,7 @@ class Connection(object):
objects that you can use to perform any required action with the service.
"""
- def __init__(self, username, api_key, region, auth_url=None):
+ def __init__(self, username, api_key, region, auth_url=None, **kwargs):
"""
Use this to create your connection to the service with your Rackspace
Cloud user name and API key, and access the Cloud Databases service in
@@ -27,4 +30,34 @@ def __init__(self, username, api_key, region, auth_url=None):
The auth_url parameter can be used to connect to another compatiable
service endpoint other then Rackspace.
"""
+ self.user = username
+ self.key = api_key
+ self.token = None
+ self.region = region.upper()
+
+ if not auth_url:
+ auth_url = consts.default_authurl
+
+ (scheme, auth_netloc, auth_path, params, query, frag) = urlparse(auth_url)
+
+ self.debug = int(kwargs.get('debug', 0))
+
+ # this is set twice in here, once to authenticate and another for the
+ # host the service api endpoint is on
+ self.client = None
+
+ self._authenticate(auth_netloc, auth_path)
+
+ def _authenticate(self, auth_host, auth_path):
+ """
+ """
+ client = Requester(auth_host)
+ auth_response = client.request('POST', auth_path, data={
+ 'username': self.user,
+ 'key': self.key
+ })
+
+ def _request(self, method, path, data='', headers=None, params=None):
+ """
+ """
pass
View
@@ -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
View
@@ -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)
+
View
@@ -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

0 comments on commit 3331bd9

Please sign in to comment.