Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#32] Useful README. #42

Merged
merged 4 commits into from
Oct 17, 2014
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 154 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,161 @@
shotgun
=======

For the times you need more than just a [gun](http://github.com/extend/gun).

![](http://lbsommer-author.yolasite.com/resources/Funny%20gun%20sign%2017.jpg)
better than just a [gun](http://github.com/extend/gun)

## Rationale

After using the [gun](http://github.com/extend/gun) library on a project where
we needed to consume Server-sent Events (SSEs) we found that it provided great
flexibility, at the cost of having to handle each raw message and data,
including the construction of the reponse body data.
Although this is great for a lot of scenarios, it can get cumbersome and
repetitive after implementing it a couple of time. This is why we ended up
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timeS

creating **shogtun**, an HTTP client that uses **gun** behind the curtains but
provides a simple API that has out-of-the-box support for SSEs.

## Usage

*shotgun* is an OTP application, so before being able to use it, it has to
started. Either add it as one of the applications in your
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be started

[`.app`](http://www.erlang.org/doc/man/app.html) file or run the following
code:

```erlang
application:ensure_all_started(shotgun).
```

### Regular Requests

Once the application is started a connection needs to be created in order to
start making requests:

```erlang
{ok, Conn} = shotgun:open("google.com", 80),
{ok, Response} = shotgun:get(Conn, "/"),
io:format("~p~n", [Response]),
shotgun:close(Conn).

% #{body => <<"<HTML><HEAD>"...>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of comments, you can put this in a different code block so that this is highlighted

% headers => [
% {<<"location">>,<<"http://www.google.com/adfs">>},
% {<<"content-type">>,<<"text/html; charset=UTF-8">>},
% {<<"x-content-type-options">>,<<"nosniff">>},
% {<<"date">>,<<"Fri, 17 Oct 2014 17:18:32 GMT">>},
% {<<"expires">>,<<"Sun, 16 Nov 2014 17:18:32 GMT">>},
% {<<"cache-control">>,<<"public, max-age=2592000">>},
% {<<"server">>,<<"sffe">>},
% {<<"content-length">>,<<"223">>},
% {<<"x-xss-protection">>,<<"1; mode=block">>},
% {<<"alternate-protocol">>,<<"80:quic,p=0.01">>}
% ],
% status_code => 302}
% }

%= ok
```

Immediately after opening a connection we did a GET request, where we didn't
specify any headers or options. Every HTTP method has its own **shotgun**
function that takes a connection, a uri (which needs to include the slash),
a headers map and an options map. Some of the functions (`post/5`, `put/5`
and `patch/5`) also take a body argument.

Alternatively there's a generic `request/6` function in which the user can
specify the HTTP method as an argument in the form of an atom: `get`, `head`,
`options`, `delete`, `post`, `put` or `patch`.

**IMPORTANT:** After you are done using the shotgun connection remember to close
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"When" instead of "After"

it with `shogtun:close/1`.

### Basic Authentication

If you need to provide basic authentication credential in your requests, it is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

credentialS

as easy as specifying a `basic_auth` entry in the headers map:

```erlang
{ok, Conn} = shotgun:open("site.com", 80),
{ok, Response} = shotgun:get(Conn, "/user", #{basic_auth => {"user", "password"}),
shotgun:close(Conn).
```

### Consuming Server-sent Events

To use **shogtun** with endpoints that generate SSEs the request must be
configured using some values in the options map, which supports the following
entries:

- `async ::boolean()`: specifies if the request performed will return a chunked
response. **It currently only works for GET requests.**. Default value is
`false`.

- `async_data :: binary | sse`: when `async` is `true` the mode specifies
how the data received will be processed. `binary` mode treats eat chunk received
as raw binary. `sse` mode buffers each chunk, splitting the data received into
SSEs. Default value is `binary`.

- `handle_event :: fun((fin | nofin, ref(), binary()))`: this function will be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this spec is missing the fun result, if the result is discarded, it should be:

fun((fin | nofin, ref(), binary()) -> _) %% or…
fun((fin | nofin, ref(), binary()) -> any())

called each time either a chunk is received (`async_data` = `binary`) or
the an event is parsed (`async_data` = `sse`). If no handle_event function is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove "the"

provided the data received is added to a queue, whose values can be obtained
calling the `shotgun:events/1`. Default value is `undefined`.

The following is an example of the usage of **shotgun** when consuming SSEs.

```erlang
{ok, Conn} = shotgun:open("locahost", 8080).
%= {ok,<0.6003.0>}

Options = #{async => true, async_mode => sse}},
{ok, Ref} = shotgun:get(Conn, "/events", #{}, Options).
%= {ok,#Ref<0.0.1.186238>}

% Some event are generated on the server...
Events = shotgun:events(Conn).
%= [{nofin, #Ref<0.0.1.186238>, <<"data: pong">>}, {nofin, #Ref<0.0.1.186238>, <<"data: pong">>}]

shotgun:events(Conn).
%= []
```

Notice how the second call to `shotgun:events/1` returns an empty list. This is
because events are stored in a queue and each call to `events` returns all
events queued so far and then removes these from the queue. So it's important
to understand that `shotgun:events/1` is a function with side-effects when using
it.

Additionally **shotgun** provides a `parse_event/1` helper function that
turns a server-sent event binary into a map:

```erlang
shotgun:parse_event(<<"data: pong\ndata: ping\nid: 1\nevent: pinging">>).
%= #{data => [<<"pong">>,<<"ping">>],event => <<"pinging">>,id => <<"1">>}
```

## Building & Test-Driving

To build **shotgun** just run the following on your command shell:

```sh
make
```

To start up a shell where you can try things out run the following (after
building the project as described above):

```sh
make shell
```

## Contact Us
For **questions** or **general comments** regarding the use of this library, please use our public
[hipchat room](https://www.hipchat.com/gpBpW3SsT).
For **questions** or **general comments** regarding the use of this library,
please use our public [hipchat room](https://www.hipchat.com/gpBpW3SsT).

If you find any **bugs** or have a **problem** while using this library, please [open an issue](https://github.com/inaka/shotgun/issues/new) in this repo (or a pull request :)).
If you find any **bugs** or have a **problem** while using this library, please
[open an issue](https://github.com/inaka/shotgun/issues/new) in this repo
(or a pull request :)).

And you can check all of our open-source projects at [inaka.github.io](http://inaka.github.io)
And you can check all of our open-source projects at
[inaka.github.io](http://inaka.github.io)