Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions rocket/http_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ def __init__(self, code, msg, *args, **kwargs):
def __str__(self):
return '%s - (code: %s)' % (self.msg, self.code)

class AdvancedRequest(urllib2.Request):
"""
Advanced urllib2 request with custom http method.
"""

def __init__(self, url, method, data={}, headers={},
origin_req_host=None, unverifiable=False):
self._method = method
urllib2.Request.__init__(self, url, data, headers,
origin_req_host, unverifiable)

def get_method(self):
return self._method or urllib2.Request.get_method(self)

########################################
# URL handling #########################
Expand All @@ -49,12 +62,12 @@ def urlread(url, data=None, headers={}, method=DEFAULT_REQUEST_METHOD,

# encoded_args = unicode_urlencode(encoded_args)

if method == 'GET':
if method in ('GET', 'DELETE'):
if data is not None:
url = '%s?%s' % (url, data)
logger.debug("Make %s connection to %s" % ( method, data ) )
data = None

data = None
if basic_auth_pair:
auth_handler = urllib2.HTTPBasicAuthHandler()
auth_handler.add_password(realm=basic_auth_realm,
Expand All @@ -65,19 +78,21 @@ def urlread(url, data=None, headers={}, method=DEFAULT_REQUEST_METHOD,
# ...and install it globally so it can be used with urlopen.
urllib2.install_opener(opener)

request = urllib2.Request(url, data)
request = AdvancedRequest(url, method, data)

try:
open_req = urllib2.urlopen(request)
# commenting out the code the commented comment comments on
#if method == 'POST':
# req.add_header('Content-type', "application/x-www-form-urlencoded")
# req.add_header('Accept', "text/plain")
open_req.http_method = method
#open_req.http_method = method
except urllib2.HTTPError, http_e:
logger.error("HTTP error: %s" % http_e)
raise RocketAPIException( http_e.code, http_e )
except urllib2.URLError,e:
logger.warn("Connection error %s" % e)
# TODO: RocketAPIError is underfined here.
raise RocketAPIError(None, e )
except Exception,e:
logger.critical("uknown error %s" % e )
Expand Down
108 changes: 87 additions & 21 deletions rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import mimetypes
import logging

from collections import defaultdict

import proxies
from auth import sign_args
Expand Down Expand Up @@ -72,13 +73,35 @@ def logging_context(log_stream=sys.stdout, log_level=logging.INFO):
# set stream from user, user sys.stdout as rocket default
sh = logging.StreamHandler( log_stream )
# set the log level requested by user, default is logging.info
sh.setLevel( log_level )
formatter = logging.Formatter("%(asctime)s %(process)d %(filename)s %(lineno)d %(levelname)s #rocket| %(message)s")
sh.setLevel(log_level)
formatter = logging.Formatter("%(asctime)s %(process)d %(filename)s" \
"%(lineno)d %(levelname)s #rocket| %(message)s")
sh.setFormatter(formatter)
logger.addHandler(sh)

return logger

#########################################
# Events ################################
#########################################

class Event(object):

def __init__(self, query_url, secure, ns_fun, method, data):
self.query_url = query_url
self.secure = secure
self.data = data
self.ns_fun = ns_fun
self.method = method
self.succeed = None

def success(self, response):
self.succeed = True
self.response = response

def fail(self, exception):
self.succeed = False
self.result = exception

#########################################
# The Rocket ############################
Expand Down Expand Up @@ -118,6 +141,7 @@ def __init__(self, function_list,
self.gen_namespace_pair = gen_namespace_pair
self._log_level=log_level
self._log_stream=log_stream
self._callbacks = defaultdict(lambda : defaultdict(set))

logger = logging_context(log_stream=log_stream, log_level=log_level)
logger.debug("Create rocket: url: %s client %s" % (api_url,client))
Expand All @@ -131,6 +155,7 @@ def __init__(self, function_list,
for namespace in self.function_list:
(ns_name, ns_title) = self.gen_namespace_pair(namespace)
proxy_class = rocket_proxies['%sProxy' % ns_title]
# TODO: setattr?
self.__dict__[ns_name] = proxy_class(self,
'%s.%s' % (client, ns_name))
self.namespace_map[ns_name] = namespace
Expand All @@ -139,7 +164,8 @@ def __init__(self, function_list,
def _expand_arguments(self, args):
"""Expands arguments from native type to web friendly type
"""
logger = logging_context(log_stream=self._log_stream, log_level=self._log_level)
logger = logging_context(log_stream=self._log_stream,
log_level=self._log_level)
logger.debug("rocket _expand_arguments %s" % args )

for arg in args.items():
Expand All @@ -156,7 +182,8 @@ def _parse_response(self, response, method):
"""Parses the response according to the given (optional) format,
which should be 'json'.
"""
logger = logging_context(log_stream=self._log_stream, log_level=self._log_level)
logger = logging_context(log_stream=self._log_stream,
log_level=self._log_level)
logger.debug("rocket _parse_response, response=%s and method=%s"
% (response, method) )

Expand Down Expand Up @@ -202,30 +229,68 @@ def __call__(self, function_key=None, args=None, secure=False):
format=self.format,
method=method,
get_args=args)
event = Event(query_url, secure, ns_fun, method, args)

if self.web_proxy:
web_proxy_handler = urllib2.ProxyHandler(self.web_proxy)
opener = urllib2.build_opener(web_proxy_handler)
response = opener.open(query_url).read()
else:
response = http_handling.urlread(query_url, data=args,
method=method.upper(),
basic_auth_pair=self.basic_auth_pair,
basic_auth_realm=self.basic_auth_realm,
logger=logging_context())

if response:
return self._parse_response(response, method)
try:
if self.web_proxy:
web_proxy_handler = urllib2.ProxyHandler(self.web_proxy)
opener = urllib2.build_opener(web_proxy_handler)
response = opener.open(query_url).read()
else:
return None

return response
response = http_handling.urlread(query_url, data=args,
method=method.upper(),
basic_auth_pair=self.basic_auth_pair,
basic_auth_realm=self.basic_auth_realm,
logger=logging_context())

if response:
response = self._parse_response(response, method)
else:
response = None
except Exception, ex_inst:
event.fail(ex_inst)
raise
else:
event.success(response)
finally:
self.fire_callbacks(event)

return response

########################################
# Callbacks ############################
########################################

def add_callback(self, method, ns_fun, callback):
self._callbacks[method][ns_fun].add(callback)

def add_callbacks(self, methods, ns_funs, callback):
for method in methods:
for ns_fun in ns_funs:
self.add_callback(method, ns_fun, callback)

def remove_callback(self, method, ns_fun, callback):
self._callbacks[method][ns_fun].remove(callback)

def remove_callbacks(self, methods, ns_funs, callback):
for method in methods:
for ns_fun in ns_funs:
self.remove_callback(method, ns_fun, callback)

def fire_callbacks(self, event):

callbacks_collections = (self._callbacks[event.method][event.ns_fun],
self._callbacks[event.method][None],
self._callbacks[None][None],
)

for callbacks in callbacks_collections:
for callback in callbacks:
callback(event)

if callbacks: # stop fire a callback on a first not empty stack
break

def check_error(self, response):
"""Some API's transmit errors over successful HTTP connections, eg. 200.

Expand All @@ -234,7 +299,8 @@ def check_error(self, response):
pass


def gen_query_url(self, url, function, format=None, method=None, get_args=None):
def gen_query_url(self, url, function, format=None, method=None,
get_args=None):
"""Generates URL for request according to structure of IDL.

Implementation formats worth considering:
Expand Down