├── CNAME ├── priv ├── static │ ├── back.jpg │ ├── websocket.svg │ ├── login.htm │ ├── index.htm │ └── synrc.css └── k6 │ ├── config.js │ └── main.js ├── .gitattributes ├── .gitignore ├── vm.args ├── .travis.yml ├── src ├── sample.app.src ├── sample.erl ├── login.erl ├── routes.erl └── index.erl ├── sys.config ├── README.md ├── rebar.config ├── login.htm ├── index.htm ├── man ├── login.htm ├── index.htm └── sample.htm └── index.html /CNAME: -------------------------------------------------------------------------------- 1 | sample.n2o.dev 2 | -------------------------------------------------------------------------------- /priv/static/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synrc/sample/HEAD/priv/static/back.jpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-detectable=false 2 | *.htm linguist-detectable=false 3 | *.css linguist-detectable=false 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | log 3 | logs 4 | .applist 5 | Mnesia.nonode@nohost 6 | deps 7 | .rebar 8 | review 9 | _build 10 | rebar.lock 11 | -------------------------------------------------------------------------------- /vm.args: -------------------------------------------------------------------------------- 1 | +W w 2 | -kernel net_ticktime 60 3 | -env ERL_CRASH_DUMP log/crash.dump 4 | +e 256000 5 | -env ERL_FULLSWEEP_AFTER 1000 6 | +Q 65536 7 | +P 256000 8 | +A 32 9 | +K true 10 | -smp auto 11 | -setcookie emq_dist_cookie 12 | -name sample-1@127.0.0.1 13 | +zdbbl 32768 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 21.3 4 | - 22.0 5 | - 23.0 6 | notifications: 7 | email: 8 | - maxim@synrc.com 9 | script: 10 | - "curl -fsSL https://raw.github.com/synrc/mad/master/mad > mad" 11 | - "chmod +x mad" 12 | - "./mad dep com" 13 | - "rebar3 dialyzer" 14 | -------------------------------------------------------------------------------- /src/sample.app.src: -------------------------------------------------------------------------------- 1 | {application, sample, 2 | [{description, "SAMPLE WebSocket application"}, 3 | {vsn, "4.11.0"}, 4 | {registered, []}, 5 | {applications, [public_key,kernel,stdlib,crypto,kvs,mnesia,n2o,nitro,syn,cowlib,ssl,ranch,cowboy]}, 6 | {mod, { sample, []}}, 7 | {env, []}, 8 | {modules,[]}]}. 9 | -------------------------------------------------------------------------------- /priv/k6/config.js: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": { 3 | "local": "127.0.0.1" 4 | }, 5 | "stages": [ 6 | { 7 | "duration": "1m", 8 | "target": 10 9 | } 10 | ], 11 | "thresholds": { 12 | "http_req_duration": ["avg<100", "p(95)<200"] 13 | }, 14 | "noConnectionReuse": true, 15 | "type": "browser", 16 | "vus": "10", 17 | "duration": "3m", 18 | "userAgent": "MyK6UserAgentString/1.0" 19 | } 20 | -------------------------------------------------------------------------------- /sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | {n2o, [{port,8001}, 3 | {igor,"deps/n2o/src/protos"}, 4 | {app,sample}, 5 | {upload,"./priv/static/"}, 6 | {routes,routes}, 7 | {mq,n2o_syn}, 8 | {formatter,n2o_bert}, 9 | {protocols,[n2o_heart,nitro_n2o,n2o_ftp]}, 10 | {session,n2o_session}, 11 | {origin,<<"*">>}, 12 | {pickler,n2o_secret}, 13 | {event,pickle}]}, 14 | {kvs, [{dba,kvs_mnesia}, 15 | {dba_st,kvs_stream}, 16 | {schema, [kvs, kvs_stream ]} ]} 17 | ]. 18 | -------------------------------------------------------------------------------- /priv/static/websocket.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/sample.erl: -------------------------------------------------------------------------------- 1 | -module(sample). 2 | 3 | -behaviour(supervisor). 4 | 5 | -behaviour(application). 6 | 7 | -compile(export_all). 8 | 9 | main(A) -> mad:main(A). 10 | 11 | stop(_) -> ok. 12 | 13 | start() -> start(normal, []). 14 | 15 | start(_, _) -> 16 | cowboy:start_clear(http, 17 | [{port, application:get_env(n2o, port, 8001)}], 18 | #{env => #{dispatch => n2o_cowboy:points()}}), 19 | supervisor:start_link({local, sample}, sample, []). 20 | 21 | init([]) -> 22 | kvs:join(), 23 | {ok, {{one_for_one, 5, 10}, []}}. 24 | -------------------------------------------------------------------------------- /src/login.erl: -------------------------------------------------------------------------------- 1 | -module(login). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("nitro/include/nitro.hrl"). 6 | 7 | -include_lib("n2o/include/n2o.hrl"). 8 | 9 | event(init) -> 10 | nitro:update(loginButton, 11 | #button{id = loginButton, body = "Login", 12 | postback = login, source = [user, pass]}); 13 | event(login) -> 14 | User = nitro:to_list(nitro:q(user)), 15 | Room = nitro:to_list(nitro:q(pass)), 16 | n2o:user(User), 17 | n2o:session(room, Room), 18 | nitro:redirect("/app/index.htm?room=" ++ Room), 19 | ok; 20 | event(_) -> []. 21 | -------------------------------------------------------------------------------- /priv/k6/main.js: -------------------------------------------------------------------------------- 1 | import { browser } from 'k6/experimental/browser'; 2 | import { check } from 'k6'; 3 | 4 | export const options = { 5 | scenarios: { ui: { executor: 'shared-iterations', options: { browser: { type: 'chromium' }, }, }, }, 6 | thresholds: { checks: ['rate==1.0'], }, 7 | }; 8 | 9 | export default async function () { 10 | const context = browser.newContext(); 11 | const page = context.newPage(); 12 | try { 13 | await page.goto('http://localhost:8001/app/login.htm'); 14 | const button = page.locator('#loginButton'); 15 | button.click(); 16 | await page.waitForNavigation(); 17 | check(page, { body: (p) => p.locator('#logout').textContent() === 'Logout ', }); 18 | } finally { 19 | page.close(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/routes.erl: -------------------------------------------------------------------------------- 1 | -module(routes). 2 | 3 | -include_lib("n2o/include/n2o.hrl"). 4 | 5 | -export([init/2, finish/2]). 6 | 7 | finish(State, Ctx) -> {ok, State, Ctx}. 8 | 9 | init(State, #cx{req = Req} = Cx) -> 10 | #{path := Path} = Req, 11 | {ok, 12 | State, 13 | Cx#cx{path = Path, module = route_prefix(Path)}}. 14 | 15 | route_prefix(<<"/ws/", P/binary>>) -> route(P); 16 | route_prefix(<<"/", P/binary>>) -> route(P); 17 | route_prefix(P) -> route(P). 18 | 19 | % Don't use fancy routers, be like poor man's pattern mach. 20 | 21 | route(<<>>) -> login; 22 | route(<<"index", _/binary>>) -> index; % github static 23 | route(<<"login", _/binary>>) -> login; % github static 24 | route(<<"app/index", _/binary>>) -> index; % priv static 25 | route(<<"app/login", _/binary>>) -> login; % priv static 26 | route(_) -> login. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SAMPLE: WebSocket Application 2 | ============================= 3 | 4 | [](https://travis-ci.com/synrc/sample) 5 | 6 | NITRO/N2O pages that work on top of raw COWBOY/RANCH connections. 7 | 8 | Intro 9 | ----- 10 | 11 | SAMPLE application consist of two page modules `login` and `index`. 12 | It creates WebSocket connection to `n2o` server. 13 | The permanent example is accesible at https://sample.n2o.dev/index.htm. 14 | 15 | Index 16 | ----- 17 | 18 | Init event. 19 | Chat event. 20 | Client event. 21 | File Transfer event. 22 | 23 | Login 24 | ----- 25 | 26 | Channel init. 27 | Login event. 28 | 29 | Setup 30 | ----- 31 | 32 | To run review application just clone, build and run: 33 | 34 | ``` 35 | $ rebar3 get-deps 36 | $ rebar3 shell 37 | ``` 38 | 39 | Then open it in browser: 40 | 41 | ``` 42 | $ open http://localhost:8001/app/login.htm 43 | ``` 44 | 45 | Credits 46 | ------- 47 | * Brought with ❤ by N2O community 48 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {deps, [{cowboy, "2.8.0"}, 2 | {syn,"2.1.1"}, 3 | {nitro, "7.4.1"}, 4 | {n2o,"8.12.1"}, 5 | {kvs, "9.4.2"}]}. 6 | 7 | {relx, [{release, {sample, "0.7.0"},[sample]}, 8 | {dev_mode, false}, 9 | {sys_config, "sys.config"}, 10 | {vm_args, "vm.args"}, 11 | {include_erts, true}, 12 | {extended_start_script, true}]}. 13 | 14 | {shell,[{config, "sys.config"}, 15 | {apps, [sample]}]}. 16 | 17 | {dialyzer, [ 18 | {plt_apps, top_level_deps} 19 | ]}. 20 | 21 | {project_plugins, [rebar3_format]}. 22 | {format, [ 23 | {files, ["src/*.erl", "test/*.erl"]}, 24 | {formatter, otp_formatter}, 25 | {options, #{ line_length => 108, 26 | paper => 250, 27 | spaces_around_fields => false, 28 | inlining => all, 29 | inline_clause_bodies => true, 30 | inline_expressions => true, 31 | inline_qualified_function_composition => true, 32 | inline_simple_funs => true, 33 | inline_items => all, 34 | inline_fields => true, 35 | inline_attributes => true 36 | }}]}. 37 | -------------------------------------------------------------------------------- /login.htm: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 |Login page providees nickname introduction and room selection.
34 | 35 |This module may refer to: 47 | 48 | sample, 49 | index. 50 |
51 | 52 |Index page provides basic chat functionality and file upload feature.
34 | 35 |This module may refer to: 50 | 51 | sample, 52 | login. 53 |
54 | 55 |SAMPLE is a regular Erlang application.
34 | 35 |
43 | -module(sample).
44 | -behaviour(supervisor).
45 | -behaviour(application).
46 | -export([init/1, start/0, start/2, stop/1, main/1]).
47 |
48 | Note that suring start you should specify mauth module. 56 | Dafault implementation performs client topic autosubscription.
57 | 58 |
61 | start() -> start(normal,[]).
62 | start(_,_) -> supervisor:start_link({local,SAMPLE},SAMPLE,[]).
63 | stop(_) -> ok.
64 |
65 | SAMPLE application uses COWBOY as a static HTTP server 73 | and as a WebSocket server.
74 | 75 |
78 | start(_,_) -> cowboy:start_tls(http,n2o_cowboy:env(?MODULE),
79 | #{env=>#{dispatch=>n2o_cowboy:points() }}),
80 | supervisor:start_link({local,sample},sample,[]).
81 |
82 |
53 | $ git clone git://github.com/synrc/sample
54 | $ cd sample
55 | $ rebar3 shell
56 |
57 | $ open https://127.0.0.1:8001/app/index.htm
58 |
66 | {deps, [{cowboy, ".*", {git, "git://github.com/voxoz/cowboy2", []}},
67 | {nitro, ".*", {git, "git://github.com/synrc/nitro", []}},
68 | {n2o, ".*", {git, "git://github.com/synrc/n2o", []}},
69 | {kvs, ".*", {git, "git://github.com/synrc/kvs", []}},
70 | {syn, ".*", {git, "git://github.com/ostinelli/syn", {tag,"2.1.1"}}}
71 | ]}.
72 |
73 |
78 | Eshell V10.1.1 (abort with ^G)
79 | 1> application:which_applications().
80 | [{sample,"SAMPLE WebSocket application","0.7.0"},
81 | {nitro,"NITRO HTML5 DSL","3.10"},
82 | {syn,"A global Process Registry and Process Group manager.","2.1.1"},
83 | {cowboy,"Small, fast, modern HTTP server.","2.8.0"},
84 | {ranch,"Socket acceptor pool for TCP protocols.","1.7.1"},
85 | {ssl,"Erlang/OTP SSL application","10.1"},
86 | {public_key,"Public key infrastructure","1.9.1"},
87 | {kvs,"KVS Abstract Chain Database","7.9.1"},
88 | {cowlib,"Support library for manipulating Web protocols.","2.9.1"},
89 | {crypto,"CRYPTO","4.8"},
90 | {asn1,"The Erlang ASN1 compiler version 5.0.14","5.0.14"},
91 | {n2o,"N2O MQTT TCP WebSocket","7.10.0"},
92 | {inets,"INETS CXC 138 49","7.3"},
93 | {mnesia,"MNESIA CXC 138 12","4.18"},
94 | {syntax_tools,"Syntax tools","2.1.6"},
95 | {compiler,"ERTS CXC 138 10","7.2.6"},
96 | {stdlib,"ERTS CXC 138 10","3.13.2"},
97 | {kernel,"ERTS CXC 138 10","7.1"}]
98 |
99 |
107 |