|
@@ -2,36 +2,21 @@
|
|
== Multipart requests
|
|
== Multipart requests
|
|
|
|
|
|
Multipart originates from MIME, an Internet standard that
|
|
Multipart originates from MIME, an Internet standard that
|
|
-extends the format of emails. Multipart messages are a
|
|
|
|
-container for parts of any content-type.
|
|
|
|
|
|
+extends the format of emails.
|
|
|
|
|
|
-For example, a multipart message may have a part
|
|
|
|
-containing text and a second part containing an
|
|
|
|
-image. This is what allows you to attach files
|
|
|
|
-to emails.
|
|
|
|
|
|
+A multipart message is a list of parts. A part contains
|
|
|
|
+headers and a body. The body of the parts may be
|
|
|
|
+of any media type, and contain text or binary data.
|
|
|
|
+It is possible for parts to contain a multipart media
|
|
|
|
+type.
|
|
|
|
|
|
In the context of HTTP, multipart is most often used
|
|
In the context of HTTP, multipart is most often used
|
|
-with the `multipart/form-data` content-type. This is
|
|
|
|
-the content-type you have to use when you want browsers
|
|
|
|
-to be allowed to upload files through HTML forms.
|
|
|
|
|
|
+with the `multipart/form-data` media type. It is what
|
|
|
|
+browsers use to upload files through HTML forms.
|
|
|
|
|
|
-Multipart is of course not required for uploading
|
|
|
|
-files, it is only required when you want to do so
|
|
|
|
-through HTML forms.
|
|
|
|
-
|
|
|
|
-You can read and parse multipart messages using the
|
|
|
|
-Req object directly.
|
|
|
|
-
|
|
|
|
-Cowboy defines two functions that allows you to get
|
|
|
|
-information about each part and read their contents.
|
|
|
|
-
|
|
|
|
-=== Structure
|
|
|
|
-
|
|
|
|
-A multipart message is a list of parts. Parts may
|
|
|
|
-contain either a multipart message or a non-multipart
|
|
|
|
-content-type. This allows parts to be arranged in a
|
|
|
|
-tree structure, although this is a rare case as far
|
|
|
|
-as the Web is concerned.
|
|
|
|
|
|
+The `multipart/byteranges` is also common. It is the
|
|
|
|
+media type used to send arbitrary bytes from a resource,
|
|
|
|
+enabling clients to resume downloads.
|
|
|
|
|
|
=== Form-data
|
|
=== Form-data
|
|
|
|
|
|
@@ -42,29 +27,24 @@ values and is therefore not fit for uploading files.
|
|
|
|
|
|
That's where the `multipart/form-data` content-type
|
|
That's where the `multipart/form-data` content-type
|
|
comes in. When the form is configured to use this
|
|
comes in. When the form is configured to use this
|
|
-content-type, the browser will use one part of the
|
|
|
|
-message for each form field. This means that a file
|
|
|
|
-input field will be sent in its own part, but the
|
|
|
|
-same applies to all other kinds of fields.
|
|
|
|
|
|
+content-type, the browser will create a multipart
|
|
|
|
+message where each part corresponds to a field on
|
|
|
|
+the form. For files, it also adds some metadata in
|
|
|
|
+the part headers, like the file name.
|
|
|
|
|
|
A form with a text input, a file input and a select
|
|
A form with a text input, a file input and a select
|
|
choice box will result in a multipart message with
|
|
choice box will result in a multipart message with
|
|
three parts, one for each field.
|
|
three parts, one for each field.
|
|
|
|
|
|
-The browser does its best to determine the content-type
|
|
|
|
|
|
+The browser does its best to determine the media type
|
|
of the files it sends this way, but you should not
|
|
of the files it sends this way, but you should not
|
|
rely on it for determining the contents of the file.
|
|
rely on it for determining the contents of the file.
|
|
Proper investigation of the contents is recommended.
|
|
Proper investigation of the contents is recommended.
|
|
|
|
|
|
-=== Checking the content-type
|
|
|
|
|
|
+=== Checking for multipart messages
|
|
|
|
|
|
-While there is a variety of multipart messages, the
|
|
|
|
-most common on the Web is `multipart/form-data`. It's
|
|
|
|
-the type of message being sent when an HTML form
|
|
|
|
-allows uploading files.
|
|
|
|
-
|
|
|
|
-You can quickly figure out if a multipart message
|
|
|
|
-has been sent by parsing the `content-type` header.
|
|
|
|
|
|
+The content-type header indicates the presence of
|
|
|
|
+a multipart message:
|
|
|
|
|
|
[source,erlang]
|
|
[source,erlang]
|
|
----
|
|
----
|
|
@@ -74,96 +54,116 @@ has been sent by parsing the `content-type` header.
|
|
|
|
|
|
=== Reading a multipart message
|
|
=== Reading a multipart message
|
|
|
|
|
|
-To read a message you have to iterate over all its
|
|
|
|
-parts. Then, for each part, you can inspect its headers
|
|
|
|
-and read its body.
|
|
|
|
|
|
+Cowboy provides two sets of functions for reading
|
|
|
|
+request bodies as multipart messages.
|
|
|
|
+
|
|
|
|
+The `cowboy_req:read_part/1,2` functions return the
|
|
|
|
+next part's headers, if any.
|
|
|
|
+
|
|
|
|
+The `cowboy_req:read_part_body/1,2` functions return
|
|
|
|
+the current part's body. For large bodies you may
|
|
|
|
+need to call the function multiple times.
|
|
|
|
+
|
|
|
|
+To read a multipart message you need to iterate over
|
|
|
|
+all its parts:
|
|
|
|
|
|
[source,erlang]
|
|
[source,erlang]
|
|
----
|
|
----
|
|
-multipart(Req) ->
|
|
|
|
- case cowboy_req:part(Req) of
|
|
|
|
- {ok, _Headers, Req2} ->
|
|
|
|
- {ok, _Body, Req3} = cowboy_req:part_body(Req2),
|
|
|
|
- multipart(Req3);
|
|
|
|
- {done, Req2} ->
|
|
|
|
- Req2
|
|
|
|
|
|
+multipart(Req0) ->
|
|
|
|
+ case cowboy_req:read_part(Req0) of
|
|
|
|
+ {ok, _Headers, Req1} ->
|
|
|
|
+ {ok, _Body, Req} = cowboy_req:read_part_body(Req1),
|
|
|
|
+ multipart(Req);
|
|
|
|
+ {done, Req} ->
|
|
|
|
+ Req
|
|
end.
|
|
end.
|
|
----
|
|
----
|
|
|
|
|
|
-Parts do not have a size limit. When a part body is
|
|
|
|
-too big, Cowboy will return what it read so far and
|
|
|
|
-allow you to continue if you wish to do so.
|
|
|
|
|
|
+When part bodies are too large, Cowboy will return
|
|
|
|
+a `more` tuple, and allow you to loop until the part
|
|
|
|
+body has been fully read.
|
|
|
|
|
|
The function `cow_multipart:form_data/1` can be used
|
|
The function `cow_multipart:form_data/1` can be used
|
|
to quickly obtain information about a part from a
|
|
to quickly obtain information about a part from a
|
|
-`multipart/form-data` message. This function will
|
|
|
|
-tell you if the part is for a normal field or if it
|
|
|
|
-is a file being uploaded.
|
|
|
|
|
|
+`multipart/form-data` message. The function returns
|
|
|
|
+a `data` or a `file` tuple depending on whether this
|
|
|
|
+is a normal field or a file being uploaded.
|
|
|
|
|
|
-This can be used for example to allow large part bodies
|
|
|
|
-for files but crash when a normal field is too large.
|
|
|
|
|
|
+The following snippet will use this function and
|
|
|
|
+use different strategies depending on whether the
|
|
|
|
+part is a file:
|
|
|
|
|
|
[source,erlang]
|
|
[source,erlang]
|
|
----
|
|
----
|
|
-multipart(Req) ->
|
|
|
|
- case cowboy_req:part(Req) of
|
|
|
|
- {ok, Headers, Req2} ->
|
|
|
|
- Req4 = case cow_multipart:form_data(Headers) of
|
|
|
|
|
|
+multipart(Req0) ->
|
|
|
|
+ case cowboy_req:read_part(Req0) of
|
|
|
|
+ {ok, Headers, Req1} ->
|
|
|
|
+ Req = case cow_multipart:form_data(Headers) of
|
|
{data, _FieldName} ->
|
|
{data, _FieldName} ->
|
|
- {ok, _Body, Req3} = cowboy_req:part_body(Req2),
|
|
|
|
- Req3;
|
|
|
|
|
|
+ {ok, _Body, Req2} = cowboy_req:read_part_body(Req1),
|
|
|
|
+ Req2;
|
|
{file, _FieldName, _Filename, _CType, _CTransferEncoding} ->
|
|
{file, _FieldName, _Filename, _CType, _CTransferEncoding} ->
|
|
- stream_file(Req2)
|
|
|
|
|
|
+ stream_file(Req1)
|
|
end,
|
|
end,
|
|
- multipart(Req4);
|
|
|
|
- {done, Req2} ->
|
|
|
|
- Req2
|
|
|
|
|
|
+ multipart(Req);
|
|
|
|
+ {done, Req} ->
|
|
|
|
+ Req
|
|
end.
|
|
end.
|
|
|
|
|
|
-stream_file(Req) ->
|
|
|
|
- case cowboy_req:part_body(Req) of
|
|
|
|
- {ok, _Body, Req2} ->
|
|
|
|
- Req2;
|
|
|
|
- {more, _Body, Req2} ->
|
|
|
|
- stream_file(Req2)
|
|
|
|
|
|
+stream_file(Req0) ->
|
|
|
|
+ case cowboy_req:read_part_body(Req0) of
|
|
|
|
+ {ok, _Body, Req} ->
|
|
|
|
+ Req;
|
|
|
|
+ {more, _Body, Req} ->
|
|
|
|
+ stream_file(Req)
|
|
end.
|
|
end.
|
|
----
|
|
----
|
|
|
|
|
|
-By default the body chunk Cowboy will return is limited
|
|
|
|
-to 8MB. This can of course be overriden. Both functions
|
|
|
|
-can take a second argument, the same list of options that
|
|
|
|
-will be passed to `cowboy_req:body/2` function.
|
|
|
|
|
|
+Both the part header and body reading functions can take
|
|
|
|
+options that will be given to the request body reading
|
|
|
|
+functions. By default, `cowboy_req:read_part/1` reads
|
|
|
|
+up to 64KB for up to 5 seconds. `cowboy_req:read_part_body/1`
|
|
|
|
+has the same defaults as `cowboy_req:read_body/1`.
|
|
|
|
+
|
|
|
|
+To change the defaults for part headers:
|
|
|
|
+
|
|
|
|
+[source,erlang]
|
|
|
|
+cowboy_req:read_part(Req, #{length => 128000}).
|
|
|
|
+
|
|
|
|
+And for part bodies:
|
|
|
|
+
|
|
|
|
+[source,erlang]
|
|
|
|
+cowboy_req:read_part_body(Req, #{length => 1000000, period => 7000}).
|
|
|
|
|
|
=== Skipping unwanted parts
|
|
=== Skipping unwanted parts
|
|
|
|
|
|
-If you do not want to read a part's body, you can skip it.
|
|
|
|
-Skipping is easy. If you do not call the function to read
|
|
|
|
-the part's body, Cowboy will automatically skip it when
|
|
|
|
-you request the next part.
|
|
|
|
|
|
+Part bodies do not have to be read. Cowboy will automatically
|
|
|
|
+skip it when you request the next part's body.
|
|
|
|
|
|
The following snippet reads all part headers and skips
|
|
The following snippet reads all part headers and skips
|
|
all bodies:
|
|
all bodies:
|
|
|
|
|
|
[source,erlang]
|
|
[source,erlang]
|
|
----
|
|
----
|
|
-multipart(Req) ->
|
|
|
|
- case cowboy_req:part(Req) of
|
|
|
|
- {ok, _Headers, Req2} ->
|
|
|
|
- multipart(Req2);
|
|
|
|
- {done, Req2} ->
|
|
|
|
- Req2
|
|
|
|
|
|
+multipart(Req0) ->
|
|
|
|
+ case cowboy_req:part(Req0) of
|
|
|
|
+ {ok, _Headers, Req} ->
|
|
|
|
+ multipart(Req);
|
|
|
|
+ {done, Req} ->
|
|
|
|
+ Req
|
|
end.
|
|
end.
|
|
----
|
|
----
|
|
|
|
|
|
Similarly, if you start reading the body and it ends up
|
|
Similarly, if you start reading the body and it ends up
|
|
-being too big, you can simply continue with the next part,
|
|
|
|
|
|
+being too big, you can simply continue with the next part.
|
|
Cowboy will automatically skip what remains.
|
|
Cowboy will automatically skip what remains.
|
|
|
|
|
|
-Note that the skipping rate may not be adequate for your
|
|
|
|
-application. If you observe poor performance when skipping,
|
|
|
|
-you might want to consider manually skipping by calling
|
|
|
|
-the `cowboy_req:part_body/1` function directly.
|
|
|
|
|
|
+While Cowboy can skip part bodies automatically, the read
|
|
|
|
+rate is not configurable. Depending on your application
|
|
|
|
+you may want to skip manually, in particular if you observe
|
|
|
|
+poor performance while skipping.
|
|
|
|
+
|
|
|
|
+You do not have to read all parts either. You can stop
|
|
|
|
+reading as soon as you find the data you need.
|
|
|
|
|
|
-And if you started reading the message but decide that you
|
|
|
|
-do not need the remaining parts, you can simply stop reading
|
|
|
|
-entirely and Cowboy will automatically figure out what to do.
|
|
|
|
|
|
+// @todo Cover the building of multipart messages.
|