├── .gitignore ├── Makefile ├── README.markdown ├── deps ├── erl_helpers │ ├── Makefile │ ├── README.markdown │ └── src │ │ ├── eh_datetime.erl │ │ ├── eh_http.erl │ │ ├── eh_list.erl │ │ ├── eh_maybe.erl │ │ ├── eh_supervisor.erl │ │ ├── eh_uuid.erl │ │ ├── erl_helpers.app.src │ │ ├── mochijson2.erl │ │ └── struct.erl ├── erlang_syslog │ ├── Makefile │ ├── README.md │ ├── ebin │ │ └── syslog.app │ ├── rebar │ ├── src │ │ ├── syslog.app.src │ │ ├── syslog.erl │ │ └── syslog_transform.erl │ └── test │ │ └── usage.escript ├── misultin │ ├── LICENSE.txt │ ├── Makefile │ ├── README.txt │ ├── ebin │ │ └── misultin.app │ ├── examples │ │ ├── application │ │ │ ├── misultin_app_example.app │ │ │ ├── misultin_app_example.erl │ │ │ ├── misultin_app_example_server.erl │ │ │ └── misultin_app_example_sup.erl │ │ ├── misultin_chunked.erl │ │ ├── misultin_comet_iframe.erl │ │ ├── misultin_comet_iframe_event.erl │ │ ├── misultin_comet_long_polling.erl │ │ ├── misultin_compress.erl │ │ ├── misultin_cookies_example.erl │ │ ├── misultin_echo.erl │ │ ├── misultin_file.erl │ │ ├── misultin_file_upload.erl │ │ ├── misultin_get_variable.erl │ │ ├── misultin_hello_world.erl │ │ ├── misultin_hello_world_nameless.erl │ │ ├── misultin_multiple_servers_custom_name.erl │ │ ├── misultin_rest.erl │ │ ├── misultin_ssl.erl │ │ ├── misultin_stream.erl │ │ ├── misultin_websocket_event_example.erl │ │ ├── misultin_websocket_event_example2.erl │ │ ├── misultin_websocket_example.erl │ │ └── misultin_websocket_example_ssl.erl │ ├── include │ │ └── misultin.hrl │ ├── make.bat │ ├── priv │ │ ├── README.txt │ │ ├── test_certificate.pem │ │ └── test_privkey.pem │ ├── src │ │ ├── misultin.app.src │ │ ├── misultin.erl │ │ ├── misultin_acceptor.erl │ │ ├── misultin_acceptors_sup.erl │ │ ├── misultin_cookies.erl │ │ ├── misultin_http.erl │ │ ├── misultin_req.erl │ │ ├── misultin_server.erl │ │ ├── misultin_socket.erl │ │ ├── misultin_utility.erl │ │ ├── misultin_websocket.erl │ │ └── misultin_ws.erl │ └── test │ │ ├── .gitignore │ │ └── misultin_SUITE.erl └── redo │ ├── Makefile │ ├── README.md │ ├── ebin │ └── redo.app │ ├── rebar │ ├── src │ ├── bench.erl │ ├── redo.app.src │ ├── redo.erl │ ├── redo_concurrency_test.erl │ ├── redo_redis_proto.erl │ └── redo_uri.erl │ └── test │ └── redo_tests.erl ├── doc ├── cameron_pres.graffle └── cameron_pres.pdf ├── ebin └── cameron.app ├── include └── cameron.hrl ├── priv ├── config │ ├── development.config │ ├── production.config │ └── test.config ├── processes │ ├── development.config │ ├── production.config │ └── test.config ├── sasl │ └── all.config └── www │ └── cameron.html ├── src ├── cameron.app ├── cameron.erl ├── cameron_app.erl ├── cameron_deps.erl ├── cameron_job_data.erl ├── cameron_job_runner.erl ├── cameron_job_scheduler.erl ├── cameron_process_catalog.erl ├── cameron_process_sup.erl ├── cameron_protocol.erl ├── cameron_sup.erl ├── cameron_web_api.erl └── cameron_web_server.erl └── test ├── foo_workflow ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README ├── api │ └── workflow.rb ├── bin │ └── run ├── config.ru ├── config │ └── init.rb └── test │ └── script │ └── request_start.sh └── script ├── benchmark_request.json ├── benchmark_request.sh ├── request_for_404.sh └── request_for_foo.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.beam 3 | dump.rdb 4 | **log 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME := cameron 2 | 3 | all: compile 4 | 5 | clear: 6 | @rm -f ebin/*.beam 7 | @rm -f erl_crash.dump 8 | 9 | compile: clear 10 | @cp src/$(APP_NAME).app ebin/ 11 | @erlc -pa ebin/ \ 12 | -pa deps/ \ 13 | -pa deps/erl_helpers/ebin/ \ 14 | -pa deps/misultin/ebin/ \ 15 | -pa deps/redo/ebin/ \ 16 | -pa deps/erlang_syslog/ebin/ \ 17 | -I include/ \ 18 | -I deps/misultin/include/ \ 19 | -o ebin/ \ 20 | src/*.erl 21 | 22 | compile_prod: clear 23 | @cp src/$(APP_NAME).app ebin/ 24 | @erlc -D use_syslog \ 25 | -pa ebin/ \ 26 | -pa deps/ \ 27 | -pa deps/erl_helpers/ebin/ \ 28 | -pa deps/misultin/ebin/ \ 29 | -pa deps/redo/ebin/ \ 30 | -pa deps/erlang_syslog/ebin/ \ 31 | -I include/ \ 32 | -I deps/misultin/include/ \ 33 | -o ebin/ \ 34 | src/*.erl 35 | 36 | compile_test: compile 37 | @erlc -pa ebin/ \ 38 | -pa deps/ \ 39 | -pa deps/erl_helpers/ebin/ \ 40 | -pa deps/misultin/ebin/ \ 41 | -pa deps/redo/ebin/ \ 42 | -pa deps/erlang_syslog/ebin/ \ 43 | -I include/ \ 44 | -I deps/misultin/include/ \ 45 | -o ebin/ \ 46 | test/*.erl 47 | 48 | compile_deps: 49 | (cd deps/erl_helpers && make) 50 | (cd deps/erlang_syslog && make) 51 | (cd deps/misultin && make) 52 | (cd deps/redo && make) 53 | 54 | run_dev: 55 | @ulimit -n 2560 56 | @erl +P 100000 \ 57 | +A100 \ 58 | -env ERL_MAX_PORTS 2560 \ 59 | -env ERL_FULLSWEEP_AFTER 5 \ 60 | -sname $(APP_NAME) \ 61 | -s $(APP_NAME) \ 62 | -pa ebin/ deps/**/ebin/ \ 63 | -boot start_sasl -config priv/sasl/all.config \ 64 | -config priv/config/development \ 65 | -processes priv/processes/development.config 66 | 67 | run_test: compile_test 68 | @erl -noshell -pa ebin/ deps/**/ebin/ \ 69 | -s $(APP_NAME) \ 70 | -run $(MODULE) test -run init stop \ 71 | -config priv/config/test \ 72 | -processes priv/processes/test.config 73 | 74 | run_all_tests: compile_test 75 | @for fullfilename in `find ./test -name "*_test.erl"`; do \ 76 | filename=$$(basename $$fullfilename); \ 77 | module=$${filename%%.*}; \ 78 | echo ; \ 79 | echo running: $$module; \ 80 | erl -noshell -pa ebin/ deps/**/ebin/ -s $(APP_NAME) \ 81 | -run $$module test -run init stop \ 82 | -config priv/config/test; \ 83 | done 84 | 85 | run_prod: 86 | @ulimit -n 65535 87 | @erl +P 100000 \ 88 | +A100 \ 89 | -env ERL_MAX_PORTS 65535 \ 90 | -env ERL_FULLSWEEP_AFTER 5 \ 91 | -sname $(APP_NAME) \ 92 | -s $(APP_NAME) \ 93 | -pa ebin/ deps/**/ebin/ \ 94 | -boot start_sasl -config priv/sasl/all.config \ 95 | -config priv/config/production \ 96 | -processes priv/processes/production.config 97 | -------------------------------------------------------------------------------- /deps/erl_helpers/Makefile: -------------------------------------------------------------------------------- 1 | all: compile 2 | 3 | compile: clear 4 | @mkdir -p ebin 5 | @erlc -o ebin src/*.erl 6 | 7 | clear: 8 | @rm -f ebin/*.beam 9 | 10 | ungit: 11 | @rm -rf .git 12 | @rm -f .gitignore 13 | -------------------------------------------------------------------------------- /deps/erl_helpers/README.markdown: -------------------------------------------------------------------------------- 1 | # erl_helpers 2 | 3 | It's a set of Erlang "helper" modules, nothing any more. 4 | 5 | ## Install 6 | 7 | Inside "deps" directory of your OTP application, for example: 8 | 9 | $ git clone git@github.com:leandrosilva/erl_helpers.git 10 | $ cd erl_helpers 11 | $ make ungit 12 | $ make 13 | 14 | And finally, put it in the code path of your application and enjoy it. 15 | 16 | ## 3rd party modules 17 | 18 | These modules __mochijason2__ and __struct__ weren't built by me. However I added three new functions in __struct__: from_json/1, from_json/2, to_json/1. 19 | 20 | ## Copyright 21 | 22 | There's no formal LICENSE. Use as you want! 23 | 24 | Copyright (c) 2011 Leandro Silva (CodeZone) . 25 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/eh_datetime.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2009 Leandro Silva. 3 | 4 | %% @doc Helper module to deal with date/time. 5 | 6 | -module(eh_datetime). 7 | -export([now/0]). 8 | 9 | %% @spec now() -> "MM-DD-YYYY hh:mm:ss" 10 | %% @doc Now as "MM-DD-YYYY hh:mm:ss". 11 | now() -> 12 | {{Year, Month, Day}, {Hour, Minute, Second}} = erlang:localtime(), 13 | 14 | lists:concat([eh_maybe:maybe_padding(Month), "-", eh_maybe:maybe_padding(Day), "-", eh_maybe:maybe_padding(Year), " ", 15 | eh_maybe:maybe_padding(Hour), ":", eh_maybe:maybe_padding(Minute), ":", eh_maybe:maybe_padding(Second)]). 16 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/eh_http.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2009 Leandro Silva. 3 | 4 | %% @doc Helper module for http operations with httpc and mochiweb_request modules. 5 | 6 | -module(eh_http). 7 | -author('Leandro Silva '). 8 | 9 | -export([http_post/2, http_get/1, http_put/2, http_delete/1]). 10 | -export([http_response/2, http_response/3, http_response/4]). 11 | 12 | %% External API 13 | 14 | %% 15 | %% HTTP Request functions 16 | %% 17 | %% These functions use httpc module. 18 | %% 19 | 20 | http_post(Uri, Body) -> 21 | http_request(post, Uri, Body). 22 | 23 | http_get(Uri) -> 24 | http_request(get, Uri). 25 | 26 | http_put(Uri, Body) -> 27 | http_request(put, Uri, Body). 28 | 29 | http_delete(Uri) -> 30 | http_request(delete, Uri). 31 | 32 | %% 33 | %% HTTP Response functions 34 | %% 35 | %% These functions use mochiweb_request module. 36 | %% 37 | 38 | http_response(Request, Status) -> 39 | Request:respond({Status, [], []}). 40 | 41 | http_response(Request, Status, Body) -> 42 | Request:respond({Status, [], Body}). 43 | 44 | http_response(Request, Status, Headers, Body) -> 45 | Request:respond({Status, Headers, Body}). 46 | 47 | %% Internal API 48 | 49 | http_request(HttpMethod, Uri) -> 50 | httpc:request(HttpMethod, {Uri, []}, [], []). 51 | 52 | http_request(HttpMethod, Uri, Body) -> 53 | Headers = [], 54 | ContentType = "application/json", 55 | HttpOptions = [], 56 | Options = [{body_format, string}], 57 | 58 | httpc:request(HttpMethod, {Uri, Headers, ContentType, Body}, HttpOptions, Options). 59 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/eh_list.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2009 Leandro Silva. 3 | 4 | %% @doc Helper module to list. 5 | 6 | -module(eh_list). 7 | -export([to_properties/1]). 8 | 9 | %% @spec to_properties(List) -> PropertyList 10 | %% @doc Turn a list in a property list. 11 | to_properties(Data) -> 12 | to_properties(Data, []). 13 | 14 | to_properties([K, V | Tail], []) -> 15 | to_properties(Tail, [{K, V}]); 16 | 17 | to_properties([K, V | Tail], Acc) -> 18 | to_properties(Tail, [{K, V} | Acc]); 19 | 20 | to_properties([], Acc) -> 21 | Acc. 22 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/eh_maybe.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2009 Leandro Silva. 3 | 4 | %% @doc Helper module to leverage kind of shy "maybe monad". 5 | 6 | -module(eh_maybe). 7 | -export([maybe_string/1, maybe_padding/1, maybe_ok/1, maybe_undefined/1]). 8 | -export([maybe_binary/1, maybe_integer/1]). 9 | 10 | % --- string -------------------------------------------------------------------------------------- 11 | 12 | maybe_string(undefined) -> 13 | undefined; 14 | 15 | maybe_string([]) -> 16 | []; 17 | 18 | maybe_string([Binary | _Tail] = BinaryList) when is_binary(Binary) -> 19 | maybe_string_(BinaryList, []); 20 | 21 | maybe_string(Single) when is_list(Single) -> 22 | Single; 23 | 24 | maybe_string(Single) when is_binary(Single) -> 25 | binary_to_list(Single); 26 | 27 | maybe_string(Single) when is_integer(Single) -> 28 | integer_to_list(Single); 29 | 30 | maybe_string(Single) when is_atom(Single) -> 31 | atom_to_list(Single). 32 | 33 | maybe_string_([], StringList) -> 34 | lists:reverse(StringList); 35 | 36 | maybe_string_([Binary | Tail], StringList) when is_binary(Binary) -> 37 | String = binary_to_list(Binary), 38 | maybe_string_(Tail, [String | StringList]). 39 | 40 | % --- padding ------------------------------------------------------------------------------------- 41 | 42 | maybe_padding(Number) when is_integer(Number) and (Number < 10) -> 43 | "0" ++ integer_to_list(Number); 44 | 45 | maybe_padding(Number) when is_integer(Number) and (Number > 60) -> 46 | List = integer_to_list(Number), 47 | maybe_padding(List); 48 | 49 | maybe_padding(Number) when is_integer(Number) and (Number > 9) and (Number < 61) -> 50 | integer_to_list(Number); 51 | 52 | maybe_padding(List) when is_list(List) -> 53 | case (string:len(List) =/= 4) and (string:len(List) < 6) of 54 | true -> 55 | NewList = "0" ++ List, 56 | maybe_padding(NewList); 57 | false -> 58 | List 59 | end. 60 | 61 | % --- ok ------------------------------------------------------------------------------------------ 62 | 63 | maybe_ok("OK") -> 64 | ok; 65 | 66 | maybe_ok(<<"OK">>) -> 67 | ok; 68 | 69 | maybe_ok("ok") -> 70 | ok; 71 | 72 | maybe_ok(<<"ok">>) -> 73 | ok; 74 | 75 | maybe_ok(Other) -> 76 | Other. 77 | 78 | % --- undefined ----------------------------------------------------------------------------------- 79 | 80 | maybe_undefined(undefined) -> 81 | undefined; 82 | 83 | maybe_undefined("undefined") -> 84 | undefined; 85 | 86 | maybe_undefined(<<"undefined">>) -> 87 | undefined; 88 | 89 | maybe_undefined(Other) -> 90 | Other. 91 | 92 | % --- binary -------------------------------------------------------------------------------------- 93 | 94 | maybe_binary(undefined) -> 95 | undefined; 96 | 97 | maybe_binary(Single) when is_binary(Single) -> 98 | Single; 99 | 100 | maybe_binary(Single) when is_integer(Single) -> 101 | maybe_binary(integer_to_list(Single)); 102 | 103 | maybe_binary(Single) when is_atom(Single) -> 104 | erlang:atom_to_binary(Single, utf8); 105 | 106 | maybe_binary(Single) when is_list(Single) -> 107 | list_to_binary(Single). 108 | 109 | % --- integer ------------------------------------------------------------------------------------- 110 | 111 | maybe_integer(undefined) -> 112 | undefined; 113 | 114 | maybe_integer(Single) when is_integer(Single) -> 115 | Single; 116 | 117 | maybe_integer(Single) when is_binary(Single) -> 118 | maybe_integer(binary_to_list(Single)); 119 | 120 | maybe_integer(Single) when is_list(Single) -> 121 | list_to_integer(Single). 122 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/eh_supervisor.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Utility functions to supervisor modules. 5 | 6 | -module(eh_supervisor). 7 | -author('Leandro Silva '). 8 | 9 | % public api 10 | -export([upgrade/1]). 11 | 12 | %% 13 | %% Public API ------------------------------------------------------------------------------------- 14 | %% 15 | 16 | %% @spec upgrade(Supervisor) -> ok 17 | %% @doc Remover and add processes if necessary. 18 | upgrade(Supervisor) -> 19 | {ok, {_, Specs}} = Supervisor:init([]), 20 | 21 | Old = sets:from_list([Name || {Name, _, _, _} <- supervisor:which_children(Supervisor)]), 22 | New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), 23 | 24 | % kill children processes that doesn't exists anymore 25 | % I mean, they exist in the Old spec but no longer in the New spec 26 | Kill = sets:subtract(Old, New), 27 | 28 | sets:fold(fun (Id, ok) -> 29 | supervisor:terminate_child(Supervisor, Id), 30 | supervisor:delete_child(Supervisor, Id), 31 | ok 32 | end, ok, Kill), 33 | 34 | [supervisor:start_child(Supervisor, Spec) || Spec <- Specs], 35 | ok. 36 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/eh_uuid.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2009 Leandro Silva. 3 | 4 | %% @doc Helper module to generate UUID. 5 | 6 | -module(eh_uuid). 7 | -export([new/0, test_unique/0]). 8 | 9 | new() -> 10 | bin_to_hexstr(crypto:rand_bytes(16)). 11 | 12 | test_unique() -> 13 | Curr = new(), 14 | test_unique(1, [Curr]). 15 | 16 | test_unique(Index, Prevs) -> 17 | Curr = new(), 18 | 19 | case lists:member(Curr, Prevs) of 20 | true -> 21 | io:format("[~w] Not unique: ~s~n", [Index, Curr]); 22 | false -> 23 | io:format("[~w] Unique: ~s~n", [Index, Curr]), 24 | test_unique(Index + 1, Prevs) 25 | end. 26 | 27 | %% 28 | %% Code below is from: 29 | %% 30 | %% http://necrobious.blogspot.com/2008/03/binary-to-hex-string-back-to-binary-in.html 31 | %% 32 | 33 | hex(N) when N < 10 -> 34 | $0+N; 35 | hex(N) when N >= 10, N < 16 -> 36 | $a+(N-10). 37 | 38 | % int(C) when $0 =< C, C =< $9 -> 39 | % C - $0; 40 | % int(C) when $A =< C, C =< $F -> 41 | % C - $A + 10; 42 | % int(C) when $a =< C, C =< $f -> 43 | % C - $a + 10. 44 | 45 | to_hex(N) when N < 256 -> 46 | [hex(N div 16), hex(N rem 16)]. 47 | 48 | list_to_hexstr([]) -> 49 | []; 50 | list_to_hexstr([H|T]) -> 51 | to_hex(H) ++ list_to_hexstr(T). 52 | 53 | bin_to_hexstr(Bin) -> 54 | list_to_hexstr(binary_to_list(Bin)). 55 | 56 | % hexstr_to_bin(S) -> 57 | % list_to_binary(hexstr_to_list(S)). 58 | 59 | % hexstr_to_list([X,Y|T]) -> 60 | % [int(X)*16 + int(Y) | hexstr_to_list(T)]; 61 | % hexstr_to_list([]) -> 62 | % []. 63 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/erl_helpers.app.src: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | {application, erl_helpers, 5 | [{description, "Erlang Helper Modules"}, 6 | {vsn, "0.0.1"}, 7 | {modules, [ 8 | eh_datetime, 9 | eh_http, 10 | eh_list, 11 | eh_maybe, 12 | eh_supervisor, 13 | eh_uuid, 14 | mochijson2, 15 | struct 16 | ]}, 17 | {registered, []}, 18 | {mod, {erl_helpers_app, []}}, 19 | {env, []}, 20 | {applications, [kernel, stdlib]}]}. 21 | -------------------------------------------------------------------------------- /deps/erl_helpers/src/struct.erl: -------------------------------------------------------------------------------- 1 | %% @doc Utilities for working with mochijson2 struct. 2 | %% 3 | %% COPYRIGHT - stickyNotes sample application by beepbeep. 4 | 5 | %% struct example : 6 | %% 7 | %% S = {struct, [ 8 | %% {<<"name">>, <<"Foo">>}, 9 | %% {<<"activity">>, {struct, [ 10 | %% {<<"name">>, <<"Basketball">>} 11 | %% {<<"duration">>, 60}, 12 | %% {<<"intensity">>, 10}]}}]} 13 | %% 14 | %% get_value(<<"name">>, S) 15 | %% get_value({<<"activity">>, <<"duration">>}, S) 16 | %% set_value(<<"lastName">>, <<"Bar">>, S) 17 | %% set_value({<<"activity">>, <<"duration">>}, 75, S) 18 | %% delete(<<"name">>, S) 19 | %% delete({<<"activity">>, <<"duration">>}, S) 20 | 21 | -module(struct). 22 | 23 | -export([extend/2, withdraw/2, get_value/2, set_value/3, delete/2]). 24 | 25 | %% by codezone 26 | -export([from_json/1, from_json/2, to_json/1]). 27 | -export([new/2]). 28 | -export([get_value/3]). 29 | 30 | %% @type key() = binary() 31 | %% @type value() = [integer() | float() | atom() | tuple() | binary() | string() | list()] 32 | %% @type struct() = tuple() 33 | %% @type path() = tuple() 34 | %% @type input() = [integer() | list()] 35 | 36 | %% @spec extend(struct(), list()) -> struct() 37 | %% @doc Extend a json struct with one or more json struct (add new leaves and modify the existing ones). 38 | extend(S1, []) -> 39 | S1; 40 | 41 | extend(S1, [S|T]) -> 42 | NewS = extend(S1, S), 43 | extend(NewS, T); 44 | 45 | extend(S1, S2) -> 46 | {struct, L1} = S1, 47 | {struct, L2} = S2, 48 | ext(L1, L2, []). 49 | 50 | ext(L1, [], Result) -> 51 | {struct, lists:append(Result,L1)}; 52 | 53 | ext(L1, [{K, {struct, ChildL2}} | T], Result) -> 54 | case proplists:get_value(K, L1) of 55 | {struct, ChildL1} -> 56 | NewL1 = proplists:delete(K, L1), 57 | ext(NewL1, T, [{K, extend({struct, ChildL1}, {struct, ChildL2})} | Result]); 58 | _ -> 59 | NewL1 = proplists:delete(K, L1), 60 | ext(NewL1, T, [{K, {struct, ChildL2}} | Result]) 61 | end; 62 | 63 | ext(L1, [{K, V} | T], Result) -> 64 | NewL1 = proplists:delete(K, L1), 65 | ext(NewL1, T, [{K,V} | Result]). 66 | 67 | 68 | %% @spec withdraw(struct(), structlist()) -> struct() 69 | %% @doc withdraw acts in the exact opposite way of extend (note : you just need to specify the keys). 70 | withdraw(S1, []) -> 71 | S1; 72 | 73 | withdraw(S1, [S|T]) -> 74 | NewS = withdraw(S1, S), 75 | withdraw(NewS, T); 76 | 77 | withdraw(S1, S2) -> 78 | {struct, L1} = S1, 79 | {struct, L2} = S2, 80 | wdr(L1, L2, []). 81 | 82 | wdr([], _L2, Result) -> 83 | {struct, Result}; 84 | 85 | wdr([{K, {struct, ChildL1}} | T], L2, Result) -> 86 | case proplists:get_value(K, L2) of 87 | {struct, ChildL2} -> 88 | wdr(T, L2, [{K, withdraw({struct, ChildL1}, {struct, ChildL2})} | Result]); 89 | _ -> 90 | case proplists:is_defined(K, L2) of 91 | false -> 92 | wdr(T, L2, [{K, {struct, ChildL1}} | Result]); 93 | true -> 94 | wdr(T, L2, Result) 95 | end 96 | end; 97 | 98 | wdr([{K, V} | T], L2, Result) -> 99 | case proplists:is_defined(K, L2) of 100 | false -> 101 | wdr(T, L2, [ {K, V} | Result]); 102 | true -> 103 | wdr(T, L2, Result) 104 | end. 105 | 106 | 107 | %% @spec get_value(path() | key(), struct()) -> value() 108 | get_value(Path, Struct) when is_tuple(Path) -> 109 | L = tuple_to_list(Path), 110 | get_val(L, Struct); 111 | 112 | get_value(Key, Struct) -> 113 | {struct, L} = Struct, 114 | proplists:get_value(Key, L). 115 | 116 | get_val(_, undefined) -> 117 | undefined; 118 | 119 | get_val([Key], Struct) -> 120 | get_value(Key, Struct); 121 | 122 | get_val([Key | T], Struct) -> 123 | NewStruct = get_value(Key, Struct), 124 | get_val(T, NewStruct). 125 | 126 | 127 | %% @spec set_value(path() | key(), value(), struct()) -> struct() 128 | set_value(Path, Value, Struct) when is_tuple(Path) -> 129 | [H | T] = lists:reverse(tuple_to_list(Path)), 130 | set_val(T, Struct, {struct, [{H, Value}]}); 131 | 132 | set_value(Key, Value, Struct) -> 133 | extend(Struct, {struct, [{Key, Value}]}). 134 | 135 | set_val([], Struct, Result) -> 136 | extend(Struct, Result); 137 | 138 | set_val([Key | T], Struct, Result) -> 139 | set_val(T, Struct, {struct, [{Key, Result}]}). 140 | 141 | 142 | %% @spec delete(path() | key(), struct()) -> value() 143 | delete(Path, Struct) when is_tuple(Path) -> 144 | [H | T] = lists:reverse(tuple_to_list(Path)), 145 | del(T, Struct, {struct, [{H}]}); 146 | 147 | delete(Key, Struct) -> 148 | {struct, L} = Struct, 149 | {struct, proplists:delete(Key, L)}. 150 | 151 | del([], Struct, Result) -> 152 | withdraw(Struct, Result); 153 | 154 | del([Key | T ], Struct, Result) -> 155 | del(T, Struct, {struct, [{Key, Result}]}). 156 | 157 | 158 | %% @spec from_json(input()) -> struct() 159 | %% @doc by codezone, using mochijson2:decode/1 160 | from_json(JsonInput) -> 161 | mochijson2:decode(JsonInput). 162 | 163 | 164 | %% @spec from_json(key(), input()) -> struct() 165 | %% @doc by codezone, using mochijson2:decode/1 166 | from_json(Key, Input) -> 167 | JsonInput = proplists:get_value(Key, Input), 168 | mochijson2:decode(JsonInput). 169 | 170 | 171 | %% @spec to_json(struct()) -> list() 172 | %% @doc by codezone, using mochijson2:encode/1 173 | to_json(Struct) -> 174 | mochijson2:encode(Struct). 175 | 176 | 177 | %% @spec new(key(), value()) -> struct() 178 | %% @doc by codezone 179 | new(Key, Value) when not is_binary(Value) -> 180 | {struct, [{list_to_binary(Key), list_to_binary(Value)}]}; 181 | 182 | new(Key, Value) -> 183 | {struct, [{list_to_binary(Key), Value}]}. 184 | 185 | 186 | %% @spec get_value(as_json, key(), struct()) -> list() 187 | %% @doc by codezone 188 | get_value(Key, Struct, {format, json}) -> 189 | case get_value(Key, Struct) of 190 | undefined -> undefined; 191 | Value -> to_json(Value) 192 | end; 193 | 194 | get_value(Key, Struct, {format, atom}) -> 195 | case get_value(Key, Struct) of 196 | undefined -> undefined; 197 | Value -> binary_to_existing_atom(Value, unicode) 198 | end; 199 | 200 | get_value(Key, Struct, {format, list}) -> 201 | case get_value(Key, Struct) of 202 | undefined -> undefined; 203 | Value -> binary_to_list(Value) 204 | end. 205 | -------------------------------------------------------------------------------- /deps/erlang_syslog/Makefile: -------------------------------------------------------------------------------- 1 | all: compile 2 | 3 | compile: 4 | ./rebar compile 5 | clean: 6 | ./rebar clean 7 | -------------------------------------------------------------------------------- /deps/erlang_syslog/README.md: -------------------------------------------------------------------------------- 1 | ### Enable UDP 2 | 3 | Ensure that syslogd has udp sockets enabled: 4 | [OS X](http://stackoverflow.com/questions/1185554/how-to-enable-syslogd-to-receive-udp-logs-from-routers-in-osx) 5 | 6 | ### Build 7 | 8 | make 9 | 10 | ### Log 11 | 12 | 0> syslog:start_link(name, tag, "localhost", 514, local0). 13 | ok 14 | 2> syslog:send(name, "test"). 15 | ok 16 | 3> syslog:notice(name, "other test"). 17 | ok 18 | 19 | ### Logged 20 | 21 | $ syslog 22 | ... 23 | Tue Mar 16 18:36:48 192.168.1.101 tag[4294967295] : test 24 | Tue Mar 16 18:36:57 192.168.1.101 tag[4294967295] : other test 25 | -------------------------------------------------------------------------------- /deps/erlang_syslog/ebin/syslog.app: -------------------------------------------------------------------------------- 1 | {application,syslog, 2 | [{description,"Erlang syslog client"}, 3 | {vsn,"0.1"}, 4 | {applications,[kernel,stdlib]}, 5 | {registered,[]}, 6 | {modules,[syslog,syslog_transform]}]}. 7 | -------------------------------------------------------------------------------- /deps/erlang_syslog/rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leandrosilva/cameron/34051395b620d2c3cb2cb63c854e65234786a176/deps/erlang_syslog/rebar -------------------------------------------------------------------------------- /deps/erlang_syslog/src/syslog.app.src: -------------------------------------------------------------------------------- 1 | {application, syslog, [ 2 | {description, "Erlang syslog client"}, 3 | {vsn, "0.1"}, 4 | {applications, [kernel, stdlib]}, 5 | {registered, []} 6 | ]}. 7 | -------------------------------------------------------------------------------- /deps/erlang_syslog/src/syslog_transform.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2010 Jacob Vorreuter 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | -module(syslog_transform). 24 | -export([parse_transform/2]). 25 | 26 | parse_transform(Forms, _Options) -> 27 | expand_forms(Forms). 28 | 29 | expand_forms({attribute,_,module,Module}=Attr) -> 30 | put(module, Module), 31 | Attr; 32 | 33 | expand_forms(Forms) when is_list(Forms) -> 34 | [expand_forms(Form) || Form <- Forms]; 35 | 36 | expand_forms({call,L,{atom,_,log},Args}) -> 37 | {call,L,{atom,L,log},[{atom,L,get(module)}|Args]}; 38 | 39 | expand_forms(Form) when is_tuple(Form) -> 40 | Expanded = [expand_forms(F) || F <- tuple_to_list(Form)], 41 | list_to_tuple(Expanded); 42 | 43 | expand_forms(Form) -> 44 | Form. -------------------------------------------------------------------------------- /deps/erlang_syslog/test/usage.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %% -*- erlang -*- 3 | %%! -pa ./ebin 4 | 5 | %%==================================================================== 6 | %% An usage test as simple and direct as possible. Enjoy. 7 | %%==================================================================== 8 | 9 | main(_) -> 10 | io:format("~n-------------------------------------------~n"), 11 | io:format("Usage test :: Take a look at Syslog please."), 12 | io:format("~n-------------------------------------------~n"), 13 | 14 | test_simple(), 15 | test_advanced(). 16 | 17 | test_simple() -> 18 | syslog:start_link(), 19 | 20 | syslog:send("(simple) info message"), 21 | 22 | ok. 23 | 24 | test_advanced() -> 25 | syslog:start_link(my_syslog, my_app, "localhost", 514, local0), 26 | 27 | syslog:send(my_syslog, "(advanced - send) debug message", [{level, debug}]), 28 | syslog:send(my_syslog, "(advanced - send) info message", [{level, info}]), 29 | syslog:send(my_syslog, "(advanced - send) notice message", [{level, notice}]), 30 | syslog:send(my_syslog, "(advanced - send) warning message", [{level, warning}]), 31 | syslog:send(my_syslog, "(advanced - send) error message", [{level, error}]), 32 | syslog:send(my_syslog, "(advanced - send) critical message", [{level, critical}]), 33 | syslog:send(my_syslog, "(advanced - send) alert message", [{level, alert}]), 34 | syslog:send(my_syslog, "(advanced - send) emergency message", [{level, emergency}]), 35 | 36 | syslog:debug(my_syslog, "(advanced - debug) debug message"), 37 | syslog:info(my_syslog, "(advanced - info) info message"), 38 | syslog:notice(my_syslog, "(advanced - notice) notice message"), 39 | syslog:warning(my_syslog, "(advanced - warning) warning message"), 40 | syslog:error(my_syslog, "(advanced - error) error message"), 41 | syslog:critical(my_syslog, "(advanced - critical) critical message"), 42 | syslog:alert(my_syslog, "(advanced - alert) alert message"), 43 | syslog:emergency(my_syslog, "(advanced - emergency) emergency message"), 44 | 45 | ok. 46 | -------------------------------------------------------------------------------- /deps/misultin/LICENSE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================================================== 2 | MISULTIN - An Erlang library for building fast lightweight HTTP servers. 3 | 4 | 5 | >-|-|-(°> 6 | 7 | Copyright (C) 2011, Roberto Ostinelli , Joe Armstrong, Sean Hinde, 8 | Bob Ippolito for Mochi Media, Inc. 9 | 10 | All rights reserved. 11 | 12 | Code portions from Joe Armstrong have been originally taken under MIT license at the address: 13 | 14 | 15 | Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 16 | 17 | 18 | Code portions from Bob Ippolito have been originally taken under MIT license from MOCHIWEB: 19 | 20 | ========================================================================================================== 21 | 22 | BSD License 23 | 24 | Redistribution and use in source and binary forms, with or without modification, are permitted provided 25 | that the following conditions are met: 26 | 27 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 28 | following disclaimer. 29 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 30 | the following disclaimer in the documentation and/or other materials provided with the distribution. 31 | * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 32 | products derived from this software without specific prior written permission. 33 | 34 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 35 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 36 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 37 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 38 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 40 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 | POSSIBILITY OF SUCH DAMAGE. 42 | -------------------------------------------------------------------------------- /deps/misultin/Makefile: -------------------------------------------------------------------------------- 1 | EBIN_DIR := ebin 2 | SRC_DIR := src 3 | EXAMPLES_DIR := examples 4 | INCLUDE_DIR := include 5 | ERLC := erlc 6 | ERLC_FLAGS := -W -I $(INCLUDE_DIR) -o $(EBIN_DIR) 7 | 8 | all: 9 | @mkdir -p $(EBIN_DIR) 10 | $(ERLC) $(ERLC_FLAGS) $(SRC_DIR)/*.erl 11 | @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app 12 | 13 | clean: 14 | @rm -rf $(EBIN_DIR)/* 15 | @rm -f erl_crash.dump 16 | 17 | debug: 18 | @mkdir -p $(EBIN_DIR) 19 | $(ERLC) -D log_debug $(ERLC_FLAGS) $(SRC_DIR)/*.erl 20 | @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app 21 | 22 | dialyzer: 23 | @mkdir -p $(EBIN_DIR) 24 | $(ERLC) +debug_info $(ERLC_FLAGS) $(SRC_DIR)/*.erl 25 | @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app 26 | 27 | example: 28 | @mkdir -p $(EBIN_DIR) 29 | $(ERLC) $(ERLC_FLAGS) $(SRC_DIR)/*.erl 30 | @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app 31 | $(ERLC) $(ERLC_FLAGS) $(EXAMPLES_DIR)/*.erl 32 | 33 | tests: 34 | @mkdir -p $(PWD)/test/results 35 | @ct_run -suite $(PWD)/misultin_SUITE -pa $(PWD)/ebin -logdir $(PWD)/test/results 36 | -------------------------------------------------------------------------------- /deps/misultin/ebin/misultin.app: -------------------------------------------------------------------------------- 1 | {application, misultin, 2 | [ 3 | {description, "Lightweight HTTP(s) and Websockets Server Library"}, 4 | {vsn, "0.8"}, 5 | {modules, [ 6 | misultin, 7 | misultin_acceptor, 8 | misultin_acceptors_sup, 9 | misultin_http, 10 | misultin_req, 11 | misultin_server, 12 | misultin_socket, 13 | misultin_utility, 14 | misultin_websocket, 15 | misultin_ws 16 | ]}, 17 | {registered, []}, 18 | {env, []}, 19 | {applications, [kernel, stdlib]} 20 | ]}. 21 | -------------------------------------------------------------------------------- /deps/misultin/examples/application/misultin_app_example.app: -------------------------------------------------------------------------------- 1 | {application, misultin_app_example, 2 | [ 3 | {description, "Misultin Application Example"}, 4 | {vsn, "0.1"}, 5 | {modules, [ 6 | misultin_app_example, 7 | misultin_app_example_sup, 8 | misultin_app_example_server, 9 | misultin, 10 | misultin_acceptor, 11 | misultin_acceptors_sup, 12 | misultin_http, 13 | misultin_req, 14 | misultin_server, 15 | misultin_socket, 16 | misultin_utility, 17 | misultin_websocket, 18 | misultin_ws 19 | ]}, 20 | {registered, []}, 21 | {mod, {misultin_app_example, []}}, 22 | {env, []}, 23 | {applications, [kernel, stdlib]} 24 | ]}. 25 | -------------------------------------------------------------------------------- /deps/misultin/examples/application/misultin_app_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Application based on Misultin - MAIN APPLICATION 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_app_example). 32 | -behaviour(application). 33 | 34 | % application callbacks 35 | -export([start/2, stop/1]). 36 | 37 | % ============================ \/ API ====================================================================== 38 | 39 | % ============================ /\ API ====================================================================== 40 | 41 | 42 | % ============================ \/ APPLICATION CALLBACKS ==================================================== 43 | 44 | % ---------------------------------------------------------------------------------------------------------- 45 | % Function: -> {ok, Pid} | {ok, Pid, State} | {error, Reason} 46 | % Description: Starts the application 47 | % ---------------------------------------------------------------------------------------------------------- 48 | start(_Type, _StartArgs) -> 49 | % start main application supervisor 50 | Options = [ 51 | {port, 8080}, 52 | {loop, fun(Req) -> misultin_app_example_server:handle_http(Req) end} 53 | ], 54 | misultin_app_example_sup:start_link(Options). 55 | 56 | % ---------------------------------------------------------------------------------------------------------- 57 | % Function: stop(State) -> void() 58 | % Description: Stops the application 59 | % ---------------------------------------------------------------------------------------------------------- 60 | stop(_State) -> 61 | ok. 62 | 63 | % ============================ /\ APPLICATION CALLBACKS ==================================================== 64 | 65 | 66 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 67 | 68 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 69 | 70 | 71 | -------------------------------------------------------------------------------- /deps/misultin/examples/application/misultin_app_example_server.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Application based on Misultin - MAIN APPLICATION GEN_SERVER 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_app_example_server). 31 | -behaviour(gen_server). 32 | 33 | % gen_server callbacks 34 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 35 | 36 | % API 37 | -export([start_link/0, handle_http/1]). 38 | 39 | 40 | % ============================ \/ API ====================================================================== 41 | 42 | % Function: {ok,Pid} | ignore | {error, Error} 43 | % Description: Starts the server. 44 | start_link() -> 45 | gen_server:start_link(?MODULE, [], []). 46 | 47 | % ============================ /\ API ====================================================================== 48 | 49 | 50 | % ============================ \/ GEN_SERVER CALLBACKS ===================================================== 51 | 52 | % ---------------------------------------------------------------------------------------------------------- 53 | % Function: -> {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason} 54 | % Description: Initiates the server. 55 | % ---------------------------------------------------------------------------------------------------------- 56 | init([]) -> 57 | {ok, {}}. 58 | 59 | % ---------------------------------------------------------------------------------------------------------- 60 | % Function: handle_call(Request, From, State) -> {reply, Reply, State} | {reply, Reply, State, Timeout} | 61 | % {noreply, State} | {noreply, State, Timeout} | 62 | % {stop, Reason, Reply, State} | {stop, Reason, State} 63 | % Description: Handling call messages. 64 | % ---------------------------------------------------------------------------------------------------------- 65 | 66 | % handle_call generic fallback 67 | handle_call(_Request, _From, State) -> 68 | {reply, undefined, State}. 69 | 70 | % ---------------------------------------------------------------------------------------------------------- 71 | % Function: handle_cast(Msg, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 72 | % Description: Handling cast messages. 73 | % ---------------------------------------------------------------------------------------------------------- 74 | 75 | % handle_cast generic fallback (ignore) 76 | handle_cast(_Msg, State) -> 77 | {noreply, State}. 78 | 79 | % ---------------------------------------------------------------------------------------------------------- 80 | % Function: handle_info(Info, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 81 | % Description: Handling all non call/cast messages. 82 | % ---------------------------------------------------------------------------------------------------------- 83 | 84 | % handle_info generic fallback (ignore) 85 | handle_info(_Info, State) -> 86 | {noreply, State}. 87 | 88 | % ---------------------------------------------------------------------------------------------------------- 89 | % Function: terminate(Reason, State) -> void() 90 | % Description: This function is called by a gen_server when it is about to terminate. When it returns, 91 | % the gen_server terminates with Reason. The return value is ignored. 92 | % ---------------------------------------------------------------------------------------------------------- 93 | terminate(_Reason, _State) -> 94 | terminated. 95 | 96 | % ---------------------------------------------------------------------------------------------------------- 97 | % Func: code_change(OldVsn, State, Extra) -> {ok, NewState} 98 | % Description: Convert process state when code is changed. 99 | % ---------------------------------------------------------------------------------------------------------- 100 | code_change(_OldVsn, State, _Extra) -> 101 | {ok, State}. 102 | 103 | % ============================ /\ GEN_SERVER CALLBACKS ===================================================== 104 | 105 | 106 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 107 | 108 | % ---------------------------- \/ misultin requests -------------------------------------------------------- 109 | 110 | handle_http(Req) -> 111 | % get params depending on method 112 | Method = Req:get(method), 113 | case Method of 114 | 'GET' -> 115 | Args = Req:parse_qs(); 116 | 'POST' -> 117 | Args = Req:parse_post() 118 | end, 119 | % build an XML with all parameters and values 120 | BuildXml = fun({Param, Value}, Acc) -> 121 | [lists:flatten(io_lib:format("~s~s", [Param, Value]))|Acc] 122 | end, 123 | Xml = lists:flatten(lists:reverse(lists:foldl(BuildXml, [], Args))), 124 | % output 125 | Req:ok([{"Content-Type", "text/xml"}], "~s~s", [Method, Xml]). 126 | 127 | % ---------------------------- /\ misultin requests -------------------------------------------------------- 128 | 129 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 130 | 131 | -------------------------------------------------------------------------------- /deps/misultin/examples/application/misultin_app_example_sup.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Application based on Misultin - MAIN APPLICATION SUPERVISOR 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_app_example_sup). 32 | -behaviour(supervisor). 33 | 34 | % API 35 | -export([start_link/1]). 36 | 37 | % supervisor callbacks 38 | -export([init/1]). 39 | 40 | % ============================ \/ API ====================================================================== 41 | 42 | % ---------------------------------------------------------------------------------------------------------- 43 | % Function: start_link() -> {ok,Pid} | ignore | {error,Error} 44 | % Description: Starts the supervisor 45 | % ---------------------------------------------------------------------------------------------------------- 46 | start_link(Options) -> 47 | supervisor:start_link(?MODULE, [Options]). 48 | 49 | % ============================ /\ API ====================================================================== 50 | 51 | 52 | % ============================ \/ SUPERVISOR CALLBACKS ===================================================== 53 | 54 | % ---------------------------------------------------------------------------------------------------------- 55 | % Function: -> {ok, {SupFlags, [ChildSpec]}} | ignore | {error, Reason} 56 | % Description: Starts the supervisor 57 | % ---------------------------------------------------------------------------------------------------------- 58 | init([Options]) -> 59 | % misultin specs 60 | MisultinSpecs = {misultin, 61 | {misultin, start_link, [Options]}, 62 | permanent, infinity, supervisor, [misultin] 63 | }, 64 | % application gen server specs 65 | ServerSpecs = {misultin_app_example_server, 66 | {misultin_app_example_server, start_link, []}, 67 | permanent, 60000, worker, [misultin_app_example_server] 68 | }, 69 | % spawn 70 | {ok, {{one_for_all, 5, 30}, [MisultinSpecs, ServerSpecs]}}. 71 | 72 | % ============================ /\ SUPERVISOR CALLBACKS ===================================================== 73 | 74 | 75 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 76 | 77 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 78 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_chunked.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Chunk. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_chunked). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % send headers 44 | Req:chunk(head, [{"Content-Type", "text/html"}]), 45 | % send chunk 46 | Req:chunk("Sending CHUNK 1
"), 47 | timer:sleep(2000), 48 | % send chunk 49 | Req:chunk("Sending CHUNK 2
"), 50 | timer:sleep(2000), 51 | % send chunk 52 | Req:chunk("Sending CHUNK 3
"), 53 | % close 54 | Req:chunk(done). 55 | 56 | 57 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_comet_iframe.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Comet - iFrame Method 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_comet_iframe). 32 | -export([start/1, stop/0]). 33 | 34 | % start misultin http server 35 | start(Port) -> 36 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}]). 37 | 38 | % stop misultin 39 | stop() -> 40 | misultin:stop(). 41 | 42 | handle_http(Req, Port) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req, Port). 45 | 46 | % handle a GET on / 47 | handle('GET', [], Req, Port) -> 48 | % output 49 | Req:ok([{"Content-Type", "text/html"}], 50 | [" 51 | 52 | 53 | Comet demo 54 | 55 | 56 | 57 | 58 |
The server time will be shown here in 5 seconds.
59 | 117 | 118 | 119 | "]); 120 | 121 | % handle a GET on /comet 122 | handle('GET', ["comet"], Req, _Port) -> 123 | % set comet true, this will allow trapping client closing the connection 124 | Req:options([{comet, true}]), 125 | % send headers 126 | Req:stream(head, [{"Content-Type", "text/html"}, {"Cache-Control", "no-cache, must-revalidate"}, {"Expires", "Mon, 26 Jul 1997 05:00:00 GMT"}]), 127 | % start the page 128 | Req:stream(" 129 | 130 | 131 | Comet php backend 132 | 133 | 134 | 135 | 148 | "), 149 | % enter notification loop 150 | notify(Req); 151 | 152 | % handle the 404 page not found 153 | handle(_, _, Req, _Port) -> 154 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 155 | 156 | % notification loop 157 | notify(Req) -> 158 | % send a message every 5 seconds 159 | timer:sleep(5000), 160 | % get server local time 161 | {_Date, {Hour, Minutes, Seconds}} = erlang:localtime(), 162 | % send 163 | Req:stream([" 164 | 167 | "]), 168 | % loop 169 | notify(Req). 170 | 171 | 172 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_comet_iframe_event.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Comet - iFrame Method 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Example taken from 7 | % 8 | % All rights reserved. 9 | % 10 | % BSD License 11 | % 12 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | % that the following conditions are met: 14 | % 15 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | % following disclaimer. 17 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | % products derived from this software without specific prior written permission. 21 | % 22 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | % POSSIBILITY OF SUCH DAMAGE. 30 | % ========================================================================================================== 31 | -module(misultin_comet_iframe_event). 32 | -export([start/1, stop/0]). 33 | 34 | % start misultin http server 35 | start(Port) -> 36 | misultin:start_link([{port, Port}, {autoexit, false}, {loop, fun(Req) -> handle_http(Req, Port) end}]). 37 | 38 | % stop misultin 39 | stop() -> 40 | misultin:stop(). 41 | 42 | handle_http(Req, Port) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req, Port). 45 | 46 | % handle a GET on / 47 | handle('GET', [], Req, Port) -> 48 | % output 49 | Req:ok([{"Content-Type", "text/html"}], 50 | [" 51 | 52 | 53 | Comet demo 54 | 55 | 56 | 57 | 58 |
The server time will be shown here in 5 seconds.
59 | 117 | 118 | 119 | "]); 120 | 121 | % handle a GET on /comet 122 | handle('GET', ["comet"], Req, _Port) -> 123 | % set comet true, this will allow trapping client closing the connection 124 | Req:options([{comet, true}]), 125 | % send headers 126 | Req:stream(head, [{"Content-Type", "text/html"}, {"Cache-Control", "no-cache, must-revalidate"}, {"Expires", "Mon, 26 Jul 1997 05:00:00 GMT"}]), 127 | % start the page 128 | Req:stream(" 129 | 130 | 131 | Comet php backend 132 | 133 | 134 | 135 | 148 | "), 149 | % enter notification loop 150 | notify(Req); 151 | 152 | % handle the 404 page not found 153 | handle(_, _, Req, _Port) -> 154 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 155 | 156 | % notification loop 157 | notify(Req) -> 158 | % send 159 | receive 160 | closed -> 161 | % IMPORTANT: since we specified the {autoexit, false} option, we need to manually ensure that this process exits 162 | % [otherwise it will become a zombie] 163 | io:format("The client closed the connection, exiting process!~n"); 164 | _Ignore -> 165 | notify(Req) 166 | after 5000 -> 167 | % get server local time 168 | {_Date, {Hour, Minutes, Seconds}} = erlang:localtime(), 169 | % send 170 | Req:stream([" 171 | 174 | "]), 175 | % loop 176 | notify(Req) 177 | end. 178 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_comet_long_polling.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Comet - Long Polling Method 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_comet_long_polling). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | handle_http(Req, Port) -> 42 | % dispatch to rest 43 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req, Port). 44 | 45 | % handle a GET on / 46 | handle('GET', [], Req, Port) -> 47 | % output 48 | Req:ok([{"Content-Type", "text/html"}], 49 | [" 50 | 51 | 52 | 53 | 67 | 68 | 69 | Long Polling example, please wait 10 seconds for incoming data.

70 |
71 | 72 | 73 | "]); 74 | 75 | % handle a GET on /comet 76 | handle('GET', ["comet"], Req, _Port) -> 77 | % set comet true, this will allow trapping client closing the connection 78 | Req:options([{comet, true}]), 79 | % simulate a long polling with timer 80 | timer:sleep(10000), 81 | Req:ok([{"Content-Type", "text/plain"}], ["Message received from Long Polling, next message in 10 seconds."]); 82 | 83 | % handle the 404 page not found 84 | handle(_, _, Req, _Port) -> 85 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 86 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_compress.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World Compressed. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_compress). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {compress, true}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:ok("Hello World Compressed."). 44 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_cookies_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Show how to set/retrieve cookies. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_cookies_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get cookies 44 | Cookies = Req:get_cookies(), 45 | case Req:get_cookie_value("misultin_test_cookie", Cookies) of 46 | undefined -> 47 | % no cookies preexists, create one that will expire in 365 days 48 | Req:ok([Req:set_cookie("misultin_test_cookie", "value of the test cookie", [{max_age, 365*24*3600}])], "A cookie has been set. Refresh the browser to see it."); 49 | CookieVal -> 50 | Req:ok([Req:delete_cookie("misultin_test_cookie")], ["The set cookie value was set to \"", CookieVal,"\", and has now been removed. Refresh the browser to see this."]) 51 | end. 52 | 53 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_echo.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Echoes inputted GET variables into an XML. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_echo). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get params depending on method 44 | Method = Req:get(method), 45 | Args = case Method of 46 | 'GET' -> 47 | Req:parse_qs(); 48 | 'POST' -> 49 | Req:parse_post() 50 | end, 51 | % build an XML with all parameters and values 52 | BuildXml = fun({Param, Value}, Acc) -> 53 | [lists:flatten(io_lib:format("~s~s", [Param, Value]))|Acc] 54 | end, 55 | Xml = lists:flatten(lists:reverse(lists:foldl(BuildXml, [], Args))), 56 | % output 57 | Req:ok([{"Content-Type", "text/xml"}], "~s~s", [Method, Xml]). 58 | 59 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_file.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Sends file as attachment. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_file). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:file(attachment, "1.png"). 44 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_file_upload.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: File Upload. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_file_upload). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 45 | 46 | % ---------------------------- \/ handle rest -------------------------------------------------------------- 47 | 48 | % handle a GET on / 49 | handle('GET', [], Req) -> 50 | DestPath = get_destination_path(), 51 | Req:ok([{"Content-Type", "text/html"}], [" 52 | 53 | Misultin File Upload 54 | 55 | 56 |

57 | Upload a File. This file will be saved in \"", DestPath, "\", please ensure that the appropriate writing permissions have been set. 58 |

59 |
60 | 61 | 62 |
63 | 64 | "]); 65 | 66 | % handle a POST on / -> file received 67 | handle('POST', [], Req) -> 68 | case Req:parse_post() of 69 | [{_Tag, Attributes, FileData}] -> 70 | % build destination file path 71 | DestPath = get_destination_path(), 72 | FileName = misultin_utility:get_key_value("filename", Attributes), 73 | DestFile = filename:join(DestPath, FileName), 74 | % save file 75 | case file:write_file(DestFile, FileData) of 76 | ok -> 77 | Req:ok(["File has been successfully saved to \"", DestFile, "\"."]); 78 | {error, _Reason} -> 79 | Req:respond(500) 80 | end; 81 | _ -> 82 | Req:respond(500) 83 | end; 84 | 85 | % handle the 404 page not found 86 | handle(_, _, Req) -> 87 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 88 | 89 | % ---------------------------- /\ handle rest -------------------------------------------------------------- 90 | 91 | % gets the destination path 92 | get_destination_path() -> 93 | filename:dirname(code:which(?MODULE)). 94 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_get_variable.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Gets the GET variable 'value' and prints it out as XML if found. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_get_variable). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % get params 44 | Args = Req:parse_qs(), 45 | case Req:get_variable("value", Args) of 46 | undefined -> 47 | Req:ok([{"Content-Type", "text/xml"}], "no value specified"); 48 | Value -> 49 | Req:ok([{"Content-Type", "text/xml"}], "~s", [Value]) 50 | end. 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_hello_world.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_hello_world). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | Req:ok("Hello World."). 44 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_hello_world_nameless.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World with a non-registered misultin server. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_hello_world_nameless). 31 | -export([start/1, stop/1]). 32 | 33 | % Description: Start misultin http server 34 | % For example: 35 | % {ok, Pid} = misultin_hello_world_nameless:start(8080). 36 | start(Port) -> 37 | misultin:start_link([{port, Port}, {name, false}, {loop, fun(Req) -> handle_http(Req) end}]). 38 | 39 | % Description: Stop misultin 40 | % Continuing the above example: 41 | % misultin_hello_world_nameless:stop(Pid). 42 | stop(ServerPid) -> 43 | misultin:stop(ServerPid). 44 | 45 | % callback on request received 46 | handle_http(Req) -> 47 | Req:ok("Hello World."). 48 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_multiple_servers_custom_name.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World with two custom name registered misultin servers. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_multiple_servers_custom_name). 31 | -export([start/2, stop/0]). 32 | 33 | % Description: Start misultin http servers 34 | start(Port1, Port2) -> 35 | % start misultin1 36 | misultin:start_link([{port, Port1}, {name, misultin1}, {loop, fun(Req) -> handle_http(Req, misultin1) end}]), 37 | % start misultin2 38 | misultin:start_link([{port, Port2}, {name, misultin2}, {loop, fun(Req) -> handle_http(Req, misultin2) end}]). 39 | 40 | % Description: Stop misultin servers 41 | stop() -> 42 | misultin:stop(misultin1), 43 | misultin:stop(misultin2). 44 | 45 | % callback on request received 46 | handle_http(Req, RegName) -> 47 | Req:ok(["Hello World from ", atom_to_list(RegName)]). 48 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_rest.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: RESTful support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_rest). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback function called on incoming http request 42 | handle_http(Req) -> 43 | % dispatch to rest 44 | handle(Req:get(method), Req:resource([lowercase, urldecode]), Req). 45 | 46 | % ---------------------------- \/ handle rest -------------------------------------------------------------- 47 | 48 | % handle a GET on / 49 | handle('GET', [], Req) -> 50 | Req:ok([{"Content-Type", "text/plain"}], "Main home page."); 51 | 52 | % handle a GET on /users 53 | handle('GET', ["users"], Req) -> 54 | Req:ok([{"Content-Type", "text/plain"}], "Main users root."); 55 | 56 | % handle a GET on /users/{username} 57 | handle('GET', ["users", UserName], Req) -> 58 | Req:ok([{"Content-Type", "text/plain"}], "This is ~s's page.", [UserName]); 59 | 60 | % handle a GET on /users/{username}/messages 61 | handle('GET', ["users", UserName, "messages"], Req) -> 62 | Req:ok([{"Content-Type", "text/plain"}], "This is ~s's messages page.", [UserName]); 63 | 64 | % handle the 404 page not found 65 | handle(_, _, Req) -> 66 | Req:ok([{"Content-Type", "text/plain"}], "Page not found."). 67 | 68 | % ---------------------------- /\ handle rest -------------------------------------------------------------- 69 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_ssl.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Hello World SSL. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_ssl). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}, 36 | {ssl, [ 37 | {certfile, filename:join([filename:dirname(code:which(?MODULE)), "..", "priv", "test_certificate.pem"])}, 38 | {keyfile, filename:join([filename:dirname(code:which(?MODULE)), "..", "priv", "test_privkey.pem"])}, 39 | {password, "misultin"} 40 | ]} 41 | ]). 42 | 43 | % stop misultin 44 | stop() -> 45 | misultin:stop(). 46 | 47 | % callback on request received 48 | handle_http(Req) -> 49 | % output 50 | Req:ok("Hello World SSL."). 51 | 52 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_stream.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Stream data gradually. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_stream). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req) -> 43 | % send headers 44 | Req:stream(head, [{"Content-Type", "text/plain"}]), 45 | % send stream 46 | Req:stream("1"), 47 | timer:sleep(2000), 48 | % send stream 49 | Req:stream("2"), 50 | timer:sleep(2000), 51 | % send stream 52 | Req:stream("3"), 53 | % close socket 54 | Req:stream(close). 55 | 56 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_websocket_event_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin Websocket With an event support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_event_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([ 36 | {port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, 37 | {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, {ws_autoexit, false} 38 | ]). 39 | 40 | % stop misultin 41 | stop() -> 42 | misultin:stop(). 43 | 44 | % callback on request received 45 | handle_http(Req, Port) -> 46 | % output 47 | Req:ok([{"Content-Type", "text/html"}], 48 | [" 49 | 50 | 51 | 81 | 82 | 83 |
84 | 85 | "]). 86 | 87 | % callback on received websockets data 88 | handle_websocket(Ws) -> 89 | receive 90 | {browser, Data} -> 91 | Ws:send(["received '", Data, "'"]), 92 | handle_websocket(Ws); 93 | closed -> 94 | % IMPORTANT: since we specified the {ws_autoexit, false} option, we need to manually ensure that this process exits 95 | % [otherwise it will become a zombie] 96 | io:format("The WebSocket was CLOSED!~n"); 97 | _Ignore -> 98 | handle_websocket(Ws) 99 | after 5000 -> 100 | Ws:send("pushing!"), 101 | handle_websocket(Ws) 102 | end. 103 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_websocket_event_example2.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin Websocket With an event support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2010, Leandro Silva 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_event_example2). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([ 36 | {port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, 37 | {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, {ws_autoexit, false} 38 | ]). 39 | 40 | % stop misultin 41 | stop() -> 42 | misultin:stop(). 43 | 44 | % callback on request received 45 | handle_http(Req, Port) -> 46 | % output 47 | Req:ok([{"Content-Type", "text/html"}], 48 | [" 49 | 50 | 51 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | "]). 98 | 99 | % callback on received websockets data 100 | handle_websocket(Ws) -> 101 | receive 102 | {browser, Data} -> 103 | Ws:send(["received '", Data, "'"]), 104 | handle_websocket(Ws); 105 | closed -> 106 | % IMPORTANT: since we specified the {ws_autoexit, false} option, we need to manually ensure that this process exits 107 | % [otherwise it will become a zombie] 108 | io:format("The WebSocket was CLOSED!~n"), 109 | closed; 110 | _Ignore -> 111 | handle_websocket(Ws) 112 | after 5000 -> 113 | Ws:send("pushing!"), 114 | handle_websocket(Ws) 115 | end. 116 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_websocket_example.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin Websocket support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_example). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]). 36 | 37 | % stop misultin 38 | stop() -> 39 | misultin:stop(). 40 | 41 | % callback on request received 42 | handle_http(Req, Port) -> 43 | % output 44 | Req:ok([{"Content-Type", "text/html"}], 45 | [" 46 | 47 | 48 | 78 | 79 | 80 |
81 | 82 | "]). 83 | 84 | % callback on received websockets data 85 | handle_websocket(Ws) -> 86 | receive 87 | {browser, Data} -> 88 | Ws:send(["received '", Data, "'"]), 89 | handle_websocket(Ws); 90 | _Ignore -> 91 | handle_websocket(Ws) 92 | after 5000 -> 93 | Ws:send("pushing!"), 94 | handle_websocket(Ws) 95 | end. 96 | -------------------------------------------------------------------------------- /deps/misultin/examples/misultin_websocket_example_ssl.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Example: Shows misultin SSL Websocket support. 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_websocket_example_ssl). 31 | -export([start/1, stop/0]). 32 | 33 | % start misultin http server 34 | start(Port) -> 35 | misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, 36 | {ssl, [ 37 | {certfile, "../priv/test_certificate.pem"}, 38 | {keyfile, "../priv/test_privkey.pem"}, 39 | {password, "misultin"} 40 | ]}]). 41 | 42 | % stop misultin 43 | stop() -> 44 | misultin:stop(). 45 | 46 | % callback on request received 47 | handle_http(Req, Port) -> 48 | % output 49 | Req:ok([{"Content-Type", "text/html"}], 50 | [" 51 | 52 | 53 | 83 | 84 | 85 |
86 | 87 | "]). 88 | 89 | % callback on received websockets data 90 | handle_websocket(Ws) -> 91 | receive 92 | {browser, Data} -> 93 | Ws:send(["received '", Data, "'"]), 94 | handle_websocket(Ws); 95 | _Ignore -> 96 | handle_websocket(Ws) 97 | after 5000 -> 98 | Ws:send("pushing!"), 99 | handle_websocket(Ws) 100 | end. 101 | -------------------------------------------------------------------------------- /deps/misultin/make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM ========================================================================================================== 3 | REM MISULTIN - compile 4 | REM 5 | REM >-|-|-<°> 6 | REM 7 | REM Copyright (C) 2009, Roberto Ostinelli 8 | REM All rights reserved. 9 | REM 10 | REM BSD License 11 | REM 12 | REM Redistribution and use in source and binary forms, with or without modification, are permitted provided 13 | REM that the following conditions are met: 14 | REM 15 | REM * Redistributions of source code must retain the above copyright notice, this list of conditions and the 16 | REM following disclaimer. 17 | REM * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 18 | REM the following disclaimer in the documentation and/or other materials provided with the distribution. 19 | REM * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 20 | REM products derived from this software without specific prior written permission. 21 | REM 22 | REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 23 | REM WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | REM PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | REM ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | REM TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | REM HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | REM NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | REM POSSIBILITY OF SUCH DAMAGE. 30 | REM ========================================================================================================== 31 | 32 | :BEGIN 33 | IF "%1"=="debug" GOTO SETDEBUG 34 | IF "%1"=="example" GOTO EXAMPLES 35 | IF "%1"=="clean" GOTO CLEAN 36 | GOTO COMPILE 37 | 38 | :SETDEBUG 39 | SET command=-D log_debug 40 | GOTO COMPILE 41 | 42 | :EXAMPLES 43 | mkdir ebin 44 | FOR %%f in (examples\*.erl) DO erlc -W %command% -o ebin "%%f" 45 | 46 | :COMPILE 47 | mkdir ebin 48 | FOR %%f in (src\*.erl) DO erlc -W %command% -o ebin "%%f" 49 | copy src\misultin.app.src ebin\misultin.app /Y 50 | GOTO END 51 | 52 | :CLEAN 53 | FOR %%f in (ebin\*) DO del "%%f" 54 | 55 | :END 56 | -------------------------------------------------------------------------------- /deps/misultin/priv/README.txt: -------------------------------------------------------------------------------- 1 | ========================================================================================================== 2 | DISCLAIMER 3 | ========================================================================================================== 4 | 5 | Please note that the included certificate 'test_certificate.pem' and private key 'test_privkey.pem' are 6 | publicly available via the Misultin repositories, and should NOT be used for any secure application. These 7 | have been provided here for your testing comfort only. 8 | 9 | You may consider getting your copy of OpenSSL and generate your own certificate 10 | and private key by issuing a command similar to: 11 | 12 | openssl req -new -x509 -newkey rsa:1024 -days 365 -keyout test_privkey.pem -out test_certificate.pem 13 | -------------------------------------------------------------------------------- /deps/misultin/priv/test_certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhzCCAvCgAwIBAgIJAPFFl046vv7vMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD 3 | VQQGEwJJVDENMAsGA1UECBMEQ29tbzENMAsGA1UEBxMEQ29tbzERMA8GA1UEChMI 4 | TWlzdWx0aW4xETAPBgNVBAsTCE1pc3VsdGluMREwDwYDVQQDEwhNaXN1bHRpbjEk 5 | MCIGCSqGSIb3DQEJARYVcm9iZXJ0b0Bvc3RpbmVsbGkubmV0MB4XDTEwMDQyMDE3 6 | NDczOFoXDTIwMDQxNzE3NDczOFowgYoxCzAJBgNVBAYTAklUMQ0wCwYDVQQIEwRD 7 | b21vMQ0wCwYDVQQHEwRDb21vMREwDwYDVQQKEwhNaXN1bHRpbjERMA8GA1UECxMI 8 | TWlzdWx0aW4xETAPBgNVBAMTCE1pc3VsdGluMSQwIgYJKoZIhvcNAQkBFhVyb2Jl 9 | cnRvQG9zdGluZWxsaS5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM8A 10 | BT4OzNCcgrfVnzmEp8VdyR+O0ubsSBunX8J/BTKbWgZVrrGrY9fO8AkVmD1VFm8n 11 | w/yLlz/Ow24j40UCY82Y9gMDgADa3BqcDPn1lPdGpOHhaMXMRFKnrVOfwMPE0wfx 12 | kpr9/I5rAPAnkX1WtvOWMK0V5yGsuIMBd2S4VzmrAgMBAAGjgfIwge8wHQYDVR0O 13 | BBYEFItpRD/8fT21N/pLeSWKexZHWP/3MIG/BgNVHSMEgbcwgbSAFItpRD/8fT21 14 | N/pLeSWKexZHWP/3oYGQpIGNMIGKMQswCQYDVQQGEwJJVDENMAsGA1UECBMEQ29t 15 | bzENMAsGA1UEBxMEQ29tbzERMA8GA1UEChMITWlzdWx0aW4xETAPBgNVBAsTCE1p 16 | c3VsdGluMREwDwYDVQQDEwhNaXN1bHRpbjEkMCIGCSqGSIb3DQEJARYVcm9iZXJ0 17 | b0Bvc3RpbmVsbGkubmV0ggkA8UWXTjq+/u8wDAYDVR0TBAUwAwEB/zANBgkqhkiG 18 | 9w0BAQUFAAOBgQA5ePVuJo6LcHegSKfD1GlTu2Ffkom547e9PYmKBaDyuNhCfP4F 19 | YB4GMi1SZeCsaYzJpEOCY9JEJD8hJO7xPnyKnwc3FhT7KfYHWO7FNZdmdMxE99mi 20 | lolCiDfglJkQCPihtxK5TKBDK+lzMub+1Xmc3VOvIuonzjh+VkSz1rCciQ== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /deps/misultin/priv/test_privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,E6F891BC96EC9B16 4 | 5 | kBpeVQGiACcY96k5ix5H+/Htt6XIGB2cyUhBMZlgBPQSAttU9B94YV+nXZmIVi8f 6 | r0cBDqGZ7uv+YjQom1HWw/NilWJr5x67VOhBGvg3kb2wYe3aMeGrmqRpPqNi6K2U 7 | t9QuragXj7tyFu8+sYnW0SeI7GN9aghF2S9bVKasOFDBHGDg1igb7PzBsctrQh1Q 8 | Hhkl2/Ql3+j18yUNZ2vCZgvGE2UfX5TJ79irpUCFiSgbf31EU7WCePZfSuNZfJB7 9 | hjQM5q/vfEmgqVCdVR1W8wFxdalPJOA819gKwpKBBgpfWPn2Gvyw/yAhm3FPLasG 10 | OgrY9PgsAcfozXgCOJP6NEP8IHzvb7kWoTussgBCd8P2Vv+YTp8WkBQDtpAlQExf 11 | 03sKUE9+pDXnzLnq4Pore6xBlzcZ4hu35WUo854UApRT/OQB5+Kmth9pqax43bbp 12 | 9Lfg6Zg9NXDuGHbRdK3U48uXLa7lDi5TMJ3LHuJ+DxZq4WNCTbMA3YZTFnjGiD/N 13 | NvTy54oQThjn67N7BQe3PeWI2ryGEWJAXShnc0ZTaxQaQn+18zVAe2tQOFUPhbbA 14 | Bq7zx49gea1tlJC1DHLktmw72v0g5W3HZ2fP1m+9socH9n4iORGEpicwuMgf9Tlb 15 | T0mFP3hL0y1wEdBoohF6Euk1Y33P44tbXsYn6bP2/mVmWphVA3wWOocMYw/UgSxM 16 | pzpC+z6y19dhqYNJyywgsMv6GQdrovW1DF2udmjx1Mv7qbwdJlH0GyLdyBL8aZFb 17 | WBXI2PjWWtZS/F1U7QsELzV3mM1U8n+K5hZuBPtvLzohpq2W59tPkg== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /deps/misultin/src/misultin.app.src: -------------------------------------------------------------------------------- 1 | {application, misultin, 2 | [ 3 | {description, "Lightweight HTTP(s) and Websockets Server Library"}, 4 | {vsn, "0.8"}, 5 | {modules, [ 6 | misultin, 7 | misultin_acceptor, 8 | misultin_acceptors_sup, 9 | misultin_http, 10 | misultin_req, 11 | misultin_server, 12 | misultin_socket, 13 | misultin_utility, 14 | misultin_websocket, 15 | misultin_ws 16 | ]}, 17 | {registered, []}, 18 | {env, []}, 19 | {applications, [kernel, stdlib]} 20 | ]}. 21 | -------------------------------------------------------------------------------- /deps/misultin/src/misultin_acceptors_sup.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Acceptors Supervisor 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Sean Hinde. 7 | % All rights reserved. 8 | % 9 | % Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 10 | % 11 | % 12 | % BSD License 13 | % 14 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 15 | % that the following conditions are met: 16 | % 17 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 18 | % following disclaimer. 19 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 20 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 21 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 22 | % products derived from this software without specific prior written permission. 23 | % 24 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 25 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 27 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 28 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | % POSSIBILITY OF SUCH DAMAGE. 32 | % ========================================================================================================== 33 | -module(misultin_acceptors_sup). 34 | -behaviour(supervisor). 35 | 36 | % API 37 | -export([start_link/7]). 38 | 39 | % supervisor callbacks 40 | -export([init/1]). 41 | 42 | % includes 43 | -include("../include/misultin.hrl"). 44 | 45 | % ============================ \/ API ====================================================================== 46 | 47 | % ---------------------------------------------------------------------------------------------------------- 48 | % Starts the supervisor 49 | % ---------------------------------------------------------------------------------------------------------- 50 | -spec start_link( 51 | MainSupRef::pid(), 52 | Port::non_neg_integer(), 53 | OptionsTcp::[misultin_option_tcp], 54 | AcceptorsPoolsize::non_neg_integer(), 55 | RecvTimeout::non_neg_integer(), 56 | SocketMode::socketmode(), 57 | CustomOpts::[misultin_option_server()]) -> {ok, Pid::pid()}. 58 | start_link(MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts) -> 59 | supervisor:start_link(?MODULE, {MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts}). 60 | 61 | % ============================ /\ API ====================================================================== 62 | 63 | 64 | % ============================ \/ SUPERVISOR CALLBACKS ===================================================== 65 | 66 | % ---------------------------------------------------------------------------------------------------------- 67 | % Function: -> {ok, {SupFlags, [ChildSpec]}} | ignore | {error, Reason} 68 | % Description: Starts the supervisor 69 | % ---------------------------------------------------------------------------------------------------------- 70 | -spec init({ 71 | MainSupRef::pid(), 72 | Port::non_neg_integer(), 73 | OptionsTcp::[misultin_option_tcp], 74 | AcceptorsPoolsize::non_neg_integer(), 75 | RecvTimeout::non_neg_integer(), 76 | SocketMode::socketmode(), 77 | CustomOpts::[misultin_option_server()]}) -> {ok, term()} | {error, Reason::term()}. 78 | init({MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts}) -> 79 | ?LOG_DEBUG("starting listening ~p socket with options ~p on port ~p", [SocketMode, OptionsTcp, Port]), 80 | case misultin_socket:listen(Port, OptionsTcp, SocketMode) of 81 | {ok, ListenSocket} -> 82 | Acceptors = [ 83 | {{acceptor, N}, {misultin_acceptor, start_link, [MainSupRef, ListenSocket, Port, RecvTimeout, SocketMode, CustomOpts]}, 84 | permanent, brutal_kill, worker, dynamic} 85 | || N <- lists:seq(1, AcceptorsPoolsize) 86 | ], 87 | {ok, {{one_for_one, 5, 10}, Acceptors}}; 88 | {error, Reason} -> 89 | % error 90 | {error, Reason} 91 | end. 92 | 93 | % ============================ /\ SUPERVISOR CALLBACKS ===================================================== 94 | 95 | 96 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 97 | 98 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 99 | -------------------------------------------------------------------------------- /deps/misultin/src/misultin_socket.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Socket 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli , Sean Hinde. 7 | % All rights reserved. 8 | % 9 | % Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address: 10 | % 11 | % 12 | % BSD License 13 | % 14 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 15 | % that the following conditions are met: 16 | % 17 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 18 | % following disclaimer. 19 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 20 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 21 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 22 | % products derived from this software without specific prior written permission. 23 | % 24 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 25 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 27 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 28 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | % POSSIBILITY OF SUCH DAMAGE. 32 | % ========================================================================================================== 33 | -module(misultin_socket). 34 | -vsn("0.8"). 35 | 36 | % API 37 | -export([listen/3, accept/2, controlling_process/3, peername/2, peercert/2, setopts/3, recv/4, send/3, close/2]). 38 | 39 | % includes 40 | -include("../include/misultin.hrl"). 41 | 42 | 43 | % ============================ \/ API ====================================================================== 44 | 45 | % socket listen 46 | -spec listen(Port::non_neg_integer(), Options::gen_proplist(), socketmode()) -> {ok, ListenSock::socket()} | {error, Reason::term()}. 47 | listen(Port, Options, http) -> gen_tcp:listen(Port, Options); 48 | listen(Port, Options, ssl) -> ssl:listen(Port, Options). 49 | 50 | % socket accept 51 | -spec accept(ListenSocket::socket(), socketmode()) -> {ok, ListenSock::socket()} | {error, Reason::term()}. 52 | accept(ListenSocket, http) -> gen_tcp:accept(ListenSocket); 53 | accept(ListenSocket, ssl) -> 54 | try ssl:transport_accept(ListenSocket) 55 | catch 56 | error:{badmatch, {error, Reason}} -> 57 | {error, Reason} 58 | end. 59 | 60 | % socket controlling process 61 | -spec controlling_process(Sock::socket(), Pid::pid(), socketmode()) -> ok | {error, Reason::term()}. 62 | controlling_process(Sock, Pid, http) -> gen_tcp:controlling_process(Sock, Pid); 63 | controlling_process(Sock, Pid, ssl) -> ssl:controlling_process(Sock, Pid). 64 | 65 | % Get socket peername 66 | -spec peername(Sock::socket(), socketmode() | function()) -> {inet:ip_address(), non_neg_integer()}. 67 | peername(Sock, http) -> peername(Sock, fun inet:peername/1); 68 | peername(Sock, ssl) -> peername(Sock, fun ssl:peername/1); 69 | peername(Sock, F) -> 70 | case F(Sock) of 71 | {ok, {Addr, Port}} -> 72 | {Addr, Port}; 73 | {error, _Reason} -> 74 | {undefined, undefined} 75 | end. 76 | 77 | % Get socket certificate 78 | -spec peercert(Sock::socket(), socketmode()) -> Cert::term() | undefined. 79 | peercert(_Sock, http) -> undefined; 80 | peercert(Sock, ssl) -> 81 | case ssl:peercert(Sock) of 82 | {ok, Cert} -> Cert; 83 | {error, _Reason} -> undefined 84 | end. 85 | 86 | % socket set options 87 | -spec setopts(Sock::socket(), Options::gen_proplist(), socketmode()) -> ok | {error, Reason::term()}. 88 | setopts(Sock, Options, http) -> inet:setopts(Sock, Options); 89 | setopts(Sock, Options, ssl) -> ssl:setopts(Sock, Options). 90 | 91 | % socket receive 92 | -spec recv(Sock::socket(), Len::non_neg_integer(), RecvTimeout::non_neg_integer(), socketmode()) -> {ok, Data::list() | binary()} | {error, Reason::term()}. 93 | recv(Sock, Len, RecvTimeout, http) -> gen_tcp:recv(Sock, Len, RecvTimeout); 94 | recv(Sock, Len, RecvTimeout, ssl) -> ssl:recv(Sock, Len, RecvTimeout). 95 | 96 | % socket send 97 | -spec send(Sock::socket(), Data::binary() | iolist() | list(), socketmode() | function()) -> ok. 98 | send(Sock, Data, http) -> send(Sock, Data, fun gen_tcp:send/2); 99 | send(Sock, Data, ssl) -> send(Sock, Data, fun ssl:send/2); 100 | send(Sock, Data, F) -> 101 | ?LOG_DEBUG("sending data: ~p", [Data]), 102 | case F(Sock, Data) of 103 | ok -> 104 | ok; 105 | {error, _Reason} -> 106 | ?LOG_ERROR("error sending data: ~p", [_Reason]), 107 | exit(kill) 108 | end. 109 | 110 | % TCP close 111 | -spec close(Sock::socket(), socketmode() | function()) -> ok. 112 | close(Sock, http) -> close(Sock, fun gen_tcp:close/1); 113 | close(Sock, ssl) -> close(Sock, fun ssl:close/1); 114 | close(Sock, F) -> 115 | ?LOG_DEBUG("closing socket", []), 116 | case catch F(Sock) of 117 | ok -> 118 | ok; 119 | _Else -> 120 | ?LOG_WARNING("could not close socket: ~p", [_Else]), 121 | exit(kill) 122 | end. 123 | 124 | % ============================ /\ API ====================================================================== 125 | 126 | 127 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 128 | 129 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 130 | -------------------------------------------------------------------------------- /deps/misultin/src/misultin_ws.erl: -------------------------------------------------------------------------------- 1 | % ========================================================================================================== 2 | % MISULTIN - Websocket Request 3 | % 4 | % >-|-|-(°> 5 | % 6 | % Copyright (C) 2011, Roberto Ostinelli . 7 | % All rights reserved. 8 | % 9 | % BSD License 10 | % 11 | % Redistribution and use in source and binary forms, with or without modification, are permitted provided 12 | % that the following conditions are met: 13 | % 14 | % * Redistributions of source code must retain the above copyright notice, this list of conditions and the 15 | % following disclaimer. 16 | % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 17 | % the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote 19 | % products derived from this software without specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 22 | % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 23 | % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | % POSSIBILITY OF SUCH DAMAGE. 29 | % ========================================================================================================== 30 | -module(misultin_ws). 31 | -vsn("0.8"). 32 | 33 | % API 34 | -export([raw/1, get/2, send/2]). 35 | 36 | % includes 37 | -include("../include/misultin.hrl"). 38 | 39 | % types 40 | -type wst() :: {misultin_ws, #ws{}, SocketPid::pid()}. 41 | 42 | 43 | % ============================ \/ API ====================================================================== 44 | 45 | % Returns raw websocket content. 46 | -spec raw(wst()) -> #ws{}. 47 | raw({misultin_ws, Ws, _SocketPid}) -> 48 | Ws. 49 | 50 | % Get websocket info. 51 | -spec get(WsInfo::atom(), wst()) -> term(). 52 | get(socket, {misultin_ws, Ws, _SocketPid}) -> 53 | Ws#ws.socket; 54 | get(socket_mode, {misultin_ws, Ws, _SocketPid}) -> 55 | Ws#req.socket_mode; 56 | get(peer_addr, {misultin_ws, #ws{headers = Headers} = Ws, _SocketPid}) -> 57 | Host = case misultin_utility:get_key_value("X-Real-Ip", Headers) of 58 | undefined -> 59 | case misultin_utility:get_key_value("X-Forwarded-For", Headers) of 60 | undefined -> undefined; 61 | Hosts0 -> string:strip(lists:nth(1, string:tokens(Hosts0, ","))) 62 | end; 63 | Host0 -> Host0 64 | end, 65 | case Host of 66 | undefined -> 67 | Ws#ws.peer_addr; 68 | _ -> 69 | case inet_parse:address(Host) of 70 | {error, _Reason} -> 71 | Ws#ws.peer_addr; 72 | {ok, IpTuple} -> 73 | IpTuple 74 | end 75 | end; 76 | get(peer_port, {misultin_ws, Ws, _SocketPid}) -> 77 | Ws#ws.peer_port; 78 | get(peer_cert, {misultin_ws, Ws, _SocketPid}) -> 79 | Ws#ws.peer_cert; 80 | get(vsn, {misultin_ws, Ws, _SocketPid}) -> 81 | Ws#ws.vsn; 82 | get(origin, {misultin_ws, Ws, _SocketPid}) -> 83 | Ws#ws.origin; 84 | get(host, {misultin_ws, Ws, _SocketPid}) -> 85 | Ws#ws.host; 86 | get(path, {misultin_ws, Ws, _SocketPid}) -> 87 | Ws#ws.path; 88 | get(headers, {misultin_ws, Ws, _SocketPid}) -> 89 | Ws#ws.headers. 90 | 91 | % send data 92 | -spec send(Data::list() | binary() | iolist(), wst()) -> term(). 93 | send(Data, {misultin_ws, _Ws, SocketPid}) -> 94 | SocketPid ! {send, Data}. 95 | 96 | % ============================ /\ API ====================================================================== 97 | 98 | 99 | 100 | % ============================ \/ INTERNAL FUNCTIONS ======================================================= 101 | 102 | % ============================ /\ INTERNAL FUNCTIONS ======================================================= 103 | -------------------------------------------------------------------------------- /deps/misultin/test/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | results* -------------------------------------------------------------------------------- /deps/redo/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ./rebar compile 3 | 4 | clean: 5 | ./rebar clean 6 | -------------------------------------------------------------------------------- /deps/redo/README.md: -------------------------------------------------------------------------------- 1 | ### Philosophy 2 | 3 | > If you wish to write a redis client from scratch, you must first invent the universe. 4 | 5 | ### About 6 | 7 | Redo is a pipelined redis client written in Erlang. It lacks any sort of syntactic sugar. The only API function is redo:cmd, which takes a raw redis command. 8 | 9 | ### Build 10 | 11 | $ make 12 | 13 | ### Test 14 | 15 | #### Unit tests 16 | 17 | $ ./rebar eunit suite=redo 18 | 19 | #### Local read benchmark 20 | 21 | $ erl -pa ebin 22 | 1> bench:sync(1000). 23 | 91ms 24 | 10989 req/sec 25 | 26 | 2> bench:async(1000, 100). 27 | 38ms 28 | 26315 req/sec 29 | 30 | #### Concurrency test 31 | 32 | $ erl -pa ebin 33 | 1> redo_concurrency_test:run(20, 100). %% 20 pids, 100 random operations performed per pid 34 | 35 | ### Start 36 | 37 | No arguments: register process as "redo" 38 | 39 | $ erl -pa ebin 40 | 1> redo:start_link(). 41 | {ok, <0.33.0> 42 | 2> whereis(redo). 43 | <0.33.0> 44 | 3> redo:cmd(["PING"]). 45 | <<"PONG">> 46 | 47 | Register with custom name 48 | 49 | erl -pa ebin 50 | 1> redo:start_link(myclient). 51 | {ok,<0.33.0>} 52 | 2> whereis(myclient). 53 | <0.33.0> 54 | 8> redo:cmd(myclient, ["PING"]). 55 | <<"PONG">> 56 | 57 | Start anonymous Redo process 58 | 59 | erl -pa ebin 60 | 1> {ok, Pid} = redo:start_link(undefined). 61 | {ok,<0.33.0>} 62 | 2> redo:cmd(Pid, ["PING"]). 63 | <<"PONG">> 64 | 65 | Specifying connection options 66 | 67 | erl -pa ebin 68 | 1> redo:start_link([{host, "localhost"}, {port, 6379}]). 69 | {ok,<0.33.0>} 70 | 2> redo:cmd(["PING"]). 71 | <<"PONG">> 72 | 73 | 3> redo:start_link(myclient, [{host, "localhost"}, {port, 6379}]). 74 | {ok,<0.37.0>} 75 | 4> redo:cmd(myclient, ["PING"]). 76 | <<"PONG">> 77 | 78 | ### Commands 79 | 80 | erl -pa ebin 81 | 1> redo:start_link(). 82 | <0.33.0> 83 | 2> redo:cmd(["SET", "foo"]). 84 | {error,<<"ERR wrong number of arguments for 'set' command">>} 85 | 3> redo:cmd(["SET", "foo", "bar"]). 86 | <<"OK">> 87 | 4> redo:cmd(["GET", "foo"]). 88 | <<"bar">> 89 | 5> redo:cmd(["HMSET", "hfoo", "ONE", "ABC", "TWO", "DEF"]). 90 | <<"OK">> 91 | 6> redo:cmd(["HGETALL", "hfoo"]). 92 | [<<"ONE">>,<<"ABC">>,<<"TWO">>,<<"DEF">>] 93 | 94 | ### Pipelined commands 95 | 96 | 1> redo:start_link(). 97 | <0.33.> 98 | 2> redo:cmd([["GET", "foo"], ["HGETALL", "hfoo"]]). 99 | [<<"bar">>, [<<"ONE">>,<<"ABC">>,<<"TWO">>,<<"DEF">>]] 100 | 101 | ### Pub/Sub 102 | 103 | $ erl -pa ebin 104 | 1> redo:start_link(). 105 | {ok,<0.33.0>} 106 | 2> Ref = redo:subscribe("chfoo"). 107 | #Ref<0.0.0.42> 108 | 3> (fun() -> receive {Ref, Res} -> Res after 10000 -> timeout end end)(). 109 | [<<"subscribe">>,<<"chfoo">>,1] 110 | 4> redo:start_link(client). 111 | {ok,<0.39.0>} 112 | 5> redo:cmd(client, ["PUBLISH", "chfoo", "hello"]). 113 | 1 114 | 6> (fun() -> receive {Ref, Res} -> Res after 10000 -> timeout end end)(). 115 | [<<"message">>,<<"chfoo">>,<<"hello">>] 116 | 117 | %% restart redis server... 118 | 119 | 7> (fun() -> receive {Ref, Res} -> Res after 10000 -> timeout end end)(). 120 | closed 121 | 8> f(Ref). 122 | ok 123 | 9> Ref = redo:subscribe("chfoo"). 124 | #Ref<0.0.0.68> 125 | 10> (fun() -> receive {Ref, Res} -> Res after 10000 -> timeout end end)(). 126 | [<<"subscribe">>,<<"chfoo">>,1] 127 | 11> redo:cmd(client, ["PUBLISH", "chfoo", "hello again"]). 128 | 1 129 | 12> (fun() -> receive {Ref, Res} -> Res after 10000 -> timeout end end)(). 130 | [<<"message">>,<<"chfoo">>,<<"hello again">>] 131 | -------------------------------------------------------------------------------- /deps/redo/ebin/redo.app: -------------------------------------------------------------------------------- 1 | {application,redo, 2 | [{description,"Pipelined Redis Erlang Driver"}, 3 | {vsn,"1.0"}, 4 | {registered,[]}, 5 | {applications,[kernel,stdlib]}, 6 | {env,[]}, 7 | {modules,[bench,redo,redo_concurrency_test,redo_redis_proto, 8 | redo_uri]}]}. 9 | -------------------------------------------------------------------------------- /deps/redo/rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leandrosilva/cameron/34051395b620d2c3cb2cb63c854e65234786a176/deps/redo/rebar -------------------------------------------------------------------------------- /deps/redo/src/bench.erl: -------------------------------------------------------------------------------- 1 | -module(bench). 2 | -compile(export_all). 3 | 4 | sync(Num) -> 5 | Redo = setup(), 6 | A = now(), 7 | ok = loop(Redo, Num), 8 | B = now(), 9 | print(Num,A,B), 10 | ok. 11 | 12 | async(Num, Concurrency) -> 13 | Redo = setup(), 14 | Self = self(), 15 | A = now(), 16 | Pids = [spawn_link(fun() -> loop(Redo, Num div Concurrency), Self ! {self(), done} end) || _ <- lists:seq(1, Concurrency)], 17 | [receive {Pid, done} -> ok end || Pid <- Pids], 18 | B = now(), 19 | print(Num,A,B), 20 | ok. 21 | 22 | setup() -> 23 | {ok, Redo} = redo:start_link(undefined, []), 24 | redo:cmd(Redo, ["SET", "foo", "bar"]), 25 | Redo. 26 | 27 | print(Num,A,B) -> 28 | Microsecs = timer:now_diff(B,A), 29 | Time = Microsecs div Num, 30 | PerSec = 1000000 div Time, 31 | io:format("~wms~n~w req/sec~n", [Microsecs div 1000, PerSec]). 32 | 33 | loop(_Pid, 0) -> ok; 34 | 35 | loop(Pid, Count) -> 36 | <<"bar">> = redo:cmd(Pid, ["GET", "foo"]), 37 | loop(Pid, Count-1). 38 | -------------------------------------------------------------------------------- /deps/redo/src/redo.app.src: -------------------------------------------------------------------------------- 1 | {application, redo, 2 | [ 3 | {description, "Pipelined Redis Erlang Driver"}, 4 | {vsn, "1.0"}, 5 | {registered, []}, 6 | {applications, [kernel,stdlib]}, 7 | {env, []} 8 | ]}. 9 | -------------------------------------------------------------------------------- /deps/redo/src/redo_concurrency_test.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011 Jacob Vorreuter 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | -module(redo_concurrency_test). 24 | -export([run/2]). 25 | 26 | run(NumPids, NumOps) -> 27 | io:format("================================================================================~n"), 28 | io:format("== THIS TEST RELIES ON A LOCAL REDIS SERVER RUNNING ON localhost:6379 ==~n"), 29 | io:format("================================================================================~n"), 30 | 31 | random:seed(now()), 32 | 33 | case whereis(redo) of 34 | undefined -> 35 | {ok, _Pid} = redo:start_link(); 36 | _ -> 37 | ok 38 | end, 39 | 40 | <<"OK">> = redo:cmd(["SELECT", "6"]), 41 | <<"OK">> = redo:cmd(["FLUSHDB"]), 42 | 43 | Self = self(), 44 | Pids = [spawn_link(fun() -> io:format("spawned ~w~n", [N]), worker(Self, N, NumOps) end) || N <- lists:seq(1, NumPids)], 45 | 46 | [receive {Pid, N, done} -> io:format("finished ~w~n", [N]) end || Pid <- Pids], 47 | 48 | ok. 49 | 50 | worker(Parent, N, 0) -> 51 | Parent ! {self(), N, done}; 52 | 53 | worker(Parent, N, NumOps) -> 54 | StrN = integer_to_list(N), 55 | StrOp = integer_to_list(NumOps), 56 | case random:uniform(100) of 57 | R when R > 0, R =< 24 -> 58 | Key = iolist_to_binary([StrN, ":", StrOp, ":STRING"]), 59 | Val = iolist_to_binary(["STRING:", StrN, ":", StrOp]), 60 | <<"OK">> = redo:cmd(["SET", Key, Val]), 61 | Val = redo:cmd(["GET", Key]); 62 | R when R > 24, R =< 48 -> 63 | Key = iolist_to_binary([StrN, ":", StrOp, ":SET"]), 64 | Val1 = iolist_to_binary(["SET:1:", StrN, ":", StrOp]), 65 | Val2 = iolist_to_binary(["SET:2:", StrN, ":", StrOp]), 66 | 1 = redo:cmd(["SADD", Key, Val1]), 67 | 1 = redo:cmd(["SADD", Key, Val2]), 68 | Set = redo:cmd(["SMEMBERS", Key]), 69 | true = lists:member(Val1, Set), 70 | true = lists:member(Val2, Set); 71 | R when R > 48, R =< 72 -> 72 | Key = iolist_to_binary([StrN, ":", StrOp, ":HASH"]), 73 | Vals = [iolist_to_binary(["HASH:1:", StrN, ":Key:", StrOp]), 74 | iolist_to_binary(["HASH:1:", StrN, ":Val:", StrOp]), 75 | iolist_to_binary(["HASH:2:", StrN, ":Key", StrOp]), 76 | iolist_to_binary(["HASH:2:", StrN, ":Val", StrOp])], 77 | <<"OK">> = redo:cmd(["HMSET", Key | Vals]), 78 | Vals = redo:cmd(["HGETALL", Key]); 79 | R when R > 72, R =< 98 -> 80 | Key1 = iolist_to_binary([StrN, ":", StrOp, ":PIPESET"]), 81 | Val1 = iolist_to_binary(["PIPESET:1:", StrN, ":", StrOp]), 82 | Val2 = iolist_to_binary(["PIPESET:2:", StrN, ":", StrOp]), 83 | 1 = redo:cmd(["SADD", Key1, Val1]), 84 | 1 = redo:cmd(["SADD", Key1, Val2]), 85 | Key2 = iolist_to_binary([StrN, ":", StrOp, ":PIPEHASH"]), 86 | Vals = [iolist_to_binary(["PIPEHASH:1:", StrN, ":Key:", StrOp]), 87 | iolist_to_binary(["PIPEHASH:1:", StrN, ":Val:", StrOp]), 88 | iolist_to_binary(["PIPEHASH:2:", StrN, ":Key", StrOp]), 89 | iolist_to_binary(["PIPEHASH:2:", StrN, ":Val", StrOp])], 90 | <<"OK">> = redo:cmd(["HMSET", Key2 | Vals]), 91 | [Set, Hash] = redo:cmd([["SMEMBERS", Key1], ["HGETALL", Key2]]), 92 | true = lists:member(Val1, Set), 93 | true = lists:member(Val2, Set), 94 | Vals = Hash; 95 | R when R > 98 -> 96 | Keys = [begin 97 | Key = iolist_to_binary([StrN, ":", StrOp, ":", string:right(integer_to_list(ItemNum), 200, $0)]), 98 | <<"OK">> = redo:cmd(["SET", Key, "0"]), 99 | Key 100 | end || ItemNum <- lists:seq(1,1000)], 101 | Keys1 = redo:cmd(["KEYS", iolist_to_binary([StrN, ":", StrOp, ":*"])]), 102 | L = length(Keys), 103 | L1 = length(Keys1), 104 | L = L1 105 | end, 106 | worker(Parent, N, NumOps-1). 107 | 108 | -------------------------------------------------------------------------------- /deps/redo/src/redo_redis_proto.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011 Jacob Vorreuter 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | -module(redo_redis_proto). 24 | -export([package/1, parse/2]). 25 | 26 | -define(CRLF, <<"\r\n">>). 27 | 28 | -spec package(binary() | list()) -> list(). 29 | %% packet is already a binary 30 | package(Packet) when is_binary(Packet) -> 31 | [Packet]; 32 | 33 | %% list of strings - single cmd 34 | package([[Char|_]|_]=Args) when is_integer(Char) -> 35 | [build_request(Args)]; 36 | 37 | %% list of binaries - single cmd 38 | package([Bin|_]=Args) when is_binary(Bin) -> 39 | [build_request(Args)]; 40 | 41 | %% list of multiple cmds 42 | package(Args) when is_list(Args) -> 43 | build_pipelined_request(Args, []). 44 | 45 | build_request(Args) -> 46 | Count = length(Args), 47 | Args1 = [begin 48 | Arg1 = to_arg(Arg), 49 | [<<"$">>, integer_to_list(iolist_size(Arg1)), ?CRLF, Arg1, ?CRLF] 50 | end || Arg <- Args], 51 | iolist_to_binary(["*", integer_to_list(Count), ?CRLF, Args1, ?CRLF]). 52 | 53 | build_pipelined_request([], Acc) -> 54 | lists:reverse(Acc); 55 | 56 | build_pipelined_request([Args|Rest], Acc) -> 57 | build_pipelined_request(Rest, [build_request(Args)|Acc]). 58 | 59 | to_arg(List) when is_list(List) -> 60 | List; 61 | 62 | to_arg(Bin) when is_binary(Bin) -> 63 | Bin; 64 | 65 | to_arg(Int) when is_integer(Int) -> 66 | integer_to_list(Int); 67 | 68 | to_arg(Atom) when is_atom(Atom) -> 69 | atom_to_list(Atom). 70 | 71 | -spec parse(list(), {raw, binary()} | {multi_bulk, integer(), binary()}) -> 72 | {ok, undefined, {raw, binary()}} | 73 | {ok, binary(), {raw, binary()}} | 74 | {eof, list(), {raw, binary()}} | 75 | {ok, {error, term()}, {raw, binary()}}. 76 | %% Single line reply 77 | parse(Acc, {raw, <<"+", Rest/binary>> = Data}) -> 78 | case read_line(Rest) of 79 | {ok, Str, Rest1} -> 80 | {ok, Str, {raw, Rest1}}; 81 | {error, eof} -> 82 | {eof, Acc, {raw, Data}} 83 | end; 84 | 85 | %% Error msg reply 86 | parse(Acc, {raw, <<"-", Rest/binary>> = Data}) -> 87 | case read_line(Rest) of 88 | {ok, Err, Rest1} -> 89 | {ok, {error, Err}, {raw, Rest1}}; 90 | {error, eof} -> 91 | {eof, Acc, {raw, Data}} 92 | end; 93 | 94 | %% Integer reply 95 | parse(Acc, {raw, <<":", Rest/binary>> = Data}) -> 96 | case read_line(Rest) of 97 | {ok, Int, Rest1} -> 98 | Val = list_to_integer(binary_to_list(Int)), 99 | {ok, Val, {raw, Rest1}}; 100 | {error, eof} -> 101 | {eof, Acc, {raw, Data}} 102 | end; 103 | 104 | %% Bulk reply 105 | parse(Acc, {raw, <<"$", Rest/binary>> = Data}) -> 106 | case read_line(Rest) of 107 | {ok, BinSize, Rest1} -> 108 | Size = list_to_integer(binary_to_list(BinSize)), 109 | case Size >= 0 of 110 | true -> 111 | case Rest1 of 112 | <> -> 113 | {ok, Str, {raw, Rest2}}; 114 | _ -> 115 | {eof, Acc, {raw, Data}} 116 | end; 117 | false -> 118 | {ok, undefined, {raw, Rest1}} 119 | end; 120 | {error, eof} -> 121 | {eof, Acc, {raw, Data}} 122 | end; 123 | 124 | %% Multi bulk reply 125 | parse(Acc, {raw, <<"*", Rest/binary>> = Data}) -> 126 | case read_line(Rest) of 127 | {ok, BinNum, Rest1} -> 128 | Num = list_to_integer(binary_to_list(BinNum)), 129 | parse(Acc, {multi_bulk, Num, Rest1}); 130 | {error, eof} -> 131 | {eof, Acc, {raw, Data}} 132 | end; 133 | 134 | parse(Acc, {multi_bulk, Num, Data}) -> 135 | multi_bulk(Acc, Num, Data). 136 | 137 | read_line(Data) -> 138 | read_line(Data, <<>>). 139 | 140 | read_line(<<"\r\n", Rest/binary>>, Acc) -> 141 | {ok, Acc, Rest}; 142 | 143 | read_line(<<>>, _Acc) -> 144 | {error, eof}; 145 | 146 | read_line(<>, Acc) -> 147 | read_line(Rest, <>). 148 | 149 | multi_bulk(Acc, 0, Rest) -> 150 | {ok, lists:reverse(Acc), {raw, Rest}}; 151 | 152 | multi_bulk(Acc, Num, <<>>) -> 153 | {eof, Acc, {multi_bulk, Num, <<>>}}; 154 | 155 | multi_bulk(Acc, Num, Rest) -> 156 | case parse(Acc, {raw, Rest}) of 157 | {ok, Result, {raw, Rest1}} -> 158 | multi_bulk([Result|Acc], Num-1, Rest1); 159 | {eof, Acc1, _} -> 160 | {eof, Acc1, {multi_bulk, Num, Rest}} 161 | end. 162 | 163 | -------------------------------------------------------------------------------- /deps/redo/src/redo_uri.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011 Jacob Vorreuter 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | -module(redo_uri). 24 | -export([parse/1]). 25 | 26 | -spec parse(binary() | list()) -> list(). 27 | parse(Url) when is_binary(Url) -> 28 | parse(binary_to_list(Url)); 29 | 30 | parse("redis://" ++ Rest) -> 31 | parse(Rest); 32 | 33 | parse(":" ++ Rest) -> 34 | parse(Rest); 35 | 36 | % redis://:password@localhost:6379/1 37 | parse(Url) when is_list(Url) -> 38 | {Pass, Rest1} = pass(Url), 39 | {Host, Rest2} = host(Rest1), 40 | {Port, Db} = port(Rest2), 41 | [{host, Host} || Host =/= ""] ++ 42 | [{port, Port} || Port =/= ""] ++ 43 | [{pass, Pass} || Pass =/= ""] ++ 44 | [{db, Db} || Db =/= ""]. 45 | 46 | pass("") -> 47 | {"", ""}; 48 | 49 | pass(Url) -> 50 | case string:tokens(Url, "@") of 51 | [Rest] -> {"", Rest}; 52 | [Pass, Rest] -> {Pass, Rest}; 53 | [Pass | Rest] -> {Pass, string:join(Rest, "@")} 54 | end. 55 | 56 | host("") -> 57 | {"", ""}; 58 | 59 | host(Url) -> 60 | case string:tokens(Url, ":") of 61 | [Rest] -> 62 | case string:tokens(Rest, "/") of 63 | [Host] -> {Host, ""}; 64 | [Host, Rest1] -> {Host, "/" ++ Rest1}; 65 | [Host | Rest1] -> {Host, string:join(Rest1, "/")} 66 | end; 67 | [Host, Rest] -> {Host, Rest}; 68 | [Host | Rest] -> {Host, string:join(Rest, ":")} 69 | end. 70 | 71 | port("") -> 72 | {"", ""}; 73 | 74 | port("/" ++ Rest) -> 75 | {"", Rest}; 76 | 77 | port(Url) -> 78 | case string:tokens(Url, "/") of 79 | [Port] -> {list_to_integer(Port), ""}; 80 | [Port, Rest] -> {list_to_integer(Port), Rest}; 81 | [Port | Rest] -> {list_to_integer(Port), string:join(Rest, "/")} 82 | end. 83 | -------------------------------------------------------------------------------- /deps/redo/test/redo_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2011 Jacob Vorreuter 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | -module(redo_tests). 24 | -include_lib("eunit/include/eunit.hrl"). 25 | 26 | redis_uri_test() -> 27 | ?assertEqual([{host, "127.0.0.1"}, 28 | {port, 666}, 29 | {pass, "password"}, 30 | {db, "1"}], 31 | redo_uri:parse("redis://:password@127.0.0.1:666/1")), 32 | 33 | ?assertEqual([{host, "127.0.0.1"}, 34 | {port, 666}, 35 | {pass, "password"}, 36 | {db, "1"}], 37 | redo_uri:parse("redis://password@127.0.0.1:666/1")), 38 | 39 | ?assertEqual([{host, "127.0.0.1"}, 40 | {pass, "password"}], 41 | redo_uri:parse("redis://password@127.0.0.1")), 42 | 43 | ?assertEqual([{host, "127.0.0.1"}, 44 | {port, 666}, 45 | {db, "1"}], 46 | redo_uri:parse("redis://127.0.0.1:666/1")), 47 | 48 | ?assertEqual([{host, "127.0.0.1"}, 49 | {db, "1"}], 50 | redo_uri:parse("redis://127.0.0.1/1")), 51 | 52 | ?assertEqual([{host, "127.0.0.1"}, 53 | {port, 666}], 54 | redo_uri:parse("redis://127.0.0.1:666")), 55 | 56 | ?assertEqual([{host, "127.0.0.1"}], redo_uri:parse("redis://127.0.0.1")), 57 | 58 | ?assertEqual([], redo_uri:parse("redis://")), 59 | 60 | ?assertEqual([], redo_uri:parse("")), 61 | 62 | ok. 63 | 64 | redis_proto_test() -> 65 | redo_redis_proto:parse( 66 | fun(Val) -> ?assertEqual(undefined, Val) end, 67 | {raw, <<"$-1\r\n">>}), 68 | 69 | redo_redis_proto:parse( 70 | fun(Val) -> ?assertEqual(<<>>, Val) end, 71 | {raw, <<"$0\r\n\r\n">>}), 72 | 73 | redo_redis_proto:parse( 74 | fun(Val) -> ?assertEqual(<<"OK">>, Val) end, 75 | {raw, <<"+OK\r\n">>}), 76 | 77 | redo_redis_proto:parse( 78 | fun(Val) -> ?assertEqual({error, <<"Error">>}, Val) end, 79 | {raw, <<"-Error\r\n">>}), 80 | 81 | redo_redis_proto:parse( 82 | fun(Val) -> ?assertEqual(<<"FOO">>, Val) end, 83 | {raw, <<"$3\r\nFOO\r\n">>}), 84 | 85 | redo_redis_proto:parse( 86 | fun(Val) -> ?assertEqual(1234, Val) end, 87 | {raw, <<":1234\r\n">>}), 88 | 89 | ok. 90 | -------------------------------------------------------------------------------- /doc/cameron_pres.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leandrosilva/cameron/34051395b620d2c3cb2cb63c854e65234786a176/doc/cameron_pres.pdf -------------------------------------------------------------------------------- /ebin/cameron.app: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | {application, cameron, 5 | [{description, "Cameron Workflow Engine"}, 6 | {vsn, "0.0.1"}, 7 | {modules, [ 8 | cameron, 9 | cameron_app, 10 | cameron_sup, 11 | cameron_process_catalog, 12 | cameron_process_sup, 13 | cameron_job_data, 14 | cameron_job_scheduler, 15 | cameron_job_runner, 16 | cameron_web_server, 17 | cameron_web_api, 18 | cameron_deps 19 | ]}, 20 | {registered, []}, 21 | {mod, {cameron_app, []}}, 22 | {env, [{web_server, [{host, "0.0.0.0"}, 23 | {port, 8080}, 24 | {backlog, 128}, 25 | {docroot, "priv/www"}]}, 26 | {redis_server, [{host, "127.0.0.1"}, 27 | {port, 6379}]}]}, 28 | {applications, [kernel, stdlib]}]}. 29 | -------------------------------------------------------------------------------- /include/cameron.hrl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% 5 | %% Global define ---------------------------------------------------------------------------------- 6 | %% 7 | 8 | -define(pname(UUID), list_to_atom("cameron_" ++ UUID)). 9 | 10 | %% 11 | %% Data model ------------------------------------------------------------------------------------- 12 | %% 13 | 14 | % activity definition 15 | -record(activity_definition, {name, url}). 16 | 17 | % process definition 18 | -record(process_definition, {name, start_activity = #activity_definition{}}). 19 | 20 | % job is an instance of a process definition, when that's scheduled to be executed 21 | -record(job_input, {key, data, requestor}). 22 | 23 | -record(job, {uuid, 24 | process = #process_definition{}, 25 | input = #job_input{}}). 26 | 27 | % an instance of an activity in the context of a job 28 | -record(task_input, {key, data, requestor}). 29 | -record(task_output, {data, next_activities = undefined}). 30 | 31 | -record(task, {context_job = #job{}, 32 | activity = #activity_definition{}, 33 | input = #task_input{}, 34 | output = #task_output{}, 35 | failed = no}). 36 | 37 | %% 38 | %% Log levels ------------------------------------------------------------------------------------- 39 | %% 40 | 41 | -ifdef(use_syslog). 42 | 43 | -define(DEBUG(Msg), erlang:apply(syslog, debug, [cameron_syslog, Msg])). 44 | -define(DEBUG(Msg, Args), erlang:apply(syslog, debug, [cameron_syslog, io_lib:format(Msg, Args)])). 45 | 46 | -define(INFO(Msg), erlang:apply(syslog, info, [cameron_syslog, Msg])). 47 | -define(INFO(Msg, Args), erlang:apply(syslog, info, [cameron_syslog, io_lib:format(Msg, Args)])). 48 | 49 | -define(NOTICE(Msg), erlang:apply(syslog, notice, [cameron_syslog, Msg])). 50 | -define(NOTICE(Msg, Args), erlang:apply(syslog, notice, [cameron_syslog, io_lib:format(Msg, Args)])). 51 | 52 | -define(WARNING(Msg), erlang:apply(syslog, warning, [cameron_syslog, Msg])). 53 | -define(WARNING(Msg, Args), erlang:apply(syslog, warning, [cameron_syslog, io_lib:format(Msg, Args)])). 54 | 55 | -define(ERROR(Msg), erlang:apply(syslog, error, [cameron_syslog, Msg])). 56 | -define(ERROR(Msg, Args), erlang:apply(syslog, error, [cameron_syslog, io_lib:format(Msg, Args)])). 57 | 58 | -define(CRITICAL(Msg), erlang:apply(syslog, critical, [cameron_syslog, Msg])). 59 | -define(CRITICAL(Msg, Args), erlang:apply(syslog, critical, [cameron_syslog, io_lib:format(Msg, Args)])). 60 | 61 | -define(ALERT(Msg), erlang:apply(syslog, alert, [cameron_syslog, Msg])). 62 | -define(ALERT(Msg, Args), erlang:apply(syslog, alert, [cameron_syslog, io_lib:format(Msg, Args)])). 63 | 64 | -define(EMERGENCY(Msg), erlang:apply(syslog, emergency, [cameron_syslog, Msg])). 65 | -define(EMERGENCY(Msg, Args), erlang:apply(syslog, emergency, [cameron_syslog, io_lib:format(Msg, Args)])). 66 | 67 | -else. 68 | 69 | -define(DEBUG(Msg), erlang:apply(io, format, ["[DEBUG] " ++ Msg ++ "~n"])). 70 | -define(DEBUG(Msg, Args), erlang:apply(io, format, ["[DEBUG] " ++ Msg ++ "~n", Args])). 71 | 72 | -define(INFO(Msg), erlang:apply(io, format, ["[INFO] " ++ Msg ++ "~n"])). 73 | -define(INFO(Msg, Args), erlang:apply(io, format, ["[INFO] " ++ Msg ++ "~n", Args])). 74 | 75 | -define(NOTICE(Msg), erlang:apply(io, format, ["[NOTICE] " ++ Msg ++ "~n"])). 76 | -define(NOTICE(Msg, Args), erlang:apply(io, format, ["[NOTICE] " ++ Msg ++ "~n", Args])). 77 | 78 | -define(WARNING(Msg), erlang:apply(io, format, ["[WARNING] " ++ Msg ++ "~n"])). 79 | -define(WARNING(Msg, Args), erlang:apply(io, format, ["[WARNING] " ++ Msg ++ "~n", Args])). 80 | 81 | -define(ERROR(Msg), erlang:apply(io, format, ["[ERROR] " ++ Msg ++ "~n"])). 82 | -define(ERROR(Msg, Args), erlang:apply(io, format, ["[ERROR] " ++ Msg ++ "~n", Args])). 83 | 84 | -define(CRITICAL(Msg), erlang:apply(io, format, ["[CRITICAL] " ++ Msg ++ "~n"])). 85 | -define(CRITICAL(Msg, Args), erlang:apply(io, format, ["[CRITICAL] " ++ Msg ++ "~n", Args])). 86 | 87 | -define(ALERT(Msg), erlang:apply(io, format, ["[ALERT] " ++ Msg ++ "~n"])). 88 | -define(ALERT(Msg, Args), erlang:apply(io, format, ["[ALERT] " ++ Msg ++ "~n", Args])). 89 | 90 | -define(EMERGENCY(Msg), erlang:apply(io, format, ["[EMERGENCY] " ++ Msg ++ "~n"])). 91 | -define(EMERGENCY(Msg, Args), erlang:apply(io, format, ["[EMERGENCY] " ++ Msg ++ "~n", Args])). 92 | 93 | -endif. 94 | -------------------------------------------------------------------------------- /priv/config/development.config: -------------------------------------------------------------------------------- 1 | [{cameron, [{web_server, [{host, "0.0.0.0"}, 2 | {port, 8080}, 3 | {backlog, 128}, 4 | {docroot, "priv/www"}]}, 5 | {redis_server, [{host, "127.0.0.1"}, 6 | {port, 6379}]}]}]. 7 | -------------------------------------------------------------------------------- /priv/config/production.config: -------------------------------------------------------------------------------- 1 | [{cameron, [{web_server, [{host, "123.456.789.10"}, 2 | {port, 8001}, 3 | {backlog, 128}, 4 | {docroot, "priv/www"}]}, 5 | {redis_server, [{host, "127.0.0.1"}, 6 | {port, 6379}]}]}]. 7 | -------------------------------------------------------------------------------- /priv/config/test.config: -------------------------------------------------------------------------------- 1 | [{cameron, [{web_server, [{host, "127.0.0.1"}, 2 | {port, 8008}, 3 | {backlog, 111}, 4 | {docroot, "priv/www"}]}, 5 | {redis_server, [{host, "127.0.0.1"}, 6 | {port, 6379}]}]}]. 7 | -------------------------------------------------------------------------------- /priv/processes/development.config: -------------------------------------------------------------------------------- 1 | {processes, [{foo, {start_activity_url, "http://localhost:9292/foo/v0.0.1/start"}}, 2 | {bar, {start_activity_url, "http://bar.com/process/bar/start"}}]}. 3 | -------------------------------------------------------------------------------- /priv/processes/production.config: -------------------------------------------------------------------------------- 1 | {processes, [{foo, {start_activity_url, "http://localhost:9292/foo/v0.0.1/start"}}, 2 | {bar, {start_activity_url, "http://bar.com/process/bar/start"}}]}. 3 | -------------------------------------------------------------------------------- /priv/processes/test.config: -------------------------------------------------------------------------------- 1 | {processes, [{foo, {start_activity_url, "http://localhost:9292/foo/v0.0.1/start"}}, 2 | {bar, {start_activity_url, "http://bar.com/process/bar/start"}}]}. 3 | -------------------------------------------------------------------------------- /priv/sasl/all.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{sasl_error_logger, {file, "priv/sasl/log/report.log"}}, 2 | {errlog_type, all}, % error | progress | all 3 | {error_logger_mf_dir, "priv/sasl/log"}, % Log directory 4 | {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size 5 | {error_logger_mf_maxfiles, 3}]}]. % 3 files max 6 | -------------------------------------------------------------------------------- /priv/www/cameron.html: -------------------------------------------------------------------------------- 1 | Cameron Workflow Engine -------------------------------------------------------------------------------- /src/cameron.app: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | {application, cameron, 5 | [{description, "Cameron Workflow Engine"}, 6 | {vsn, "0.0.1"}, 7 | {modules, [ 8 | cameron, 9 | cameron_app, 10 | cameron_sup, 11 | cameron_process_catalog, 12 | cameron_process_sup, 13 | cameron_job_data, 14 | cameron_job_scheduler, 15 | cameron_job_runner, 16 | cameron_web_server, 17 | cameron_web_api, 18 | cameron_deps 19 | ]}, 20 | {registered, []}, 21 | {mod, {cameron_app, []}}, 22 | {env, [{web_server, [{host, "0.0.0.0"}, 23 | {port, 8080}, 24 | {backlog, 128}, 25 | {docroot, "priv/www"}]}, 26 | {redis_server, [{host, "127.0.0.1"}, 27 | {port, 6379}]}]}, 28 | {applications, [kernel, stdlib]}]}. 29 | -------------------------------------------------------------------------------- /src/cameron.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Startup module for the Cameron application. 5 | 6 | -module(cameron). 7 | -author('Leandro Silva '). 8 | 9 | % admin api 10 | -export([start/0, stop/0, upgrade/0]). 11 | % public api 12 | -export([get_version/0, get_basedir/0, get_web_server_config/0, get_redis_server_config/0]). 13 | -export([get_processes_config/0]). 14 | 15 | %% 16 | %% Admin API -------------------------------------------------------------------------------------- 17 | %% 18 | 19 | %% @spec start() -> ok 20 | %% @doc Start the cameron server. 21 | start() -> 22 | cameron_deps:ensure(), 23 | application:start(?MODULE). 24 | 25 | %% @spec stop() -> ok 26 | %% @doc Stop the cameron server. 27 | stop() -> 28 | _Res = application:stop(?MODULE). 29 | 30 | %% @spec upgrade() -> ok 31 | %% @doc Upgrade the cameron server code. 32 | upgrade() -> 33 | upgrade_code(), 34 | upgrade_app(). 35 | 36 | %% 37 | %% Public API ------------------------------------------------------------------------------------- 38 | %% 39 | 40 | %% @spec get_version() -> string() 41 | %% @doc The Cameron version. 42 | get_version() -> 43 | get_key(vsn). 44 | 45 | %% @spec get_basedir() -> string() 46 | %% @doc Return the application directory for the cameron server. 47 | get_basedir() -> 48 | {file, Here} = code:is_loaded(?MODULE), 49 | filename:dirname(filename:dirname(Here)). 50 | 51 | %% @spec get_web_server_config() -> string() 52 | %% @doc The Cameron web server configuration. 53 | get_web_server_config() -> 54 | get_env(web_server). 55 | 56 | %% @spec get_redis_server_config() -> string() 57 | %% @doc Redis server configuration. 58 | get_redis_server_config() -> 59 | get_env(redis_server). 60 | 61 | %% @spec get_processes_config() -> {processes, [{process, {name, Name}, {start_activity_url, URL}}]} 62 | %% @doc All processes configuration. 63 | get_processes_config() -> 64 | {ok, [[ProcessesConfigFile]]} = init:get_argument(processes), 65 | {ok, [{processes, ProcessesConfig}]} = file:consult(ProcessesConfigFile), 66 | 67 | ProcessesConfig. 68 | 69 | %% 70 | %% Internal Functions ----------------------------------------------------------------------------- 71 | %% 72 | 73 | %% @spec get_key(Name) -> Val | undefined 74 | %% @doc Get a attribute info of this application. 75 | get_key(Name) -> 76 | case application:get_key(?MODULE, Name) of 77 | {ok, Value} -> 78 | Value; 79 | undefined -> 80 | undefined 81 | end. 82 | 83 | %% @spec get_env(Name) -> Val | undefined 84 | %% @doc Get a configuration parameter of this application. 85 | get_env(Name) -> 86 | case application:get_env(?MODULE, Name) of 87 | {ok, Value} -> 88 | Value; 89 | undefined -> 90 | undefined 91 | end. 92 | 93 | %% @spec upgrade_code() -> [{module, Module}] 94 | %% @doc Upgrade the cameron server code. 95 | upgrade_code() -> 96 | {ok, LoadedModules} = application:get_key(cameron, modules), 97 | 98 | [code:purge(Module) || Module <- LoadedModules], 99 | [code:load_file(Module) || Module <- LoadedModules]. 100 | 101 | %% @spec upgrade_app() -> [{module, Module}] 102 | %% @doc Upgrade the cameron server application. 103 | upgrade_app() -> 104 | {ok, {AppName, _}} = application:get_key(?MODULE, mod), 105 | AppName:upgrade(). 106 | -------------------------------------------------------------------------------- /src/cameron_app.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Callbacks for the Cameron application. 5 | 6 | -module(cameron_app). 7 | -author('Leandro Silva '). 8 | 9 | -behaviour(application). 10 | 11 | % admin api 12 | -export([start/2, stop/1, upgrade/0]). 13 | 14 | %% 15 | %% Admin API -------------------------------------------------------------------------------------- 16 | %% 17 | 18 | %% @spec start(_Type, _StartArgs) -> {ok, SupervisorPid} | ignore | {error, Error} 19 | %% @doc application start callback for cameron. 20 | start(_Type, _StartArgs) -> 21 | cameron_deps:ensure(), 22 | inets:start(), 23 | cameron_sup:start_link(). 24 | 25 | %% @spec stop(_State) -> ok 26 | %% @doc application stop callback for cameron. 27 | stop(_State) -> 28 | % inets:stop(), 29 | ok. 30 | 31 | %% @spec upgrade() -> ok 32 | %% @doc Upgrade the Cameron application code. 33 | upgrade() -> 34 | cameron_sup:upgrade(). 35 | -------------------------------------------------------------------------------- /src/cameron_deps.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Ensure that the relatively-installed dependencies are on the code 5 | %% loading path, and locate resources relative 6 | %% to this application's path. 7 | 8 | -module(cameron_deps). 9 | -author('Leandro Silva '). 10 | 11 | % public api 12 | -export([ensure/0, ensure/1]). 13 | -export([get_base_dir/0, get_base_dir/1]). 14 | -export([local_path/1, local_path/2]). 15 | -export([deps_on_path/0, new_siblings/1]). 16 | 17 | %% 18 | %% Public API ------------------------------------------------------------------------------------- 19 | %% 20 | 21 | %% @spec ensure() -> ok 22 | %% @doc Ensure that the ebin and include paths for dependencies of 23 | %% this application are on the code path. Equivalent to 24 | %% ensure(?MODULE). 25 | ensure() -> 26 | ensure(?MODULE). 27 | 28 | %% @spec ensure(Module) -> ok 29 | %% @doc Ensure that all ebin and include paths for dependencies 30 | %% of the application for Module are on the code path. 31 | ensure(Module) -> 32 | code:add_paths(new_siblings(Module)), 33 | code:clash(), 34 | ok. 35 | 36 | %% @spec get_base_dir(Module) -> string() 37 | %% @doc Return the application directory for Module. It assumes Module is in 38 | %% a standard OTP layout application in the ebin or src directory. 39 | get_base_dir(Module) -> 40 | {file, Here} = code:is_loaded(Module), 41 | filename:dirname(filename:dirname(Here)). 42 | 43 | %% @spec get_base_dir() -> string() 44 | %% @doc Return the application directory for this application. Equivalent to 45 | %% get_base_dir(?MODULE). 46 | get_base_dir() -> 47 | get_base_dir(?MODULE). 48 | 49 | %% @spec local_path(Components) -> string() 50 | %% @doc Return an application-relative directory for this application. 51 | %% Equivalent to local_path(Components, ?MODULE). 52 | local_path(Components) -> 53 | local_path(Components, ?MODULE). 54 | 55 | %% @spec local_path([string()], Module) -> string() 56 | %% @doc Return an application-relative directory from Module's application. 57 | local_path(Components, Module) -> 58 | filename:join([get_base_dir(Module) | Components]). 59 | 60 | %% @spec deps_on_path() -> [ProjNameAndVers] 61 | %% @doc List of project dependencies on the path. 62 | deps_on_path() -> 63 | F = fun (X, Acc) -> 64 | ProjDir = filename:dirname(X), 65 | case {filename:basename(X), 66 | filename:basename(filename:dirname(ProjDir))} of 67 | {"ebin", "deps"} -> 68 | [filename:basename(ProjDir) | Acc]; 69 | _ -> 70 | Acc 71 | end 72 | end, 73 | ordsets:from_list(lists:foldl(F, [], code:get_path())). 74 | 75 | %% @spec new_siblings(Module) -> [Dir] 76 | %% @doc Find new siblings paths relative to Module that aren't already on the 77 | %% code path. 78 | new_siblings(Module) -> 79 | Existing = deps_on_path(), 80 | SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), 81 | Siblings = [filename:dirname(X) || X <- SiblingEbin, 82 | ordsets:is_element( 83 | filename:basename(filename:dirname(X)), 84 | Existing) =:= false], 85 | lists:filter(fun filelib:is_dir/1, 86 | lists:append([[filename:join([X, "ebin"]), 87 | filename:join([X, "include"])] || 88 | X <- Siblings])). 89 | -------------------------------------------------------------------------------- /src/cameron_job_scheduler.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Generic server responsable to create process instances (a.k.a. jobs), enqueue it, and 5 | %% dispatch to cameron_job_runner, whose which runs it. 6 | 7 | -module(cameron_job_scheduler). 8 | -author('Leandro Silva '). 9 | 10 | -behaviour(gen_server). 11 | 12 | % admin api 13 | -export([start_link/0, stop/0]). 14 | % public api 15 | -export([schedule/2]). 16 | % gen_server callbacks 17 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 18 | 19 | %% 20 | %% Includes and Records --------------------------------------------------------------------------- 21 | %% 22 | 23 | -include("cameron.hrl"). 24 | 25 | -record(state, {}). 26 | 27 | %% 28 | %% Admin API -------------------------------------------------------------------------------------- 29 | %% 30 | 31 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 32 | %% @doc Start cameron_job_scheduler generic server. 33 | start_link() -> 34 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 35 | 36 | %% @spec stop() -> ok 37 | %% @doc Manually stops the server. 38 | stop() -> 39 | gen_server:cast(?MODULE, stop). 40 | 41 | %% 42 | %% Public API ------------------------------------------------------------------------------------- 43 | %% 44 | 45 | %% @spec schedule(Process, {Key, Data, Requestor}) -> {ok, NewUUID} | {error, Reason} 46 | %% @doc It triggers an async schedule of a resquest to create a new job and enqueue it to run. 47 | schedule(Process, {Key, Data, Requestor}) -> 48 | {ok, NewJob} = cameron_job_data:create_new_job(Process, {Key, Data, Requestor}), 49 | ok = gen_server:cast(?MODULE, {dispatch_new_job, NewJob}), 50 | {ok, NewJob#job.uuid}. 51 | 52 | %% 53 | %% Gen_Server Callbacks --------------------------------------------------------------------------- 54 | %% 55 | 56 | %% @spec init(_Options) -> {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason} 57 | %% @doc Initiates the server. 58 | init(_Options) -> 59 | {ok, #state{}}. 60 | 61 | %% @spec handle_call(Request, From, State) -> 62 | %% {reply, Reply, State} | {reply, Reply, State, Timeout} | {noreply, State} | 63 | %% {noreply, State, Timeout} | {stop, Reason, Reply, State} | {stop, Reason, State} 64 | %% @doc Handling call messages. 65 | 66 | % handle_call generic fallback 67 | handle_call(_Request, _From, State) -> 68 | {reply, undefined, State}. 69 | 70 | %% @spec handle_cast(Msg, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 71 | %% @doc Handling cast messages. 72 | 73 | % schedules a new job to be done 74 | handle_cast({dispatch_new_job, NewJob}, State) -> 75 | ok = cameron_job_runner:run_job(NewJob), 76 | {noreply, State}; 77 | 78 | % manual shutdown 79 | handle_cast(stop, State) -> 80 | {stop, normal, State}; 81 | 82 | % handle_cast generic fallback (ignore) 83 | handle_cast(_Msg, State) -> 84 | {noreply, State}. 85 | 86 | %% @spec handle_info(Info, State) -> 87 | %% {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 88 | %% @doc Handling all non call/cast messages. 89 | 90 | % handle_info generic fallback (ignore) 91 | handle_info(_Info, State) -> 92 | {noreply, State}. 93 | 94 | %% @spec terminate(Reason, State) -> void() 95 | %% @doc This function is called by a gen_server when it is about to terminate. When it returns, 96 | %% the gen_server terminates with Reason. The return value is ignored. 97 | terminate(_Reason, _State) -> 98 | ok. 99 | 100 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 101 | %% @doc Convert process state when code is changed. 102 | code_change(_OldVsn, State, _Extra) -> 103 | {ok, State}. 104 | 105 | %% 106 | %% Internal Functions ----------------------------------------------------------------------------- 107 | %% 108 | -------------------------------------------------------------------------------- /src/cameron_process_catalog.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Catalog of available workflow processes. 5 | 6 | -module(cameron_process_catalog). 7 | -author('Leandro Silva '). 8 | 9 | -behaviour(gen_server). 10 | 11 | % admin api 12 | -export([start_link/1, stop/0]). 13 | % public api 14 | -export([lookup/1]). 15 | % gen_server callbacks 16 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 17 | 18 | %% 19 | %% Includes and Records --------------------------------------------------------------------------- 20 | %% 21 | 22 | -include("cameron.hrl"). 23 | 24 | -record(state, {processes = []}). 25 | 26 | %% 27 | %% Admin API -------------------------------------------------------------------------------------- 28 | %% 29 | 30 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 31 | %% @doc Start cameron_process_catalog generic server. 32 | start_link(Processes) -> 33 | gen_server:start_link({local, ?MODULE}, ?MODULE, [Processes], []). 34 | 35 | %% @spec stop() -> ok 36 | %% @doc Manually stops the server. 37 | stop() -> 38 | gen_server:cast(?MODULE, stop). 39 | 40 | %% 41 | %% Public API ------------------------------------------------------------------------------------- 42 | %% 43 | 44 | %% @spec lookup(Name) -> Process | undefined 45 | %% @doc Get a process configuration by name. 46 | lookup(Name) when is_atom(Name) -> 47 | gen_server:call(?MODULE, {lookup, Name}); 48 | 49 | lookup(Name) when is_list(Name) -> 50 | lookup(list_to_atom(Name)). 51 | 52 | %% 53 | %% Gen_Server Callbacks --------------------------------------------------------------------------- 54 | %% 55 | 56 | %% @spec init(Processes) -> {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason} 57 | %% @doc Initiates the server. 58 | init([Processes]) -> 59 | {ok, #state{processes = Processes}}. 60 | 61 | %% @spec handle_call(Request, From, State) -> 62 | %% {reply, Reply, State} | {reply, Reply, State, Timeout} | {noreply, State} | 63 | %% {noreply, State, Timeout} | {stop, Reason, Reply, State} | {stop, Reason, State} 64 | %% @doc Handling call messages. 65 | 66 | % get an available process by name 67 | handle_call({lookup, Name}, _From, State) -> 68 | Spec = proplists:get_value(Name, State#state.processes, undefined), 69 | 70 | case Spec of 71 | undefined -> 72 | {reply, undefined, State}; 73 | {start_activity_url, URL} -> 74 | {reply, #process_definition{name = Name, 75 | start_activity = #activity_definition{name = "start", 76 | url = URL}}, State} 77 | end; 78 | 79 | % handle_call generic fallback 80 | handle_call(_Request, _From, State) -> 81 | {reply, undefined, State}. 82 | 83 | %% @spec handle_cast(Msg, State) -> 84 | %% {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 85 | %% @doc Handling cast messages. 86 | 87 | % manual shutdown 88 | handle_cast(stop, State) -> 89 | {stop, normal, State}; 90 | 91 | % handle_cast generic fallback (ignore) 92 | handle_cast(_Msg, State) -> 93 | {noreply, State}. 94 | 95 | %% @spec handle_info(Info, State) -> 96 | %% {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 97 | %% @doc Handling all non call/cast messages. 98 | 99 | % handle_info generic fallback (ignore) 100 | handle_info(_Info, State) -> 101 | {noreply, State}. 102 | 103 | %% @spec terminate(Reason, State) -> void() 104 | %% @doc This function is called by a gen_server when it is about to terminate. When it returns, 105 | %% the gen_server terminates with Reason. The return value is ignored. 106 | terminate(_Reason, _State) -> 107 | ok. 108 | 109 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 110 | %% @doc Convert process state when code is changed. 111 | code_change(_OldVsn, State, _Extra) -> 112 | {ok, State}. 113 | 114 | %% 115 | %% Internal Functions ----------------------------------------------------------------------------- 116 | %% 117 | -------------------------------------------------------------------------------- /src/cameron_process_sup.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Supervisor for generic servers on process domain. 5 | 6 | -module(cameron_process_sup). 7 | -author('Leandro Silva '). 8 | 9 | -behaviour(supervisor). 10 | 11 | % admin api 12 | -export([start_link/0, upgrade/0]). 13 | % public api 14 | -export([start_child/1, stop_child/1, which_children/0]). 15 | % supervisor callback 16 | -export([init/1]). 17 | 18 | %% 19 | %% Includes and Records --------------------------------------------------------------------------- 20 | %% 21 | 22 | -include("cameron.hrl"). 23 | 24 | %% 25 | %% Admin API -------------------------------------------------------------------------------------- 26 | %% 27 | 28 | %% @spec start_link() -> Result = {ok, Pid} | ignore | {error, Error} 29 | %% @doc API for starting the supervisor. 30 | start_link() -> 31 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 32 | 33 | %% @spec upgrade() -> ok 34 | %% @doc Remove and add processes if necessary. 35 | upgrade() -> 36 | supervisor_utility:upgrade(?MODULE). 37 | 38 | %% 39 | %% Public API ------------------------------------------------------------------------------------- 40 | %% 41 | 42 | %% @spec start_child(Job) -> {ok, ChildPid} | {ok, ChildPid, Info} | {error, Error} 43 | %% @dynamic Start a cameron_{UUID} process to do a job given. 44 | start_child(#job{} = Job) -> 45 | Pname = pname(Job), 46 | 47 | ProcessSpec = {Pname, {cameron_job_runner, start_link, [Pname, Job]}, temporary, 5000, worker, dynamic}, 48 | supervisor:start_child(cameron_process_sup, ProcessSpec). 49 | 50 | %% @spec stop_child(Job) -> ok | {error, Error} 51 | %% @dynamic Stop a cameron_{UUID}. 52 | stop_child(#job{} = Job) -> 53 | stop_child(pname(Job)); 54 | 55 | stop_child(UUID) when is_list(UUID) -> 56 | stop_child(pname(UUID)); 57 | 58 | stop_child(Pname) when is_atom(Pname) -> 59 | supervisor:terminate_child(cameron_process_sup, Pname), 60 | supervisor:delete_child(cameron_process_sup, Pname). 61 | 62 | %% @spec which_children() -> [ChildSpec] | {error, Error} 63 | %% @dynamic List of children workers. 64 | which_children() -> 65 | supervisor:which_children(cameron_process_sup). 66 | 67 | %% 68 | %% Supervisor Callback ---------------------------------------------------------------------------- 69 | %% 70 | 71 | %% @spec init([]) -> SupervisorTree = {ok, {SupervisorSpec, [ChildSpec]}} | ignore 72 | %% 73 | %% Types: 74 | %% 75 | %% SupervisorSpec = {RestartStrategy, AllowedRestarts, MaxSeconds} 76 | %% ChildSpec = {Id, {Module, Function, Arguments}, Restart, Shutdown, Type, ModuleList} 77 | %% 78 | %% @doc supervisor callback. 79 | init([]) -> 80 | ProcessCatalogConfig = cameron:get_processes_config(), 81 | ProcessCatalog = {cameron_process_catalog, {cameron_process_catalog, start_link, [ProcessCatalogConfig]}, 82 | permanent, 5000, worker, dynamic}, 83 | 84 | JobData = {cameron_job_data, {cameron_job_data, start_link, []}, 85 | permanent, 5000, worker, dynamic}, 86 | 87 | JobScheduler = {cameron_job_scheduler, {cameron_job_scheduler, start_link, []}, 88 | permanent, 5000, worker, dynamic}, 89 | 90 | {ok, {{one_for_one, 10, 10}, [ProcessCatalog, JobData, JobScheduler]}}. 91 | 92 | %% 93 | %% Internal Functions ----------------------------------------------------------------------------- 94 | %% 95 | 96 | pname(#job{uuid = UUID}) -> 97 | pname(UUID); 98 | 99 | pname(UUID) when is_list(UUID) -> 100 | ?pname(UUID). 101 | -------------------------------------------------------------------------------- /src/cameron_protocol.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc JSON-based protocol. 5 | 6 | -module(cameron_protocol). 7 | -author('Leandro Silva '). 8 | 9 | % api for web api 10 | -export([parse_request_payload/1, build_response_payload/2]). 11 | % api for task execution 12 | -export([build_request_payload/2, parse_response_payload/1]). 13 | 14 | %% 15 | %% Includes and Records --------------------------------------------------------------------------- 16 | %% 17 | 18 | -include("cameron.hrl"). 19 | 20 | %% @spec parse_request_payload(Payload) -> {Key, Data, Requestor} | {error, Reason} 21 | %% @doc Parses a JSON string from client request. 22 | parse_request_payload(Payload) -> 23 | Struct = struct:from_json(Payload), 24 | 25 | Key = struct:get_value(<<"key">>, Struct, {format, list}), 26 | Data = struct:get_value(<<"data">>, Struct, {format, list}), 27 | Requestor = struct:get_value(<<"requestor">>, Struct, {format, list}), 28 | 29 | {Key, Data, Requestor}. 30 | 31 | %% @spec build_response_payload({ProcessName, Key, UUID}, Data) -> Struct | {error, Reason} 32 | %% @doc Builds JSON string to respond a client request. 33 | build_response_payload({ProcessName, Key, UUID}, Data) -> 34 | Struct = {struct, [{<<"process">>, eh_maybe:maybe_binary(ProcessName)}, 35 | {<<"uuid">>, eh_maybe:maybe_binary(UUID)}, 36 | {<<"key">>, eh_maybe:maybe_binary(Key)}, 37 | {<<"requestor">>, get_job_value(<<"requestor">>, Data)}, 38 | {<<"status">>, expand_job_status(Data)}, 39 | {<<"tasks">>, expand_job_tasks(Data)}]}, 40 | struct:to_json(Struct). 41 | 42 | %% @spec build_request_payload(Job, {Data, Requestor}) -> JSONString | {error, Reason} 43 | %% @doc Builds a JSON string to request a task execution. 44 | build_request_payload(Job, {Data, Requestor}) -> 45 | #job{uuid = UUID, 46 | input = #job_input{key = Key}} = Job, 47 | 48 | RequestPayload = struct:to_json({struct, [{<<"job">>, list_to_binary(UUID)}, 49 | {<<"key">>, list_to_binary(Key)}, 50 | {<<"data">>, list_to_binary(Data)}, 51 | {<<"requestor">>, list_to_binary(Requestor)}]}), 52 | 53 | unicode:characters_to_list(RequestPayload). 54 | 55 | %% @spec build_request_payload(Job, {Data, Requestor}) -> JSONString | {error, Reason} 56 | %% @doc Parses a JSON string of a task result. 57 | parse_response_payload(ResponsePayload) -> 58 | Struct = struct:from_json(ResponsePayload), 59 | 60 | Name = struct:get_value(<<"name">>, Struct, {format, list}), 61 | Data = struct:get_value(<<"data">>, Struct, {format, json}), 62 | NextActivities = struct:get_value(<<"next_activities">>, Struct, {format, json}), 63 | 64 | {Name, Data, NextActivities}. 65 | 66 | %% 67 | %% Internal Functions ----------------------------------------------------------------------------- 68 | %% 69 | 70 | % --- helpers to build_response_payload function -------------------------------------------------- 71 | 72 | expand_job_status(Data) -> 73 | Status = get_job_value(<<"status.current">>, Data), 74 | Time = get_job_value([<<"status.">>, Status, <<".time">>], Data), 75 | 76 | {struct, [{<<"current">>, Status}, 77 | {<<"time">>, Time}]}. 78 | 79 | expand_job_tasks(Data) -> 80 | Tasks = get_job_tasks(Data), 81 | expand_job_tasks(Tasks, Data, []). 82 | 83 | expand_job_tasks([Task | Others], Data, Acc) -> 84 | Struct = {struct, [{<<"name">>, eh_maybe:maybe_binary(Task)}, 85 | {<<"requestor">>, get_task_value(Task, <<"requestor">>, Data)}, 86 | {<<"status">>, expand_task_status(Task, Data)}, 87 | {<<"data">>, expand_task_output(Task, Data)}]}, 88 | expand_job_tasks(Others, Data, [Struct | Acc]); 89 | 90 | expand_job_tasks([], _Data, Acc) -> 91 | lists:reverse(Acc). 92 | 93 | expand_task_status(Task, Data) -> 94 | Status = get_task_value(Task, <<"status.current">>, Data), 95 | Time = get_task_value(Task, [<<"status.">>, Status, <<".time">>], Data), 96 | 97 | {struct, [{<<"current">>, Status}, 98 | {<<"time">>, Time}]}. 99 | 100 | expand_task_output(Task, Data) -> 101 | case get_task_value(Task, <<"output.data">>, Data) of 102 | undefined -> 103 | <<"nothing yet">>; 104 | Binary -> 105 | String = binary_to_list(Binary), 106 | try struct:from_json(String) catch _:_ -> Binary end 107 | end. 108 | 109 | get_value(Key, Data) when is_list(Key) -> 110 | BinaryKey = list_to_binary(Key), 111 | get_value(BinaryKey, Data); 112 | 113 | get_value(Key, Data) -> 114 | proplists:get_value(Key, Data). 115 | 116 | get_job_value(Key, Data) -> 117 | get_value([<<"job.">>, Key], Data). 118 | 119 | get_job_tasks(Data) -> 120 | case proplists:get_value(<<"job.tasks">>, Data) of 121 | undefined -> []; 122 | Tasks -> re:split(Tasks, ",") 123 | end. 124 | 125 | get_task_value(Task, Key, Data) -> 126 | get_value([<<"task">>, <<".">>, Task, <<".">>, Key], Data). 127 | -------------------------------------------------------------------------------- /src/cameron_sup.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc Top-level supervisor for the Cameron application. 5 | 6 | -module(cameron_sup). 7 | -author('Leandro Silva '). 8 | 9 | -behaviour(supervisor). 10 | 11 | % admin api 12 | -export([start_link/0, upgrade/0]). 13 | % supervisor callback 14 | -export([init/1]). 15 | 16 | %% 17 | %% Admin API -------------------------------------------------------------------------------------- 18 | %% 19 | 20 | %% @spec start_link() -> Result = {ok, Pid} | ignore | {error, Error} 21 | %% @doc API for starting the supervisor. 22 | start_link() -> 23 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 24 | 25 | %% @spec upgrade() -> ok 26 | %% @doc Remove and add processes if necessary. 27 | upgrade() -> 28 | eh_supervisor:upgrade(?MODULE). 29 | 30 | %% 31 | %% Supervisor Callback ---------------------------------------------------------------------------- 32 | %% 33 | 34 | %% @spec init([]) -> SupervisorTree = {ok, {SupervisorSpec, [ChildSpec]}} | ignore 35 | %% 36 | %% Types: 37 | %% 38 | %% SupervisorSpec = {RestartStrategy, AllowedRestarts, MaxSeconds} 39 | %% ChildSpec = {Id, {Module, Function, Arguments}, Restart, Shutdown, Type, ModuleList} 40 | %% 41 | %% @doc supervisor callback. 42 | init([]) -> 43 | ProcessSup = {cameron_process_sup, {cameron_process_sup, start_link, []}, 44 | permanent, 5000, supervisor, dynamic}, 45 | 46 | WebServerConfig = cameron:get_web_server_config(), 47 | WebServer = {cameron_web_server, {cameron_web_server, start_link, [WebServerConfig]}, 48 | permanent, 5000, worker, dynamic}, 49 | 50 | RedoConfig = cameron:get_redis_server_config(), 51 | Redo = {cameron_redo, {redo, start_link, [cameron_redo, RedoConfig]}, 52 | permanent, 5000, worker, dynamic}, 53 | 54 | SyslogConfig = [cameron_syslog, cameron, "localhost", 514, local0], 55 | Syslog = {cameron_syslog, {syslog, start_link, SyslogConfig}, 56 | permanent, 5000, worker, dynamic}, 57 | 58 | {ok, {{one_for_one, 10, 10}, [ProcessSup, WebServer, Redo, Syslog]}}. 59 | -------------------------------------------------------------------------------- /src/cameron_web_api.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc The misultin-based web handler module for handle HTTP requests at Cameron web API. 5 | 6 | -module(cameron_web_api). 7 | -author('Leandro Silva '). 8 | 9 | % misultin web handler callbacks 10 | -export([handle_http/3]). 11 | 12 | %% 13 | %% Includes and Records --------------------------------------------------------------------------- 14 | %% 15 | 16 | -include("cameron.hrl"). 17 | -include("misultin.hrl"). 18 | 19 | %% 20 | %% Misultin-based Callbacks for cameron_web_api --------------------------------------------------- 21 | %% 22 | 23 | % --- HTTP Routes to support handle_http callback ------------------------------------------------- 24 | 25 | % handle a GET on /api 26 | handle_http('GET', ["api"], HttpRequest) -> 27 | HttpRequest:ok([{"Content-Type", "text/plain"}], "Cameron Workflow Engine // Web API"); 28 | 29 | handle_http('POST', ["api", "process", ProcessName, "start"], HttpRequest) -> 30 | build_http_response(HttpRequest, {start_process, ProcessName}); 31 | 32 | % handle a GET on /api/process/{name}/key/{key}/job/{uuid} 33 | handle_http('GET', ["api", "process", ProcessName, "key", Key, "job", UUID], HttpRequest) -> 34 | build_http_response(HttpRequest, {get_job_data, ProcessName, Key, UUID}); 35 | 36 | % handle the 404 page not found 37 | handle_http(_, _, HttpRequest) -> 38 | HttpRequest:respond(404, [{"Content-Type", "text/plain"}], "Page not found."). 39 | 40 | %% 41 | %% Internal Functions ----------------------------------------------------------------------------- 42 | %% 43 | 44 | % --- helpers to POST on /api/process/{name}/start ------------------------------------------------ 45 | 46 | get_request_payload(HttpRequest) -> 47 | #req{body = Body} = HttpRequest:raw(), 48 | binary_to_list(Body). 49 | 50 | build_http_response(HttpRequest, {start_process, ProcessName}) -> 51 | RequestPayload = get_request_payload(HttpRequest), 52 | Process = cameron_process_catalog:lookup(ProcessName), 53 | build_http_response(start_process, {HttpRequest, RequestPayload}, {ProcessName, Process}); 54 | 55 | build_http_response(HttpRequest, {get_job_data, ProcessName, Key, UUID}) -> 56 | JobData = cameron_job_data:get_job_data(ProcessName, Key, UUID), 57 | build_http_response(get_job_data, HttpRequest, {ProcessName, Key, UUID}, JobData). 58 | 59 | build_http_response(start_process, {HttpRequest, RequestPayload}, {_ProcessName, undefined}) -> 60 | HttpRequest:respond(404, [{"Content-Type", "application/json"}], 61 | "{\"payload\":\"~s\"}", [RequestPayload]); 62 | 63 | build_http_response(start_process, {HttpRequest, RequestPayload}, {ProcessName, Process}) -> 64 | {Key, Data, Requestor} = cameron_protocol:parse_request_payload(RequestPayload), 65 | {ok, UUID} = cameron_job_scheduler:schedule(Process, {Key, Data, Requestor}), 66 | 67 | HttpRequest:respond(201, [{"Content-Type", "application/json"}, 68 | {"Location", ["http://localhost:8080/api/process/", ProcessName, 69 | "/key/", Key, 70 | "/job/", UUID]}], 71 | "{\"payload\":\"~s\"}", [RequestPayload]). 72 | 73 | build_http_response(get_job_data, HttpRequest, {_ProcessName, _Key, _UUID}, undefined) -> 74 | HttpRequest:respond(404, [{"Content-Type", "text/plain"}], "Job not found."); 75 | 76 | build_http_response(get_job_data, HttpRequest, {ProcessName, Key, UUID}, {_, JobData}) -> 77 | Payload = cameron_protocol:build_response_payload({ProcessName, Key, UUID}, JobData), 78 | HttpRequest:respond(200, [{"Content-Type", "application/json"}], "~s", [Payload]). 79 | 80 | -------------------------------------------------------------------------------- /src/cameron_web_server.erl: -------------------------------------------------------------------------------- 1 | %% @author Leandro Silva 2 | %% @copyright 2011 Leandro Silva. 3 | 4 | %% @doc The misultin-based web server module of the Cameron application that handles HTTP requests 5 | %% and WebSocket. 6 | 7 | -module(cameron_web_server). 8 | -author('Leandro Silva '). 9 | 10 | -behaviour(gen_server). 11 | 12 | % admin api 13 | -export([start_link/1, stop/0]). 14 | % public api 15 | -export([get_host/0, get_port/0, get_backlog/0, get_docroot/0]). 16 | % gen_server callbacks 17 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 18 | 19 | %% 20 | %% Includes and Records --------------------------------------------------------------------------- 21 | %% 22 | 23 | -record(state, {host, port, backlog, docroot}). 24 | 25 | %% 26 | %% Admin API -------------------------------------------------------------------------------------- 27 | %% 28 | 29 | %% @spec start_link(WebConfig) -> {ok, Pid} | ignore | {error, Error} 30 | %% @doc Start misultin HTTP server. 31 | start_link(WebConfig) -> 32 | gen_server:start_link({local, ?MODULE}, ?MODULE, WebConfig, []). 33 | 34 | %% @spec stop() -> ok 35 | %% @doc Manually stops the server. 36 | stop() -> 37 | gen_server:cast(?MODULE, stop). 38 | 39 | %% 40 | %% Public API ------------------------------------------------------------------------------------- 41 | %% 42 | 43 | %% @spec get_host() -> string() 44 | %% @doc Host property from configuration properties. 45 | get_host() -> 46 | gen_server:call(?MODULE, {get, host}). 47 | 48 | %% @spec get_port() -> int() 49 | %% @doc Port property from configuration properties. 50 | get_port() -> 51 | gen_server:call(?MODULE, {get, port}). 52 | 53 | %% @spec get_backlog() -> int() 54 | %% @doc Backlog property from configuration properties. 55 | get_backlog() -> 56 | gen_server:call(?MODULE, {get, backlog}). 57 | 58 | %% @spec get_docroot() -> string() 59 | %% @doc DocRoot property from configuration properties. 60 | get_docroot() -> 61 | gen_server:call(?MODULE, {get, docroot}). 62 | 63 | %% 64 | %% Gen_Server Callbacks --------------------------------------------------------------------------- 65 | %% 66 | 67 | %% @spec init(WebConfig) -> {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason} 68 | %% @doc Initiates the server. 69 | init(WebConfig) -> 70 | process_flag(trap_exit, true), 71 | 72 | [{host, Host}, {port, Port}, {backlog, Backlog}, {docroot, DocRoot}] = WebConfig, 73 | 74 | misultin:start_link([{ip, Host}, 75 | {port, Port}, 76 | {backlog, Backlog}, 77 | {loop, fun(Req) -> handle_http(Req) end}, 78 | {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, 79 | {ws_autoexit, false}]), 80 | 81 | erlang:monitor(process, misultin), 82 | 83 | {ok, #state{host = Host, port = Port, backlog = Backlog, docroot = DocRoot}}. 84 | 85 | %% @spec handle_call(Request, From, State) -> 86 | %% {reply, Reply, State} | {reply, Reply, State, Timeout} | {noreply, State} | 87 | %% {noreply, State, Timeout} | {stop, Reason, Reply, State} | {stop, Reason, State} 88 | %% @doc Handling call messages. 89 | 90 | % return host property 91 | handle_call({get, host}, _From, State) -> 92 | {reply, State#state.host, State}; 93 | 94 | % return port property 95 | handle_call({get, port}, _From, State) -> 96 | {reply, State#state.port, State}; 97 | 98 | % return backlog property 99 | handle_call({get, backlog}, _From, State) -> 100 | {reply, State#state.backlog, State}; 101 | 102 | % return docroot property 103 | handle_call({get, docroot}, _From, State) -> 104 | {reply, State#state.docroot, State}; 105 | 106 | % handle_call generic fallback 107 | handle_call(_Request, _From, State) -> 108 | {reply, undefined, State}. 109 | 110 | %% @spec handle_cast(Msg, State) -> 111 | %% {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 112 | %% @doc Handling cast messages. 113 | 114 | % manual shutdown 115 | handle_cast(stop, State) -> 116 | {stop, normal, State}; 117 | 118 | % handle_cast generic fallback (ignore) 119 | handle_cast(_Msg, State) -> 120 | {noreply, State}. 121 | 122 | %% @spec handle_info(Info, State) -> 123 | %% {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State} 124 | %% @doc Handling all non call/cast messages. 125 | 126 | % handle info when misultin server goes down -> take down misultin_gen_server too [the supervisor 127 | % will take everything up again] 128 | handle_info({'DOWN', _Reference, process, {misultin, _}, _Reason}, State) -> 129 | {stop, normal, State}; 130 | 131 | % handle_info generic fallback (ignore) 132 | handle_info(_Info, State) -> 133 | {noreply, State}. 134 | 135 | %% @spec terminate(Reason, State) -> void() 136 | %% @doc This function is called by a gen_server when it is about to terminate. When it returns, 137 | %% the gen_server terminates with Reason. The return value is ignored. 138 | terminate(_Reason, _State) -> 139 | misultin:stop(), 140 | terminated. 141 | 142 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 143 | %% @doc Convert process state when code is changed. 144 | code_change(_OldVsn, State, _Extra) -> 145 | {ok, State}. 146 | 147 | %% 148 | %% Misultin Callbacks ----------------------------------------------------------------------------- 149 | %% 150 | 151 | % callback on HTTP request received 152 | handle_http(Req) -> 153 | Method = Req:get(method), 154 | Resource = Req:resource([lowercase, urldecode]), 155 | 156 | cameron_web_api:handle_http(Method, Resource, Req). 157 | 158 | % callback on received websockets data 159 | handle_websocket(Ws) -> 160 | Path = string:tokens(Ws:get(path), "/"), 161 | 162 | cameron_web_api:handle_websocket(Path, Ws). 163 | 164 | %% 165 | %% Internal Functions ----------------------------------------------------------------------------- 166 | %% 167 | -------------------------------------------------------------------------------- /test/foo_workflow/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .bundler -------------------------------------------------------------------------------- /test/foo_workflow/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "rack-api", "~> 0.2.1", :require => "rack/api" 4 | gem "json" 5 | -------------------------------------------------------------------------------- /test/foo_workflow/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activesupport (3.0.9) 5 | json (1.5.3) 6 | rack (1.3.0) 7 | rack-api (0.2.2) 8 | activesupport (>= 3.0.0) 9 | rack (>= 1.0.0) 10 | rack-mount (>= 0.6.0) 11 | rack-mount (0.8.1) 12 | rack (>= 1.0.0) 13 | 14 | PLATFORMS 15 | ruby 16 | 17 | DEPENDENCIES 18 | json 19 | rack-api (~> 0.2.1) 20 | -------------------------------------------------------------------------------- /test/foo_workflow/README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leandrosilva/cameron/34051395b620d2c3cb2cb63c854e65234786a176/test/foo_workflow/README -------------------------------------------------------------------------------- /test/foo_workflow/api/workflow.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | # Foo is an awesome fake process used to test stuffs. 4 | # 5 | Rack::API.app do 6 | prefix "foo" 7 | respond_to :json 8 | 9 | version "v0.0.1" do 10 | post "/start" do 11 | body = request.body.read 12 | payload = JSON.parse(body) 13 | 14 | puts "[Request :: start] #{payload}" 15 | 16 | { 17 | process: "foo", 18 | name: "whois", 19 | requestor: payload["requestor"], 20 | 21 | data: { who_id: payload["key"], 22 | who_name: "Leandro Silva", 23 | who_login: "leandrosilva.codezone", 24 | who_web_info: { blog: "http://leandrosilva.com.br", 25 | twitter: "codezone" }, 26 | who_dev_info: { github: "http://github.com/leandrosilva" }}, 27 | 28 | next_activities: 29 | { 30 | definitions: 31 | [ 32 | { name: "act_1", 33 | url: "http://localhost:9292/foo/v0.0.1/activity/act_1" }, 34 | { name: "act_2", 35 | url: "http://localhost:9292/foo/v0.0.1/activity/act_2" }, 36 | { name: "act_3", 37 | url: "http://localhost:9292/foo/v0.0.1/activity/act_3" }, 38 | { name: "act_4", 39 | url: "http://localhost:9292/foo/v0.0.1/activity/act_4" }, 40 | { name: "act_5", 41 | url: "http://localhost:9292/foo/v0.0.1/activity/act_5" } 42 | ] 43 | } 44 | } 45 | end 46 | 47 | post "/activity/act_1" do 48 | body = request.body.read 49 | payload = JSON.parse(body) 50 | 51 | puts "[Request :: act_1] #{payload}" 52 | 53 | { 54 | process: "foo", 55 | name: "foo_act_1", 56 | requestor: payload["requestor"], 57 | 58 | data: { bar: "the likable bar", 59 | baz: { qux: "the awesome qux", 60 | quux: "the amazing quux", 61 | corge: "the great corge" }} 62 | } 63 | end 64 | 65 | post "/activity/act_2" do 66 | body = request.body.read 67 | payload = JSON.parse(body) 68 | 69 | puts "[Request :: act_2] #{payload}" 70 | 71 | { 72 | process: "foo", 73 | name: "foo_act_2", 74 | requestor: payload["requestor"], 75 | 76 | data: { bar: "the likable bar", 77 | baz: { qux: "the awesome qux", 78 | quux: "the amazing quux", 79 | corge: "the great corge" }} 80 | } 81 | end 82 | 83 | post "/activity/act_3" do 84 | body = request.body.read 85 | payload = JSON.parse(body) 86 | 87 | puts "[Request :: act_3] #{payload}" 88 | 89 | { 90 | process: "foo", 91 | name: "foo_act_3", 92 | requestor: payload["requestor"], 93 | 94 | data: { bar: "the likable bar", 95 | baz: { qux: "the awesome qux", 96 | quux: "the amazing quux", 97 | corge: "the great corge" }} 98 | } 99 | end 100 | 101 | post "/activity/act_4" do 102 | body = request.body.read 103 | payload = JSON.parse(body) 104 | 105 | puts "[Request :: act_4] #{payload}" 106 | 107 | { 108 | process: "foo", 109 | name: "foo_act_4", 110 | requestor: payload["requestor"], 111 | 112 | data: { bar: "the likable bar", 113 | baz: { qux: "the awesome qux", 114 | quux: "the amazing quux", 115 | corge: "the great corge" }} 116 | } 117 | end 118 | 119 | post "/activity/act_5" do 120 | body = request.body.read 121 | payload = JSON.parse(body) 122 | 123 | puts "[Request :: act_5] #{payload}" 124 | 125 | { 126 | process: "foo", 127 | name: "foo_act_4", 128 | requestor: payload["requestor"], 129 | 130 | data: { bar: "the likable bar", 131 | baz: { qux: "the awesome qux", 132 | quux: "the amazing quux", 133 | corge: "the great corge" }}, 134 | 135 | next_activities: 136 | { 137 | definitions: 138 | [ 139 | { name: "act_5_sub_1", 140 | url: "http://localhost:9292/foo/v0.0.1/activity/act_5_sub_1" } 141 | ] 142 | } 143 | } 144 | end 145 | 146 | post "/activity/act_5_sub_1" do 147 | body = request.body.read 148 | payload = JSON.parse(body) 149 | 150 | puts "[Request :: act_5_sub_1] #{payload}" 151 | 152 | { 153 | process: "foo", 154 | name: "foo_act_5_sub_1", 155 | requestor: payload["requestor"], 156 | 157 | data: { grault: "the likable grault", 158 | garply: { waldo: "the awesome waldo", 159 | fred: "the amazing fred", 160 | plugh: "the great plugh" }} 161 | } 162 | end 163 | end 164 | end 165 | -------------------------------------------------------------------------------- /test/foo_workflow/bin/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Running on $RUBY_VERSION" 4 | 5 | rackup config.ru 6 | -------------------------------------------------------------------------------- /test/foo_workflow/config.ru: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.expand_path(".")) 2 | 3 | require "config/init" 4 | 5 | run Rack::API 6 | -------------------------------------------------------------------------------- /test/foo_workflow/config/init.rb: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | 3 | require "bundler/setup" 4 | Bundler.require(:default) 5 | 6 | require "yaml" 7 | 8 | $ENVIRONMENT = ENV["RACK_ENV"] || "development" 9 | 10 | require "api/workflow" 11 | -------------------------------------------------------------------------------- /test/foo_workflow/test/script/request_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # for benchmark: 4 | # ab -n 10000 -c 50 -p benchmark_request.json http://localhost:8080/api/foo/ask 5 | 6 | curl -X POST http://localhost:9292/foo/v0.0.1/start \ 7 | -d '{"key":"(id,007)", "data":"be careful with that data", "requestor":"bob_the_thin"}' \ 8 | -i \ 9 | --header 'Content-Type: application/json' 10 | -------------------------------------------------------------------------------- /test/script/benchmark_request.json: -------------------------------------------------------------------------------- 1 | {"key":"(id,007)", "data":"be careful with that data", "requestor":"bob_the_thin"} 2 | -------------------------------------------------------------------------------- /test/script/benchmark_request.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # nooo! ~ 150 rps 4 | # ab -n 10000 -c 50 -p test/script/benchmark_request.json http://localhost:8080/api/process/foo/start 5 | 6 | # not so good ~ 55 rps 7 | # ab -n 10000 -c 5 -p test/script/benchmark_request.json http://localhost:8080/api/process/foo/start 8 | 9 | # ok ~ 500 rps 10 | ab -n 1000 -c 30 -p test/script/benchmark_request.json http://localhost:8080/api/process/foo/start 11 | -------------------------------------------------------------------------------- /test/script/request_for_404.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # for benchmark: 4 | # ab -n 10000 -c 50 -p benchmark_request.json http://localhost:8080/api/foo/ask 5 | 6 | curl -X POST http://localhost:8080/api/process/gime404/start \ 7 | -d '{"key":"(id,007)", "data":"be careful with that data", "requestor":"bob_the_thin"}' \ 8 | -i \ 9 | --header 'Content-Type: application/json' 10 | -------------------------------------------------------------------------------- /test/script/request_for_foo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # for benchmark: 4 | # ab -n 10000 -c 50 -p benchmark_request.json http://localhost:8080/api/foo/ask 5 | 6 | curl -X POST http://localhost:8080/api/process/foo/start \ 7 | -d '{"key":"(id,007)", "data":"be careful with that data", "requestor":"bob_the_thin"}' \ 8 | -i \ 9 | --header 'Content-Type: application/json' 10 | --------------------------------------------------------------------------------