cassette
cassette copied to clipboard
Cannot playback `mutlipart/form-data`
I am attempting to use cassette with a multipart/form-data request via requests/urllib3. The requests are saving successfully. However, they are not being resolved via playback.
We ran into the same problem in eight-track. The root of the problem is multipart/form-data operates on boundaries. A boundary is a delimiter used in the POST body that doesn't occur within any other part of the request.
https://github.com/twolfson/css-validator/blob/0.5.1/test/test-files/fake-jigsaw/POST_%252Fcss-validator%252Fvalidator_bc57c9bdeae0aba1f5a961ea5024775f.json
POST http://localhost:1337/css-validator/validator HTTP/1.1
content-type: multipart/form-data; boundary=--------------------------646267990491631137772609
host:
connection: keep-alive
transfer-encoding: chunked
----------------------------646267990491631137772609
Content-Disposition: form-data; name="output"
soap12
----------------------------646267990491631137772609
Content-Disposition: form-data; name="w3cUrl"
http://localhost:1337/css-validator/validator
----------------------------646267990491631137772609
Content-Disposition: form-data; name="highWaterMark"
1024000
----------------------------646267990491631137772609
Content-Disposition: form-data; name="text"
body {
background: url(ab'cd');
-moz-box-sizing: content-box;
}
----------------------------646267990491631137772609--
Most programmatic implementations generate random strings as their boundaries. In the example above, node-form-data generated ---...646267990491631137772609. In requests/urllib3, we are generating an hexadecimal string (e.g. f7b462765acf413faae8e5c3b29e7385).
https://github.com/kennethreitz/requests/blob/v2.5.1/requests/packages/urllib3/filepost.py#L13-L17
https://github.com/kennethreitz/requests/blob/v2.5.1/requests/packages/urllib3/filepost.py#L70-L74
For reference: To work around this in eight-track, we introduced normalizeFn which is a hook that allows us to normalize request data for consistent hash lookups. Then, we wrote eight-track-normalize-multipart to take care of the multipart/form-data case.
https://github.com/twolfson/eight-track-normalize-multipart
For the interim, I am working around this by using mock.patch:
import mock
@mock.patch('requests.packages.urllib3.filepost.choose_boundary')
def test_abc(self, choose_boundary_mock):
choose_boundary_mock.return_value = 'abcdef'
# with cassette.play(...