Re: Issues with HTTP multipart/form-data file upload
Xavier Del Campo Romero <[email protected]> Wed, 28 Aug 2024 01:04:04 +0200
| Newsgroups | gmane.comp.web.dillo.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi Rodrigo,
> Glad to read that you also consider Dillo for slcl, and thanks for preparing the patches :-)
Thank you! I want slcl to be useful to anyone, including users who care
about minimalist software like Dillo. The web is already too crowded
with bloated "webapps" and other terrible things. :)
> Sounds good, not sure how complicated it would be to do this.
I still need to investigate this further, but I assume this would
require Dillo to at least implement a sink callback.
In other words, the component responsible for transmitting the data
(probably src/IO/IO.c) should trigger a user-defined callback with an
arbitrarily-sized buffer (typically, of BUFSIZ bytes, as defined by
stdio.h) that must filled with file data. Then, the user-defined
callback can fill from zero up to BUFSIZ bytes, which are eventually
trasmitted to the server.
That said, I am still not sure how much actual effort this would take.
But I am glad to receive positive feedback so far - I will then continue
to find a solution.
> However, being able to upload multiple files at the same time sounds reasonable, so feel free to try on your own in the meanwhile.
Uploading multiple files at once seems doable - the patches I sent on my
previous email are probably already doing most of the required work.
Again, the trickiest task is to send data on-the-fly for each selected file.
> Shouldn't it be 68 then?
I understand the opposite: the boundary string with the two leading
dashes ("--") included can be up to 72 bytes long, and 74 bytes long for
the ending boundary (which includes two more dashes after the boundary
string). This is confirmed by reading the BNF defined by RFC 2046 (some
bits omitted for simplicity), section 5.1.1 [1]:
> boundary := 0*69<bchars> bcharsnospace
> bchars := bcharsnospace / " "
> bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
> "+" / "_" / "," / "-" / "." /
> "/" / ":" / "=" / "?"
> dash-boundary := "--" boundary
> ; boundary taken from the value of
> ; boundary parameter of the
> ; Content-Type field> multipart-body := [preamble CRLF]
> dash-boundary transport-padding CRLF
> body-part *encapsulation
> close-delimiter transport-padding
> [CRLF epilogue]
> delimiter := CRLF dash-boundary
> close-delimiter := delimiter "--"
Note: even if the specification tells receivers to handle transport
padding, for the time being I am assuming "transport-padding" as zero
length since composers must not generate non-zero length transport
padding. I am still not sure where transport padding would apply,
anyway. Probably outside web browsers?
> I would leave out all the symbols to avoid quoting and only use A-Z a-z and 0-9.
Interestingly, Dillo would always quote boundary strings [2], even if
only using A-Z, a-z and 0-9. In fact, this is one of the wrong
assumptions I spotted when testing slcl against Dillo.
> Which, if I computed it correctly, is still too small to worry about.
Not only it is too small of a chance: if we really wanted to do "the
right thing" and make Dillo absolutely sure the boundary string is not
contained within the selected files, this would imply a noticeable
performance impact when dealing with large files, much likely for a
near-zero benefit.
I have not inspected their source code yet (and I do not want to), but I
understand both Gecko and Chromium are also making that assumption,
because otherwise it would take them a lot of CPU time to upload large
files.
> Why sizeof " " instead of just 2?
Because, to my eyes, sizeof " " has more meaningful semantics, compared
to a magic integer constant such as 2. However, for this simple
scenario, I would still consider both acceptable.
I can replace it with 2 if you find the other construct unacceptable.
> PS: When are you playing?
Sorry, I did not understand your last sentence. Could you please give a
bit more context? :)
Best regards,
Xavi
[1]: https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1
[2]:
https://github.com/dillo-browser/dillo/blob/8a360e32ac3136494a494379a6dbbacef6f95da2/src/IO/http.c#L362
On 27/8/24 22:59, Rodrigo Arias wrote:
> Hi Xavier,
>
> On Mon, Aug 26, 2024 at 01:27:29AM +0200, Xavier Del Campo Romero wrote:
>> Hello Dillo dev community,
>>
>> I am testing support among web browsers for slcl [1], a JS-less
>> minimalist web storage solution. slcl relies on
>> "multipart/form-data"-encoded HTTP requests to upload files to a server.
>> Whereas Dillo helped me to uncover a few wrong assumptions on my code, I
>> have realised a few issues on Dillo itself related to filue uploads that
>> should be considered.
>
> Glad to read that you also consider Dillo for slcl, and thanks for
> preparing the patches :-)
>
>> Issue #1:
>>
>> While Dillo is fine uploading small files (up to a few MiB), things go
>> wrong with larger files, so much that memory usage increases up to
>> multiple GiB and can even lock the system up. This is because Dillo is
>> designed to send requests always from memory, which means file contents
>> must be dumped into memory first, and this might be unfeasible for large
>> files.
>>
>> Suggestion:
>>
>> Instead, Dillo should ideally send file contents on-the-fly, so that
>> memory usage is kept to a minimum regardless the file size.
>
> Sounds good, not sure how complicated it would be to do this.
>
>> Issue #2:
>>
>> Even if Dillo generates a 70-byte, random boundary string (yet mostly
>> filled with '-', similarly to Firefox [2]), it ensures it is not found
>> anywhere inside the file contents. Again, this can be a serious
>> bottleneck in the case of large files, as it requires to scan the whole
>> file for a match.
>>
>> Suggestion:
>>
>> Define *all* of the 70 bytes in the boundary string as random, and
>> assume they would never be found inside a file. The chance of accidental
>> collision is so low that it is not worth the effort into checking them.
>>
>> Suggested patches:
>>
>> - 0001-dialog.cc-Generate-more-random-boundaries.patch)
>
> Yes, I think is a good idea.
>
> @@ -1246,6 +1246,24 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput
> *active_submit)
> return DataStr;
> }
>
> +static void generate_boundary(Dstr *boundary)
> +{
> + for (int i = 0; i < 70; i++) {
>
> I think this is too long:
>
>> Boundary delimiters must not appear within the encapsulated material,
>> and must be no longer than 70 characters, not counting the two
>> leading hyphens.
>
> Shouldn't it be 68 then?
>
> + /* Extracted from RFC 2046, section 5.1.1. */
> + static const char set[] = "abcdefghijklmnopqrstuvwxyz"
> + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
> + "0123456789"
> + "'()+_,-./:=? ";
>
> I would leave out all the symbols to avoid quoting and only use A-Z a-z
> and 0-9.
>
> Assuming you are left with 62 symbols (26*2 + 10), the probability of
> finding a random 70 character string in a random sample of 70 characters
> is (using ** as ^):
>
> p = 1/62**70 = 3.4086e-126
>
> But as you will be dealing with large files, each line of the input
> could match it. We are interested in the probability that a given
> boundary will mach any of the N lines. So we compute the probability
> that it will match none of the lines (1-p)**N and then the opposite of
> that (matches at least one line):
>
> 1 - (1 - p) ** N
>
> For example, a 1 TB encoded file, with 2**40/70 lines will have:
>
> p = 1 - (1 - 1/62**70) ** (2**40 / 70)
>
> Of occurring, which is around 5.354e-116.
>
> Which, if I computed it correctly, is still too small to worry about.
>
> + char s[sizeof " "] = {0};
>
> Why sizeof " " instead of just 2?
>
> +
> + do {
> + *s = rand();
> + } while (!strspn(s, set));
> +
> + dStr_append(boundary, s);
> + }
> +}
> +
> /**
> * Generate a boundary string for use in separating the parts of a
> * multipart/form-data submission.
>
>> Issue #3:
>>
>> Dillo only supports uploading 1 file at a time. This is mostly because
>> it relies (probably temporarily?) on the a_Dialog_save_file function
>> [3]. However, this is not a limitation on the HTTP protocol, and other
>> implementation such as Firefox or Chromium-based browsers support this.
>>
>> Suggested patches:
>>
>> - 0002-dialog-Add-a_Dialog_select_files.patch
>> - 0003-WIP-multi-file-uploads.patch
>>
>> Conclusions:
>>
>> Dillo seems designed to always send requests from memory, so it is not
>> straightforward to break this assumption in order to support large file
>> uploads. 0003-WIP-multi-file-uploads.patch is an incomplete first step
>> into fixing this, but it surely needs deeper design changes.
>>
>> I did not put more effort into these patches for the time being because,
>> after seeing the potential complexity behind this task, I thought it was
>> a better idea to ask the community for feedback and guidelines.
>
> I'm not very familiar with this part of Dillo, so I'll have to check a
> bit more before providing more feedback.
>
> However, being able to upload multiple files at the same time sounds
> reasonable, so feel free to try on your own in the meanwhile.
>
> PS: When are you playing?
>
> Best,
> Rodrigo.
> _______________________________________________
> Dillo-dev mailing list -- dillo-dev-lx9mn2B4QYRWk0Htik3J/[email protected]
> To unsubscribe send an email to dillo-dev-leave-lx9mn2B4QYRWk0Htik3J/[email protected]
_______________________________________________
Dillo-dev mailing list -- dillo-dev-lx9mn2B4QYRWk0Htik3J/[email protected]
To unsubscribe send an email to dillo-dev-leave-lx9mn2B4QYRWk0Htik3J/[email protected]
OpenPGP_0x84FF3612A9BF43F2.asc
(application/pgp-keys, 4.8 KB)
-----BEGIN PGP PUBLIC KEY BLOCK----- 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 =Om+c -----END PGP PUBLIC KEY BLOCK-----
OpenPGP_signature.asc
(application/pgp-signature, 840 B)
-----BEGIN PGP SIGNATURE----- wsF5BAABCAAjFiEEL4wEMJP9YSkk8Lb8hP82Eqm/Q/IFAmbOW2UFAwAAAAAACgkQhP82Eqm/Q/I3 iw//Ze1FRswxVzFkNl2DjQjuXperY1J+s+Eb7JiXte8WqKXJiKCaUgGUsdRImV1ZHoCLBso6IVSK fKJk3q7RU4vdZXQ7XReriIVbrfVKafTEYTPbkcAm5NpTCB0UvXwClcOFo3BJCFXyjzk6fP8Q1mZD kxeuFwOMJaFKQpVZo9eDlHMyOzdXR60rP6IfnL5jfz8Yk/b6kujGOtvvVy/9Mj/1jWZK5pOm3FpO UORZOy8zZH/3imq9iH3iRd3FS6bA1TVNzA/jdhp/q7PjGPhueKyDf0oS02Yk5+Z1W+NRCcTTwghr 9wXlIiYd5YcKmTzXwUbcCZAaSrHoxkGVgIHO/ta43+tUBBGH156gysEqTBdxDAEhCtdBlkiagNHe BW3jXpJn6l6wc0ofNQnE2/+TwB2AWugMz9VXvYtSJkFjAFUuPelOUmSnAu6cafdniCl1Piv0vz9h IkgPYwBJmu4ksOMSZkTJmqcSBVHf8WtZp40KwhCsg8PgjM1q/ZfV4iQ/QWo9xQMpIF9s4Zmg8OfB UGX5C9xehPRoXsdrSfvLBNn02PQLAKOlBORZPbkKP5WInPaqidy754xcK8pfFBbGv4NZy6ay1+0u 5mQ9YgHEMYvHDMc1vjoWwU1IO+Xlsjc78OFzB9Yjfc+VMYeV3jWJy/AyvHmzdvryl2PSymaNDelC Sa4= =IG1m -----END PGP SIGNATURE-----