Content-Length is missing

Please see the following example

```

_data = """callCount=1 
page=/internetsales/iSnapVehicle.xhtml?_gk=_c083894BB-4392-5290-8580-DE159D03B2B0_k9C5EF4F0-0269-3CC9-0994-633224433265

httpSessionId=
scriptSessionId=DD51AC690A8BC3BEDB1AEDF1B2A7A9DD575
c0-scriptName=PresentationRulesFacade
c0-methodName=execute
c0-id=0
c0-param0=string:VehicleForm c0-param1=string:VehicleForm%3Amake
c0-param2=string:%7B%22VehicleForm%3AdaysDriven%22%20%3A%20%221%22%2C%20%22VehicleForm%3AmilesDriven%22%20%3A%20%220%22%2C%20%22VehicleForm%3Ayear%22%20%3A%20%221998%22%2C%20%22VehicleForm%3AestimatedMileage%22%20%3A%20%22%22%2C%20%22VehicleForm%3AodometerReading%22%20%3A%20%22%22%2C%20%22VehicleForm%3Amake%22%20%3A%20%22AUDI%22%2C%20%22VehicleForm%3Ayear-txt%22%20%3A%20%22%22%2C%20%22VehicleForm%3Amake-txt%22%20%3A%20%22%22%2C%20%22VehicleForm%3Amodel-txt%22%20%3A%20%22%22%2C%20%22VehicleForm%3AmodelDesc%22%20%3A%20%22Other%22%2C%20%22VehicleForm%3Amodel%22%20%3A%20%22Other%22%2C%20%22VehicleForm%3Abodystyle%22%20%3A%20%22VAN%22%7D
c0-param3=boolean:false
batchId=327
"""

print len(_data)
_headers = {
    'referer' : 'https://sales2.geico.com/internetsales/iSnapVehicle.xhtml?_gk=_c083894BB-4392-5290-8580-DE159D03B2B0_k9C5EF4F0-0269-3CC9-0994-633224433265'
}
r = requests.post(
    url = 'https://sales2.geico.com/internetsales/dwr/call/plaincall/PresentationRulesFacade.execute.dwr',
    data = _data,
    headers = _headers
)

print r.request.headers
{'referer': 'https://sales2.geico.com/internetsales/iSnapVehicle.xhtml?_gk=_c083894BB-4392-5290-8580-DE159D03B2B0_k9C5EF4F0-0269-3CC9-0994-633224433265', 'Accept-Encoding': 'identity, deflate, compress, gzip', 'User-Agent': 'python-requests/0.7.3'}
```

In this example, the 'Content-Length' is missing.

Haven't got any spare time to try and patch the bug - as this was only a quick test to explore new libs.

Cal

[revised post 3 - multiple edits due to mistake on bug report]


Should be good now


``` python
import requests
URL = "http://github.com"

g_url = "http://clients6.google.com/rpc?key=AIzaSyCKSbrvQasunBoV16zDH9R33D88CeLr9gQ"
params = {
    'method': 'pos.plusones.get',
    'id': 'p',
    'params': {
        'nolog': 'true',
        'id': URL,
        'source': 'widget',
        'userId': '@viewer',
        'groupId':'@self'
    },
    'jsonrpc': '2.0',
    'key': 'p',
    'apiVersion': 'v1'
}
headers = {
    'Content-type': 'application/json'
}

r = requests.post(g_url, params=params, headers=headers)
print r.request.headers
```

content length still isn't set in this code with version 0.8.3


You're not uploading any body. Why would there be a content-length?


kennethreitz, iirc - the Content-length header should always be set (even if it's zero) when a POST is involved.

But by looking at the code pasted by zoranzaric, he is indeed sending a POST with a request body - so the content-length should surely be included? 

Am I missing something here??

Cal


foxx, I thought setting params was correct for a POST.  What am I doing wrong?

kennethreitz, the Google API that I'm talking to responds with 411 (Length Required)


@zoranzaric: `params` is used for query url parameters. `data` is used for body data.


@kennethreitz ok yeah snap... with `data` it works... thanks and sorry for the trouble!


@zoranzaric, no worries! A lot of other libraries are quite inconsistent, so I completely understand the confusion. That's the whole reason I started Requests :)


Ah - sorry I missed the 'params' / 'data' difference. My bad!


This is still broken with regard to sending a blank POST request:

r = requests.post(full_uri)

While a Content-Length header is sent, the value is blank (it should be 0 per the RFC).  The request as the server see is below:

`````` BASE_ENVIRON
{'CONTENT_LENGTH': '',
 'CONTENT_TYPE': '',
 'HTTP_ACCEPT': '*/*',
 'HTTP_ACCEPT_ENCODING': 'identity, deflate, compress, gzip',
 'HTTP_HOST': 'localhost:8086',
 'HTTP_USER_AGENT': 'python-requests/0.8.3',
 'PATH_INFO': '/__snap__/sldb/cc/heartbeat/4f14a8282be5c42b87000002',
 'QUERY_STRING': '',
 'REMOTE_ADDR': '127.0.0.1',
 'REMOTE_PORT': 49676,
 'REQUEST_METHOD': 'POST',
 'SCRIPT_NAME': '',
 'SERVER_NAME': '127.0.0.1',
 'SERVER_PORT': '8086',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'SERVER_SOFTWARE': 'Werkzeug/0.8.1',
 'werkzeug.request': <BaseRequest 'http://localhost:8086/__snap__/sldb/cc/heartbeat/4f14a8282be5c42b87000002' [POST]>,
 'werkzeug.server.shutdown': <function shutdown_server at 0x105cd3de8>,
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0x105373270>,
 'wsgi.input': <socket._fileobject object at 0x105d62350>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': False,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)}```
``````


Are you working on the invalid content-length for POST requests without data payload issue? I was just bit by this bug while trying to POST and PUT to a WCF service. I could luckily work around it by specifying a content-length of 0 via the 'headers' keyword argument to the 'post' and 'put' functions.


@aknuds1 what version are you using? This is what I see in trunk:

```
>>> import requests
>>> print requests.post('http://httpbin.org/post').text
{
  "origin": "[REDACTED]", 
  "files": {}, 
  "form": {}, 
  "url": "http://httpbin.org/post", 
  "args": {}, 
  "headers": {
    "Content-Length": "0", 
    "Accept-Encoding": "identity, deflate, compress, gzip", 
    "Connection": "keep-alive", 
    "Accept": "*/*", 
    "User-Agent": "python-requests/0.11.3", 
    "Host": "httpbin.org", 
    "Content-Type": ""
  }, 
  "json": null, 
  "data": ""
}
```


@slingamn Ah, maybe it's fixed in trunk then. I'm just using the version installed via pip, i.e. 0.11.2.


Oh, weird, I get `"Content-Length": "0"` on 0.11.2 (2159c802706f) also.

Can you try the above test case and post the result?


I tried your case and get the following output:

```
{
  "origin": "[REDACTED]",
  "files": {},
  "form": {},
  "url": "http://httpbin.org/post",
  "args": {},
  "headers": {
    "Content-Length": "0",
    "Via": "1.1 EUR-PRXY-13",
    "Connection": "keep-alive",
    "Accept": "*/*",
    "User-Agent": "python-requests/0.11.2",
    "Host": "httpbin.org",
    "Content-Type": ""
  },
  "json": null,
  "data": ""
}
```

Is there a bug at httpbin.org then, since there is clearly a difference in the request when I define the content-length header myself?


Sorry, I'm confused. Isn't that the expected output?

```
$ curl -d "" http://httpbin.org/post
{
  "origin": "[REDACTED]", 
  "files": {}, 
  "form": {}, 
  "url": "http://httpbin.org/post", 
  "args": {}, 
  "headers": {
    "Content-Length": "0", 
    "Connection": "keep-alive", 
    "Accept": "*/*", 
    "User-Agent": "curl/7.21.7 (x86_64-redhat-linux-gnu) libcurl/7.21.7 NSS/3.13.3.0 zlib/1.2.5 libidn/1.22 libssh2/1.2.7", 
    "Host": "httpbin.org", 
    "Content-Type": "application/x-www-form-urlencoded"
  }, 
  "json": null, 
  "data": ""
}
```


@slingamn It's the desired output, yes, but why is there a difference towards WCF when I define the content-length header myself?

I mean, httpbin.org reports that the request has defined content-length as "0", even though the same request directed at WCF fails with error 411.


I'm inclined to suspect a bug with the WCF service, since I get the same result using http://requestb.in.

```
>>> requests.post('http://requestb.in/10uyxph1')
<Response [200]>
>>> 
```

and Requestbin saw the following headers:

```
Content-Length 0
Accept-Encoding identity, deflate, compress, gzip
Connection keep-alive
Accept */*
User-Agent python-requests/0.11.1
Host requestb.in
```


But there _must_ be a difference wrt. content-length, since defining that header myself in the client library (requests) results in a request accepted by WCF. I'll see if I can get hold of the POST request received by WCF.


One possible explanation: this results in the headers appearing in a different order, and the WCF service (incorrectly) takes note of this.

To get the POST request, you could (as a last resort) try something like Wireshark.


According to Fiddler, the POST request without user-defined content-length header looks like so:

```
POST [REDACTED] HTTP/1.1
Host: [REDACTED]
Connection: Keep-Alive
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
User-Agent: python-requests/0.11.2
```

If I define the content-length header, via the 'headers' argument to requests.post, however, the request looks like so:

```
POST [REDACTED] HTTP/1.1
Host: [REDACTED]
content-length: 0
Connection: Keep-Alive
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
User-Agent: python-requests/0.11.2
```


Interesting! The line of code that produces the bad behavior is just `requests.post(my_url)`, right?


I'm on Python 2.7.2. Here's what I see in my httplib.py:

``` python
    def _set_content_length(self, body):
        # Set the content-length based on the body.
        thelen = None
        try:
            thelen = str(len(body))
        except TypeError, te:
            # If this is a file-like object, try to
            # fstat its file descriptor
            try:
                thelen = str(os.fstat(body.fileno()).st_size)
            except (AttributeError, OSError):
                # Don't send a length if this failed
                if self.debuglevel > 0: print "Cannot stat!!"

        if thelen is not None:
            self.putheader('Content-Length', thelen)
```

If you can't provide a reproducible test case for security/privacy reasons (totally understandable), can you try and monkeypatch your httplib and see if that `except` block is being hit? You could also try setting `debuglevel` from the client code in `requests.packages.urllib3`.


My code is as simple as this (the HTTP proxy is Fiddler):

```
import requests

resp = requests.post("http://[REDACTED]", proxies={"http": "localhost:8888"})
resp.raise_for_status()
```

If you run this script against Fiddler on your machine, you would probably see the same request as I do.

I can't see that httplib._set_content_length is called at all. I injected some code in there to see if it was called by requests, but that code wasn't hit.


And Fiddler shows a `Content-length: 0` header when querying httpbin.org and requestsb.in?


```
resp = requests.post("http://httpbin.org/post", proxies={"http": "localhost:8888"})
```

```
POST http://httpbin.org/post HTTP/1.1
Host: httpbin.org
Proxy-Connection: Keep-Alive
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
User-Agent: python-requests/0.11.2
```


Neat, I'll check up on that.

By the way, what's your platform and Python version?


Also, just for kicks, you might want to upgrade to Requests 0.12.0 (although I don't think any of the changes are relevant to this issue).


I'm on Python 2.7.2/Windows 7. I tried Requests 0.12.0, it made no difference unfortunately.


You, sir, are correct!

```
>>> print requests.post('http://httpbin.org/post').content
{
  "origin": "[REDACTED]", 
  "files": {}, 
  "form": {}, 
  "url": "http://httpbin.org/post", 
  "args": {}, 
  "headers": {
    "Content-Length": "0", 
    "Accept-Encoding": "identity, deflate, compress, gzip", 
    "Connection": "keep-alive", 
    "Accept": "*/*", 
    "User-Agent": "python-requests/0.12.0", 
    "Host": "httpbin.org", 
    "Content-Type": ""
  }, 
  "json": null, 
  "data": ""
}
```

but `strace -v -e trace=network -p 32122 -s 2000` says:

```
connect(4, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("107.21.123.247")}, 16) = 0
sendto(4, "POST /post HTTP/1.1\r\nHost: httpbin.org\r\nAccept-Encoding: identity, deflate, compress, gzip\r\nAccept: */*\r\nUser-Agent: python-requests/0.12.0\r\n\r\n", 143, 0, NULL, 0) = 143
```

Content-length is not being set on zero-data POSTs; httpbin.org is reporting it anyway.