🧚 A practical web framework for Gleam

Do not guess encoding

+34 -21
+2 -5
examples/8-working-with-cookies/src/app/router.gleam
··· 1 1 import app/web 2 2 import gleam/http.{Delete, Get, Post} 3 3 import gleam/list 4 - import gleam/result 5 - import gleam/crypto 6 - import gleam/bit_string 7 4 import gleam/string_builder 8 5 import wisp.{Request, Response} 9 6 ··· 20 17 } 21 18 22 19 pub fn home(req: Request) -> Response { 23 - case wisp.get_cookie(req, cookie_name) { 20 + case wisp.get_cookie(req, cookie_name, wisp.Signed) { 24 21 Ok(name) -> { 25 22 [ 26 23 "<h1>Hello, " <> wisp.escape_html(name) <> "!</h1>", ··· 61 58 62 59 pub fn destroy_session(req: Request) -> Response { 63 60 let resp = wisp.redirect("/session") 64 - case wisp.get_cookie(req, cookie_name) { 61 + case wisp.get_cookie(req, cookie_name, wisp.Signed) { 65 62 Ok(value) -> wisp.set_cookie(resp, req, cookie_name, value, wisp.Signed, 0) 66 63 Error(_) -> resp 67 64 }
+3 -3
manifest.toml
··· 8 8 { name = "gleam_erlang", version = "0.22.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "367D8B41A7A86809928ED1E7E55BFD0D46D7C4CF473440190F324AFA347109B4" }, 9 9 { name = "gleam_http", version = "3.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "FAE9AE3EB1CA90C2194615D20FFFD1E28B630E84DACA670B28D959B37BCBB02C" }, 10 10 { name = "gleam_json", version = "0.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "C6CC5BEECA525117E97D0905013AB3F8836537455645DDDD10FE31A511B195EF" }, 11 - { name = "gleam_otp", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang"], otp_app = "gleam_otp", source = "hex", outer_checksum = "ED7381E90636E18F5697FD7956EECCA635A3B65538DC2BE2D91A38E61DCE8903" }, 11 + { name = "gleam_otp", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "ED7381E90636E18F5697FD7956EECCA635A3B65538DC2BE2D91A38E61DCE8903" }, 12 12 { name = "gleam_stdlib", version = "0.30.2", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "8D8BF3790AA31176B1E1C0B517DD74C86DA8235CF3389EA02043EE4FD82AE3DC" }, 13 13 { name = "gleeunit", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "1397E5C4AC4108769EE979939AC39BF7870659C5AFB714630DEEEE16B8272AD5" }, 14 - { name = "glisten", version = "0.8.2", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "364E9B3D4F78308D2EEE5D73E0FB16C686E08516943EFDA501B17177B382907C" }, 15 - { name = "mist", version = "0.13.2", build_tools = ["gleam"], requirements = ["glisten", "gleam_erlang", "gleam_stdlib", "gleam_http", "gleam_otp"], otp_app = "mist", source = "hex", outer_checksum = "51C385C58A78A2013A30F92705814560AD9A2B8EC3ECBA94C620F22D3ACB50BC" }, 14 + { name = "glisten", version = "0.8.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang", "gleam_otp"], otp_app = "glisten", source = "hex", outer_checksum = "364E9B3D4F78308D2EEE5D73E0FB16C686E08516943EFDA501B17177B382907C" }, 15 + { name = "mist", version = "0.13.2", build_tools = ["gleam"], requirements = ["glisten", "gleam_erlang", "gleam_http", "gleam_stdlib", "gleam_otp"], otp_app = "mist", source = "hex", outer_checksum = "51C385C58A78A2013A30F92705814560AD9A2B8EC3ECBA94C620F22D3ACB50BC" }, 16 16 { name = "simplifile", version = "0.1.10", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "263B7C7F4B29263555DEA2D30BA918425A27120CDD1E1352744EAB4D56CE01CE" }, 17 17 { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, 18 18 ]
+10 -6
src/wisp.gleam
··· 1784 1784 1785 1785 /// Get a cookie from the request. 1786 1786 /// 1787 - /// If you wish to get multiple cookies you may want to use the `get_cookies` 1788 - /// function to avoid parsing the cookie headers multiple times. 1787 + /// If a cookie is missing, found to be malformed, or the signature is invalid 1788 + /// for a signed cookie, then `Error(Nil)` is returned. 1789 1789 /// 1790 1790 /// ```gleam 1791 1791 /// wisp.get_cookie(request, "group") 1792 1792 /// // -> Ok("A") 1793 1793 /// ``` 1794 1794 /// 1795 - pub fn get_cookie(request: Request, name: String) -> Result(String, Nil) { 1795 + pub fn get_cookie( 1796 + request: Request, 1797 + name: String, 1798 + security: Security, 1799 + ) -> Result(String, Nil) { 1796 1800 use value <- result.try( 1797 1801 request 1798 1802 |> request.get_cookies 1799 1803 |> list.key_find(name), 1800 1804 ) 1801 - case value { 1802 - "SFM1MTI." <> _ -> verify_signed_message(request, value) 1803 - _ -> base.decode64(value) 1805 + case security { 1806 + PlainText -> base.decode64(value) 1807 + Signed -> verify_signed_message(request, value) 1804 1808 } 1805 1809 |> result.try(bit_string.to_string) 1806 1810 }
+19 -7
test/wisp_test.gleam
··· 911 911 "/", 912 912 [ 913 913 // Plain text 914 - #("cookie", "id=MTIz"), 914 + #("cookie", "plain=MTIz"), 915 915 // Signed 916 916 #( 917 917 "cookie", 918 - "flash=SFM1MTI.aGktdGhlcmU.uWUWvrAleKQ2jsWcU97HzGgPqtLjjUgl4oe40-RPJ5qRRcE_soXPacgmaHTLxK3xZbOJ5DOTIRMI0szD4Re7wA", 918 + "signed=SFM1MTI.aGktdGhlcmU.uWUWvrAleKQ2jsWcU97HzGgPqtLjjUgl4oe40-RPJ5qRRcE_soXPacgmaHTLxK3xZbOJ5DOTIRMI0szD4Re7wA", 919 919 ), 920 920 // Signed but tampered with 921 921 #( ··· 926 926 ) 927 927 928 928 request 929 - |> wisp.get_cookie("id") 929 + |> wisp.get_cookie("plain", wisp.PlainText) 930 930 |> should.equal(Ok("123")) 931 + request 932 + |> wisp.get_cookie("plain", wisp.Signed) 933 + |> should.equal(Error(Nil)) 931 934 932 935 request 933 - |> wisp.get_cookie("flash") 936 + |> wisp.get_cookie("signed", wisp.PlainText) 937 + |> should.equal(Error(Nil)) 938 + request 939 + |> wisp.get_cookie("signed", wisp.Signed) 934 940 |> should.equal(Ok("hi-there")) 935 941 936 942 request 937 - |> wisp.get_cookie("signed-and-tampered-with") 943 + |> wisp.get_cookie("signed-and-tampered-with", wisp.PlainText) 944 + |> should.equal(Error(Nil)) 945 + request 946 + |> wisp.get_cookie("signed-and-tampered-with", wisp.Signed) 938 947 |> should.equal(Error(Nil)) 939 948 940 949 request 941 - |> wisp.get_cookie("unknown") 950 + |> wisp.get_cookie("unknown", wisp.PlainText) 951 + |> should.equal(Error(Nil)) 952 + request 953 + |> wisp.get_cookie("unknown", wisp.Signed) 942 954 |> should.equal(Error(Nil)) 943 955 } 944 956 ··· 952 964 let req = testing.get("/", []) 953 965 let signed = wisp.sign_message(req, <<message:utf8>>, crypto.Sha512) 954 966 let req = testing.get("/", [#("cookie", "message=" <> signed)]) 955 - let assert Ok(out) = wisp.get_cookie(req, "message") 967 + let assert Ok(out) = wisp.get_cookie(req, "message", wisp.Signed) 956 968 out 957 969 |> should.equal(message) 958 970 }