boundary for custom 'Content-Type: multipart/form-data' header

# Test Script

Send a muli-part post file request with custom boundary.

```
import requests
import os.path

filepath = '/tmp/test.tgz'

with open(filepath, 'rb') as f:
    files = {'file':f}
    headers = {'Content-Type':'multipart/form-data;boundary=***someboundary***'}
    r = requests.post('http://127.0.0.1:56500/', files=files, headers=headers)
```
# Actual Result

```
$ nc -p 56500 -l
POST / HTTP/1.1
Host: 127.0.0.1:56500
Accept-Encoding: gzip, deflate, compress
User-Agent: python-requests/2.2.1 CPython/3.4.0 Linux/3.8.0-35-generic
Content-Length: 277
Content-Type: multipart/form-data;boundary=***someboundary***
Accept: */*

--84989444e2484915a216e1718e0f93f0
Content-Disposition: form-data; name="file"; filename="test.tgz"

VlES�
��aws9���"ـ����E�)f��i�>.!;қ�>1ܼ5��I�<���Q���|lf���S���~    �8�C
--84989444e2484915a216e1718e0f93f0--
```
# Expected Result

```
$ nc -p 56500 -l
POST / HTTP/1.1
Host: 127.0.0.1:56500
Accept-Encoding: gzip, deflate, compress
User-Agent: python-requests/2.2.1 CPython/3.4.0 Linux/3.8.0-35-generic
Content-Length: 277
Content-Type: multipart/form-data;boundary=***someboundary***
Accept: */*

--***someboundary***
Content-Disposition: form-data; name="file"; filename="test.tgz"

VlES�
��aws9���"ـ����E�)f��i�>.!;қ�>1ܼ5��I�<���Q���|lf���S���~    �8�C
--***someboundary***--
```

'boundary' is a parameter required by multipart entities. However, when I specify the custom boundary in headers, the sent request still uses generated boundary. It dosen't help even if I remove the 'boundary=...' part.

The only way to avoid this issue is to **not** specify the custom **Content-Type** as multipart in headers and let the scripts themselves generate it.

I am new to this package and the issue got me stuck for hours. So... maybe there should be a warning about this in document or maybe the customized boundary part should be implemented?


This is similar to problems many people have when coming from less useful HTTP libraries. However, the documentation is very clear about how to [post multipart files](http://docs.python-requests.org/en/latest/user/quickstart/#post-a-multipart-encoded-file). Otherwise, all we could reasonably do is have a general note that says "Please stop providing your own headers, requests can do it by itself".


At the end of that section that @Lukasa linked to, there's a bit about the [requests-toolbelt](https://gitlab.com/sigmavirus24/toolbelt). Using that library would allow you to specify your own boundary without having to craft the header yourself.


@Lukasa Yes, I appreciate how simple and clear the document said about posting files. However, from the same page of document it also specified [custom headers](http://docs.python-requests.org/en/latest/user/quickstart/#custom-headers). And some samples from the document also have custom 'Content-Type' header. So it would seem totally OK to do that.
@sigmavirus24 Thanks for point that out. But I've encountered this issue when I'm just playing around with fairly small sized files. So at first glance, I'd think that I don't need streaming and skip that part.

The reason I report this is that when I'm trying requests, there's no warning about custom boundaries in document or stderr ouput. If there were, it'd save me a lot of time trying to figure out why the sent request isn't working.
So I think at least there should be a note like "Custom boundaries are not supported. Please let requests generate that specific header for you".


@jcfrank there's a difference in the documentation (that may be too subtle) between the parts that specify a `Content-Type` header and the Multipart Post part -- The former require you to format the data yourself, specifically when posting JSON data. You're relying on requests to format the multipart request so you should not send the header.

Also, the toolbelt is desired mostly by people who have large file sizes but it even works if you need neither streaming or large file handling. You can do the following:

``` python
from requests_toolbelt import MultipartEncoder

fields = {
# your multipart form fields
}

m = MultipartEncoder(fields, boundary='my_super_custom_header')
r = requests.post(url, headers={'Content-Type': m.content_type}, data=m.to_string())
```

In this case you do want to set your own header **because** you're formatting the data.


I see the difference you mean. Thanks for the explanation.
I'd still suggest a note be added in the document, or a warning print when the 'boundary' word is detected in content-type headers, though.
You can close this if it doesn't seem necessary.

Thank you both for checking this out! :)


@jcfrank
you saved my life :)
I removed the headers and can now post the file using requests.


```
files = { 'file': ('test_one.xlsx', open('test_one.xlsx', 'rb')),}
url = 'https://XXXXX/Y-Y-Y-Y/ZZZZ?XX=YY'
r=requests.post(url, files=files)

```

@parmeshwor Glad to know it helps anyone after more than 3 years this was posted. :)

I updated the description so it's easier to see the difference. In case anyone else has to look this up in the future. :/

@jcfrank just saved me too! I've been stuck on this for at least an hour. Glad I came across this :+1: 

The same thing happened to me ,  although I solved  by myself , I am still feel happy to see this 

@Lukasa I feel this issue should be reopened, the number of people having it is a testament to either a lack of documentation or unintuitive behaviour. I've myself encountered the issue and was unable to remediate it despite reading the part of the documentation about posting multipart/form-data requests.

If requests relies on the absence of a custom Content-Type header to craft a correct request, it should raise an error when it is provided by the user, otherwise it just silently crafts a malformed request and it is very hard to debug.

If you feel such an error goes against the design philosophy of requests (where you're supposed to refrain from using custom headers and use the keyword arguments which will take care of selecting the appropriate one, if I understood correctly.) then a warning should be added to the [POST a Multipart-Encoded File](http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file) part of the documentation. Currently there's only a  warning about the Content-Length header. 

**Issues opened with the same problem**:
https://github.com/requests/requests/issues/3744
https://github.com/requests/requests/issues/621
https://github.com/requests/requests/issues/4740
https://github.com/requests/requests/issues/4589
https://github.com/requests/requests/issues/4554


@sigmavirus24 all day fighting a custom content-disposition boundary and this fixed it !

Well done and thank you for leaving this here hah. Damn. So useful. Done in minutes once I found this post.

@jcfrank Can you please check the pull request #5521 to see if it supports what you were looking for?

@virajkanwade Thanks for working on a solution to it! Seems it does handle this issue. I'm a bit concerned about [the alternative way](https://requests.readthedocs.io/en/master/user/quickstart/?highlight=multipart#post-a-multipart-encoded-file) to provide the content-type header. I don't know whether it's possible for users to try adding the boundary parameter there.

If manually handling the boundary instead of using a 3rd-party library is a concern, maybe detecting such parameter and raising an exception would also do? Just a thought.

@jcfrank I am working on a third party service which uses a custom boundary as another level of verification. So I need to set it. I approached the solution from bottom up. I noticed the urllib3 function encode_multipart_formdata already supports setting boundary. It just wasn't being passed by requests. Kept following the traceback till I managed to find a place where I could get it from the request headers. If the alternative way sets the header such that the boundary reaches till this point, i believe it should work. That is something probably the requests team can take a call on. But I don't like the approach if there is another library which does it, we won't update our. If that was the case, Python should have stayed on 2 and we should have been using libraries for everything. Just my personal thoughts.