diff --git a/httpie/cliparse.py b/httpie/cliparse.py index 226fad66..a0c17bd1 100644 --- a/httpie/cliparse.py +++ b/httpie/cliparse.py @@ -384,9 +384,6 @@ def parse_items(items, data=None, headers=None, files=None, params=None): else: raise ParseError('%s is not valid item' % item.orig) - if key in target: - ParseError('duplicate item %s (%s)' % (item.key, item.orig)) - target[key] = value return headers, data, files, params diff --git a/httpie/models.py b/httpie/models.py index 0aa2209d..51324818 100644 --- a/httpie/models.py +++ b/httpie/models.py @@ -63,36 +63,53 @@ class HTTPMessage(object): @staticmethod def from_request(request): """Make an `HTTPMessage` from `requests.models.Request`.""" - url = urlparse(request.url) - request_headers = dict(request.headers) - if 'Host' not in request_headers: - request_headers['Host'] = url.netloc + url = urlparse(request.url) + + # Querystring + qs = '' + if url.query or request.params: + qs = '?' + if url.query: + qs += url.query + # Requests doesn't make params part of ``request.url``. + if request.params: + if url.query: + qs += '&' + qs += type(request)._encode_params(request.params) + + # Request-Line + request_line = '{method} {path}{query} HTTP/1.1'.format( + method=request.method, + path=url.path or '/', + query=qs + ) + + # Headers + headers = dict(request.headers) + content_type = headers.get('Content-Type') + if 'Host' not in headers: + headers['Host'] = url.netloc + headers = '\n'.join( + str('%s: %s') % (name, value) + for name, value + in headers.items() + ) + + # Body try: body = request.data except AttributeError: # requests < 0.12.1 body = request._enc_data - if isinstance(body, dict): - # --form - body = request.__class__._encode_params(body) + body = type(request)._encode_params(body) - request_line = '{method} {path}{query} HTTP/1.1'.format( - method=request.method, - path=url.path or '/', - query='' if url.query is '' else '?' + url.query - ) - headers = '\n'.join( - str('%s: %s') % (name, value) - for name, value - in request_headers.items() - ) return HTTPMessage( line=request_line, headers=headers, body=body, - content_type=request_headers.get('Content-Type') + content_type=content_type ) @classmethod diff --git a/tests/tests.py b/tests/tests.py index e5d238de..0dbf31c5 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -107,8 +107,10 @@ def http(*args, **kwargs): except ValueError: pass else: - r.strip().index('\n') - r.json = json.loads(j) + try: + r.json = json.loads(j) + except ValueError: + pass return r @@ -217,6 +219,56 @@ class HTTPieTest(BaseTestCase): self.assertIn('"Foo": "bar"', r) +class QuerystringTest(BaseTestCase): + + def test_query_string_params_in_url(self): + r = http( + '--print=Hhb', + 'GET', + httpbin('/get?a=1&b=2') + ) + + path = '/get?a=1&b=2' + url = httpbin(path) + + self.assertIn('HTTP/1.1 200', r) + self.assertIn('GET %s HTTP/1.1' % path, r) + self.assertIn('"url": "%s"' % url, r) + + def test_query_string_params_items(self): + r = http( + '--print=Hhb', + 'GET', + httpbin('/get'), + 'a==1', + 'b==2' + ) + + path = '/get?a=1&b=2' + url = httpbin(path) + + self.assertIn('HTTP/1.1 200', r) + self.assertIn('GET %s HTTP/1.1' % path, r) + self.assertIn('"url": "%s"' % url, r) + + def test_query_string_params_in_url_and_items_with_duplicates(self): + r = http( + '--print=Hhb', + 'GET', + httpbin('/get?a=1&a=1'), + 'a==1', + 'a==1', + 'b==2', + ) + + path = '/get?a=1&a=1&a=1&a=1&b=2' + url = httpbin(path) + + self.assertIn('HTTP/1.1 200', r) + self.assertIn('GET %s HTTP/1.1' % path, r) + self.assertIn('"url": "%s"' % url, r) + + class AutoContentTypeAndAcceptHeadersTest(BaseTestCase): """ Test that Accept and Content-Type correctly defaults to JSON,