requests follows a PUT request's 301 and does a PUT request at the end

Not only is that wrong (should behave like POST), but the end request is broken in that it has no content-length and no body.


This should only be the case of `allow_redirects` is explicitly set to `True`.


Not only is that false:

```
In [4]: requests.put('http://127.0.0.1:5000/', data='a')
Out[4]: <Response [200]>

In [5]: requests.put('http://127.0.0.1:5000/', data='a', allow_redirects=False)
Out[5]: <Response [301]>
```

But, as I said, the behaviour is not only inconsistent with both the POST behaviour and the RFC, but also completely useless, as the data is lost and the request is not valid.


Please be cordial or be on your way.

I said that it should only the be case. If it isn't, it's a bug :)


Uh...that output is totally unclear. Is the method not returning anything at all?


Also, the logic for redirects is method-independent. Given that it works for GET requests, I also assume that it works for PUT, POST, HEAD, OPTIONS etc.


Yeah. Github ate my code (it interpreted the repr of the request as tags. bleh). Basically, I have a simple flask app that sends 301 from / to /new/. 

When I PUT something to /, without specifying `allow_redirects` (so, not explicitly set to True), the redirect is followed to /new/, and the following request is being generated for the new URL:

```
In [14]: r=requests.put('http://127.0.0.1:5000/', data='a')
In [16]: r.request.path_url, r.request.method, r.request.headers, r.request.body
Out[16]: 
('/new/',
 'PUT',
 {'Accept': '*/*',
  'Accept-Encoding': 'gzip, deflate, compress',
  'User-Agent': 'python-requests/1.2.0 CPython/2.7.3 Linux/3.2.0-4-amd64'},
 None)
```

With `allow_redirects` set to `False`, yes, I just get a 301.

Sorry if I sounded abrasive or rude, wasn't my intention.

And, Lukasa, the logic is not method independent. There's a special case for POST, which should actually be extended to all methods except GET or HEAD.


@Lukasa False https://github.com/kennethreitz/requests/blob/master/requests/sessions.py#L121


The POST special-case is actually a deviation from the RFC, implemented to match what web browsers do. Web browsers don't generally send PUT requests, so we don't special case that.

As for explicitly set to True, that was likely a brief oversight: the default value of allow_redirects is true.


In that case, the behaviour should be:

10.3.2 301 Moved Permanently

If the 301 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.


Indeed, that is exactly why `allow_redirects` is named what it is, to allow the user to confirm that they want to follow redirects.

It absolutely shouldn't be True for PUT.


Okay, yes, that would solve part of the problem. The other one is what exactly happens _after_ the redirect. Since the RFC does not mention, I think the request should be changed to GET.


I'll handle this. I was likely the last person to touch any of this code.


I think this will be as simple as making allow_redirects default to false for the request method. I'm not sure why it isn't...

## 

Kenneth Reitz

On Wednesday, April 10, 2013 at 12:57 PM, Ian Cordasco wrote:

> I'll handle this. I was likely the last person to touch any of this code.
> 
> —
> Reply to this email directly or view it on GitHub (https://github.com/kennethreitz/requests/issues/1303#issuecomment-16198018).


That and making sure we aren't making a second PUT


I just ran into a similar issue while playing with elephant, making a `DELETE` request issues a redirect, which means that a `DELETE` request is made to the next resource, resulting in a 405 (method not allowed).

```
127.0.0.1 - - [13/Apr/2013 16:35:10] "DELETE /68f5a299-e15a-42d5-83a3-099229f79579 HTTP/1.1" 302 -
127.0.0.1 - - [13/Apr/2013 16:35:10] "DELETE / HTTP/1.1" 405 -
```

[RFC2616's section about 302 redirects](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3) suggests that "most" implementations treat a 302 like a 303. Maybe all redirects should be done with a `GET` request to be consistent with other user agents.

What do you think?


This definitely confirms the original report:

``` python
from flask import Flask, redirect, url_for, request, jsonify

app = Flask(__name__)


@app.route('/put', methods=['PUT'])
def put():
    return redirect(url_for('home'))


@app.route('/', methods=['GET', 'PUT'])
def home():
    return jsonify(method=request.method, form=request.form,
                   data=request.data, json=request.json,
                   headers=dict(request.headers))


if __name__ == '__main__':
    app.run(debug=True)
```

``` pycon
>>> r = requests.put('http://localhost:5000/put', data='foo')
>>> r.json()
{u'headers': {u'Content-Length': u'', u'Accept-Encoding': u'gzip, deflate, compress', u'Accept': u'*/*', u'User-Agent': u'python-requests/1.2.0 CPython/2.6.2 Linux/2.6.30.3', u'Host': u'localhost:5000', u'Content-Type': u''}, u'json': None, u'data': u'', u'method': u'PUT', u'form': {}}
```


This probably should have been mentioned in the release notes... Our application was relying on the old behaviour for DELETE requests. When we tested against requests 1.2.3 it took me a while to figure out what had changed and whether it was deliberate or a bug somewhere.


Yeah, we used to be pretty bad at release noting things. We're getting better. Sorry! =(


Is there any way to retroactively add one? Do you think it's worth it?


We certainly could. I'll update it now.


@danc86 for what it's worth, @Lukasa took care of that for you. =)