Skip to content

Commit

Permalink
Doc updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kalta committed Jul 10, 2014
1 parent 81fffcc commit 32a9b83
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 145 deletions.
149 changes: 82 additions & 67 deletions doc/guide/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,85 +13,96 @@ Firt of all install last stable version of NkSIP:
> make tutorial
```

Now you can start a simple SipApp proxy server using the included [server callback module](../samples/nksip_tutorial/src/nksip_tutorial_sipapp_server.erl). We will listen on all interfaces, port `5060` for udp and tcp and `5061` for tls. We also include the `registrar` option to tell NkSIP to process registrations (without having to implement `register/3` callback function, see [nksip_siapp.erl](../nksip/src/nksip_sipapp.erl)):
Now you can start a simple SipApp proxy server using the included [server callback module](../samples/nksip_tutorial/src/nksip_tutorial_sipapp_server.erl). We will listen on all interfaces, port `5060` for udp and tcp and `5061` for tls. We also activate the [nksip_registrar](../plugins/registrar.md) plugin to tell NkSIP to process registrations:
```erlang
1> nksip:start(server, nksip_tutorial_sipapp_server, [server],
[
registrar,
{transport, {udp, any, 5060}},
{transport, {tls, any, 5061}}
]).
ok
1> nksip:start(server, nksip_tutorial_sipapp_server, [],
[
{plugins, [nksip_registrar]},
{transports, [{udp, all, 5060}, {tls, all, 5061}]}
]),
{ok,armejl7}
```

Now we can start two clients, using the included [client callback module](../samples/nksip_tutorial/src/nksip_tutorial_sipapp_client.erl). The first is called `client1` and listens on `127.0.0.1` ports `5070` for udp and tcp and `5071` for tls, and the second is called `client2` and listens on all interfaces, random ports. We also configure the _From_ header to be used on each one, the second one using `sips`:
NkSip returns the _internal name_ of the application, which is alwats an `atom()`, calculated as a hash over the name.

Now we can start two clients, using the included [client callback module](../samples/nksip_tutorial/src/nksip_tutorial_sipapp_client.erl). The first is called `client1` and listens on `127.0.0.1` ports `5070` for udp and tcp and `5071` for tls, and the second is called `client2` and listens on all interfaces, random ports. We also configure the _From_ header to be used on each one, the second one using `sips`.

We also activate the [nksip_uac_auto_auth](../plugins/auto_auth.md) plugin to generate automatic digest authentications:

```erlang
2> nksip:start(client1, nksip_tutorial_sipapp_client, [client1],
[
{from, "sip:client1@nksip"},
{transport, {udp, {127,0,0,1}, 5070}},
{transport, {tls, {127,0,0,1}, 5071}}
]).
ok
3> nksip:start(client2, nksip_tutorial_sipapp_client, [client2],
[ {from, "sips:client2@nksip"},
{transport, {udp, {127,0,0,1}, 5080}},
{transport, {tls, {127,0,0,1}, 5081}}
]).
ok
2> nksip:start(client1, nksip_tutorial_sipapp_client, [],
[
{plugins, [nksip_uac_auto_auth]},
{from, "sip:client1@nksip"},
{transports, [{udp, {127,0,0,1}, 5070}, {tls, {127,0,0,1}, 5071}]}
]).
{ok,b3finlv}
3> nksip:start(client2, nksip_tutorial_sipapp_client, [],
[
{plugins, [nksip_uac_auto_auth]},
{from, "sips:client2@nksip"},
{transports, [{udp, {127,0,0,1}, 5080}, {tls, {127,0,0,1}, 5081}]}
]).
{ok,ayr8ncm}

```

From now on, you could start tracing to see all SIP messages on the console, using `nksip_trace:start(true).`
From now on, you could start tracing to see all SIP messages on the console, using `nksip_trace:start().`

Let's try now to send an _OPTIONS_ from `client2` to `client1` and from `client1` to the `server`:
```erlang
4> nksip_uac:options(client2, "sip:127.0.0.1:5070", []).
{ok,200,[]}
5> nksip_uac:options(client1, "sip:127.0.0.1", [{fields, [reason]}]).
{ok,407,[{reason,<<"Proxy Authentication Required">>}]}
5> nksip_uac:options(client1, "sip:127.0.0.1", [{meta,[reason_phrase]}]).
{ok,407,[{reason_phrase, <<"Proxy Authentication Required">>}]}
```

Oops, the `server` didn't accept the request (we have used the `fields` option to
Oops, the `server` didn't accept the request (we have used the `meta` option to
order NkSIP to return the reason phrase).

In the client callback module there is no authentication related callback function implemented, so every request is accepted. But server callback module is different:

```erlang
%% @doc Called to check user's password.
%%
%% If the incoming user's realm is "nksip", the password for any user is "1234".
%% For other realms, no password is valid.
get_user_pass(_User, <<"nksip">>, _From, State) ->
{reply, <<"1234">>, State};
get_user_pass(_User, _Realm, _From, State) ->
{reply, false, State}.
%%
sip_get_user_pass(_User, <<"nksip">>, _Req, _Call) ->
<<"1234">>;
sip_get_user_pass(_User, _Realm, _Req, _Call) ->
false.


%% @doc Called to check if a request should be authorized.
%%
%% 1) We first check to see if the request is an in-dialog request, coming from
%% the same ip and port of a previously authorized request.
%%
%% 2) If not, we check if we have a previous authorized REGISTER request from
%% the same ip and port.
%%
%% 3) Next, we check if the request has a valid authentication header with realm
%% "nksip". If '{{digest, <<"nksip">>}, true}' is present, the user has
%% "nksip". If `{{digest, <<"nksip">>}, true}' is present, the user has
%% provided a valid password and it is authorized.
%% If '{{digest, <<"nksip">>}, false}' is present, we have presented
%% If `{{digest, <<"nksip">>}, false}' is present, we have presented
%% a challenge, but the user has failed it. We send 403.
%%
%% 4) If no digest header is present, reply with a 407 response sending
%% a challenge to the user.
authorize(Auth, _ReqId, _From, State) ->
case lists:member(dialog, Auth) orelse lists:member(register, Auth) of
%%
sip_authorize(AuthList, _Req, _Call) ->
case lists:member(dialog, AuthList) orelse lists:member(register, AuthList) of
true ->
{reply, true, State};
ok;
false ->
case nksip_lib:get_value({digest, <<"nksip">>}, Auth) of
case proplists:get_value({digest, <<"nksip">>}, AuthList) of
true ->
{reply, true, State}; % Password is valid
ok; % Password is valid
false ->
{reply, false, State}; % User has failed authentication
forbidden; % User has failed authentication
undefined ->
{reply, {proxy_authenticate, <<"nksip">>}, State}
{proxy_authenticate, <<"nksip">>}

end
end.
Expand All @@ -106,20 +117,22 @@ use `tls` transport. Note we must use `<` and `>` if including `uri` parameters
{ok,200,[]}
```

Let's register now both clients with the server. We use the option `make_contact` to tell NkSIP to include a valid _Contact_ header in the request, and the `fields` option to get the _Contact_ header from the response, to be sure the server has stored the contact:
Both requests receive a 407 response, but the nksip_uac_auto_auth plugin takes the included password and generates a new request with the correct headers, that are accepted at the server.

Let's register now both clients with the server. We use the option `contact` to tell NkSIP to include a valid _Contact_ header in the request, and the `mtea` option to get the _Contact_ header from the response, to be sure the server has stored the contact:

```erlang
8> nksip_uac:register(client1, "sip:127.0.0.1",
[{pass, "1234"}, make_contact, {fields, [<<"Contact">>]}]).
{ok,200,[{<<"Contact">>, [<<"<sip:[email protected]:5070>;expires=3600">>]}]}
10> nksip_uac:register(client2, "sips:127.0.0.1", [{pass, "1234"}, make_contact]).
[{pass, "1234"}, contact, {meta, [<<"contact">>]}]).
{ok,200,[{<<"contact">>, [<<"<sip:[email protected]:5070>...">>]}]}
10> nksip_uac:register(client2, "sips:127.0.0.1", [{pass, "1234"}, contact]).
{ok,200,[]}
```

We can check this second registration has worked. If we send a _REGISTER_ request with no _Contact_ header, the server will include one for each stored registration. This time, lets get all the header from the response using `all_headers` as field specification:

```erlang
12> nksip_uac:register(client2, "sips:127.0.0.1", [{pass, "1234"}, {fields, [all_headers]}]).
12> nksip_uac:register(client2, "sips:127.0.0.1", [{pass, "1234"}, {meta, [all_headers]}]).
{ok,200,[{all_headers, [{<<"CallId">>, ...}]}]}
```

Expand All @@ -139,56 +152,58 @@ The first request is not authorized. The reason is that we are using a `sips` ur
15> nksip_uac:options(client1, "sips:client2@nksip", [{route, "<sip:127.0.0.1;lr>"}]).
{ok,407,[]}
16> nksip_uac:options(client1, "sips:client2@nksip",
[{route, "<sip:127.0.0.1;lr>"}, {pass, "1234"},
{fields, [<<"Nksip-Id">>]}]).
{ok,200,[{<<"Nksip-Id">>, [<<"client2">>]}]}
[{route, "<sip:127.0.0.1;lr>"}, {pass, "1234"},
{meta, [<<"x-nk-id">>]}]).
{ok,200,[{<<"x-nk-id">>, [<<"client2">>]}]}
```
In the second case we want to get the _Nksip-Id_ header from the response.
Our callback `options/3` is called for every received options request, which includes the custom header:
In the second case we want to get the _X-Nk-Id header from the response (in NkSIP, all headers must be spelled lowercase).
Our callback `options/2` is called for every received options request, which includes the custom header:

```erlang
options(_ReqId, _From, #state{id=Id}=State) ->
Headers = [{"Nksip-Id", Id}],
Opts = [make_contact, make_allow, make_accept, make_supported],
{reply, {ok, Headers, <<>>, Opts}, State}.
sip_options(Req, _Call) ->
AppName = nksip_request:app_name(Req),
{reply, {ok, [{add, "x-nk-id", AppName}, contact, allow, accept, supported]}}.
```

Now let's try a _INVITE_ from `client2` to `client1` through the proxy. NkSIP will call the callback `invite/3` in `client1`'s callback module:
Now let's try a _INVITE_ from `client2` to `client1` through the proxy. NkSIP will call the callback `invite/2` in `client1`'s callback module:

```erlang
invite(Req, Call) ->
SDP = nksip_request:body(ReqId),
case nksip_sdp:is_sdp(SDP) of
sip_invite(Req, _Call) ->
Body = nksip_request:body(Req),
case nksip_sdp:is_sdp(Body) of
true ->
ReqId = nksip_request:get_id(Req),
Fun = fun() ->
nksip_request:reply(ringing, ReqId),
timer:sleep(2000),
nksip_request:reply({answer, SDP}, ReqId)
nksip_request:reply({answer, Body}, ReqId)
end,
spawn(Fun),
{noreply, State};
noreply;
false ->
{reply, {not_acceptable, <<"Invalid SDP">>}, State}
{reply, {not_acceptable, <<"Invalid SDP">>}}
end.
```

In the first call, since we don't include a body, `client1` will reply `not_acceptable` (code `488`).
In the second, we _spawn_ a new process, reply a _provisional_ `180 Ringing`, wait two seconds and reply a `final` `200 Ok` with the same body. For _INVITE_ responses, NkSIP will allways include the `dialog_id` value, After receiving each `2xx` response to an _INVITE_, we must send an _ACK_ inmediatly:
In the second, we _spawn_ a new process, reply a _provisional_ `180 Ringing`, wait two seconds and reply a `final` `200 Ok` with the same body. For 2xx _INVITE_ responses, NkSIP will allways include the `dialog_id` value, After receiving each `2xx` response to an _INVITE_, we must send an _ACK_ inmediatly:

```erlang
17> nksip_uac:invite(client2, "sip:client1@nksip", [{route, "<sips:127.0.0.1;lr>"}]).
{ok,488,[{dialog_id, ...}]}
18> {ok,200,[{dialog_id, DlgId}]}= nksip_uac:invite(client2, "sip:client1@nksip",
[{route, "<sips:127.0.0.1;lr>"}, {body, nksip_sdp:new()}]).
{ok,488,[]}
18> {ok,200,[{dialog_id, DlgId}]} = nksip_uac:invite(client2, "sip:client1@nksip",
[
{route, "<sips:127.0.0.1;lr>"},
{body, nksip_sdp:new()}
]).
{ok,200,[{dialog_id, <<"...">>}]}
19> nksip_uac:ack(client2, DlgId, []).
19> nksip_uac:ack(DlgId, []),
ok
```

The call is accepted and we have started a _dialog_:
```erlang
19> nksip_dialog:field(client2, DlgId, status).
19> nksip_dialog:meta(invite_status, DlgId).
confirmed
```

Expand All @@ -199,7 +214,7 @@ You can _print_ all dialogs in the console. We see dialogs at `client1`, `client

Ok, let's stop the call, the dialogs and the SipApps:
```erlang
27> nksip_uac:bye(client2, DlgId, []).
27> nksip_uac:bye(DlgId, []).
{ok,200,[]}
28> nksip:stop_all().
ok
Expand Down
1 change: 0 additions & 1 deletion samples/nksip_loadtest/src/nksip_loadtest_sipapp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
%% @doc SipApp callback module

-module(nksip_loadtest_sipapp).
-behaviour(nksip_sipapp).

-export([init/1, sip_route/5, sip_invite/3]).

Expand Down
1 change: 0 additions & 1 deletion samples/nksip_pbx/src/nksip_pbx_sipapp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
%% See {@link //nksip_pbx} for an overview.

-module(nksip_pbx_sipapp).
-behaviour(nksip_sipapp).

-export([start/0, stop/0, check_speed/1, get_speed/0]).
-export([init/1, sip_get_user_pass/3, sip_authorize/4, sip_route/6]).
Expand Down
3 changes: 3 additions & 0 deletions samples/nksip_tutorial/priv/vm.args
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
-pa deps/goldrush/ebin
-pa deps/lager/ebin
-pa deps/ranch/ebin
-pa deps/cowlib/ebin
-pa deps/cowboy/ebin
-pa ../nksip/ebin
-pa ../nksip/plugins/ebin
-pa samples/nksip_tutorial/ebin

## Name of the node
Expand Down
58 changes: 37 additions & 21 deletions samples/nksip_tutorial/src/nksip_tutorial.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,35 @@

%% @doc Launches the full tutorial.
launch() ->
ok = nksip:start(server, nksip_tutorial_sipapp_server, [server],
{ok, _} = nksip:start(server, nksip_tutorial_sipapp_server, [],
[
registrar,
{transport, {udp, any, 5060}},
{transport, {tls, any, 5061}}
{plugins, [nksip_registrar]},
{transports, [{udp, all, 5060}, {tls, all, 5061}]}
]),
ok = nksip:start(client1, nksip_tutorial_sipapp_client, [client1],
{ok, _} = nksip:start(client1, nksip_tutorial_sipapp_client, [],
[
{plugins, [nksip_uac_auto_auth]},
{from, "sip:client1@nksip"},
{transport, {udp, {127,0,0,1}, 5070}},
{transport, {tls, {127,0,0,1}, 5071}}
{transports, [{udp, {127,0,0,1}, 5070}, {tls, {127,0,0,1}, 5071}]}
]),
ok = nksip:start(client2, nksip_tutorial_sipapp_client, [client2],
[ {from, "sips:client2@nksip"},
{transport, {udp, {127,0,0,1}, 5080}},
{transport, {tls, {127,0,0,1}, 5081}}
{ok, _} = nksip:start(client2, nksip_tutorial_sipapp_client, [],
[
{plugins, [nksip_uac_auto_auth]},
{from, "sips:client2@nksip"},
{transports, [{udp, {127,0,0,1}, 5080}, {tls, {127,0,0,1}, 5081}]}
]),

nksip_registrar_util:clear(),


{ok,200,[]} = nksip_uac:options(client2, "sip:127.0.0.1:5070", []),
{ok,407,[{reason_phrase, <<"Proxy Authentication Required">>}]} =
nksip_uac:options(client1, "sip:127.0.0.1", [{meta,[reason_phrase]}]),

{ok,200,[]} = nksip_uac:options(client1, "sip:127.0.0.1", [{pass, "1234"}]),
{ok,200,[]} = nksip_uac:options(client2, "<sip:127.0.0.1;transport=tls>", [{pass, "1234"}]),

{ok,200,[{<<"contact">>, [<<"<sip:[email protected]:5070>;expires=3600">>]}]} =
{ok,200,[{<<"contact">>, [<<"<sip:[email protected]:5070>", _/binary>>]}]} =
nksip_uac:register(client1, "sip:127.0.0.1",
[{pass, "1234"}, contact, {meta, [<<"contact">>]}]),

Expand All @@ -66,20 +69,20 @@ launch() ->
{ok,200,[]} = nksip_uac:options(client2, "sips:127.0.0.1", []),

{ok,407,[]} = nksip_uac:options(client1, "sips:client2@nksip", [{route, "<sip:127.0.0.1;lr>"}]),
{ok,200,[{<<"nksip-id">>, [<<"client2">>]}]} =
{ok,200,[{<<"x-nk-id">>, [<<"client2">>]}]} =
nksip_uac:options(client1, "sips:client2@nksip",
[{route, "<sip:127.0.0.1;lr>"}, {pass, "1234"},
{meta, [<<"nksip-id">>]}]),
{meta, [<<"x-nk-id">>]}]),

{ok,488,[{dialog_id, _}]} =
{ok,488,[]} =
nksip_uac:invite(client2, "sip:client1@nksip", [{route, "<sips:127.0.0.1;lr>"}]),

{ok,200,[{dialog_id, DlgId}]}=
nksip_uac:invite(client2, "sip:client1@nksip",
[{route, "<sips:127.0.0.1;lr>"}, {body, nksip_sdp:new()}]),
ok = nksip_uac:ack(DlgId, []),

confirmed = nksip_dialog:meta(DlgId, status, client2),
confirmed = nksip_dialog:meta(invite_status, DlgId),
[_, _, _] = nksip_dialog:get_all_data(),

{ok,200,[]} = nksip_uac:bye(DlgId, []),
Expand All @@ -92,14 +95,27 @@ launch() ->
%% ===================================================================

%% @doc Enables SIP trace messages to console.
-spec trace(Start::boolean()) -> ok.
-spec trace(Start::boolean()) ->
ok.

trace(true) -> nksip_trace:start();
trace(false) -> nksip_trace:stop().
trace(true) ->
nksip_trace:start(),
ok;
trace(false) ->
nksip_trace:stop(),
ok.


%% @doc Changes console log level.
%% Availanle options are `debug' (maximum), `info' (medium) and `notice' (minimum).
-spec loglevel(debug|info|notice) -> ok.
-spec loglevel(debug|info|notice) ->
ok.

loglevel(Level) ->
lager:set_loglevel(lager_console_backend, Level),
{ok, _} = nksip:update(server, [{log_level, Level}]),
{ok, _} = nksip:update(client1, [{log_level, Level}]),
{ok, _} = nksip:update(client2, [{log_level, Level}]),
ok.


loglevel(Level) -> lager:set_loglevel(lager_console_backend, Level).
Loading

0 comments on commit 32a9b83

Please sign in to comment.