From 46aacfb621c54e723e2805eba6c1e09350d9ce96 Mon Sep 17 00:00:00 2001 From: Peter Harpending Date: Mon, 12 Jan 2026 18:25:34 -0800 Subject: [PATCH] file cache seems to work --- gex_httpd/README.md | 10 ++++-- gex_httpd/src/gh_client.erl | 71 ++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/gex_httpd/README.md b/gex_httpd/README.md index 5a00f9e..27f7f06 100644 --- a/gex_httpd/README.md +++ b/gex_httpd/README.md @@ -8,8 +8,14 @@ GOALS GOAL STACK -------------------------------------------------------------------- -- replace `io:format` calls with zx log -- use `gh_sfc` +- use `gh_sfc` in `gh_client` +- don't spam filesystem for 404/500 + + +TODONE +-------------------------------------------------------------------- + +- ~~replace `io:format` calls with zx log~~ - ~~write out call paths for `gh_sfc`~~ diff --git a/gex_httpd/src/gh_client.erl b/gex_httpd/src/gh_client.erl index 23bc649..9cc7292 100644 --- a/gex_httpd/src/gh_client.erl +++ b/gex_httpd/src/gh_client.erl @@ -110,7 +110,7 @@ listen(Parent, Debug, ListenSocket) -> {ok, Socket} -> {ok, _} = start(ListenSocket), {ok, Peer} = inet:peername(Socket), - ok = log("~p Connection accepted from: ~p~n", [self(), Peer]), + ok = tell("~p Connection accepted from: ~p~n", [self(), Peer]), ok = gh_client_man:enroll(), State = #s{socket = Socket}, loop(Parent, Debug, State); @@ -151,7 +151,13 @@ loop(Parent, Debug, State = #s{socket = Socket, received = Received}) -> %% see: https://git.qpq.swiss/QPQ-AG/QHL/pulls/1 case qhl:parse(Socket, Message2) of {ok, Request, NewReceived} -> - ok = handle_request(Socket, Request), + try + ok = handle_request(Socket, Request) + catch + X:Y:Z -> + tell(error, "~tp ~tp: CRASH: ~tp:~tp:~tp, returning 500", [?MODULE, self(), X, Y, Z]), + http_err(Socket, 500) + end, NewState = State#s{received = NewReceived}, loop(Parent, Debug, NewState); {error, Reason} -> @@ -230,23 +236,52 @@ system_replace_state(StateFun, State) -> %% 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 -> - log("~p ~p error: ~p~n", [?MODULE, self(), Error]), - http_err(Socket, 500) +handle_request(Socket, #request{method = get, path = Path}) -> + % future-proofing for hardcoded paths + case Path of + _ -> handle_static(Socket, Path) end; handle_request(Socket, _) -> http_err(Socket, 404). +-spec handle_static(Socket, Path) -> ok + when Socket :: gen_tcp:socket(), + Path :: binary(). + +handle_static(Socket, <<"/">>) -> + handle_static(Socket, <<"/index.html">>); +handle_static(Socket, Path) -> + case gh_sfc:query(Path) of + {found, Entry} -> handle_entry(Socket, Entry); + not_found -> http_err(Socket, 404) + end. + + +-spec handle_entry(Socket, Entry) -> ok + when Socket :: gen_tcp:socket(), + Entry :: gh_sfc_entry:entry(). + +handle_entry(Socket, Entry) -> + % -type encoding() :: none | gzip. + % -record(e, {fs_path :: file:filename(), + % last_modified :: file:date_time(), + % mime_type :: string(), + % encoding :: encoding(), + % contents :: binary()}). + Encoding = gh_sfc_entry:encoding(Entry) , + MimeType = gh_sfc_entry:mime_type(Entry), + Contents = gh_sfc_entry:contents(Entry), + Headers0 = + case Encoding of + gzip -> [{"content-encoding", "gzip"}]; + none -> [] + end, + Headers1 = [{"content-type", MimeType} | Headers0], + Response = #response{headers = Headers1, + body = Contents}, + respond(Socket, Response). + http_err(Socket, 404) -> HtmlPath = filename:join([zx:get_home(), "priv", "404.html"]), {ok, ResponseBody} = file:read_file(HtmlPath), @@ -268,9 +303,11 @@ http_err(Socket, _) -> 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)}], + BodyBytes = iolist_to_binary(Body), + ContentLength = byte_size(BodyBytes), + DefaultHeaders = [{"Server", "gex_httpd 0.1.0"}, + {"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}).