1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
[[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 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,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 `gun:request/4,5,6` 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 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}).
----
|