Compare commits
4 Commits
9c5b332e00
...
3a8c2cf4b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a8c2cf4b0 | ||
|
|
e6fa8481e8 | ||
|
|
f0725700f0 | ||
|
|
2037444f54 |
242
README.md
242
README.md
@ -563,3 +563,245 @@ start_acceptor(ListenSocket) ->
|
||||
- Everything else is boilerplate
|
||||
- So our task is to remove the relay-messages logic, and replace it with http
|
||||
parse/respond logic.
|
||||
|
||||
|
||||
### Auto-listening to port `8000`
|
||||
|
||||
- Start commit: `9c5b332e009b88a9400a4e7008f760ce872b810a`
|
||||
|
||||
```diff
|
||||
diff --git a/gex_httpd/src/gex_httpd.erl b/gex_httpd/src/gex_httpd.erl
|
||||
index 242c82f..3a724b0 100644
|
||||
--- a/gex_httpd/src/gex_httpd.erl
|
||||
+++ b/gex_httpd/src/gex_httpd.erl
|
||||
@@ -9,8 +9,11 @@
|
||||
-copyright("Peter Harpending <peterharpending@qpq.swiss>").
|
||||
|
||||
|
||||
+%% for our edification
|
||||
-export([listen/1, ignore/0]).
|
||||
--export([start/0, start/1]).
|
||||
+-export([start/0]).
|
||||
+
|
||||
+%% erlang expects us to export these functions
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
|
||||
@@ -45,17 +48,17 @@ start() ->
|
||||
io:format("Starting...").
|
||||
|
||||
|
||||
--spec start(PortNum) -> ok
|
||||
- when PortNum :: inet:port_number().
|
||||
-%% @doc
|
||||
-%% Start the server and begin listening immediately. Slightly more convenient when
|
||||
-%% playing around in the shell.
|
||||
-
|
||||
-start(PortNum) ->
|
||||
- ok = start(),
|
||||
- ok = gh_client_man:listen(PortNum),
|
||||
- io:format("Startup complete, listening on ~w~n", [PortNum]).
|
||||
-
|
||||
+%-spec start(PortNum) -> ok
|
||||
+% when PortNum :: inet:port_number().
|
||||
+%%% @doc
|
||||
+%%% Start the server and begin listening immediately. Slightly more convenient when
|
||||
+%%% playing around in the shell.
|
||||
+%
|
||||
+%start(PortNum) ->
|
||||
+% ok = start(),
|
||||
+% ok = gh_client_man:listen(PortNum),
|
||||
+% io:format("Startup complete, listening on ~w~n", [PortNum]).
|
||||
+%
|
||||
|
||||
-spec start(normal, term()) -> {ok, pid()}.
|
||||
%% @private
|
||||
@@ -64,7 +67,10 @@ start(PortNum) ->
|
||||
%% See: http://erlang.org/doc/apps/kernel/application.html
|
||||
|
||||
start(normal, _Args) ->
|
||||
- gh_sup:start_link().
|
||||
+ Result = gh_sup:start_link(),
|
||||
+ % auto-listen to port 8000
|
||||
+ ok = listen(8000),
|
||||
+ Result.
|
||||
|
||||
|
||||
-spec stop(term()) -> ok.
|
||||
```
|
||||
|
||||
### Deleting the relay logic
|
||||
|
||||
- Start commit: `2037444f54d8f95a959c7e67673d55782fad5ad3`
|
||||
|
||||
```diff
|
||||
diff --git a/gex_httpd/src/gh_client.erl b/gex_httpd/src/gh_client.erl
|
||||
index ea5b3ef..44d206d 100644
|
||||
--- a/gex_httpd/src/gh_client.erl
|
||||
+++ b/gex_httpd/src/gh_client.erl
|
||||
@@ -127,22 +127,10 @@ listen(Parent, Debug, ListenSocket) ->
|
||||
loop(Parent, Debug, State = #s{socket = Socket}) ->
|
||||
ok = inet:setopts(Socket, [{active, once}]),
|
||||
receive
|
||||
- {tcp, Socket, <<"bye\r\n">>} ->
|
||||
- ok = io:format("~p Client saying goodbye. Bye!~n", [self()]),
|
||||
- ok = gen_tcp:send(Socket, "Bye!\r\n"),
|
||||
- ok = gen_tcp:shutdown(Socket, read_write),
|
||||
- exit(normal);
|
||||
{tcp, Socket, Message} ->
|
||||
ok = io:format("~p received: ~tp~n", [self(), Message]),
|
||||
ok = gh_client_man:echo(Message),
|
||||
loop(Parent, Debug, State);
|
||||
- {relay, Sender, Message} when Sender == self() ->
|
||||
- ok = gen_tcp:send(Socket, ["Message from YOU: ", Message]),
|
||||
- loop(Parent, Debug, State);
|
||||
- {relay, Sender, Message} ->
|
||||
- From = io_lib:format("Message from ~tp: ", [Sender]),
|
||||
- ok = gen_tcp:send(Socket, [From, Message]),
|
||||
- loop(Parent, Debug, State);
|
||||
{tcp_closed, Socket} ->
|
||||
ok = io:format("~p Socket closed, retiring.~n", [self()]),
|
||||
exit(normal);
|
||||
diff --git a/gex_httpd/src/gh_client_man.erl b/gex_httpd/src/gh_client_man.erl
|
||||
index 9c78921..c913dcc 100644
|
||||
--- a/gex_httpd/src/gh_client_man.erl
|
||||
+++ b/gex_httpd/src/gh_client_man.erl
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
|
||||
-export([listen/1, ignore/0]).
|
||||
--export([enroll/0, echo/1]).
|
||||
+-export([enroll/0]).
|
||||
-export([start_link/0]).
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
code_change/3, terminate/2]).
|
||||
@@ -74,17 +74,6 @@ enroll() ->
|
||||
gen_server:cast(?MODULE, {enroll, self()}).
|
||||
|
||||
|
||||
--spec echo(Message) -> ok
|
||||
- when Message :: string().
|
||||
-%% @doc
|
||||
-%% The function that tells the manager to broadcast a message to all clients.
|
||||
-%% This can broadcast arbitrary strings to clients from non-clients as well.
|
||||
-
|
||||
-echo(Message) ->
|
||||
- gen_server:cast(?MODULE, {echo, Message, self()}).
|
||||
-
|
||||
-
|
||||
-
|
||||
%%% Startup Functions
|
||||
|
||||
|
||||
@@ -145,9 +134,6 @@ handle_call(Unexpected, From, State) ->
|
||||
handle_cast({enroll, Pid}, State) ->
|
||||
NewState = do_enroll(Pid, State),
|
||||
{noreply, NewState};
|
||||
-handle_cast({echo, Message, Sender}, State) ->
|
||||
- ok = do_echo(Message, Sender, State),
|
||||
- {noreply, State};
|
||||
handle_cast(ignore, State) ->
|
||||
NewState = do_ignore(State),
|
||||
{noreply, NewState};
|
||||
@@ -262,17 +248,6 @@ do_enroll(Pid, State = #s{clients = Clients}) ->
|
||||
end.
|
||||
|
||||
|
||||
--spec do_echo(Message, Sender, State) -> ok
|
||||
- when Message :: string(),
|
||||
- Sender :: pid(),
|
||||
- State :: state().
|
||||
-%% @private
|
||||
-%% The "doer" procedure called when an "echo" message is received.
|
||||
-
|
||||
-do_echo(Message, Sender, #s{clients = Clients}) ->
|
||||
- Send = fun(Client) -> Client ! {relay, Sender, Message} end,
|
||||
- lists:foreach(Send, Clients).
|
||||
-
|
||||
|
||||
-spec handle_down(Mon, Pid, Reason, State) -> NewState
|
||||
when Mon :: reference(),
|
||||
```
|
||||
|
||||
### Adding QHL as a dependency
|
||||
|
||||
[QHL](https://git.qpq.swiss/qPQ-AG/qhl) is our own internal parsing library for
|
||||
HTTP/1.1 requests
|
||||
|
||||
```bash
|
||||
zx set dep otpr-qhl-0.1.0
|
||||
```
|
||||
|
||||
```diff
|
||||
diff --git a/gex_httpd/zomp.meta b/gex_httpd/zomp.meta
|
||||
index 7111f2d..09d84c2 100644
|
||||
--- a/gex_httpd/zomp.meta
|
||||
+++ b/gex_httpd/zomp.meta
|
||||
@@ -1,17 +1,17 @@
|
||||
-{a_email,"peterharpending@qpq.swiss"}.
|
||||
+{name,"Gajumaru DEX HTTP Daemon"}.
|
||||
+{type,app}.
|
||||
+{modules,[]}.
|
||||
+{prefix,"gh"}.
|
||||
{author,"Peter Harpending"}.
|
||||
+{desc,"Gajumaru Exchange HTTP Daemon"}.
|
||||
+{package_id,{"otpr","gex_httpd",{0,1,0}}}.
|
||||
+{deps,[{"otpr","qhl",{0,1,0}}]}.
|
||||
+{key_name,none}.
|
||||
+{a_email,"peterharpending@qpq.swiss"}.
|
||||
{c_email,"peterharpending@qpq.swiss"}.
|
||||
{copyright,"Peter Harpending"}.
|
||||
-{deps,[]}.
|
||||
-{desc,"Gajumaru Exchange HTTP Daemon"}.
|
||||
{file_exts,[]}.
|
||||
-{key_name,none}.
|
||||
{license,skip}.
|
||||
-{modules,[]}.
|
||||
-{name,"Gajumaru DEX HTTP Daemon"}.
|
||||
-{package_id,{"otpr","gex_httpd",{0,1,0}}}.
|
||||
-{prefix,"gh"}.
|
||||
{repo_url,"https://git.qpq.swiss/QPQ-AG/gex"}.
|
||||
{tags,[]}.
|
||||
-{type,app}.
|
||||
{ws_url,"https://git.qpq.swiss/QPQ-AG/gex"}.
|
||||
```
|
||||
|
||||
### Adding the QHL header file
|
||||
|
||||
```bash
|
||||
wget https://git.qpq.swiss/QPQ-AG/QHL/raw/branch/master/include/http.hrl -O gex_httpd/include/http.hrl
|
||||
```
|
||||
|
||||
```diff
|
||||
diff --git a/gex_httpd/include/http.hrl b/gex_httpd/include/http.hrl
|
||||
new file mode 100644
|
||||
index 0000000..27d24d2
|
||||
--- /dev/null
|
||||
+++ b/gex_httpd/include/http.hrl
|
||||
@@ -0,0 +1,25 @@
|
||||
+-record(request,
|
||||
+ {method = undefined :: undefined | method(),
|
||||
+ path = undefined :: undefined | binary(),
|
||||
+ qargs = undefined :: undefined | #{Key :: binary() := Value :: binary()},
|
||||
+ fragment = undefined :: undefined | none | binary(),
|
||||
+ version = undefined :: undefined | http10 | http11 | http20,
|
||||
+ headers = undefined :: undefined | [{Key :: binary(), Value :: binary()}],
|
||||
+ cookies = undefined :: undefined | #{Key :: binary() := Value :: binary()},
|
||||
+ enctype = undefined :: undefined | none | urlencoded | multipart(),
|
||||
+ size = undefined :: undefined | none | non_neg_integer(),
|
||||
+ body = undefined :: undefined | none | body()}).
|
||||
+
|
||||
+-record(response,
|
||||
+ {type = page :: page | {data, string()},
|
||||
+ version = http11 :: http11,
|
||||
+ code = 200 :: pos_integer(),
|
||||
+ slogan = "" :: string(),
|
||||
+ headers = [] :: [{Key :: string(), Value :: iolist()}],
|
||||
+ body = "" :: iolist()}).
|
||||
+
|
||||
+-type method() :: get | post | options.
|
||||
+-type multipart() :: {multipart, Boundary :: binary()}.
|
||||
+-type body() :: {partial, binary()} | {multipart, [body_part()]} | binary().
|
||||
+-type body_part() :: {Field :: binary(), Data :: binary()}
|
||||
+ | {Field :: binary(), Name :: binary(), Data :: binary()}.
|
||||
```
|
||||
|
||||
25
gex_httpd/include/http.hrl
Normal file
25
gex_httpd/include/http.hrl
Normal file
@ -0,0 +1,25 @@
|
||||
-record(request,
|
||||
{method = undefined :: undefined | method(),
|
||||
path = undefined :: undefined | binary(),
|
||||
qargs = undefined :: undefined | #{Key :: binary() := Value :: binary()},
|
||||
fragment = undefined :: undefined | none | binary(),
|
||||
version = undefined :: undefined | http10 | http11 | http20,
|
||||
headers = undefined :: undefined | [{Key :: binary(), Value :: binary()}],
|
||||
cookies = undefined :: undefined | #{Key :: binary() := Value :: binary()},
|
||||
enctype = undefined :: undefined | none | urlencoded | multipart(),
|
||||
size = undefined :: undefined | none | non_neg_integer(),
|
||||
body = undefined :: undefined | none | body()}).
|
||||
|
||||
-record(response,
|
||||
{type = page :: page | {data, string()},
|
||||
version = http11 :: http11,
|
||||
code = 200 :: pos_integer(),
|
||||
slogan = "" :: string(),
|
||||
headers = [] :: [{Key :: string(), Value :: iolist()}],
|
||||
body = "" :: iolist()}).
|
||||
|
||||
-type method() :: get | post | options.
|
||||
-type multipart() :: {multipart, Boundary :: binary()}.
|
||||
-type body() :: {partial, binary()} | {multipart, [body_part()]} | binary().
|
||||
-type body_part() :: {Field :: binary(), Data :: binary()}
|
||||
| {Field :: binary(), Name :: binary(), Data :: binary()}.
|
||||
@ -9,8 +9,11 @@
|
||||
-copyright("Peter Harpending <peterharpending@qpq.swiss>").
|
||||
|
||||
|
||||
%% for our edification
|
||||
-export([listen/1, ignore/0]).
|
||||
-export([start/0, start/1]).
|
||||
-export([start/0]).
|
||||
|
||||
%% erlang expects us to export these functions
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
|
||||
@ -45,17 +48,17 @@ start() ->
|
||||
io:format("Starting...").
|
||||
|
||||
|
||||
-spec start(PortNum) -> ok
|
||||
when PortNum :: inet:port_number().
|
||||
%% @doc
|
||||
%% Start the server and begin listening immediately. Slightly more convenient when
|
||||
%% playing around in the shell.
|
||||
|
||||
start(PortNum) ->
|
||||
ok = start(),
|
||||
ok = gh_client_man:listen(PortNum),
|
||||
io:format("Startup complete, listening on ~w~n", [PortNum]).
|
||||
|
||||
%-spec start(PortNum) -> ok
|
||||
% when PortNum :: inet:port_number().
|
||||
%%% @doc
|
||||
%%% Start the server and begin listening immediately. Slightly more convenient when
|
||||
%%% playing around in the shell.
|
||||
%
|
||||
%start(PortNum) ->
|
||||
% ok = start(),
|
||||
% ok = gh_client_man:listen(PortNum),
|
||||
% io:format("Startup complete, listening on ~w~n", [PortNum]).
|
||||
%
|
||||
|
||||
-spec start(normal, term()) -> {ok, pid()}.
|
||||
%% @private
|
||||
@ -64,7 +67,10 @@ start(PortNum) ->
|
||||
%% See: http://erlang.org/doc/apps/kernel/application.html
|
||||
|
||||
start(normal, _Args) ->
|
||||
gh_sup:start_link().
|
||||
Result = gh_sup:start_link(),
|
||||
% auto-listen to port 8000
|
||||
ok = listen(8000),
|
||||
Result.
|
||||
|
||||
|
||||
-spec stop(term()) -> ok.
|
||||
|
||||
@ -127,22 +127,10 @@ listen(Parent, Debug, ListenSocket) ->
|
||||
loop(Parent, Debug, State = #s{socket = Socket}) ->
|
||||
ok = inet:setopts(Socket, [{active, once}]),
|
||||
receive
|
||||
{tcp, Socket, <<"bye\r\n">>} ->
|
||||
ok = io:format("~p Client saying goodbye. Bye!~n", [self()]),
|
||||
ok = gen_tcp:send(Socket, "Bye!\r\n"),
|
||||
ok = gen_tcp:shutdown(Socket, read_write),
|
||||
exit(normal);
|
||||
{tcp, Socket, Message} ->
|
||||
ok = io:format("~p received: ~tp~n", [self(), Message]),
|
||||
ok = gh_client_man:echo(Message),
|
||||
loop(Parent, Debug, State);
|
||||
{relay, Sender, Message} when Sender == self() ->
|
||||
ok = gen_tcp:send(Socket, ["Message from YOU: ", Message]),
|
||||
loop(Parent, Debug, State);
|
||||
{relay, Sender, Message} ->
|
||||
From = io_lib:format("Message from ~tp: ", [Sender]),
|
||||
ok = gen_tcp:send(Socket, [From, Message]),
|
||||
loop(Parent, Debug, State);
|
||||
{tcp_closed, Socket} ->
|
||||
ok = io:format("~p Socket closed, retiring.~n", [self()]),
|
||||
exit(normal);
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
|
||||
-export([listen/1, ignore/0]).
|
||||
-export([enroll/0, echo/1]).
|
||||
-export([enroll/0]).
|
||||
-export([start_link/0]).
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
code_change/3, terminate/2]).
|
||||
@ -74,17 +74,6 @@ enroll() ->
|
||||
gen_server:cast(?MODULE, {enroll, self()}).
|
||||
|
||||
|
||||
-spec echo(Message) -> ok
|
||||
when Message :: string().
|
||||
%% @doc
|
||||
%% The function that tells the manager to broadcast a message to all clients.
|
||||
%% This can broadcast arbitrary strings to clients from non-clients as well.
|
||||
|
||||
echo(Message) ->
|
||||
gen_server:cast(?MODULE, {echo, Message, self()}).
|
||||
|
||||
|
||||
|
||||
%%% Startup Functions
|
||||
|
||||
|
||||
@ -145,9 +134,6 @@ handle_call(Unexpected, From, State) ->
|
||||
handle_cast({enroll, Pid}, State) ->
|
||||
NewState = do_enroll(Pid, State),
|
||||
{noreply, NewState};
|
||||
handle_cast({echo, Message, Sender}, State) ->
|
||||
ok = do_echo(Message, Sender, State),
|
||||
{noreply, State};
|
||||
handle_cast(ignore, State) ->
|
||||
NewState = do_ignore(State),
|
||||
{noreply, NewState};
|
||||
@ -262,17 +248,6 @@ do_enroll(Pid, State = #s{clients = Clients}) ->
|
||||
end.
|
||||
|
||||
|
||||
-spec do_echo(Message, Sender, State) -> ok
|
||||
when Message :: string(),
|
||||
Sender :: pid(),
|
||||
State :: state().
|
||||
%% @private
|
||||
%% The "doer" procedure called when an "echo" message is received.
|
||||
|
||||
do_echo(Message, Sender, #s{clients = Clients}) ->
|
||||
Send = fun(Client) -> Client ! {relay, Sender, Message} end,
|
||||
lists:foreach(Send, Clients).
|
||||
|
||||
|
||||
-spec handle_down(Mon, Pid, Reason, State) -> NewState
|
||||
when Mon :: reference(),
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
{a_email,"peterharpending@qpq.swiss"}.
|
||||
{name,"Gajumaru DEX HTTP Daemon"}.
|
||||
{type,app}.
|
||||
{modules,[]}.
|
||||
{prefix,"gh"}.
|
||||
{author,"Peter Harpending"}.
|
||||
{desc,"Gajumaru Exchange HTTP Daemon"}.
|
||||
{package_id,{"otpr","gex_httpd",{0,1,0}}}.
|
||||
{deps,[{"otpr","qhl",{0,1,0}}]}.
|
||||
{key_name,none}.
|
||||
{a_email,"peterharpending@qpq.swiss"}.
|
||||
{c_email,"peterharpending@qpq.swiss"}.
|
||||
{copyright,"Peter Harpending"}.
|
||||
{deps,[]}.
|
||||
{desc,"Gajumaru Exchange HTTP Daemon"}.
|
||||
{file_exts,[]}.
|
||||
{key_name,none}.
|
||||
{license,skip}.
|
||||
{modules,[]}.
|
||||
{name,"Gajumaru DEX HTTP Daemon"}.
|
||||
{package_id,{"otpr","gex_httpd",{0,1,0}}}.
|
||||
{prefix,"gh"}.
|
||||
{repo_url,"https://git.qpq.swiss/QPQ-AG/gex"}.
|
||||
{tags,[]}.
|
||||
{type,app}.
|
||||
{ws_url,"https://git.qpq.swiss/QPQ-AG/gex"}.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user