aboutsummaryrefslogtreecommitdiffstats
path: root/doc/src/guide/http.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src/guide/http.asciidoc')
-rw-r--r--doc/src/guide/http.asciidoc363
1 files changed, 363 insertions, 0 deletions
diff --git a/doc/src/guide/http.asciidoc b/doc/src/guide/http.asciidoc
new file mode 100644
index 0000000..929860a
--- /dev/null
+++ b/doc/src/guide/http.asciidoc
@@ -0,0 +1,363 @@
+== HTTP
+
+This chapter describes how to use the Gun client for
+communicating with an HTTP/1.1 or SPDY server.
+
+=== Streams
+
+Every time a request is initiated, Gun creates a _stream_.
+A _stream reference_ uniquely identifies a set of request and
+response(s) and must be used to perform additional operations
+with a stream or to identify its messages.
+
+Stream references use the Erlang _reference_ data type and
+are therefore unique.
+
+Streams can be canceled at any time. This will stop any further
+messages from being sent to the owner process. Depending on
+its capabilities, the server will also be instructed to cancel
+the request.
+
+Canceling a stream may result in Gun dropping the connection
+temporarily, to avoid uploading or downloading data that will
+not be used.
+
+.Cancelling a stream
+[source,erlang]
+gun:cancel(ConnPid, StreamRef).
+
+=== Sending requests
+
+Gun provides many convenient functions for performing common
+operations, like GET, POST or DELETE. It also provides a
+general purpose function in case you need other methods.
+
+The availability of these methods on the server can vary
+depending on the software used but also on a per-resource
+basis.
+
+Gun will automatically set a few headers depending on the
+method used. For all methods however it will set the host
+header if it has not been provided in the request arguments.
+
+This section focuses on the act of sending a request. The
+handling of responses will be explained further on.
+
+==== GET and HEAD
+
+Use `gun:get/{2,3}` to request a resource.
+
+.GET "/organizations/ninenines"
+
+[source,erlang]
+StreamRef = gun:get(ConnPid, "/organizations/ninenines").
+
+.GET "/organizations/ninenines" with custom headers
+
+[source,erlang]
+StreamRef = gun:get(ConnPid, "/organizations/ninenines", [
+ {<<"accept">>, "application/json"},
+ {<<"user-agent">>, "revolver/1.0"}
+]).
+
+Note that the list of headers has the field name as a binary.
+The field value is iodata, which is either a binary or an
+iolist.
+
+Use `gun:head/{2,3}` if you don't need the response body.
+
+.HEAD "/organizations/ninenines"
+
+[source,erlang]
+StreamRef = gun:head(ConnPid, "/organizations/ninenines").
+
+.HEAD "/organizations/ninenines" with custom headers
+
+[source,erlang]
+StreamRef = gun:head(ConnPid, "/organizations/ninenines", [
+ {<<"accept">>, "application/json"},
+ {<<"user-agent">>, "revolver/1.0"}
+]).
+
+It is not possible to send a request body with a GET or HEAD
+request.
+
+==== POST, PUT and PATCH
+
+HTTP defines three methods to create or update a resource.
+
+POST is generally used when the resource identifier (URI) isn't known
+in advance when creating the resource. POST can also be used to
+replace an existing resource, although PUT is more appropriate
+in that situation.
+
+PUT creates or replaces a resource identified by the URI.
+
+PATCH provides instructions on how to modify the resource.
+
+Both POST and PUT send the entire resource representation in their
+request body. The PATCH method can be used when this is not
+desirable. The request body of a PATCH method may be a partial
+representation or a list of instructions on how to update the
+resource.
+
+The `gun:post/4`, `gun:put/4` and `gun:patch/4` functions
+take a body as their fourth argument. These functions do
+not require any body-specific header to be set, although
+it is always recommended to set the content-type header.
+Gun will set the other headers automatically.
+
+In this and the following examples in this section, `gun:post`
+can be replaced by `gun:put` or `gun:patch` for performing
+a PUT or PATCH request, respectively.
+
+.POST "/organizations/ninenines"
+
+[source,erlang]
+Body = "{\"msg\": \"Hello world!\"}",
+StreamRef = gun:post(ConnPid, "/organizations/ninenines", [
+ {<<"content-type">>, "application/json"}
+], Body).
+
+The `gun:post/3`, `gun:put/3` and `gun:patch/3` functions
+do not take a body in their arguments. If a body is to be
+provided later on, using the `gun:data/4` function, then
+the request headers must indicate this. This can be done
+by setting the content-length or content-type request
+headers. If these headers are not set then Gun will assume
+the request has no body.
+
+It is recommended to send the content-length header if you
+know it in advance, although this is not required. If it
+is not set, HTTP/1.1 will use the chunked transfer-encoding,
+and SPDY will continue normally as it is chunked by design.
+
+@todo Stop relying on transfer encoding header in Gun
+
+@todo SPDY needs to remove invalid headers, and to detect
+a body if content-length is set
+
+.POST "/organizations/ninenines" with delayed body
+
+[source,erlang]
+Body = "{\"msg\": \"Hello world!\"}",
+StreamRef = gun:post(ConnPid, "/organizations/ninenines", [
+ {<<"content-length">>, integer_to_binary(length(Body))},
+ {<<"content-type">>, "application/json"}
+]),
+gun:data(ConnPid, StreamRef, fin, Body).
+
+The atom `fin` indicates this is the last chunk of data to
+be sent. You can call the `gun:data/4` function as many
+times as needed until you have sent the entire body. The
+last call must use `fin` and all the previous calls must
+use `nofin`. The last chunk may be empty.
+
+@todo what to do about empty chunk, ignore?
+
+.Streaming the request body
+
+[source,erlang]
+----
+sendfile(ConnPid, StreamRef, Filepath) ->
+ {ok, IoDevice} = file:open(Filepath, [read, binary, raw]),
+ do_sendfile(ConnPid, StreamRef, IoDevice).
+
+do_sendfile(ConnPid, StreamRef, IoDevice) ->
+ case file:read(IoDevice, 8000) of
+ eof ->
+ gun:data(ConnPid, StreamRef, fin, <<>>),
+ file:close(IoDevice);
+ {ok, Bin} ->
+ gun:data(ConnPid, StreamRef, nofin, Bin),
+ do_sendfile(ConnPid, StreamRef, IoDevice)
+ end.
+----
+
+==== DELETE
+
+Use `gun:delete/{2,3}` to delete a resource.
+
+.DELETE "/organizations/ninenines"
+
+[source,erlang]
+StreamRef = gun:delete(ConnPid, "/organizations/ninenines").
+
+.DELETE "/organizations/ninenines" with custom headers
+
+[source,erlang]
+StreamRef = gun:delete(ConnPid, "/organizations/ninenines", [
+ {<<"user-agent">>, "revolver/1.0"}
+]).
+
+==== OPTIONS
+
+Use `gun:options/{2,3}` to request information about a resource.
+
+.OPTIONS "/organizations/ninenines"
+
+[source,erlang]
+StreamRef = gun:options(ConnPid, "/organizations/ninenines").
+
+.OPTIONS "/organizations/ninenines" with custom headers
+
+[source,erlang]
+StreamRef = gun:options(ConnPid, "/organizations/ninenines", [
+ {<<"user-agent">>, "revolver/1.0"}
+]).
+
+You can also use this function to request information about
+the server itself.
+
+.OPTIONS "*"
+
+[source,erlang]
+StreamRef = gun:options(ConnPid, "*").
+
+==== Requests with an arbitrary method
+
+The `gun:request/{4,5}` function can be used to send requests
+with a configurable method name. It is mostly useful when you
+need a method that Gun does not understand natively.
+
+.Example of a TRACE request
+
+[source,erlang]
+gun:request(ConnPid, "TRACE", "/", [
+ {<<"max-forwards">>, "30"}
+]).
+
+=== Processing responses
+
+All data received from the server is sent to the owner
+process as a message. First a `gun_response` message is sent,
+followed by zero or more `gun_data` messages. If something goes wrong,
+a `gun_error` message is sent instead.
+
+The response message will inform you whether there will be
+data messages following. If it contains `fin` there will be
+no data messages. If it contains `nofin` then one or more data
+messages will follow.
+
+When using SPDY this value is sent with the frame and simply
+passed on in the message. When using HTTP/1.1 however Gun must
+guess whether data will follow by looking at the response headers.
+
+You can receive messages directly, or you can use the _await_
+functions to let Gun receive them for you.
+
+.Receiving a response using receive
+
+[source,erlang]
+----
+print_body(ConnPid, MRef) ->
+ StreamRef = gun:get(ConnPid, "/"),
+ receive
+ {gun_response, ConnPid, StreamRef, fin, Status, Headers} ->
+ no_data;
+ {gun_response, ConnPid, StreamRef, nofin, Status, Headers} ->
+ receive_data(ConnPid, MRef, StreamRef);
+ {'DOWN', MRef, process, ConnPid, Reason} ->
+ error_logger:error_msg("Oops!"),
+ exit(Reason)
+ after 1000 ->
+ exit(timeout)
+ end.
+
+receive_data(ConnPid, MRef, StreamRef) ->
+ receive
+ {gun_data, ConnPid, StreamRef, nofin, Data} ->
+ io:format("~s~n", [Data]),
+ receive_data(ConnPid, MRef, StreamRef);
+ {gun_data, ConnPid, StreamRef, fin, Data} ->
+ io:format("~s~n", [Data]);
+ {'DOWN', MRef, process, ConnPid, Reason} ->
+ error_logger:error_msg("Oops!"),
+ exit(Reason)
+ after 1000 ->
+ exit(timeout)
+ end.
+----
+
+While it may seem verbose, using messages like this has the
+advantage of never locking your process, allowing you to
+easily debug your code. It also allows you to start more than
+one connection and concurrently perform queries on all of them
+at the same time.
+
+You can also use Gun in a synchronous manner by using the _await_
+functions.
+
+The `gun:await/{2,3,4}` function will wait until it receives
+a response. The `gun:await/2` call will automatically monitor
+the process and use a timeout of 5000. The monitor and the
+timeout can be passed by using `gun:await/3` or `gun:await/4`.
+
+The `gun:await_body/{2,3,4}` works similarly, but returns the
+body received.
+
+.Receiving a response using await
+
+[source,erlang]
+StreamRef = gun:get(ConnPid, "/"),
+case gun:await(ConnPid, StreamRef) of
+ {response, fin, Status, Headers} ->
+ no_data;
+ {response, nofin, Status, Headers} ->
+ {ok, Body} = gun:await_body(ConnPid, StreamRef),
+ io:format("~s~n", [Body])
+end.
+
+=== Handling streams pushed by the server
+
+The SPDY protocol allows the server to push more than one
+resource for every request. It will start sending those
+extra resources before it starts sending the response itself,
+so Gun will send you `gun_push` messages before `gun_response`
+when that happens.
+
+You can safely choose to ignore `gun_push` messages, or
+you can handle them. If you do, you can either receive the
+messages directly or use _await_ functions.
+
+The `gun_push` message contains both the new stream reference
+and the stream reference of the original request.
+
+.Receiving a pushed response using receive
+
+[source,erlang]
+receive
+ {gun_push, ConnPid, OriginalStreamRef, PushedStreamRef,
+ Method, Host, Path, Headers} ->
+ enjoy()
+end.
+
+If you use the `gun:await/{2,3,4}` function, however, Gun
+will use the original reference to identify the message but
+will return a tuple that doesn't contain it.
+
+.Receiving a pushed response using await
+
+[source,erlang]
+{push, PushedStreamRef, Method, Host, Path, Headers}
+ = gun:await(ConnPid, OriginalStreamRef).
+
+The `PushedStreamRef` variable can then be used with `gun:await_body/{2,3,4}`
+if needed.
+
+=== Flushing unwanted messages
+
+Gun provides the function `gun:flush/1` to quickly get rid
+of unwanted messages sitting in the process mailbox. You
+can use it to get rid of all messages related to a connection,
+or just the messages related to a stream.
+
+.Flush all messages from a Gun connection
+
+[source,erlang]
+gun:flush(ConnPid).
+
+.Flush all messages from a specific stream
+
+[source,erlang]
+gun:flush(StreamRef).