From 39a5fa24d48ca7d539c9c7c884a479883dcf14d3 Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:33:52 +0530 Subject: [PATCH 1/7] fix: methods are case-sensitive --- src/gleam/http.gleam | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/gleam/http.gleam b/src/gleam/http.gleam index f2d0afc..6282bed 100644 --- a/src/gleam/http.gleam +++ b/src/gleam/http.gleam @@ -32,31 +32,31 @@ pub type Method { // TODO: check if the a is a valid HTTP method (i.e. it is a token, as per the // spec) and return Ok(Other(s)) if so. pub fn parse_method(s) -> Result(Method, Nil) { - case string.lowercase(s) { - "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) + case s { + "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) } } pub fn method_to_string(method: Method) -> String { case method { - Connect -> "connect" - Delete -> "delete" - Get -> "get" - Head -> "head" - Options -> "options" - Patch -> "patch" - Post -> "post" - Put -> "put" - Trace -> "trace" + Connect -> "CONNECT" + Delete -> "DELETE" + Get -> "GET" + Head -> "HEAD" + Options -> "OPTIONS" + Patch -> "PATCH" + Post -> "POST" + Put -> "PUT" + Trace -> "TRACE" Other(s) -> s } } From 19880bfa53ba033219870ac44f055fb02b7191b3 Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:09:54 +0530 Subject: [PATCH 2/7] feat: check if the given method is valid --- src/gleam/http.gleam | 51 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/gleam/http.gleam b/src/gleam/http.gleam index 6282bed..16a7edd 100644 --- a/src/gleam/http.gleam +++ b/src/gleam/http.gleam @@ -29,6 +29,51 @@ pub type Method { Other(String) } +// A token is defined as: +// +// token = 1*tchar +// +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +// / DIGIT / ALPHA +// ; any VCHAR, except delimiters +// +// (From https://www.rfc-editor.org/rfc/rfc9110.html#name-tokens) +// +// Where DIGIT = %x30-39 +// ALPHA = %x41-5A / %x61-7A +// (%xXX is a hexadecimal ASCII value) +// +// (From https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1) +// +fn is_valid_token(s: String) -> Bool { + bit_array.from_string(s) + |> do_is_valid_token(True) +} + +fn do_is_valid_token(bytes: BitArray, acc: Bool) { + case bytes, acc { + <>, True -> do_is_valid_token(rest, is_valid_tchar(char)) + _, _ -> acc + } +} + +// tchar = "33" / "35" / "36" / "37" / "38" / "39" / "42" +// / "43" / "45" / "46" / "94" / "95" / "96" / "124" / "126" +fn is_valid_tchar(ch: Int) -> Bool { + case ch { + // "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" + // | "." | "^" | "_" | "`" | "|" | "~" + 33 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 45 | 46 | 94 | 95 | 96 | 124 | 126 -> + True + // DIGIT + ch if ch >= 0x30 && ch <= 0x39 -> True + // ALPHA + ch if ch >= 0x41 && ch <= 0x5A || ch >= 0x61 && ch <= 0x7A -> True + _ -> False + } +} + // TODO: check if the a is a valid HTTP method (i.e. it is a token, as per the // spec) and return Ok(Other(s)) if so. pub fn parse_method(s) -> Result(Method, Nil) { @@ -42,7 +87,11 @@ pub fn parse_method(s) -> Result(Method, Nil) { "POST" -> Ok(Post) "PUT" -> Ok(Put) "TRACE" -> Ok(Trace) - _ -> Error(Nil) + s -> + case is_valid_token(s) { + True -> Ok(Other(s)) + False -> Error(Nil) + } } } From e3993b1d25f6076108c6d0530ed1e76d8a4f6a52 Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:21:35 +0530 Subject: [PATCH 3/7] feat: dynamic method decoding --- src/gleam/http.gleam | 18 ++----- src/gleam_http_native.erl | 101 +++++++++++++++++--------------------- src/gleam_http_native.mjs | 56 +++++++++++++++++---- 3 files changed, 94 insertions(+), 81 deletions(-) diff --git a/src/gleam/http.gleam b/src/gleam/http.gleam index 16a7edd..311b29c 100644 --- a/src/gleam/http.gleam +++ b/src/gleam/http.gleam @@ -58,21 +58,9 @@ fn do_is_valid_token(bytes: BitArray, acc: Bool) { } } -// tchar = "33" / "35" / "36" / "37" / "38" / "39" / "42" -// / "43" / "45" / "46" / "94" / "95" / "96" / "124" / "126" -fn is_valid_tchar(ch: Int) -> Bool { - case ch { - // "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" - // | "." | "^" | "_" | "`" | "|" | "~" - 33 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 45 | 46 | 94 | 95 | 96 | 124 | 126 -> - True - // DIGIT - ch if ch >= 0x30 && ch <= 0x39 -> True - // ALPHA - ch if ch >= 0x41 && ch <= 0x5A || ch >= 0x61 && ch <= 0x7A -> True - _ -> False - } -} +@external(erlang, "gleam_http_native", "is_valid_tchar") +@external(javascript, "../gleam_http_native.mjs", "is_valid_tchar") +fn is_valid_tchar(ch: Int) -> Bool // TODO: check if the a is a valid HTTP method (i.e. it is a token, as per the // spec) and return Ok(Other(s)) if so. diff --git a/src/gleam_http_native.erl b/src/gleam_http_native.erl index bb499bb..2eae638 100644 --- a/src/gleam_http_native.erl +++ b/src/gleam_http_native.erl @@ -1,17 +1,47 @@ -module(gleam_http_native). --export([decode_method/1]). +-export([decode_method/1, is_valid_tchar/1]). + +is_valid_tchar(X) when is_integer(X) -> + case X of + $! -> true; + $# -> true; + $$ -> true; + $% -> true; + $& -> true; + $' -> true; + $* -> true; + $+ -> true; + $- -> true; + $. -> true; + $^ -> true; + $_ -> true; + $` -> true; + $| -> true; + $~ -> true; + % DIGIT + _ when X >= 16#30 andalso X =< 16#39 -> true; + % ALPHA + _ when (X >= 16#41 andalso X =< 16#5A) orelse (X >= 16#61 andalso X =< 16#7A) -> true; + _ -> false + end; +is_valid_tchar(_) -> false. + +is_valid_token([]) -> false; +is_valid_token(<<>>) -> false; +is_valid_token(X) when is_list(X) orelse is_bitstring(X) -> do_is_valid_token(X, true); +is_valid_token(_) -> false. + +do_is_valid_token(_, false) -> false; +do_is_valid_token([H|T] = X, true) when is_list(X) -> do_is_valid_token(T, is_valid_tchar(H)); +do_is_valid_token(<> = X, true) when is_bitstring(X) -> do_is_valid_token(T, is_valid_tchar(H)); +do_is_valid_token(_, X) -> X. + +normalise_method(X) when is_bitstring(X) -> {ok, {other, X}}; +normalise_method(X) when is_list(X) -> {ok, {other, list_to_binary(X)}}; +normalise_method(_) -> {error, nil}. decode_method(Term) -> case Term 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}; "CONNECT" -> {ok, connect}; "DELETE" -> {ok, delete}; "GET" -> {ok, get}; @@ -21,24 +51,6 @@ decode_method(Term) -> "POST" -> {ok, post}; "PUT" -> {ok, put}; "TRACE" -> {ok, trace}; - "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}; - '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}; 'CONNECT' -> {ok, connect}; 'DELETE' -> {ok, delete}; 'GET' -> {ok, get}; @@ -48,24 +60,6 @@ decode_method(Term) -> 'POST' -> {ok, post}; 'PUT' -> {ok, put}; 'TRACE' -> {ok, trace}; - '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}; - <<"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}; <<"CONNECT">> -> {ok, connect}; <<"DELETE">> -> {ok, delete}; <<"GET">> -> {ok, get}; @@ -75,14 +69,9 @@ decode_method(Term) -> <<"POST">> -> {ok, post}; <<"PUT">> -> {ok, put}; <<"TRACE">> -> {ok, trace}; - <<"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} + X -> + case is_valid_token(X) of + true -> normalise_method(X); + false -> {error, nil} + end end. diff --git a/src/gleam_http_native.mjs b/src/gleam_http_native.mjs index c871a8b..19a5150 100644 --- a/src/gleam_http_native.mjs +++ b/src/gleam_http_native.mjs @@ -9,30 +9,66 @@ import { Connect, Options, Patch, + Other, } from "./gleam/http.mjs"; +/** + @param {number} ch +*/ +export function is_valid_tchar(ch) { + try { + switch (ch) { + case '33': return true; + case '35': return true; + case '36': return true; + case '37': return true; + case '38': return true; + case '39': return true; + case '42': return true; + case '43': return true; + case '45': return true; + case '46': return true; + case '94': return true; + case '95': return true; + case '96': return true; + case '124': return true; + case '126': return true; + } + // DIGIT + if (ch >= 0x30 && ch <= 0x39) return true; + // ALPHA + if (ch >= 0x41 && ch <= 0x5A || ch >= 0x61 && ch <= 0x7A) return true; + } catch {} + return false; +} + export function decode_method(value) { try { - switch (value.toLowerCase()) { - case "get": + switch (value) { + case "GET": return new Ok(new Get()); - case "post": + case "POST": return new Ok(new Post()); - case "head": + case "HEAD": return new Ok(new Head()); - case "put": + case "PUT": return new Ok(new Put()); - case "delete": + case "DELETE": return new Ok(new Delete()); - case "trace": + case "TRACE": return new Ok(new Trace()); - case "connect": + case "CONNECT": return new Ok(new Connect()); - case "options": + case "OPTIONS": return new Ok(new Options()); - case "patch": + case "PATCH": return new Ok(new Patch()); } + if (typeof value === 'string') { + for (const v of value) + if (!is_valid_tchar(v)) return new Error(undefined); + return new Ok(new Other(value)); + } } catch {} return new Error(undefined); } From 0ba3a2933abc20b5df0ca7e6b19dab08d9cce1b3 Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:21:38 +0530 Subject: [PATCH 4/7] fix: tests --- test/gleam/http_test.gleam | 202 ++++++++++++++++++++++--------------- 1 file changed, 121 insertions(+), 81 deletions(-) diff --git a/test/gleam/http_test.gleam b/test/gleam/http_test.gleam index 87c442e..23ff70a 100644 --- a/test/gleam/http_test.gleam +++ b/test/gleam/http_test.gleam @@ -5,7 +5,7 @@ import gleeunit/should pub fn parse_method_test() { "Connect" |> http.parse_method - |> should.equal(Ok(http.Connect)) + |> should.equal(Ok(http.Other("Connect"))) "CONNECT" |> http.parse_method @@ -13,11 +13,11 @@ pub fn parse_method_test() { "connect" |> http.parse_method - |> should.equal(Ok(http.Connect)) + |> should.equal(Ok(http.Other("connect"))) "Delete" |> http.parse_method - |> should.equal(Ok(http.Delete)) + |> should.equal(Ok(http.Other("Delete"))) "DELETE" |> http.parse_method @@ -25,11 +25,11 @@ pub fn parse_method_test() { "delete" |> http.parse_method - |> should.equal(Ok(http.Delete)) + |> should.equal(Ok(http.Other("delete"))) "Get" |> http.parse_method - |> should.equal(Ok(http.Get)) + |> should.equal(Ok(http.Other("Get"))) "GET" |> http.parse_method @@ -37,11 +37,11 @@ pub fn parse_method_test() { "get" |> http.parse_method - |> should.equal(Ok(http.Get)) + |> should.equal(Ok(http.Other("get"))) "Head" |> http.parse_method - |> should.equal(Ok(http.Head)) + |> should.equal(Ok(http.Other("Head"))) "HEAD" |> http.parse_method @@ -49,11 +49,11 @@ pub fn parse_method_test() { "head" |> http.parse_method - |> should.equal(Ok(http.Head)) + |> should.equal(Ok(http.Other("head"))) "Options" |> http.parse_method - |> should.equal(Ok(http.Options)) + |> should.equal(Ok(http.Other("Options"))) "OPTIONS" |> http.parse_method @@ -61,11 +61,11 @@ pub fn parse_method_test() { "options" |> http.parse_method - |> should.equal(Ok(http.Options)) + |> should.equal(Ok(http.Other("options"))) "Patch" |> http.parse_method - |> should.equal(Ok(http.Patch)) + |> should.equal(Ok(http.Other("Patch"))) "PATCH" |> http.parse_method @@ -73,11 +73,11 @@ pub fn parse_method_test() { "patch" |> http.parse_method - |> should.equal(Ok(http.Patch)) + |> should.equal(Ok(http.Other("patch"))) "Post" |> http.parse_method - |> should.equal(Ok(http.Post)) + |> should.equal(Ok(http.Other("Post"))) "POST" |> http.parse_method @@ -85,11 +85,11 @@ pub fn parse_method_test() { "post" |> http.parse_method - |> should.equal(Ok(http.Post)) + |> should.equal(Ok(http.Other("post"))) "Put" |> http.parse_method - |> should.equal(Ok(http.Put)) + |> should.equal(Ok(http.Other("Put"))) "PUT" |> http.parse_method @@ -97,11 +97,11 @@ pub fn parse_method_test() { "put" |> http.parse_method - |> should.equal(Ok(http.Put)) + |> should.equal(Ok(http.Other("put"))) "Trace" |> http.parse_method - |> should.equal(Ok(http.Trace)) + |> should.equal(Ok(http.Other("Trace"))) "TRACE" |> http.parse_method @@ -109,10 +109,18 @@ pub fn parse_method_test() { "trace" |> http.parse_method - |> should.equal(Ok(http.Trace)) + |> should.equal(Ok(http.Other("trace"))) "thingy" |> http.parse_method + |> should.equal(Ok(http.Other("thingy"))) + + "!#$%&'*+-.^_`|~abcABC123" + |> http.parse_method + |> should.equal(Ok(http.Other("!#$%&'*+-.^_`|~abcABC123"))) + + "In-valid method" + |> http.parse_method |> should.equal(Error(Nil)) } @@ -129,7 +137,7 @@ pub fn method_from_dynamic_atom_test() { "Connect" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "CONNECT" |> make_atom @@ -139,12 +147,12 @@ pub fn method_from_dynamic_atom_test() { "connect" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Delete" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "DELETE" |> make_atom @@ -154,12 +162,12 @@ pub fn method_from_dynamic_atom_test() { "delete" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Get" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "GET" |> make_atom @@ -169,12 +177,12 @@ pub fn method_from_dynamic_atom_test() { "get" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Head" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "HEAD" |> make_atom @@ -184,12 +192,12 @@ pub fn method_from_dynamic_atom_test() { "head" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Options" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "OPTIONS" |> make_atom @@ -199,12 +207,12 @@ pub fn method_from_dynamic_atom_test() { "options" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Patch" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "PATCH" |> make_atom @@ -214,12 +222,12 @@ pub fn method_from_dynamic_atom_test() { "patch" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Post" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "POST" |> make_atom @@ -229,12 +237,12 @@ pub fn method_from_dynamic_atom_test() { "post" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Put" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "PUT" |> make_atom @@ -244,12 +252,12 @@ pub fn method_from_dynamic_atom_test() { "put" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "Trace" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "TRACE" |> make_atom @@ -259,12 +267,22 @@ pub fn method_from_dynamic_atom_test() { "trace" |> make_atom |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) "thingy" |> make_atom |> http.method_from_dynamic |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) + + "!#$%&'*+-.^_`|~abcABC123" + |> make_atom + |> http.method_from_dynamic + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) + + "In-valid method" + |> make_atom + |> http.method_from_dynamic + |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) } @target(erlang) @@ -273,7 +291,7 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) + |> should.equal(Ok(http.Other("Connect"))) "CONNECT" |> to_charlist @@ -285,13 +303,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) + |> should.equal(Ok(http.Other("connect"))) "Delete" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) + |> should.equal(Ok(http.Other("Delete"))) "DELETE" |> to_charlist @@ -303,13 +321,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) + |> should.equal(Ok(http.Other("delete"))) "Get" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) + |> should.equal(Ok(http.Other("Get"))) "GET" |> to_charlist @@ -321,13 +339,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) + |> should.equal(Ok(http.Other("get"))) "Head" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) + |> should.equal(Ok(http.Other("Head"))) "HEAD" |> to_charlist @@ -339,13 +357,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) + |> should.equal(Ok(http.Other("head"))) "Options" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) + |> should.equal(Ok(http.Other("Options"))) "OPTIONS" |> to_charlist @@ -357,13 +375,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) + |> should.equal(Ok(http.Other("options"))) "Patch" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) + |> should.equal(Ok(http.Other("Patch"))) "PATCH" |> to_charlist @@ -375,13 +393,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) + |> should.equal(Ok(http.Other("patch"))) "Post" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) + |> should.equal(Ok(http.Other("Post"))) "POST" |> to_charlist @@ -393,13 +411,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) + |> should.equal(Ok(http.Other("post"))) "Put" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) + |> should.equal(Ok(http.Other("Put"))) "PUT" |> to_charlist @@ -411,13 +429,13 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) + |> should.equal(Ok(http.Other("put"))) "Trace" |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) + |> should.equal(Ok(http.Other("Trace"))) "TRACE" |> to_charlist @@ -429,12 +447,24 @@ pub fn charlist_to_method_test() { |> to_charlist |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) + |> should.equal(Ok(http.Other("trace"))) "thingy" |> to_charlist |> dynamic.from |> http.method_from_dynamic + |> should.equal(Ok(http.Other("thingy"))) + + "!#$%&'*+-.^_`|~abcABC123" + |> to_charlist + |> dynamic.from + |> http.method_from_dynamic + |> should.equal(Ok(http.Other("!#$%&'*+-.^_`|~abcABC123"))) + + "In-valid method" + |> to_charlist + |> dynamic.from + |> http.method_from_dynamic |> should.equal(Error([DecodeError("HTTP method", "List", [])])) } @@ -442,7 +472,7 @@ pub fn method_from_dynamic_test() { "Connect" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) + |> should.equal(Ok(http.Other("Connect"))) "CONNECT" |> dynamic.from @@ -452,12 +482,12 @@ pub fn method_from_dynamic_test() { "connect" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) + |> should.equal(Ok(http.Other("connect"))) "Delete" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) + |> should.equal(Ok(http.Other("Delete"))) "DELETE" |> dynamic.from @@ -467,12 +497,12 @@ pub fn method_from_dynamic_test() { "delete" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) + |> should.equal(Ok(http.Other("delete"))) "Get" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) + |> should.equal(Ok(http.Other("Get"))) "GET" |> dynamic.from @@ -482,12 +512,12 @@ pub fn method_from_dynamic_test() { "get" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) + |> should.equal(Ok(http.Other("get"))) "Head" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) + |> should.equal(Ok(http.Other("Head"))) "HEAD" |> dynamic.from @@ -497,12 +527,12 @@ pub fn method_from_dynamic_test() { "head" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) + |> should.equal(Ok(http.Other("head"))) "Options" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) + |> should.equal(Ok(http.Other("Options"))) "OPTIONS" |> dynamic.from @@ -512,12 +542,12 @@ pub fn method_from_dynamic_test() { "options" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) + |> should.equal(Ok(http.Other("options"))) "Patch" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) + |> should.equal(Ok(http.Other("Patch"))) "PATCH" |> dynamic.from @@ -527,12 +557,12 @@ pub fn method_from_dynamic_test() { "patch" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) + |> should.equal(Ok(http.Other("patch"))) "Post" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) + |> should.equal(Ok(http.Other("Post"))) "POST" |> dynamic.from @@ -542,12 +572,12 @@ pub fn method_from_dynamic_test() { "post" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) + |> should.equal(Ok(http.Other("post"))) "Put" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) + |> should.equal(Ok(http.Other("Put"))) "PUT" |> dynamic.from @@ -557,12 +587,12 @@ pub fn method_from_dynamic_test() { "put" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) + |> should.equal(Ok(http.Other("put"))) "Trace" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) + |> should.equal(Ok(http.Other("Trace"))) "TRACE" |> dynamic.from @@ -572,50 +602,60 @@ pub fn method_from_dynamic_test() { "trace" |> dynamic.from |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) + |> should.equal(Ok(http.Other("trace"))) "thingy" |> dynamic.from |> http.method_from_dynamic + |> should.equal(Ok(http.Other("thingy"))) + + "!#$%&'*+-.^_`|~abcABC123" + |> dynamic.from + |> http.method_from_dynamic + |> should.equal(Ok(http.Other("!#$%&'*+-.^_`|~abcABC123"))) + + "In-valid method" + |> dynamic.from + |> http.method_from_dynamic |> should.equal(Error([DecodeError("HTTP method", "String", [])])) } pub fn method_to_string_test() { http.Connect |> http.method_to_string - |> should.equal("connect") + |> should.equal("CONNECT") http.Delete |> http.method_to_string - |> should.equal("delete") + |> should.equal("DELETE") http.Get |> http.method_to_string - |> should.equal("get") + |> should.equal("GET") http.Head |> http.method_to_string - |> should.equal("head") + |> should.equal("HEAD") http.Options |> http.method_to_string - |> should.equal("options") + |> should.equal("OPTIONS") http.Patch |> http.method_to_string - |> should.equal("patch") + |> should.equal("PATCH") http.Post |> http.method_to_string - |> should.equal("post") + |> should.equal("POST") http.Put |> http.method_to_string - |> should.equal("put") + |> should.equal("PUT") http.Trace |> http.method_to_string - |> should.equal("trace") + |> should.equal("TRACE") http.Other("ok") |> http.method_to_string From f5bd211c8eaa8dbe07ed7bd4fa3cad34a5b2968d Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:52:24 +0530 Subject: [PATCH 5/7] fix: js tests --- src/gleam/http.gleam | 5 +++-- src/gleam_http_native.mjs | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/gleam/http.gleam b/src/gleam/http.gleam index 311b29c..f439f47 100644 --- a/src/gleam/http.gleam +++ b/src/gleam/http.gleam @@ -53,7 +53,7 @@ fn is_valid_token(s: String) -> Bool { fn do_is_valid_token(bytes: BitArray, acc: Bool) { case bytes, acc { - <>, True -> do_is_valid_token(rest, is_valid_tchar(char)) + <>, True -> do_is_valid_token(rest, is_valid_tchar(char)) _, _ -> acc } } @@ -143,7 +143,8 @@ pub fn scheme_from_string(scheme: String) -> Result(Scheme, Nil) { pub fn method_from_dynamic(value: Dynamic) -> Result(Method, List(DecodeError)) { case do_method_from_dynamic(value) { Ok(method) -> Ok(method) - Error(_) -> Error([DecodeError("HTTP method", dynamic.classify(value), [])]) + Error(Nil) -> + Error([DecodeError("HTTP method", dynamic.classify(value), [])]) } } diff --git a/src/gleam_http_native.mjs b/src/gleam_http_native.mjs index 19a5150..65add70 100644 --- a/src/gleam_http_native.mjs +++ b/src/gleam_http_native.mjs @@ -18,21 +18,21 @@ import { export function is_valid_tchar(ch) { try { switch (ch) { - case '33': return true; - case '35': return true; - case '36': return true; - case '37': return true; - case '38': return true; - case '39': return true; - case '42': return true; - case '43': return true; - case '45': return true; - case '46': return true; - case '94': return true; - case '95': return true; - case '96': return true; - case '124': return true; - case '126': return true; + case 33: return true; + case 35: return true; + case 36: return true; + case 37: return true; + case 38: return true; + case 39: return true; + case 42: return true; + case 43: return true; + case 45: return true; + case 46: return true; + case 94: return true; + case 95: return true; + case 96: return true; + case 124: return true; + case 126: return true; } // DIGIT if (ch >= 0x30 && ch <= 0x39) return true; @@ -66,7 +66,7 @@ export function decode_method(value) { } if (typeof value === 'string') { for (const v of value) - if (!is_valid_tchar(v)) return new Error(undefined); + if (!is_valid_tchar(v.charCodeAt(0))) return new Error(undefined); return new Ok(new Other(value)); } } catch {} From f8d8b43589968067ba7fa6b80042db9e03e2e3a2 Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Tue, 4 Feb 2025 17:48:37 +0530 Subject: [PATCH 6/7] feat: remove method_from_dynamic --- src/gleam/http.gleam | 13 - src/gleam_http_native.erl | 77 ------ src/gleam_http_native.mjs | 74 ------ test/gleam/http_test.gleam | 497 ------------------------------------- 4 files changed, 661 deletions(-) delete mode 100644 src/gleam_http_native.erl delete mode 100644 src/gleam_http_native.mjs diff --git a/src/gleam/http.gleam b/src/gleam/http.gleam index f439f47..ae92834 100644 --- a/src/gleam/http.gleam +++ b/src/gleam/http.gleam @@ -7,7 +7,6 @@ import gleam/bit_array import gleam/bool -import gleam/dynamic.{type DecodeError, type Dynamic, DecodeError} import gleam/list import gleam/result import gleam/string @@ -140,14 +139,6 @@ pub fn scheme_from_string(scheme: String) -> Result(Scheme, Nil) { } } -pub fn method_from_dynamic(value: Dynamic) -> Result(Method, List(DecodeError)) { - case do_method_from_dynamic(value) { - Ok(method) -> Ok(method) - Error(Nil) -> - Error([DecodeError("HTTP method", dynamic.classify(value), [])]) - } -} - pub type MultipartHeaders { /// The headers for the part have been fully parsed. /// Header keys are all lowercase. @@ -591,10 +582,6 @@ fn more_please_body( |> Ok } -@external(erlang, "gleam_http_native", "decode_method") -@external(javascript, "../gleam_http_native.mjs", "decode_method") -fn do_method_from_dynamic(a: Dynamic) -> Result(Method, Nil) - /// A HTTP header is a key-value pair. Header keys must be all lowercase /// characters. pub type Header = diff --git a/src/gleam_http_native.erl b/src/gleam_http_native.erl deleted file mode 100644 index 2eae638..0000000 --- a/src/gleam_http_native.erl +++ /dev/null @@ -1,77 +0,0 @@ --module(gleam_http_native). --export([decode_method/1, is_valid_tchar/1]). - -is_valid_tchar(X) when is_integer(X) -> - case X of - $! -> true; - $# -> true; - $$ -> true; - $% -> true; - $& -> true; - $' -> true; - $* -> true; - $+ -> true; - $- -> true; - $. -> true; - $^ -> true; - $_ -> true; - $` -> true; - $| -> true; - $~ -> true; - % DIGIT - _ when X >= 16#30 andalso X =< 16#39 -> true; - % ALPHA - _ when (X >= 16#41 andalso X =< 16#5A) orelse (X >= 16#61 andalso X =< 16#7A) -> true; - _ -> false - end; -is_valid_tchar(_) -> false. - -is_valid_token([]) -> false; -is_valid_token(<<>>) -> false; -is_valid_token(X) when is_list(X) orelse is_bitstring(X) -> do_is_valid_token(X, true); -is_valid_token(_) -> false. - -do_is_valid_token(_, false) -> false; -do_is_valid_token([H|T] = X, true) when is_list(X) -> do_is_valid_token(T, is_valid_tchar(H)); -do_is_valid_token(<> = X, true) when is_bitstring(X) -> do_is_valid_token(T, is_valid_tchar(H)); -do_is_valid_token(_, X) -> X. - -normalise_method(X) when is_bitstring(X) -> {ok, {other, X}}; -normalise_method(X) when is_list(X) -> {ok, {other, list_to_binary(X)}}; -normalise_method(_) -> {error, nil}. - -decode_method(Term) -> - case Term 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}; - '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}; - <<"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}; - X -> - case is_valid_token(X) of - true -> normalise_method(X); - false -> {error, nil} - end - end. diff --git a/src/gleam_http_native.mjs b/src/gleam_http_native.mjs deleted file mode 100644 index 65add70..0000000 --- a/src/gleam_http_native.mjs +++ /dev/null @@ -1,74 +0,0 @@ -import { Ok, Error } from "./gleam.mjs"; -import { - Get, - Post, - Head, - Put, - Delete, - Trace, - Connect, - Options, - Patch, - Other, -} from "./gleam/http.mjs"; - -/** - @param {number} ch -*/ -export function is_valid_tchar(ch) { - try { - switch (ch) { - case 33: return true; - case 35: return true; - case 36: return true; - case 37: return true; - case 38: return true; - case 39: return true; - case 42: return true; - case 43: return true; - case 45: return true; - case 46: return true; - case 94: return true; - case 95: return true; - case 96: return true; - case 124: return true; - case 126: return true; - } - // DIGIT - if (ch >= 0x30 && ch <= 0x39) return true; - // ALPHA - if (ch >= 0x41 && ch <= 0x5A || ch >= 0x61 && ch <= 0x7A) return true; - } catch {} - return false; -} - -export function decode_method(value) { - try { - switch (value) { - case "GET": - return new Ok(new Get()); - case "POST": - return new Ok(new Post()); - case "HEAD": - return new Ok(new Head()); - case "PUT": - return new Ok(new Put()); - case "DELETE": - return new Ok(new Delete()); - case "TRACE": - return new Ok(new Trace()); - case "CONNECT": - return new Ok(new Connect()); - case "OPTIONS": - return new Ok(new Options()); - case "PATCH": - return new Ok(new Patch()); - } - if (typeof value === 'string') { - for (const v of value) - if (!is_valid_tchar(v.charCodeAt(0))) return new Error(undefined); - return new Ok(new Other(value)); - } - } catch {} - return new Error(undefined); -} diff --git a/test/gleam/http_test.gleam b/test/gleam/http_test.gleam index 23ff70a..f65acab 100644 --- a/test/gleam/http_test.gleam +++ b/test/gleam/http_test.gleam @@ -1,4 +1,3 @@ -import gleam/dynamic.{DecodeError} import gleam/http import gleeunit/should @@ -124,502 +123,6 @@ pub fn parse_method_test() { |> should.equal(Error(Nil)) } -@target(erlang) -@external(erlang, "unicode", "characters_to_list") -fn to_charlist(a: String) -> List(Int) - -@target(erlang) -@external(erlang, "gleam_http_test_helper", "atom") -fn make_atom(a: String) -> dynamic.Dynamic - -@target(erlang) -pub fn method_from_dynamic_atom_test() { - "Connect" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "CONNECT" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) - - "connect" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Delete" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "DELETE" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) - - "delete" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Get" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "GET" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) - - "get" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Head" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "HEAD" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) - - "head" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Options" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "OPTIONS" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) - - "options" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Patch" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "PATCH" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) - - "patch" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Post" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "POST" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) - - "post" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Put" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "PUT" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) - - "put" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "Trace" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "TRACE" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) - - "trace" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "thingy" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "!#$%&'*+-.^_`|~abcABC123" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) - - "In-valid method" - |> make_atom - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "Atom", [])])) -} - -@target(erlang) -pub fn charlist_to_method_test() { - "Connect" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Connect"))) - - "CONNECT" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) - - "connect" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("connect"))) - - "Delete" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Delete"))) - - "DELETE" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) - - "delete" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("delete"))) - - "Get" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Get"))) - - "GET" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) - - "get" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("get"))) - - "Head" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Head"))) - - "HEAD" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) - - "head" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("head"))) - - "Options" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Options"))) - - "OPTIONS" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) - - "options" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("options"))) - - "Patch" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Patch"))) - - "PATCH" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) - - "patch" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("patch"))) - - "Post" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Post"))) - - "POST" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) - - "post" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("post"))) - - "Put" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Put"))) - - "PUT" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) - - "put" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("put"))) - - "Trace" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Trace"))) - - "TRACE" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) - - "trace" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("trace"))) - - "thingy" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("thingy"))) - - "!#$%&'*+-.^_`|~abcABC123" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("!#$%&'*+-.^_`|~abcABC123"))) - - "In-valid method" - |> to_charlist - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "List", [])])) -} - -pub fn method_from_dynamic_test() { - "Connect" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Connect"))) - - "CONNECT" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Connect)) - - "connect" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("connect"))) - - "Delete" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Delete"))) - - "DELETE" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Delete)) - - "delete" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("delete"))) - - "Get" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Get"))) - - "GET" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Get)) - - "get" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("get"))) - - "Head" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Head"))) - - "HEAD" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Head)) - - "head" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("head"))) - - "Options" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Options"))) - - "OPTIONS" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Options)) - - "options" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("options"))) - - "Patch" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Patch"))) - - "PATCH" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Patch)) - - "patch" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("patch"))) - - "Post" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Post"))) - - "POST" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Post)) - - "post" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("post"))) - - "Put" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Put"))) - - "PUT" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Put)) - - "put" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("put"))) - - "Trace" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("Trace"))) - - "TRACE" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Trace)) - - "trace" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("trace"))) - - "thingy" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("thingy"))) - - "!#$%&'*+-.^_`|~abcABC123" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Ok(http.Other("!#$%&'*+-.^_`|~abcABC123"))) - - "In-valid method" - |> dynamic.from - |> http.method_from_dynamic - |> should.equal(Error([DecodeError("HTTP method", "String", [])])) -} - pub fn method_to_string_test() { http.Connect |> http.method_to_string From 630ca050e1ca929e90ee759eba0a36d71797d84e Mon Sep 17 00:00:00 2001 From: arnu515 <52203828+arnu515@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:47:01 +0530 Subject: [PATCH 7/7] fix: add back the `is_valid_tchar` method --- src/gleam/http.gleam | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/gleam/http.gleam b/src/gleam/http.gleam index ae92834..ef5c566 100644 --- a/src/gleam/http.gleam +++ b/src/gleam/http.gleam @@ -57,9 +57,19 @@ fn do_is_valid_token(bytes: BitArray, acc: Bool) { } } -@external(erlang, "gleam_http_native", "is_valid_tchar") -@external(javascript, "../gleam_http_native.mjs", "is_valid_tchar") -fn is_valid_tchar(ch: Int) -> Bool +fn is_valid_tchar(ch: Int) -> Bool { + case ch { + // "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" + // | "." | "^" | "_" | "`" | "|" | "~" + 33 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 45 | 46 | 94 | 95 | 96 | 124 | 126 -> + True + // DIGIT + ch if ch >= 0x30 && ch <= 0x39 -> True + // ALPHA + ch if ch >= 0x41 && ch <= 0x5A || ch >= 0x61 && ch <= 0x7A -> True + _ -> False + } +} // TODO: check if the a is a valid HTTP method (i.e. it is a token, as per the // spec) and return Ok(Other(s)) if so.