From e39252ae5048156ac33999ce9bb07212798b6b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 26 Sep 2019 14:05:08 +0200 Subject: Add Gun 2.0-pre documentation But don't propagate via RSS. --- docs/en/gun/2.0/guide/http.asciidoc | 387 ++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 docs/en/gun/2.0/guide/http.asciidoc (limited to 'docs/en/gun/2.0/guide/http.asciidoc') diff --git a/docs/en/gun/2.0/guide/http.asciidoc b/docs/en/gun/2.0/guide/http.asciidoc new file mode 100644 index 00000000..51cb994f --- /dev/null +++ b/docs/en/gun/2.0/guide/http.asciidoc @@ -0,0 +1,387 @@ +[[http]] +== HTTP + +This chapter describes how to use the Gun client for +communicating with an HTTP/1.1 or HTTP/2 server. + +=== Streams + +Every time a request is initiated, Gun creates a _stream_. +A _stream reference_ uniquely identifies a set of request and +response 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 calling 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,4` 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,4` 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 a 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 functions `gun:post/4,5`, `gun:put/4,5` and `gun:patch/4,5` +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 functions `gun:post/3,4`, `gun:put/3,4` and `gun:patch/3,4` +do not take a body in their arguments: the body must be +provided later on using the `gun:data/4` function. + +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 HTTP/2 will continue normally as it is chunked by design. + +.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. + +.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,4` 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 functions `gun:headers/4,5` or `gun:request/5,6` 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 calling +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 HTTP/2 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 to, a pushed resource related to, or data from +the given stream. + +When calling `gun:await/2,3` and not passing a monitor +reference, one is automatically created for you for the +duration of the call. + +The `gun:await_body/2,3,4` works similarly, but returns the +body received. Both functions can be combined to receive the +response and its body sequentially. + +.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 HTTP/2 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, URI, Headers} + = gun:await(ConnPid, OriginalStreamRef). +---- + +The `PushedStreamRef` variable can then be used with `gun:await/2,3,4` +and `gun:await_body/2,3,4`. + +=== 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). +---- + +=== Redirecting responses to a different process + +Gun allows you to specify which process will handle responses +to a request via the `reply_to` request option. + +.GET "/organizations/ninenines" to a different process +[source,erlang] +---- +StreamRef = gun:get(ConnPid, "/organizations/ninenines", [], + #{reply_to => Pid}). +---- -- cgit v1.2.3