Commits

Asbjørn Andersen committed 2140131

Now supporting params and post data

Comments (0)

Files changed (4)

+.Python
+bin
+lib
+src
+include
 .pyc
 dist
 MANIFEST
 ## Tests
 #
 
-@flake('/nonExistingPath', itr=3, random=2)
+@flake('/nonExistingPath?id=123', params={'name':'some name','eple':'test'}, itr=3, random=2)
 def login(req,resp):
     """ Perform the login
     """
             return resp.code == 200
 
     return True
-
-snow('http://www.google.com', report_file='report.json', show_responses=False)
+    
+snow('http://www.google.com')
+#snow('http://www.google.com', report_file='report.json', show_responses=False)
 # License - BSD
 #
 
-import time, random, urllib, urllib2, json, sys, threading, pprint, inspect, copy
+import time, random, urllib, urllib2, urlenc, json, sys, threading, pprint, inspect, copy
 
 snowflakes = []
 results = []
            self.method = 'GET'
        
     def __deepcopy__(self, kwargs):
-        clone = Flake(self.url, self.pre, 0, self.random, self.func, self.params, self.headers)
+        clone = Flake(self.url, self.pre, 0, self.random, self.func, self.params, self.data, self.headers)
         return clone
        
     def run(self):
         failure = False
 
         t1 = time.time()
-        response = curl(conf['host']+self.url, self.data, self.headers)
+        response = curl(conf['host']+self.url, self.params, self.data, self.headers)
         t2 = time.time()
         td    = t2-t1
         failure = not self.func(self, response)
         else:
             stats[self.url]['oks'] += 1
             
-        stats[self.url]['responses'].append({'time':td, 'code':response.code, 'failure':failure})
+        stats[self.url]['responses'].append({
+            'time'    : td,
+            'code'    : response.code,
+            'failure' :failure,
+            'url'     : response.url
+        })
         
         calls.pop()
         if len(calls) == 0:
             end()
                                 
-def curl(url, data, headers):
+def curl(url, params, data, headers):
+    if len(params.keys()) > 0:
+        adder     = url.find('?') >= 0 and '&' or '?'
+        urlparams = urlenc.compose_qs(params)
+        url  = '%s%s%s' % (url,adder,urlparams)
+
     req = urllib2.Request(url)    
     for header in headers.keys():
         req.add_header(header, headers[header])
+import cgi
+import collections
+import urllib
+
+
+def escape(value):
+    """
+    Escape the string according to:
+
+    RFC3986: http://tools.ietf.org/html/rfc3986
+    http://oauth.net/core/1.0/#encoding_parameters
+
+    Arguments:
+
+        `value`
+            The string to escape.
+
+    >>> urlencoding.escape('a b & c')
+    'a%20b%20%26%20c'
+    >>> urlencoding.escape('abc123-._~')
+    'abc123-._~'
+
+    """
+    return urllib.quote(value, safe='~')
+
+def is_nonstring_iterable(i):
+    return not isinstance(i, basestring) and isinstance(i, collections.Iterable)
+
+def parse_qs(query):
+    """
+    Parse a query string into a dict. Values my be strings or arrays.
+
+    Arguments:
+
+        `query`
+            The query string or form encoded body to parse.
+
+    >>> urlencoding.parse_qs('a=1&b=%20c+d')
+    {'a': '1', 'b': ' c d'}
+    >>> urlencoding.parse_qs('a=2&a=1')
+    {'a': ['2', '1']}
+
+    """
+    d = {}
+    for k, v in cgi.parse_qs(query, keep_blank_values=False).iteritems():
+        if len(v) == 1:
+            d[k] = v[0]
+        else:
+            d[k] = v
+    return d
+
+ENCODED_OPEN_BRACKET = escape('[')
+ENCODED_CLOSE_BRACKET = escape(']')
+def compose_qs(params, sort=False, pattern='%s=%s', join='&', wrap=None):
+    """
+    Compose a single string using RFC3986 specified escaping using
+    `urlencoding.escape`_ for keys and values.
+
+    Arguments:
+
+        `params`
+            The dict of parameters to encode into a query string.
+
+        `sort`
+            Boolean indicating if the key/values should be sorted.
+
+    >>> urlencoding.compose_qs({'a': '1', 'b': ' c d'})
+    'a=1&b=%20c%20d'
+    >>> urlencoding.compose_qs({'a': ['2', '1']})
+    'a=2&a=1'
+    >>> urlencoding.compose_qs({'a': ['2', '1', '3']}, sort=True)
+    'a=1&a=2&a=3'
+    >>> urlencoding.compose_qs({'a': '1', 'b': {'c': 2, 'd': 3}}, sort=True)
+    'a=1&b%5Bc%5D=2&b%5Bd%5D=3'
+
+    """
+
+    if sort:
+        params = SortedDict(params)
+
+    pieces = []
+    for key, value in params.iteritems():
+        escaped_key = escape(str(key))
+        if wrap:
+            escaped_key = wrap + ENCODED_OPEN_BRACKET + escaped_key + ENCODED_CLOSE_BRACKET
+
+        if isinstance(value, collections.Mapping):
+            p = compose_qs(value, sort, pattern, join, escaped_key)
+        elif is_nonstring_iterable(value):
+            p = join.join([pattern % (escaped_key, escape(str(v))) for v in value])
+        else:
+            p = pattern % (escaped_key, escape(str(value)))
+        pieces.append(p)
+    return join.join(pieces)
+
+class SortedDict(dict):
+    def iteritems(self):
+        """
+        Iterates in a sorted fashion. Values are sorted before being yielded if
+        they can be. It should result in sorted by key, then value semantics.
+
+        http://oauth.net/core/1.0/#rfc.section.9.1.1
+
+        """
+        for key in sorted(self):
+            value = self[key]
+            if isinstance(value, collections.Mapping):
+                value = SortedDict(value)
+            elif is_nonstring_iterable(value):
+                value = sorted(value)
+            yield key, value