scottlamb / multipart-stream-rs

Rust library to parse and serialize async multipart/x-mixed-replace streams.
https://crates.io/crates/multipart-stream
Apache License 2.0
10 stars 8 forks source link

About `multipart/mixed` support #1

Open YenForYang opened 3 years ago

YenForYang commented 3 years ago

I'm actually not sure if this crate supports multipart/mixed or not, since the README mentions it as part of the introduction. Nonetheless I think this crate would find much more use if it supported multipart/mixed (explicitly) since it's quite similar to multipart/x-mixed-replace. In fact, the WHATWG page mentioned in the README even states:

... Conformance requirements for generating resources with this type [multipart/x-mixed-replace] are the same as for multipart/mixed. [RFC2046]

I'm looking for a crate that can create/parse requests/responses like these (batch requests to Google APIs, like Drive v3), while working with reqwest/hyper.

Request example:

POST https://www.googleapis.com/batch/drive/v3
Accept-Encoding: gzip
User-Agent: Google-HTTP-Java-Client/1.20.0 (gzip)
Content-Type: multipart/mixed; boundary=END_OF_PART
Content-Length: 963

--END_OF_PART
Content-Length: 337
Content-Type: application/http
content-id: 1
content-transfer-encoding: binary

POST https://www.googleapis.com/drive/v3/files/fileId/permissions?fields=id
Authorization: Bearer authorization_token
Content-Length: 70
Content-Type: application/json; charset=UTF-8

{
  "emailAddress":"example@appsrocks.com",
  "role":"writer",
  "type":"user"
}
--END_OF_PART
Content-Length: 353
Content-Type: application/http
content-id: 2
content-transfer-encoding: binary

POST https://www.googleapis.com/drive/v3/files/fileId/permissions?fields=id&sendNotificationEmail=false
Authorization: Bearer authorization_token
Content-Length: 58
Content-Type: application/json; charset=UTF-8

{
   "domain":"appsrocks.com",
   "role":"reader",
   "type":"domain"
}
--END_OF_PART--

Response example:

HTTP/1.1 200 OK
Alt-Svc: quic=":443"; p="1"; ma=604800
Server: GSE
Alternate-Protocol: 443:quic,p=1
X-Frame-Options: SAMEORIGIN
Content-Encoding: gzip
X-XSS-Protection: 1; mode=block
Content-Type: multipart/mixed; boundary=batch_6VIxXCQbJoQ_AATxy_GgFUk
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
Date: Fri, 13 Nov 2015 19:28:59 GMT
Cache-Control: private, max-age=0
Vary: X-Origin
Vary: Origin
Expires: Fri, 13 Nov 2015 19:28:59 GMT

--batch_6VIxXCQbJoQ_AATxy_GgFUk
Content-Type: application/http
Content-ID: response-1

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Fri, 13 Nov 2015 19:28:59 GMT
Expires: Fri, 13 Nov 2015 19:28:59 GMT
Cache-Control: private, max-age=0
Content-Length: 35

{
 "id": "12218244892818058021i"
}

--batch_6VIxXCQbJoQ_AATxy_GgFUk
Content-Type: application/http
Content-ID: response-2

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Fri, 13 Nov 2015 19:28:59 GMT
Expires: Fri, 13 Nov 2015 19:28:59 GMT
Cache-Control: private, max-age=0
Content-Length: 35

{
 "id": "04109509152946699072k"
}

--batch_6VIxXCQbJoQ_AATxy_GgFUk--

Pretty much all the requirements your README mentions are met, except for:

No extra -- suffix on the final part's boundary. I've never seen one.

The Content-Length requirement is also limiting, but it happens to work perfectly for use cases surrounding batch Google Drive/Gmail requests. The APIs seem to support sending multipart/mixed requests with/without Content-Length, and always returns parts that include Content-Length (as seen above).

There don't appear to be any libraries around multipart/*mixed* in Rust (all the ones that could work seem to predate async support), so I thought yours was promising since it has both async and hyper support.

scottlamb commented 3 years ago

I hadn't noticed the text you mentioned. Yes, I'd be happy for the crate to explicitly support multipart/mixed then. The caveats you mentioned could certainly be fixed. (To support bodies without Content-Length, maybe it could make a memchr::memmem::Finder the first time it sees part headers without a Content-Length.)

Are you interested in putting together a PR?

scottlamb commented 3 years ago

One other caveat I didn't mention is that it buffers each part fully in memory. If that's not what you want, we'd need an API change to have each part's body be a Stream itself. And we'd need to decide what happens if you try to get the next part before fully consuming previous part's body streams: does getting the next part implicitly wait for you to fully consume or drop the previous part's body stream, is it an error, or does it force it to be buffered in RAM.

grasevski commented 2 years ago

I found the Content-Length thing useful for my camera so I made a pull request here: https://github.com/scottlamb/multipart-stream-rs/pull/3