Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python requests can't send multiple headers with same key

I'm trying to send a GET request to a server with two headers which have the same name, but different values:

url = 'whatever'
headers = {'X-Attribute': 'A', 'X-Attribute': 'B'}
requests.get(url, headers = headers)

This obviously doesn't work, since the headers dictionary can't contain two keys X-Attribute.

Is there anything I can do, i.e. can I pass headers as something other than a dictionary? The requirement to send requests in this manner is a feature of the server, and I can't change it.

like image 239
Stefan Avatar asked May 28 '13 10:05

Stefan


People also ask

Can you have multiple HTTP headers with the same name?

Combining header fields:A recipient MAY combine multiple header fields with the same field name into one field-name: field-value pair, without changing the semantics of the message, by appending each subsequent field value to the combined field value in order, separated by a comma.

Which method is used to fetch multiple header values for the same name?

The get() method of the Headers interface returns a byte string of all the values of a header within a Headers object with a given name.

How do I pass a header request in Python?

In order to pass HTTP headers into a POST request using the Python requests library, you can use the headers= parameter in the . post() function. The headers= parameter accepts a Python dictionary of key-value pairs, where the key represents the header type and the value is the header value.

What are headers in Python requests?

headers – (optional) Dictionary of HTTP Headers to send with the Request . cookies – (optional) CookieJar object to send with the Request . auth – (optional) AuthObject to enable Basic HTTP Auth.


2 Answers

requests stores the request headers in a dict, which means every header can only appear once. So without making changes to the requests library itself it won't be possible to send multiple headers with the same name.

However, if the server is HTTP1.1 compliant, it must be able to accept the same as one header with a comma separated list of the single values.


Late edit:
Since this was brought back to my attention, a way to make this work would be to use a custom instance of str that allows to store multiple of the same value in a dict by implementing the hash contract differently (or actually in a CaseInsensitiveDict, wich uses lower() on the names). Example:

class uniquestr(str):

    _lower = None

    def __hash__(self):
        return id(self)

    def __eq__(self, other):
        return self is other

    def lower(self):
        if self._lower is None:
            lower = str.lower(self)
            if str.__eq__(lower, self): 
                self._lower = self
            else:
                self._lower = uniquestr(lower)
        return self._lower

r = requests.get("https://httpbin.org/get", headers={uniquestr('X'): 'A',
                                                     uniquestr('X'): 'B'})
print(r.text)

Produces something like:

{
  ...
  "headers": {
    ...
    "X": "A,B",
  }, 
  ...
}

Interestingly, in the response the headers are combined, but they are really sent as two separate header lines.

like image 173
mata Avatar answered Oct 12 '22 23:10

mata


requests is using urllib2.urlencode under the hood (or something similar) in order to encode the headers.

This means that a list of tuples can be sent as the payload argument instead of a dictionary, freeing the headers list from the unique key constraint imposed by the dictionary. Sending a list of tuples is described in the urlib2.urlencode documentation. http://docs.python.org/2/library/urllib.html#urllib.urlencode

The following code will solve the problem without flattening or dirty hacks:

url = 'whatever'
headers = [('X-Attribute', 'A'),
           ('X-Attribute', 'B')]
requests.get(url, headers = headers)
like image 29
user1171968 Avatar answered Oct 13 '22 00:10

user1171968