diff --git a/dudeswave/Makefile b/dudeswave/Makefile new file mode 100644 index 0000000..160022d --- /dev/null +++ b/dudeswave/Makefile @@ -0,0 +1,7 @@ +.PHONY: all clean + +all: + ${MAKE} -C src + +clean: + ${MAKE} -C src clean diff --git a/dudeswave/ebin/dudeswave.app b/dudeswave/ebin/dudeswave.app new file mode 100644 index 0000000..ce137e4 --- /dev/null +++ b/dudeswave/ebin/dudeswave.app @@ -0,0 +1,12 @@ +{application,dudeswave, + [{description,"The dudeswave web experience"}, + {vsn,"1.0.0"}, + {modules,[dudeswave,dudeswave_app,dudeswave_handler,dudeswave_supervisor]}, + {registered,[]}, + {applications,[kernel,stdlib,erts,cowboy,ranch]}, + {mod,{dudeswave_app,[]}}, + {env, [ + {ip,"127.0.0.1"}, + {port,8080}, + ]}, + {start_phases,[]}]}. diff --git a/dudeswave/ebin/dudeswave.appE b/dudeswave/ebin/dudeswave.appE new file mode 100644 index 0000000..f550b3d --- /dev/null +++ b/dudeswave/ebin/dudeswave.appE @@ -0,0 +1,15 @@ +{application,dudeswave, + [{description,"The dudeswave web experience"}, + {vsn,"1.0.0"}, + {modules,[dudeswave,dudeswave_app,bigweb_handler,bigweb_supervisor]}, + {registered,[]}, + {applications,[kernel,stdlib,erts,cowboy,ranch]}, + {mod,{bigweb_app,[]}}, + {env, [ + {proxy_host,"gemini.abiscuola.com"}, + {ip,"127.0.0.1"}, + {port,10443}, + {certfile, "/var/_erlang/cert/www.abiscuola.com.crt"}, + {keyfile, "/var/_erlang/private/www.abiscuola.com.key"} + ]}, + {start_phases,[]}]}. diff --git a/dudeswave/src/Makefile b/dudeswave/src/Makefile new file mode 100644 index 0000000..7c52e6a --- /dev/null +++ b/dudeswave/src/Makefile @@ -0,0 +1,16 @@ +.PHONY: all clean +.SUFFIXES: .erl .beam + +ERLC?= erlc -server + +OBJS= dudeswave.beam dudeswave_app.beam +OBJS+= dudeswave_supervisor.beam dudeswave_handler.beam + +all: ${OBJS} + +.erl.beam: + ${ERLC} ${ERLOPTS} ${ERLFLAGS} $< + +clean: + rm -f *.beam + diff --git a/dudeswave/src/dudeswave.erl b/dudeswave/src/dudeswave.erl new file mode 100644 index 0000000..9893223 --- /dev/null +++ b/dudeswave/src/dudeswave.erl @@ -0,0 +1,46 @@ +% +% Copyright (c) 2024 Andrea Biscuola +% +% Permission to use, copy, modify, and distribute this software for any +% purpose with or without fee is hereby granted, provided that the above +% copyright notice and this permission notice appear in all copies. +% +% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +% +-module(dudeswave). + +-behaviour(gen_server). + +% Server start function +-export([start_link/0]). + +% Module callbacks +-export([init/1, handle_call/3, handle_cast/2, terminate/2]). + +% +% Startup functions +% + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + process_flag(trap_exit, true), + + {ok, 0}. + +% +% Callbacks +% + +handle_call(_Msg, _From, State) -> {noreply, State}. + +handle_cast(_Msg, State) -> {noreply, State}. + +terminate(_Reason, _N) -> ok. diff --git a/dudeswave/src/dudeswave_app.erl b/dudeswave/src/dudeswave_app.erl new file mode 100644 index 0000000..5d09989 --- /dev/null +++ b/dudeswave/src/dudeswave_app.erl @@ -0,0 +1,44 @@ +% +% Copyright (c) 2024 Andrea Biscuola +% +% Permission to use, copy, modify, and distribute this software for any +% purpose with or without fee is hereby granted, provided that the above +% copyright notice and this permission notice appear in all copies. +% +% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +% +-module(dudeswave_app). +-behaviour(application). + +-export([start/2, stop/1]). + +start(_Type, StartArgs) -> + {ok, Addr} = case application:get_env(ip) of + {ok, AddrConf} -> inet:parse_address(AddrConf); + undefined -> undefined + end, + + {ok, Port} = application:get_env(port), + {ok, Bucket} = application:get_env(bucket), + + Dispatch = cowboy_router:compile([ + {'_', [{"/", dudeswave_handler, #{bucket => Bucket}}]} + ]), + + {ok, ListenerPid} = cowboy:start_tls(dudeswave_listener, [ + {port, Port}, + {ip, Addr} + ], #{env => #{dispatch => Dispatch}}), + + link(ListenerPid), + + dudeswave_supervisor:start_link(StartArgs). + +stop(_State) -> + cowboy:stop_listener(dudeswave_listener). \ No newline at end of file diff --git a/dudeswave/src/dudeswave_handler.erl b/dudeswave/src/dudeswave_handler.erl new file mode 100644 index 0000000..7d45eaf --- /dev/null +++ b/dudeswave/src/dudeswave_handler.erl @@ -0,0 +1,89 @@ +% +% Copyright (c) 2024 Andrea Biscuola +% +% Permission to use, copy, modify, and distribute this software for any +% purpose with or without fee is hereby granted, provided that the above +% copyright notice and this permission notice appear in all copies. +% +% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +% +-module(dudeswave_handler). +-moduledoc """ +The dudeswave GET handler. + +This module handles only fetching and returning resources. Anything +else is considered a bad request, apart from the obvious case of 500 +(internal server error) and 404 (not found). +""". + +-behaviour(cowboy_handler). + +-export([init/2, terminate/3]). + +-include_lib("storage/include/storage.hrl"). + +% +% Protocol functions +% + +init(Req, State) -> + {ok, Bucket} = maps:find(bucket, State), + Path = cowboy_req:path(Req), + + case cowboy_req:method(Req) of + <<"GET">> -> + % + % Fetch the page from storage + % + case storage:read(Bucket, Path) of + {ok, []} -> + Resp = cowboy_req:reply(404, + #{<<"content-type">> => <<"text/plain">>}, <<"Not found">>, Req), + {ok, Resp, State}; + % + % If we get a resource, send it back to the client with + % it's proper mime type. + % + {ok, [R]} -> + {Mime, Bin} = get_final_object(R), + Resp = cowboy_req:reply(200, #{<<"content-type">> => Mime}, + Bin, Req), + {ok, Resp, State}; + {error, _} -> + Resp = cowboy_req:reply(500, #{<<"content-type">> => <<"text/plain">>}, + <<"Internal server error">>, Req), + {ok, Resp, State} + end; + _ -> + Resp = cowboy_req:reply(400, #{<<"content-type">> => <<"text/plain">>}, + <<"Bad request">>, Req), + {ok, Resp, State} + end. + +terminate(_Reason, _Req, _State) -> ok. + +-spec get_final_object(Record) -> {Mime, Bin} when + Record :: storage:object(), + Mime :: binary(), + Bin :: binary(). + +get_final_object(Record) -> + case lists:keyfind(mime, 1, Record#object.metadata) of + % + % We do not know the object mime type. This should never + % happen. + % + false -> + {<<"application/octect-stream">>, Record#object.value}; + % + % Everything else, just return it as is. + % + {mime, Mime} -> + {list_to_binary(Mime), Record#object.value} + end. \ No newline at end of file diff --git a/dudeswave/src/dudeswave_supervisor.erl b/dudeswave/src/dudeswave_supervisor.erl new file mode 100644 index 0000000..dad62ce --- /dev/null +++ b/dudeswave/src/dudeswave_supervisor.erl @@ -0,0 +1,31 @@ +% +% Copyright (c) 2024 Andrea Biscuola +% +% Permission to use, copy, modify, and distribute this software for any +% purpose with or without fee is hereby granted, provided that the above +% copyright notice and this permission notice appear in all copies. +% +% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +% +-module(dudeswave_supervisor). +-behaviour(supervisor). + +-export([start/0, + start_link/1, + init/1]). + +start() -> + spawn(fun() -> supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []) end). + +start_link(Args) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, Args). + +init([]) -> + {ok, {{one_for_one, 3, 10}, [{tag1, {dudeswave, start_link, []}, permanent, + 10000, worker, [dudeswave]}]}}. \ No newline at end of file