-
+
- + + Client + + +
- + + Connection + + +
- + + CookieJar + + +
- + + Multipart + + +
+ Multipart +
+provides multipart/form-data encoding for HTTP requests +(RFC 7578).
+Encoding manually
+(let [parts [(Multipart.text-part "name" "Carp")
+ (Multipart.file-part "upload" "test.txt"
+ "text/plain" "file contents")]
+ boundary (Multipart.boundary-for &parts)]
+ (Client.post url
+ {@"Content-Type" [(Multipart.content-type-header &boundary)]}
+ &(Multipart.encode &parts &boundary)))
+
+Pick the boundary with Multipart.boundary-for, not with
+Multipart.generate-boundary: only the former is checked against the parts,
+which is what keeps a body that happens to contain the delimiter from
+splitting the message.
Convenience function
+(Client.post-multipart url {}
+ &[(Multipart.text-part "field" "value")])
+
+
+ + boundary-for +
+ ++ (Fn [(Ref (Array Part) a)] String) +
++ (boundary-for parts) ++
+
returns a boundary that occurs nowhere in parts, as RFC 2046 §5.1.1
+requires of the delimiter.
Starts from Multipart.generate-boundary and, while the candidate still
+occurs in some part name, filename, content type or body, appends the
+bcharsnospace character that follows the fewest of those occurrences. Each
+round therefore divides the occurrence count by 62, so the boundary stays far
+inside the 70-character limit even for a payload built to defeat it. Every
+round copies and scans each name, filename, content type and body, so a large
+upload pays a full pass per round.
+ content-type-header +
+ ++ (Fn [(Ref String a)] String) +
++ (content-type-header boundary) ++
+
returns the Content-Type header value for multipart/form-data with the +given boundary.
+ + ++ encode +
+ ++ (Fn [(Ref (Array Part) a), (Ref String b)] String) +
++ (encode parts boundary) ++
+
encodes an array of parts into a multipart/form-data body string using +the given boundary (RFC 7578).
+Pass a boundary from Multipart.boundary-for; encode cannot re-pick one,
+because the caller has already committed to it in the Content-Type header.
A CR or LF in a part name, filename or content type would end the header line +and let the rest of the value pose as headers or as a further part, so both +are percent-encoded as %0D and %0A, following the same rule as HTML form +submission. Quotes in a name or filename are backslash-escaped. Values +without those characters are emitted unchanged.
+ + ++ file-part +
+ ++ (Fn [(Ref String a), (Ref String b), (Ref String c), (Ref String d)] Part) +
++ (file-part name filename content-type data) ++
+
creates a file upload part with a filename and content type.
+The data parameter is the raw file contents as a string.
+ generate-boundary +
+ ++ (Fn [] String) +
++ (generate-boundary) ++
+
generates a boundary string for multipart encoding, using the current +time for uniqueness.
+The result is not checked against any payload, so a part that contains it is
+encoded into a message the receiver splits in the wrong places. Prefer
+Multipart.boundary-for, which rules that out.
+ parse +
+ ++ (Fn [(Ref String a), (Ref String b)] (Result (Array FormPart) String)) +
++ (parse body boundary) ++
+
decodes a multipart/form-data body with the given boundary
+into its FormParts. Fails when the opening boundary delimiter is absent.
+ text-part +
+ ++ (Fn [(Ref String a), (Ref String b)] Part) +
++ (text-part name value) ++
+
creates a text form field part with the given name and value.
+ + +