Current section
Files
Jump to
Current section
Files
src/olive@proxy.erl
-module(olive@proxy).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([start_http/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" The `proxy` module starts the proxy server that redirects to the \"main\" server\n"
" It also injects a websocket connection and handles it, via the `client_registry`\n"
" module, to add / remove them, and send them a websocket message to trigger the\n"
" actual reload of the browser.\n"
).
-file("src/olive/proxy.gleam", 37).
-spec handle_websocket(
gleam@http@request:request(mist@internal@http:connection()),
olive@client_registry:client_registry()
) -> gleam@http@response:response(mist:response_data()).
handle_websocket(Req, Clients) ->
mist:websocket(Req, fun(Client, Conn, Message) -> case Message of
{custom, reload} ->
case mist:send_text_frame(Conn, <<"reload"/utf8>>) of
{ok, _} ->
olive@logging:notice(
<<"Successfully sent reload message to client"/utf8>>
),
gleam@otp@actor:continue(Client);
{error, _} ->
olive@logging:error(
<<"Could not send message to client"/utf8>>
),
{stop, normal}
end;
closed ->
olive@logging:notice(<<"Client has disconnected"/utf8>>),
olive@client_registry:remove(Clients, Client),
{stop, normal};
shutdown ->
olive@logging:notice(<<"Client has disconnected"/utf8>>),
olive@client_registry:remove(Clients, Client),
{stop, normal};
{text, _} ->
gleam@otp@actor:continue(Client);
{binary, _} ->
gleam@otp@actor:continue(Client)
end end, fun(_) ->
Client@1 = gleam@erlang@process:new_subject(),
olive@client_registry:add(Clients, Client@1),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Client@1,
fun gleam@function:identity/1
)
end,
{Client@1, {some, Selector}}
end, fun(Client@2) ->
olive@client_registry:remove(Clients, Client@2)
end).
-file("src/olive/proxy.gleam", 102).
-spec inject(binary()) -> binary().
inject(Html) ->
Script = <<"<script>
let liveReloadWebSocket = null;
let reconnectTimeout = null;
function connect() {
clearTimeout(reconnectTimeout);
liveReloadWebSocket = new WebSocket(`ws://${window.location.host}/ws_livereload`);
liveReloadWebSocket.onmessage = (event) => {
window.location.reload();
};
liveReloadWebSocket.onclose = reconnect;
liveReloadWebSocket.onerror = reconnect;
}
function reconnect() {
clearTimeout(reconnectTimeout);
reconnectTimeout = setTimeout(connect, 5000);
}
connect();
</script>"/utf8>>,
_pipe = Html,
gleam@string:replace(
_pipe,
<<"</head>"/utf8>>,
<<Script/binary, "</head>"/utf8>>
).
-file("src/olive/proxy.gleam", 95).
-spec maybe_inject_sse(bitstring()) -> bitstring().
maybe_inject_sse(Response) ->
case gleam@bit_array:to_string(Response) of
{ok, Str} ->
gleam_stdlib:identity(inject(Str));
{error, _} ->
Response
end.
-file("src/olive/proxy.gleam", 81).
-spec handle_request(
olive@config:config(),
gleam@http@request:request(mist@internal@http:connection())
) -> gleam@http@response:response(mist:response_data()).
handle_request(Config, Req) ->
Internal_error = begin
_pipe = gleam@http@response:new(500),
gleam@http@response:set_body(_pipe, {bytes, gleam@bytes_tree:new()})
end,
_assert_subject = mist:read_body(Req, (100 * 1024) * 1024),
{ok, Req@1} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"olive/proxy"/utf8>>,
function => <<"handle_request"/utf8>>,
line => 86})
end,
_pipe@1 = begin
_record = Req@1,
{request,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
{some, erlang:element(2, Config)},
erlang:element(8, _record),
erlang:element(9, _record)}
end,
_pipe@2 = gleam@httpc:send_bits(_pipe@1),
_pipe@3 = gleam@result:map(
_pipe@2,
fun(_capture) ->
gleam@http@response:map(_capture, fun maybe_inject_sse/1)
end
),
_pipe@4 = gleam@result:map(
_pipe@3,
fun(_capture@1) ->
gleam@http@response:map(
_capture@1,
fun gleam@bytes_tree:from_bit_array/1
)
end
),
_pipe@5 = gleam@result:map(
_pipe@4,
fun(_capture@2) ->
gleam@http@response:map(
_capture@2,
fun(Field@0) -> {bytes, Field@0} end
)
end
),
gleam@result:unwrap(_pipe@5, Internal_error).
-file("src/olive/proxy.gleam", 22).
-spec start_http(olive@config:config(), olive@client_registry:client_registry()) -> {ok,
gleam@erlang@process:subject(gleam@otp@supervisor:message())} |
{error, glisten:start_error()}.
start_http(Config, Clients) ->
_pipe = fun(Req) -> case gleam@http@request:path_segments(Req) of
[<<"ws_livereload"/utf8>>] ->
handle_websocket(Req, Clients);
_ ->
handle_request(Config, Req)
end end,
_pipe@1 = mist:new(_pipe),
_pipe@2 = mist:bind(_pipe@1, erlang:element(4, Config)),
_pipe@3 = mist:port(_pipe@2, erlang:element(3, Config)),
mist:start_http(_pipe@3).