Requests icon indicating copy to clipboard operation
Requests copied to clipboard

File uploads

Open soulseekah opened this issue 7 years ago • 5 comments
trafficstars

Working on #289 :)

soulseekah avatar Feb 11 '18 19:02 soulseekah

Codecov Report

Merging #313 into master will decrease coverage by 0.06%. The diff coverage is 94.28%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #313      +/-   ##
============================================
- Coverage     92.11%   92.04%   -0.07%     
- Complexity      760      771      +11     
============================================
  Files            21       21              
  Lines          1762     1785      +23     
============================================
+ Hits           1623     1643      +20     
- Misses          139      142       +3
Impacted Files Coverage Δ Complexity Δ
library/Requests/Transport/fsockopen.php 94.68% <100%> (+0.49%) 74 <55> (+6) :arrow_up:
library/Requests.php 75.25% <100%> (-0.34%) 118 <5> (ø)
library/Requests/Transport/cURL.php 92.95% <83.33%> (-0.74%) 71 <35> (+5)

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 4055bc4...a47689d. Read the comment docs.

codecov-io avatar Feb 15 '18 17:02 codecov-io

Coded things up around a Requests_File object. I thought about the interface, but I think it complicates things without bringing anything to the table whatsoever. And we don't really need get_header, get_body or any such things, since the raw stuff is only done in the fsockopen transport. The cURL simply rewraps into a cURL_File object. So the only functions the Requests_File has right now are:

  1. Detect mimtype and filename when not provided.
  2. Retrieve content.
  3. Allow it to be mixed into the $data parameter and detected as a file upload.

Coding multichunk uploading to save RAM is possible for fsockopen, but it's really just fsockopen low-level internals and none of the other transports will use this as cURL handles large uploads intelligently on its own. Handling large uploads using fsockopen efficiently should be outside of the scope of this PR in order to get the feature in with less blood and tears.

Let me know what you think.

soulseekah avatar Feb 15 '18 17:02 soulseekah

These definitely need to be part of an interface to allow users of the library to create alternative implementations. For example, it should support the following use cases:

  • Uploading a file directly from the filesystem (i.e. you pass a path)
  • Uploading a file when you have the contents as a string (i.e. you pass a filename and contents in a string)
  • Uploading a file when you have an existing file handle (i.e. you pass a filename and pointer)
  • Uploading multiple files with any combination of the above

(For any case other than multiple files, we probably shouldn't use multipart/form-data.)

Having an interface expose get_headers() and get_body() should allow all of these to be solved in custom classes easily. There's no huge advantage I can see of using a CURLFile instance apart from streaming the file; potentially, get_body() could return either a string or a file resource, and we could stream the resource directly to the outbound socket. That feels out-of-scope for an initial implementation though. :)

rmccue avatar Feb 16 '18 04:02 rmccue

There's no huge advantage I can see of using a cURLFile instance apart from streaming the file

cURL doesn't allow streaming for multipart/form-data, using cURLFile is the only way to upload a file as part of the data. And CURLFile only accepts a path. Thus:

Uploading a file when you have the contents as a string.

On multipart/form-data you'd have to save the contents to a temporary file first for cURL.

Uploading a file when you have an existing file handle

Not possible to get a filepath from a file descriptor, especially since it could be a php://input or something. cURLFile needs a path.

we could stream the resource directly to the outbound socket

This would only be possible in cURL by using CURLOPT_READFUNCTION for the whole of the request body. Employing this would mean assembling everything for the cURL transport from the bottom up, which would end up being sort of fsockopen, where everything is very low level.

For any case other than multiple files, we probably shouldn't use multipart/form-data

This would grow the code by a whole branch in fsockopen. cURL sends one file with multipart/form-data (even from the command line), unless, you want to employ CURLOPT_READFUNCTION and handle all the low-level stuff. But then why not just use fsockopen to begin with, it's already low-level.

These definitely need to be part of an interface

The only common things among the transports at this point and the foreseeable future are: 1. filepath, 2. filename, 3. mimetype. Even get_body or a more low-level read interface would only be used by fsockopen, again, unless you want to uproot the cURL transport (CURLOPT_READFUNCTION, huge nightmare).

get_headers() and get_body()

Pointless unless you want to use them inside CURLOPT_READFUNCTION. Otherwise the header and body structure needs low-level definitions for building the request data in fsockopen only.

To summarize:

  1. Providing this flexible magic interface requires the addition of a whole new level of complexity into the cURL transport with little added benefit to the flexibility, "dumbing" cURL down to fsockopen's level.
  2. Requests has survived for over 6 years on a very tight $data interface (string or array [that is flattened to a string either way]) and no file upload support.
  3. All use cases for file uploads can be worked around by supplying a file path (use named pipes for callback-based data generators, for example) with very little work on the users part.

I feel this over-engineering is hindering a straightforward feature from landing and doing what it's supposed to do - upload a file. Decisions not options.

soulseekah avatar Feb 16 '18 06:02 soulseekah

Any info about this feature? It would be a great addition.

I had an issue where the call towards an API passed when I used cURL with CURLOPT_POSTFIELDS, but would error out if I used wp_remote_request with the same value passed to the body argument.

dingo-d avatar Sep 06 '22 07:09 dingo-d