def assert_url_equal(url1, url2): parts1 = _urlparse(url1) parts2 = _urlparse(url2) # Because the query string ordering isn't relevant, we have to parse # every single part manually and then handle the query string. assert_equal(parts1.scheme, parts2.scheme) assert_equal(parts1.netloc, parts2.netloc) assert_equal(parts1.path, parts2.path) assert_equal(parts1.params, parts2.params) assert_equal(parts1.fragment, parts2.fragment) assert_equal(parts1.username, parts2.username) assert_equal(parts1.password, parts2.password) assert_equal(parts1.hostname, parts2.hostname) assert_equal(parts1.port, parts2.port) assert_equal(parse_qs(parts1.query), parse_qs(parts2.query))
def assert_url_equal(url1, url2): parts1 = _urlparse(url1) parts2 = _urlparse(url2) # Because the query string ordering isn't relevant, we have to parse # every single part manually and then handle the query string. assert parts1.scheme == parts2.scheme assert parts1.netloc == parts2.netloc assert parts1.path == parts2.path assert parts1.params == parts2.params assert parts1.fragment == parts2.fragment assert parts1.username == parts2.username assert parts1.password == parts2.password assert parts1.hostname == parts2.hostname assert parts1.port == parts2.port assert parse_qs(parts1.query) == parse_qs(parts2.query)
def get_parsed_query_string(self, request): query_string_dict = parse_qs(urlsplit(request.url).query) # Also, parse_qs sets each value in the dict to be a list, but # because we know that we won't have repeated keys, we simplify # the dict and convert it back to a single value. for key in query_string_dict: query_string_dict[key] = query_string_dict[key][0] return query_string_dict
def _modify_request_before_signing(self, request): # We automatically set this header, so if it's the auto-set value we # want to get rid of it since it doesn't make sense for presigned urls. content_type = request.headers.get('content-type') blacklisted_content_type = ( 'application/x-www-form-urlencoded; charset=utf-8') if content_type == blacklisted_content_type: del request.headers['content-type'] # Note that we're not including X-Amz-Signature. # From the docs: "The Canonical Query String must include all the query # parameters from the preceding table except for X-Amz-Signature. signed_headers = self.signed_headers(self.headers_to_sign(request)) auth_params = { 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-Credential': self.scope(request), 'X-Amz-Date': request.context['timestamp'], 'X-Amz-Expires': self._expires, 'X-Amz-SignedHeaders': signed_headers, } if self.credentials.token is not None: auth_params['X-Amz-Security-Token'] = self.credentials.token # Now parse the original query string to a dict, inject our new query # params, and serialize back to a query string. url_parts = urlsplit(request.url) # parse_qs makes each value a list, but in our case we know we won't # have repeated keys so we know we have single element lists which we # can convert back to scalar values. query_dict = dict([(k, v[0]) for k, v in parse_qs( url_parts.query, keep_blank_values=True).items()]) # The spec is particular about this. It *has* to be: # https://<endpoint>?<operation params>&<auth params> # You can't mix the two types of params together, i.e just keep doing # new_query_params.update(op_params) # new_query_params.update(auth_params) # percent_encode_sequence(new_query_params) operation_params = '' if request.data: # We also need to move the body params into the query string. To # do this, we first have to convert it to a dict. query_dict.update(self._get_body_as_dict(request)) request.data = '' if query_dict: operation_params = percent_encode_sequence(query_dict) + '&' new_query_string = (operation_params + percent_encode_sequence(auth_params)) # url_parts is a tuple (and therefore immutable) so we need to create # a new url_parts with the new query string. # <part> - <index> # scheme - 0 # netloc - 1 # path - 2 # query - 3 <-- we're replacing this. # fragment - 4 p = url_parts new_url_parts = (p[0], p[1], p[2], new_query_string, p[4]) request.url = urlunsplit(new_url_parts)