Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP client: Simplify HTTP file uploads and submitting forms #496

Open
clue opened this issue Apr 28, 2023 · 1 comment
Open

HTTP client: Simplify HTTP file uploads and submitting forms #496

clue opened this issue Apr 28, 2023 · 1 comment

Comments

@clue
Copy link
Member

clue commented Apr 28, 2023

This library should provide better support for HTTP file uploads and submitting forms. Right now, both are supported just fine, but require manually constructing the outgoing HTTP request body. This is relatively easy for simple forms (see https://github.com/reactphp/http#post), but more challenging for forms with file uploads.

Handling file uploads is already supported on the server side (see #252, #220, #226 and others), so adding better support for this also on the client side makes a lot of sense. Implementing this is non-trivial, but doable. The major challenge right now would be evaluating possible API options and once settled, sponsoring/funding for this feature.

On top of this, we would have to consider possible API options for adding file uploads. In particular, non-blocking filesystem access remains challenging (fstat + fopen + fread), see also experimental reactphp/filesystem.

We welcome contributions, reach out if you want to support this project 👍

@clue
Copy link
Member Author

clue commented Apr 28, 2023

For the reference, submitting forms currently works like this (see also https://github.com/reactphp/http#post):

// currently: form data without file uploads (application/x-www-form-urlencoded)
// arguably good enough for most simple cases
$http->post($url, ['Content-Type' => 'application/x-www-form-urlencoded'], http_build_query(['name' => 'Alice']));

// currently: form data without file uploads (multipart/form-data)
// reasonable, but non-trivial even for simple cases
$boundary = 'foo';
$body = "--$boundary\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nAlice\r\n--$boundary--\r\n";
$http->post($url, ['Content-Type' => 'multipart/form-data; boundary=' . $boundary], $body);

// currently: form data with file uploads (multipart/form-data)
// this should be easier: same idea as above, but much harder to construct streaming request body
// major challenge: need to make sure not the entire file is kept in memory at once
// bonus challenge: non-blocking filesystem access (fstat + fopen + fread)
$body = new ThroughStream();
$size = $sizeOfAllMultipartBoundaries + $sizeOfAllFiles;
$http->post($url, ['Content-Type' => 'multipart/form-data; boundary=' . $boundary, 'Content-Length' => "$size"], $body);

Here are some possible options what this could look like in the future (brainstorming here, any input is welcome!):

// suggestion 1: accept body as array and assume form data?
// challenge: always assume form data, what about JSON?
// bonus: similar to curl
$http->post($url, [], ['name' => 'Alice', 'avatar' => $file]);

// suggestion 2: new `withFormData()` method to prefill request body
// challenge: post accepts `$body` as string, should this overwrite body?
// bonus: future `withJson()` possible
$http->withFormData(['name' => 'Alice', 'avatar' => $file])->post($url);

// suggestion 3: pass `FormData` object as request body
// challenge: post accepts `$body` as string type, should this be Stringable?
// bonus: similar to JavaScript: https://developer.mozilla.org/en-US/docs/Web/API/FormData
$http->post($url, [], new FormData(['name' => 'Alice', 'avatar' => $file]));

On top of this, file uploads need some kind of API to reference files from the filesystem (or elsewhere/external). See note above, as non-blocking filesystem access remains challenging (fstat + fopen + fread). Here are some possible options what this could look like in the future (brainstorming here, any input is welcome!):

// suggestion 1: use native stream resources
// challenge: error handling via PHP warnings, low-level access, blocking operations
// challenge: filename can be derived from resource, but what about file content-type?
$file = fopen('avater.png', 'r');

// suggestion 2: reuse SplFileObject
// challenge: slightly better error handling, but still blocking operations
// challenge: filename can be derived from object, but what about file content-type?
$file = new \SplFileObject('avatar.png', 'r');

// suggestion 3: reuse PSR-7 `UploadedFileInterface`, add public method or constructor?
// challenge: designed for server side, so slightly different semantics on client side
// bonus: already supports explicit file content-type, allows wrapping low-level filesystem access
$file = UploadedFile::open('avatar.png');

Any input is welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant