From 87621b90cdc475dfb4778ac4a39988cd2fa87f33 Mon Sep 17 00:00:00 2001 From: graphiteisaac Date: Mon, 2 Jun 2025 21:31:58 +1000 Subject: [PATCH 1/3] update to latest gleam libs --- gleam.toml | 12 +++--- manifest.toml | 28 ++++++------- src/gleam/http/elli.gleam | 56 ++++++++++++++++++++----- src/gleam_elli_native.erl | 16 ++++++- test/gleam/http/elli_logging_test.gleam | 7 ++-- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/gleam.toml b/gleam.toml index 978c36d..67284f6 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,6 +1,6 @@ name = "gleam_elli" -version = "2.4.2" -gleam = ">= 1.4.0" +version = "3.0.0" +gleam = ">= 1.10.0" licences = ["Apache-2.0"] description = "Run Gleam HTTP services with the Elli web server" @@ -12,10 +12,10 @@ links = [ ] [dependencies] -gleam_erlang = "~> 0.23" -gleam_stdlib = "~> 0.42" -gleam_http = "~> 3.0" -gleam_otp = "~> 0.3" +gleam_erlang = ">= 0.34.0 and < 1.0.0" +gleam_otp = ">= 0.16.1 and < 1.0.0" +gleam_stdlib = ">= 0.44.0 and < 2.0.0" +gleam_http = ">= 4.0.0 and < 5.0.0" elli = "~> 3.0" [dev-dependencies] diff --git a/manifest.toml b/manifest.toml index 88b0c14..be6d511 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,28 +2,28 @@ # You typically do not need to edit this file packages = [ - { name = "certifi", version = "2.12.0", build_tools = ["rebar3"], requirements = [], otp_app = "certifi", source = "hex", outer_checksum = "EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C" }, + { name = "certifi", version = "2.15.0", build_tools = ["rebar3"], requirements = [], otp_app = "certifi", source = "hex", outer_checksum = "B147ED22CE71D72EAFDAD94F055165C1C182F61A2FF49DF28BCC71D1D5B94A60" }, { name = "elli", version = "3.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "elli", source = "hex", outer_checksum = "698B13B33D05661DB9FE7EFCBA41B84825A379CCE86E486CF6AFF9285BE0CCF8" }, - { name = "gleam_erlang", version = "0.30.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "760618870AE4A497B10C73548E6E44F43B76292A54F0207B3771CBB599C675B4" }, - { name = "gleam_hackney", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib", "hackney"], otp_app = "gleam_hackney", source = "hex", outer_checksum = "066B1A55D37DBD61CC72A1C4EDE43C6015B1797FAF3818C16FE476534C7B6505" }, - { name = "gleam_http", version = "3.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "A9EE0722106FCCAB8AD3BF9D0A3EFF92BFE8561D59B83BAE96EB0BE1938D4E0F" }, - { name = "gleam_otp", version = "0.14.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5A8CE8DBD01C29403390A7BD5C0A63D26F865C83173CF9708E6E827E53159C65" }, - { name = "gleam_stdlib", version = "0.43.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "69EF22E78FDCA9097CBE7DF91C05B2A8B5436826D9F66680D879182C0860A747" }, - { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, - { name = "hackney", version = "1.20.1", build_tools = ["rebar3"], requirements = ["certifi", "idna", "metrics", "mimerl", "parse_trans", "ssl_verify_fun", "unicode_util_compat"], otp_app = "hackney", source = "hex", outer_checksum = "FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3" }, + { name = "gleam_erlang", version = "0.34.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "0C38F2A128BAA0CEF17C3000BD2097EB80634E239CE31A86400C4416A5D0FDCC" }, + { name = "gleam_hackney", version = "1.3.1", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib", "hackney"], otp_app = "gleam_hackney", source = "hex", outer_checksum = "0449AADBEBF3E979509A4079EE34B92EEE4162C5A0DC94F3DA2787E4777F6B45" }, + { name = "gleam_http", version = "4.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "0A62451FC85B98062E0907659D92E6A89F5F3C0FBE4AB8046C99936BF6F91DBC" }, + { name = "gleam_otp", version = "0.16.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "50DA1539FC8E8FA09924EB36A67A2BBB0AD6B27BCDED5A7EF627057CF69D035E" }, + { name = "gleam_stdlib", version = "0.60.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "621D600BB134BC239CB2537630899817B1A42E60A1D46C5E9F3FAE39F88C800B" }, + { name = "gleeunit", version = "1.3.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "A7DD6C07B7DA49A6E28796058AA89E651D233B357D5607006D70619CD89DAAAB" }, + { name = "hackney", version = "1.24.1", build_tools = ["rebar3"], requirements = ["certifi", "idna", "metrics", "mimerl", "parse_trans", "ssl_verify_fun", "unicode_util_compat"], otp_app = "hackney", source = "hex", outer_checksum = "F4A7392A0B53D8BBC3EB855BDCC919CD677358E65B2AFD3840B5B3690C4C8A39" }, { name = "idna", version = "6.1.1", build_tools = ["rebar3"], requirements = ["unicode_util_compat"], otp_app = "idna", source = "hex", outer_checksum = "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA" }, { name = "metrics", version = "1.0.1", build_tools = ["rebar3"], requirements = [], otp_app = "metrics", source = "hex", outer_checksum = "69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16" }, - { name = "mimerl", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "mimerl", source = "hex", outer_checksum = "A1E15A50D1887217DE95F0B9B0793E32853F7C258A5CD227650889B38839FE9D" }, + { name = "mimerl", version = "1.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "mimerl", source = "hex", outer_checksum = "13AF15F9F68C65884ECCA3A3891D50A7B57D82152792F3E19D88650AA126B144" }, { name = "parse_trans", version = "3.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "parse_trans", source = "hex", outer_checksum = "620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A" }, { name = "ssl_verify_fun", version = "1.1.7", build_tools = ["mix", "rebar3", "make"], requirements = [], otp_app = "ssl_verify_fun", source = "hex", outer_checksum = "FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8" }, - { name = "unicode_util_compat", version = "0.7.0", build_tools = ["rebar3"], requirements = [], otp_app = "unicode_util_compat", source = "hex", outer_checksum = "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521" }, + { name = "unicode_util_compat", version = "0.7.1", build_tools = ["rebar3"], requirements = [], otp_app = "unicode_util_compat", source = "hex", outer_checksum = "B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642" }, ] [requirements] elli = { version = "~> 3.0" } -gleam_erlang = { version = "~> 0.23" } +gleam_erlang = { version = ">= 0.34.0 and < 1.0.0" } gleam_hackney = { version = "~> 1.0" } -gleam_http = { version = "~> 3.0" } -gleam_otp = { version = "~> 0.3" } -gleam_stdlib = { version = "~> 0.42" } +gleam_http = { version = ">= 4.0.0 and < 5.0.0" } +gleam_otp = { version = ">= 0.16.1 and < 1.0.0" } +gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } gleeunit = { version = "~> 1.0" } diff --git a/src/gleam/http/elli.gleam b/src/gleam/http/elli.gleam index e2c8134..1db8205 100644 --- a/src/gleam/http/elli.gleam +++ b/src/gleam/http/elli.gleam @@ -1,5 +1,6 @@ import gleam/bytes_tree.{type BytesTree} import gleam/dynamic.{type Dynamic} +import gleam/dynamic/decode import gleam/erlang/atom.{type Atom} import gleam/erlang/process.{type Pid} import gleam/http @@ -22,6 +23,18 @@ type StartLinkOption { Port(Int) } +type ElliMethod { + Get + Post + Head + Put + Delete + Trace + Connect + Options + Patch +} + @external(erlang, "binary", "split") fn split(a: String, b: List(String)) -> List(String) @@ -37,14 +50,23 @@ fn get_headers(a: ElliRequest) -> List(http.Header) @external(erlang, "gleam_elli_native", "get_host") fn get_host(a: ElliRequest) -> String -@external(erlang, "elli_request", "method") -fn get_dynamic_method(a: ElliRequest) -> Dynamic - -fn get_method(req) { - req - |> get_dynamic_method - |> http.method_from_dynamic - |> result.unwrap(http.Get) +@external(erlang, "gleam_elli_native", "get_method") +fn get_elli_method(a: ElliRequest) -> Result(ElliMethod, Nil) + +fn get_method(req) -> http.Method { + case get_elli_method(req) { + Ok(Trace) -> http.Trace + Ok(Put) -> http.Put + Ok(Post) -> http.Post + Ok(Patch) -> http.Patch + Ok(Options) -> http.Options + Ok(Head) -> http.Head + Ok(Get) -> http.Get + Ok(Delete) -> http.Delete + Ok(Connect) -> http.Connect + // FIXME: Don't just default, ensure this isn't reachable. + _ -> http.Other("unknown") + } } @external(erlang, "elli_request", "port") @@ -53,7 +75,7 @@ fn get_dynamic_port(a: ElliRequest) -> Dynamic fn get_port(req) { req |> get_dynamic_port - |> dynamic.int + |> decode.run(decode.int) |> option.from_result } @@ -64,7 +86,7 @@ fn get_scheme(req) -> http.Scheme { let scheme = req |> get_dynamic_scheme - |> dynamic.string + |> decode.run(decode.string) |> result.unwrap("") |> string.lowercase case scheme { @@ -73,6 +95,20 @@ fn get_scheme(req) -> http.Scheme { } } +// pub fn method_from_dynamic( +// value: Dynamic, +// ) -> Result(Method, List(decode.DecodeError)) { +// case do_method_from_dynamic(value) { +// Ok(method) -> Ok(method) +// Error(_) -> +// Error([decode.DecodeError("HTTP method", dynamic.classify(value), [])]) +// } +// } + +// @target(erlang) +// @external(erlang, "gleam_http_native", "decode_method") +// fn do_method_from_dynamic(a: Dynamic) -> Result(ElliMethod, nil) + @external(erlang, "elli_request", "query_str") fn get_query(a: ElliRequest) -> String diff --git a/src/gleam_elli_native.erl b/src/gleam_elli_native.erl index 88af36d..3ee3122 100644 --- a/src/gleam_elli_native.erl +++ b/src/gleam_elli_native.erl @@ -3,7 +3,7 @@ -include_lib("kernel/include/logger.hrl"). -include_lib("elli/include/elli.hrl"). --export([handle/2, handle_event/3, await_shutdown/1, get_host/1]). +-export([handle/2, handle_event/3, await_shutdown/1, get_host/1, get_method/1]). handle(Req, Handler) -> Handler(Req). @@ -47,6 +47,20 @@ get_host(Request) -> Host when is_binary(Host) -> Host end. +get_method(#req{method = Method}) -> + case Method of + 'CONNECT' -> {ok, connect}; + 'DELETE' -> {ok, delete}; + 'GET' -> {ok, get}; + 'HEAD' -> {ok, head}; + 'OPTIONS' -> {ok, options}; + 'PATCH' -> {ok, patch}; + 'POST' -> {ok, post}; + 'PUT' -> {ok, put}; + 'TRACE' -> {ok, trace}; + _ -> {error, nil} + end. + path(#req{path = Path}) -> erlang:iolist_to_binary(["/"] ++ lists:join("/", Path)). diff --git a/test/gleam/http/elli_logging_test.gleam b/test/gleam/http/elli_logging_test.gleam index c5dab77..07541b9 100644 --- a/test/gleam/http/elli_logging_test.gleam +++ b/test/gleam/http/elli_logging_test.gleam @@ -1,6 +1,7 @@ import gleam/bytes_tree.{type BytesTree} import gleam/dict.{type Dict} -import gleam/dynamic.{type DecodeError, type Dynamic} +import gleam/dynamic.{type Dynamic} +import gleam/dynamic/decode.{type DecodeError} import gleam/erlang/atom.{type Atom} import gleam/hackney import gleam/http.{type Method, Get, Post, Put} @@ -120,7 +121,7 @@ fn get_string( ) -> Result(String, List(DecodeError)) { dict.get(report, key) |> result.map_error(fn(_) { [] }) - |> result.then(dynamic.string) + |> result.then(decode.run(_, decode.string)) } fn list_length( @@ -129,7 +130,7 @@ fn list_length( ) -> Result(Int, List(DecodeError)) { dict.get(report, key) |> result.map_error(fn(_) { [] }) - |> result.then(dynamic.shallow_list) + |> result.then(decode.run(_, decode.list(decode.dynamic))) |> result.map(list.length) } From 774c1f54967d482a583a7a38220f448f4647909c Mon Sep 17 00:00:00 2001 From: graphiteisaac Date: Mon, 2 Jun 2025 21:33:31 +1000 Subject: [PATCH 2/3] update example to reflect change of bytes_builder to bytes_tree --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6b38b18..faffc6d 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ gleam add gleam_elli gleam_http import gleam/http/elli import gleam/http/request.{type Request} import gleam/http/response.{type Response} -import gleam/bytes_builder.{type BytesBuilder} +import gleam/bytes_tree.{type BytesTree} // Define a HTTP service // pub fn my_service(req: Request(t)) -> Response(BytesBuilder) { - let body = bytes_builder.from_string("Hello, world!") + let body = bytes_tree.from_string("Hello, world!") response.new(200) |> response.prepend_header("made-with", "Gleam") From 00d52ddfabb02f4104e7a4179b8fd94c110c4509 Mon Sep 17 00:00:00 2001 From: graphiteisaac Date: Mon, 2 Jun 2025 21:45:17 +1000 Subject: [PATCH 3/3] update tests so they pass --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86273a2..44caae7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,8 +13,8 @@ jobs: - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 with: - otp-version: "26.1" - gleam-version: "1.4.0" + otp-version: "27.1" + gleam-version: "1.10.0" rebar3-version: "3" - run: gleam test - run: gleam format --check src test