firefox localhost:8000 works
This commit is contained in:
parent
f59ca64b1e
commit
649526a17c
11
gex_httpd/priv/404.html
Normal file
11
gex_httpd/priv/404.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>QHL: 404</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>404 Not Found</h1>
|
||||
</body>
|
||||
</html>
|
||||
11
gex_httpd/priv/500.html
Normal file
11
gex_httpd/priv/500.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>QHL: 500</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>500 Internal Server Error</h1>
|
||||
</body>
|
||||
</html>
|
||||
@ -24,11 +24,14 @@
|
||||
-export([system_continue/3, system_terminate/4,
|
||||
system_get_state/1, system_replace_state/2]).
|
||||
|
||||
-include("http.hrl").
|
||||
|
||||
|
||||
%%% Type and Record Definitions
|
||||
|
||||
|
||||
-record(s, {socket = none :: none | gen_tcp:socket()}).
|
||||
-record(s, {socket = none :: none | gen_tcp:socket(),
|
||||
received = none :: none | binary()}).
|
||||
|
||||
|
||||
%% An alias for the state record above. Aliasing state can smooth out annoyances
|
||||
@ -124,13 +127,38 @@ listen(Parent, Debug, ListenSocket) ->
|
||||
%% The service loop itself. This is the service state. The process blocks on receive
|
||||
%% of Erlang messages, TCP segments being received themselves as Erlang messages.
|
||||
|
||||
loop(Parent, Debug, State = #s{socket = Socket}) ->
|
||||
loop(Parent, Debug, State = #s{socket = Socket, received = Received}) ->
|
||||
ok = inet:setopts(Socket, [{active, once}]),
|
||||
receive
|
||||
{tcp, Socket, Message} ->
|
||||
ok = io:format("~p received: ~tp~n", [self(), Message]),
|
||||
ok = gh_client_man:echo(Message),
|
||||
loop(Parent, Debug, State);
|
||||
%% Received exists because web browsers usually use the same
|
||||
%% acceptor socket for sequential requests
|
||||
%%
|
||||
%% QHL parses a request off the socket, and consumes all the data
|
||||
%% pertinent to said task. Any additional data it finds on the
|
||||
%% socket it hands back to us.
|
||||
%%
|
||||
%% That additional data, as I said, is usually the next request.
|
||||
%%
|
||||
%% We store that in our process state in the received=Received field
|
||||
Message2 =
|
||||
case Received of
|
||||
none -> Message;
|
||||
_ -> <<Received/binary, Message/binary>>
|
||||
end,
|
||||
%% beware: wrong typespec in QHL 0.1.0
|
||||
%% see: https://git.qpq.swiss/QPQ-AG/QHL/pulls/1
|
||||
case qhl:parse(Socket, Message2) of
|
||||
{ok, Request, NewReceived} ->
|
||||
ok = handle_request(Socket, Request),
|
||||
NewState = State#s{received = NewReceived},
|
||||
loop(Parent, Debug, NewState);
|
||||
{error, Reason} ->
|
||||
io:format("~p error: ~tp~n", [self(), Reason]),
|
||||
ok = http_err(Socket, 500),
|
||||
exit(normal)
|
||||
end;
|
||||
{tcp_closed, Socket} ->
|
||||
ok = io:format("~p Socket closed, retiring.~n", [self()]),
|
||||
exit(normal);
|
||||
@ -190,3 +218,85 @@ system_get_state(State) -> {ok, State}.
|
||||
|
||||
system_replace_state(StateFun, State) ->
|
||||
{ok, StateFun(State), State}.
|
||||
|
||||
|
||||
%%%-------------------------------------------
|
||||
%%% http request handling
|
||||
%%%-------------------------------------------
|
||||
|
||||
-spec handle_request(Socket, Request) -> ok
|
||||
when Socket :: gen_tcp:socket(),
|
||||
Request :: #request{}.
|
||||
|
||||
%% ref: https://git.qpq.swiss/QPQ-AG/QHL/src/commit/7f77f9e3b19f58006df88a2a601e85835d300c37/include/http.hrl
|
||||
|
||||
handle_request(Socket, #request{method = get, path = <<"/">>}) ->
|
||||
IndexHtmlPath = filename:join([zx:get_home(), "priv", "index.html"]),
|
||||
case file:read_file(IndexHtmlPath) of
|
||||
{ok, ResponseBody} ->
|
||||
%% see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Messages#http_responses
|
||||
Headers = [{"content-type", "text/html"}],
|
||||
Response = #response{headers = Headers,
|
||||
body = ResponseBody},
|
||||
respond(Socket, Response);
|
||||
Error ->
|
||||
io:format("~p error: ~p~n", [self(), Error]),
|
||||
http_err(Socket, 500)
|
||||
end;
|
||||
handle_request(Socket, _) ->
|
||||
http_err(Socket, 404).
|
||||
|
||||
|
||||
http_err(Socket, 404) ->
|
||||
HtmlPath = filename:join([zx:get_home(), "priv", "404.html"]),
|
||||
{ok, ResponseBody} = file:read_file(HtmlPath),
|
||||
Headers = [{"content-type", "text/html"}],
|
||||
Response = #response{headers = Headers,
|
||||
code = 404,
|
||||
body = ResponseBody},
|
||||
respond(Socket, Response);
|
||||
% default error is 500
|
||||
http_err(Socket, _) ->
|
||||
HtmlPath = filename:join([zx:get_home(), "priv", "500.html"]),
|
||||
{ok, ResponseBody} = file:read_file(HtmlPath),
|
||||
Headers = [{"content-type", "text/html"}],
|
||||
Response = #response{headers = Headers,
|
||||
code = 500,
|
||||
body = ResponseBody},
|
||||
respond(Socket, Response).
|
||||
|
||||
|
||||
respond(Socket, R = #response{code = Code, headers = Headers, body = Body}) ->
|
||||
Slogan = slogan(Code),
|
||||
ContentLength = byte_size(Body),
|
||||
DefaultHeaders = [{"date", qhl:ridiculous_web_date()},
|
||||
{"content-length", integer_to_list(ContentLength)}],
|
||||
Headers2 = merge_headers(DefaultHeaders, Headers),
|
||||
really_respond(Socket, R#response{slogan = Slogan,
|
||||
headers = Headers2}).
|
||||
|
||||
|
||||
really_respond(Socket, #response{code = Code, slogan = Slogan, headers = Headers, body = Body}) ->
|
||||
Response =
|
||||
["HTTP/1.1 ", integer_to_list(Code), " ", Slogan, "\r\n",
|
||||
render_headers(Headers), "\r\n",
|
||||
Body],
|
||||
gen_tcp:send(Socket, Response).
|
||||
|
||||
|
||||
merge_headers(Defaults, Overwrites) ->
|
||||
DefaultsMap = proplists:to_map(Defaults),
|
||||
OverwritesMap = proplists:to_map(Overwrites),
|
||||
FinalMap = maps:merge(DefaultsMap, OverwritesMap),
|
||||
proplists:from_map(FinalMap).
|
||||
|
||||
render_headers([{K, V} | Rest]) ->
|
||||
[K, ": ", V, "\r\n",
|
||||
render_headers(Rest)];
|
||||
render_headers([]) ->
|
||||
[].
|
||||
|
||||
|
||||
slogan(200) -> "OK";
|
||||
slogan(404) -> "Not Found";
|
||||
slogan(500) -> "Internal Server Error".
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user