aboutsummaryrefslogtreecommitdiffstats
path: root/guide/http.md
diff options
context:
space:
mode:
Diffstat (limited to 'guide/http.md')
-rw-r--r--guide/http.md208
1 files changed, 208 insertions, 0 deletions
diff --git a/guide/http.md b/guide/http.md
new file mode 100644
index 0000000..1034a5d
--- /dev/null
+++ b/guide/http.md
@@ -0,0 +1,208 @@
+Using HTTP
+==========
+
+This chapter describes how to use the Gun client for
+communicating with an HTTP or SPDY server.
+
+Streams
+-------
+
+Every time a request is initiated, either by the client or the
+server, Gun creates a "stream". The stream controls whether
+the endpoints are still sending any data, and allows you to
+identify incoming messages.
+
+Streams are references in Gun, and are therefore always unique.
+
+Streams can be canceled at any time. This will stop any further
+messages to be sent to the controlling process. Depending on
+its capabilities, the server will also be instructed to drop
+the request.
+
+Canceling a stream may result in Gun dropping the connection
+temporarily, to avoid uploading or downloading data that will
+not be used. This situation can only occur with HTTP, as SPDY
+features stream canceling as part of its protocol.
+
+To cancel a stream, the `gun:cancel/2` function can be used.
+
+``` erlang
+gun:cancel(Pid, 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.
+
+To retrieve a resource, `gun:get/{2,3}` can be used. If you
+don't need the response body, `gun:head/{2,3}` is available.
+As this type of requests can't have a body, only the path
+and optionally the headers can be specified.
+
+``` erlang
+%% Without headers.
+StreamRef = gun:get(Pid, "/organizations/extend").
+%% With headers.
+StreamRef = gun:get(Pid, "/organizations/extend", [
+ {"accept", "application/json"},
+ {"user-agent", "revolver/1.0"}]).
+```
+
+To create or update a resource, the functions `gun:patch/{3,4}`,
+`gun:post/{3,4}` and `gun:put/{3,4}` can be used. As this type
+of request is meant to come with a body, headers are not optional,
+because you must specify at least the content-type of the body,
+and if possible also the content-length. The body is however
+optional, because there might not be any at all, or because it
+will be subsequently streamed. If a body is set here it is assumed
+to be the full body.
+
+``` erlang
+%% Without body.
+StreamRef = gun:put(Pid, "/organizations/extend", [
+ {"content-length", 23},
+ {"content-type", "application/json"}]).
+%% With body.
+StreamRef = gun:put(Pid, "/organizations/extend", [
+ {"content-length", 23},
+ {"content-type", "application/json"}],
+ "{\"msg\": \"Hello world!\"}").
+```
+
+To delete a resource, the `gun:delete/{2,3}` function can be
+used. It works similarly to the GET and HEAD functions.
+
+``` erlang
+%% Without headers.
+StreamRef = gun:delete(Pid, "/organizations/extend").
+%% With headers.
+StreamRef = gun:delete(Pid, "/organizations/extend", [
+ {"accept", "application/json"},
+ {"user-agent", "revolver/1.0"}]).
+```
+
+To obtain the functionality available for a given resource,
+the `gun:options/{2,3}` can be used. It also works like the
+GET and HEAD functions.
+
+``` erlang
+%% Without headers.
+StreamRef = gun:options(Pid, "/organizations/extend").
+%% With headers.
+StreamRef = gun:options(Pid, "/organizations/extend", [
+ {"accept", "application/json"},
+ {"user-agent", "revolver/1.0"}]).
+```
+
+You can obtain information about the server as a whole by
+using the special path `"*"`.
+
+``` erlang
+StreamRef = gun:options(Pid, "*").
+```
+
+Streaming data
+--------------
+
+When a PATCH, POST or PUT operation is performed, and a
+content-type is specified but no body is given, Gun will
+expect data to be streamed to the connection using the
+`gun:data/4` function.
+
+This function can be called as many times as needed until
+all data is sent. The third argument needs to be `nofin`
+when there is remaining data to be sent, and `fin` for the
+last chunk. The last chunk may be empty if needed.
+
+For example, with an `IoDevice` opened like follow:
+
+``` erlang
+{ok, IoDevice} = file:open(Filepath, [read, binary, raw]).
+```
+
+The following function will stream all data until the end
+of the file:
+
+``` erlang
+sendfile(Pid, StreamRef, IoDevice) ->
+ case file:read(IoDevice, 8000) of
+ eof ->
+ gun:data(Pid, StreamRef, fin, <<>>),
+ file:close(IoDevice);
+ {ok, Bin} ->
+ gun:data(Pid, StreamRef, nofin, Bin),
+ sendfile(Pid, StreamRef, IoDevice)
+ end.
+```
+
+Receiving responses
+-------------------
+
+All data received from the server is sent to the controlling
+process as a message. First a response message is sent, then
+zero or more data messages. If something goes wrong, error
+messages are sent instead.
+
+You can find out whether data will be sent by checking the
+presence of the content-type or content-length header and
+making sure the length isn't 0. If you already know a body is
+going to be sent you can skip this check, however do make
+sure you have a timeout just in case something goes wrong.
+
+``` erlang
+StreamRef = gun:get(Pid, "/"),
+receive
+ {'DOWN', Tag, _, _, Reason} ->
+ error_logger:error_msg("Oops!"),
+ exit(Reason);
+ {gun_response, Pid, StreamRef, Status, Headers} ->
+ receive_data(Pid, StreamRef)
+after 1000 ->
+ exit(timeout)
+end.
+```
+
+The `receive_data/2` function could look like this:
+
+``` erlang
+receive_data(Pid, Tag, StreamRef) ->
+ receive
+ {'DOWN', Tag, _, _, Reason} ->
+ {error, incomplete};
+ {gun_data, Pid, StreamRef, nofin, Data} ->
+ io:format("~s~n", [Data]),
+ receive_data(Pid, Tag, StreamRef);
+ {gun_data, Pid, StreamRef, fin, Data} ->
+ io:format("~s~n", [Data])
+ after 1000 ->
+ {error, 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 may also use Gun in a synchronous manner by writing your
+own functions that perform a receive like demonstrated above.
+
+Dealing with server-pushed streams
+----------------------------------
+
+When using SPDY the server may decide to push extra resources
+after a request is performed. It will send a `gun_push` message
+which contains two references, one for the pushed stream, and
+one for the request this stream is associated with.
+
+Pushed streams typically feature a body. Replying to a pushed
+stream is forbidden and Gun will send an error message if
+attempted.