aboutsummaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2015-03-25 13:44:08 +0100
committerLoïc Hoguin <[email protected]>2015-03-25 13:44:08 +0100
commit83d8b63b8abb46b374439c8c8571091968af6260 (patch)
tree11a7bb370d43a7c25e4734eb7a6f7649ce9cdc61 /doc
parenteab45765497e6eb3e031cba5dad66a7a20ec3651 (diff)
downloadgun-83d8b63b8abb46b374439c8c8571091968af6260.tar.gz
gun-83d8b63b8abb46b374439c8c8571091968af6260.tar.bz2
gun-83d8b63b8abb46b374439c8c8571091968af6260.zip
Update the guide
A number of @todo remain in it and will be worked on shortly. The guide has been converted to Asciidoc and 'make asciidoc' will generate a PDF and a chunked HTML version.
Diffstat (limited to 'doc')
-rw-r--r--doc/src/guide/book.asciidoc16
-rw-r--r--doc/src/guide/connect.asciidoc119
-rw-r--r--doc/src/guide/http.asciidoc363
-rw-r--r--doc/src/guide/introduction.asciidoc28
-rw-r--r--doc/src/guide/protocols.asciidoc119
-rw-r--r--doc/src/guide/start.asciidoc67
-rw-r--r--doc/src/guide/websocket.asciidoc100
7 files changed, 812 insertions, 0 deletions
diff --git a/doc/src/guide/book.asciidoc b/doc/src/guide/book.asciidoc
new file mode 100644
index 0000000..7e6a441
--- /dev/null
+++ b/doc/src/guide/book.asciidoc
@@ -0,0 +1,16 @@
+// a2x: --dblatex-opts "-P latex.output.revhistory=0 -P doc.publisher.show=0 -P index.numbered=0"
+// a2x: -d book --attribute tabsize=4
+
+= Gun User Guide
+
+include::introduction.asciidoc[Introduction]
+
+include::start.asciidoc[Starting and stopping]
+
+include::protocols.asciidoc[Supported protocols]
+
+include::connect.asciidoc[Connection]
+
+include::http.asciidoc[Using HTTP]
+
+include::websocket.asciidoc[Using Websocket]
diff --git a/doc/src/guide/connect.asciidoc b/doc/src/guide/connect.asciidoc
new file mode 100644
index 0000000..e1ad56e
--- /dev/null
+++ b/doc/src/guide/connect.asciidoc
@@ -0,0 +1,119 @@
+== Connection
+
+This chapter describes how to open, monitor and close
+a connection using the Gun client.
+
+=== Gun connections
+
+Gun is designed with the SPDY and Websocket protocols in mind.
+They are built for long-running connections that allow concurrent
+exchange of data, either in the form of request/responses for
+SPDY or in the form of messages for Websocket.
+
+A Gun connection is an Erlang process that manages a socket to
+a remote endpoint. This Gun connection is owned by a user
+process that is called the _owner_ of the connection, and is
+managed by the supervision tree of the `gun` application.
+
+The owner process communicates with the Gun connection
+by calling functions from the module `gun`. All functions
+perform their respective operations asynchronously. The Gun
+connection will send Erlang messages to the owner process
+whenever needed.
+
+When the remote endpoint closes the connection, Gun attempts
+to reconnect automatically.
+
+=== Opening a new connection
+
+The `gun:open/{2,3}` function must be used to open a connection.
+
+.Opening a connection to example.org on port 443
+
+[source,erlang]
+{ok, ConnPid} = gun:open("example.org", 443).
+
+@todo open/3
+@todo make opts a map
+
+If the port given is 80, Gun will attempt to connect using
+TCP and use the HTTP/1.1 protocol. For any other port, TLS
+will be used. The NPN extension for TLS allows Gun to select
+SPDY automatically if the server supports it. Otherwise,
+HTTP/1.1 will be used.
+
+@todo more about defaults
+
+=== Monitoring the connection process
+
+@todo Gun should detect the owner process being killed
+
+Because software errors are unavoidable, it is important to
+detect when the Gun process crashes. It is also important
+to detect when it exits normally. Erlang provides two ways
+to do that: links and monitors.
+
+Gun leaves you the choice as to which one will be used.
+However, if you use the `gun:await/{2,3}` or `gun:await_body/{2,3}`
+functions, a monitor may be used for you to avoid getting
+stuck waiting for a message that will never come.
+
+If you choose to monitor yourself you can do it on a permanent
+basis rather than on every message you will receive, saving
+resources. Indeed, the `gun:await/{3,4}` and `gun:await_body/{3,4}`
+functions both accept a monitor argument if you have one already.
+
+.Monitoring the connection process
+
+[source,erlang]
+{ok, ConnPid} = gun:open("example.org", 443).
+MRef = monitor(process, ConnPid).
+
+This monitor reference can be kept and used until the connection
+process exits.
+
+.Handling `DOWN` messages
+
+[source,erlang]
+receive
+ %% Receive Gun messages here...
+ {DOWN', Mref, process, ConnPid, Reason} ->
+ error_logger:error_msg("Oops!"),
+ exit(Reason);
+end.
+
+What to do when you receive a `DOWN` message is entirely up to you.
+
+=== Closing the connection abruptly
+
+The connection can be stopped abruptly at any time by calling
+the `gun:close/1` function.
+
+.Immediate closing of the connection
+
+[source,erlang]
+gun:close(ConnPid).
+
+The process is stopped immediately without having a chance to
+perform the protocol's closing handshake, if any.
+
+=== Closing the connection gracefully
+
+The connection can also be stopped gracefully by calling the
+`gun:shutdown/1` function.
+
+.Graceful shutdown of the connection
+
+[source,erlang]
+gun:shutdown(ConnPid).
+
+Gun will refuse any new requests or messages after you call
+this function. It will however continue to send you messages
+for existing streams until they are all completed.
+
+For example if you performed a GET request just before calling
+`gun:shutdown/1`, you will still receive the response before
+Gun closes the connection.
+
+If you set a monitor beforehand, you will receive a message
+when the connection has been closed.
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).
diff --git a/doc/src/guide/introduction.asciidoc b/doc/src/guide/introduction.asciidoc
new file mode 100644
index 0000000..ade3d80
--- /dev/null
+++ b/doc/src/guide/introduction.asciidoc
@@ -0,0 +1,28 @@
+== Introduction
+
+Gun is an Erlang HTTP client with support for HTTP/1.1, SPDY and Websocket.
+
+=== Prerequisites
+
+Knowledge of Erlang, but also of the HTTP/1.1, SPDY and Websocket
+protocols is required in order to read this guide.
+
+=== Supported platforms
+
+Gun is tested and supported on Linux.
+
+Gun is developed for Erlang 17+.
+
+Gun may be compiled on earlier Erlang versions with small source code
+modifications but there is no guarantee that it will work as intended.
+
+=== Conventions
+
+In the HTTP protocol, the method name is case sensitive. All standard
+method names are uppercase.
+
+Header names are case insensitive. Gun converts all the header names
+to lowercase, and expects your application to provide lowercase header
+names.
+
+The same applies to any other case insensitive value.
diff --git a/doc/src/guide/protocols.asciidoc b/doc/src/guide/protocols.asciidoc
new file mode 100644
index 0000000..2180c5b
--- /dev/null
+++ b/doc/src/guide/protocols.asciidoc
@@ -0,0 +1,119 @@
+== Supported protocols
+
+This chapter describes the protocols supported and the
+operations available to them.
+
+=== HTTP/1.1
+
+HTTP/1.1 is a text request-response protocol. The client
+sends a request, the server sends back a response.
+
+Gun provides convenience functions for performing GET, HEAD,
+OPTIONS, POST, PATCH, PUT, and DELETE requests. All these
+functions are aliases of `gun:request/{4,5}` for each respective
+methods. Gun also provides a `gun:data/4` function for streaming
+the request body.
+
+Gun will send a `gun_response` message for every response
+received, followed by zero or more `gun_data` messages for
+the response body. If something goes wrong, a `gun_error`
+will be sent instead.
+
+Gun provides convenience functions for dealing with messages.
+The `gun:await/{2,3,4}` function waits for a response to the given
+request, and the `gun:await_body/{2,3,4}` function for the
+response's body. The `gun:flush/1` function can be used to clear all
+messages related to a request or a connection from the mailbox
+of the process.
+
+The function `gun:cancel/2` can be used to silence the
+response to a request previously sent if it is no longer
+needed. When using HTTP/1.1 there is no multiplexing so
+Gun will have to receive the response fully before any
+other response can be received.
+
+Finally, Gun can upgrade an HTTP/1.1 connection to Websocket.
+It provides the `gun:ws_upgrade/{2,3,4}` function for that
+purpose. A `gun_ws_upgrade` message will be sent on success;
+a `gun_response` message otherwise.
+
+=== SPDY
+
+SPDY is a binary protocol based on HTTP, compatible with
+the HTTP semantics, that reduces the complexity of parsing
+requests and responses, compresses the HTTP headers and
+allows the server to push multiple responses to a single
+request.
+
+The SPDY interface is very similar to HTTP/1.1, so this
+section instead focuses on the differences in the interface
+for the two protocols.
+
+Because a SPDY server can push multiple responses to a
+single request, Gun might send `gun_push` messages for
+every push received. They can be ignored safely if they
+are not needed.
+
+The `gun:cancel/2` function will use the SPDY stream
+cancellation mechanism which allows Gun to inform the
+server to stop sending a response for this particular
+request, saving resources.
+
+It is not possible to upgrade a SPDY connection to Websocket
+due to protocol limitations.
+
+=== Websocket
+
+Websocket is a binary protocol built on top of HTTP that
+allows asynchronous concurrent communication between the
+client and the server. A Websocket server can push data to
+the client at any time.
+
+Websocket is only available as a connection upgrade over
+an HTTP/1.1 connection.
+
+Once the Websocket connection is established, the only
+operation available on this connection is sending Websocket
+frames using `gun:ws_send/2`.
+
+Gun will send a `gun_ws` message for every frame received.
+
+=== Summary
+
+The two following tables summarize the supported operations
+and the messages Gun sends depending on the connection's
+current protocol.
+
+.Supported operations per protocol
+[cols="<,3*^",options="header"]
+|===
+| Operation | HTTP/1.1 | SPDY | Websocket
+| delete | yes | yes | no
+| get | yes | yes | no
+| head | yes | yes | no
+| options | yes | yes | no
+| patch | yes | yes | no
+| post | yes | yes | no
+| put | yes | yes | no
+| request | yes | yes | no
+| data | yes | yes | no
+| await | yes | yes | no
+| await_body | yes | yes | no
+| flush | yes | yes | no
+| cancel | yes | yes | no
+| ws_upgrade | yes | no | no
+| ws_send | no | no | yes
+|===
+
+.Messages sent per protocol
+[cols="<,3*^",options="header"]
+|===
+| Message | HTTP/1.1 | SPDY | Websocket
+| gun_push | no | yes | no
+| gun_response | yes | yes | no
+| gun_data | yes | yes | no
+| gun_error (StreamRef) | yes | yes | no
+| gun_error | yes | yes | yes
+| gun_ws_upgrade | yes | no | no
+| gun_ws | no | no | yes
+|===
diff --git a/doc/src/guide/start.asciidoc b/doc/src/guide/start.asciidoc
new file mode 100644
index 0000000..6d93e2e
--- /dev/null
+++ b/doc/src/guide/start.asciidoc
@@ -0,0 +1,67 @@
+== Starting and stopping
+
+This chapter describes how to start and stop the Gun application.
+
+=== Setting up
+
+Before Gun can be used it needs to be in Erlang's `ERL_LIBS` path variable.
+If you use `erlang.mk` or a similar build tool, you only need to specify
+Gun as a dependency to your application and the tool will take care
+of downloading Gun and setting up paths.
+
+With `erlang.mk` this is done by adding `gun` to the `DEPS` variable
+in your Makefile.
+
+.Adding Gun as an erlang.mk dependency
+
+[source,make]
+DEPS = gun
+
+=== Starting
+
+Gun is an _OTP application_. It needs to be started before you can
+use it.
+
+.Starting Gun in an Erlang shell
+
+[source,erlang]
+----
+1> application:ensure_all_started(gun).
+{ok,[ranch,crypto,cowlib,asn1,public_key,ssl,gun]}
+----
+
+=== Stopping
+
+You can stop Gun using the `application:stop/1` function, however
+only Gun will be stopped. This is the equivalent of `application:start/1`.
+The `application_ensure_all_started/1` function has no equivalent for
+stopping all applications.
+
+.Stopping Gun
+
+[source,erlang]
+application:stop(gun).
+
+=== Using Gun with releases
+
+An _OTP release_ starts applications automatically. All you need
+to do is to set up your application resource file so that Gun can
+be included in the release. The application resource file can be
+found in `ebin/your_application.app`, or in `src/your_application.app.src`
+if you are using a build tool like `erlang.mk`.
+
+The key you need to change is the `applications` key. By default
+it only includes `kernel` and `stdlib`. You need to add `gun` to
+that list.
+
+.Adding Gun to the application resource file
+
+[source,erlang]
+{applications, [
+ kernel,
+ stdlib,
+ gun
+]}
+
+Do not put an extra comma at the end, the comma is a separator
+between the elements of the list.
diff --git a/doc/src/guide/websocket.asciidoc b/doc/src/guide/websocket.asciidoc
new file mode 100644
index 0000000..011e457
--- /dev/null
+++ b/doc/src/guide/websocket.asciidoc
@@ -0,0 +1,100 @@
+== Websocket
+
+This chapter describes how to use the Gun client for
+communicating with a Websocket server.
+
+@todo recovering from connection failure
+reconnecting to Websocket etc.
+
+=== HTTP upgrade
+
+Websocket is a protocol built on top of HTTP. To use Websocket,
+you must first request for the connection to be upgraded. Only
+HTTP/1.1 connections can be upgraded to Websocket, so you might
+need to restrict the protocol to HTTP/1.1 if you are planning
+to use Websocket over TLS.
+
+@todo add option to disable specific protocols
+
+You must use the `gun_ws:upgrade/{2,3}` function to upgrade
+to Websocket. This function can be called anytime after connection,
+so you can send HTTP requests before upgrading to Websocket.
+
+.Upgrade to Websocket
+
+[source,erlang]
+gun:ws_upgrade(ConnPid, "/websocket").
+
+Gun will set all the necessary headers for performing the
+Websocket upgrade, but you can specify additional headers
+if needed. For example you can request a custom sub-protocol.
+
+.Upgrade to Websocket and request a protocol
+
+[source,erlang]
+gun:ws_upgrade(ConnPid, "/websocket", [
+ {<<"sec-websocket-protocol">>, "mychat"}
+]).
+
+The success or failure of this operation will be sent as a
+message.
+
+@todo hmm we want the headers to be sent in the gun_ws_upgrade ok message too
+
+[source,erlang]
+receive
+ {gun_ws_upgrade, ConnPid, ok} ->
+ upgrade_success(ConnPid);
+ {gun_ws_upgrade, ConnPid, error, IsFin, Status, Headers} ->
+ exit({ws_upgrade_failed, Status, Headers});
+ %% More clauses here as needed.
+after 1000 ->
+ exit(timeout);
+end.
+
+=== Sending data
+
+Once the Websocket upgrade has completed successfully, you no
+longer have access to functions for performing requests. You
+can only send and receive Websocket messages.
+
+Use `gun:ws_send/2` to send one or more messages to the server.
+
+@todo Implement sending of N frames
+
+.Send a text frame
+
+[source,erlang]
+gun:ws_send(ConnPid, {text, "Hello!"}).
+
+.Send a text frame, a binary frame and then close the connection
+
+[source,erlang]
+gun:ws_send(ConnPid, [
+ {text, "Hello!"},
+ {binary, BinaryValue},
+ close
+]).
+
+Note that if you send a close frame, Gun will close the connection
+cleanly and will not attempt to reconnect afterwards, similar to
+calling `gun:shutdown/1`.
+
+=== Receiving data
+
+Gun sends an Erlang message to the owner process for every
+Websocket message it receives.
+
+[source,erlang]
+receive
+ {gun_ws, ConnPid, Frame} ->
+ handle_frame(ConnPid, Frame)
+end.
+
+@todo auto ping has not been implemented yet
+
+Gun will automatically send ping messages to the server to keep
+the connection alive, however if the connection dies and Gun has
+to reconnect it will not upgrade to Websocket automatically, you
+need to perform the operation when you receive the `gun_error`
+message.