Skip to content

Commit

Permalink
Set headers on the CowboyRequest directly
Browse files Browse the repository at this point in the history
Cowboy 2.11.0 removed the option of setting cookie headers in `cowboy_req:reply`, `cowboy_req:set_resp_header` or `cowboy_req:set_resp_headers`
To solve this we update the CowboyRequest headers directly, as suggested here: ninenines/cowboy#1624 (comment)
  • Loading branch information
BradLewis committed Mar 19, 2024
1 parent 6d218e1 commit 0794048
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 8 deletions.
2 changes: 1 addition & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ links = [
gleam_stdlib = "~> 0.31"
gleam_http = "~> 3.0"
gleam_otp = "~> 0.7"
cowboy = "~> 2.10.0"
cowboy = "~> 2.0"
gleam_erlang = "~> 0.22"

[dev-dependencies]
Expand Down
6 changes: 3 additions & 3 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

packages = [
{ name = "certifi", version = "2.12.0", build_tools = ["rebar3"], requirements = [], otp_app = "certifi", source = "hex", outer_checksum = "EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C" },
{ name = "cowboy", version = "2.10.0", build_tools = ["make", "rebar3"], requirements = ["cowlib", "ranch"], otp_app = "cowboy", source = "hex", outer_checksum = "3AFDCCB7183CC6F143CB14D3CF51FA00E53DB9EC80CDCD525482F5E99BC41D6B" },
{ name = "cowlib", version = "2.12.1", build_tools = ["make", "rebar3"], requirements = [], otp_app = "cowlib", source = "hex", outer_checksum = "163B73F6367A7341B33C794C4E88E7DBFE6498AC42DCD69EF44C5BC5507C8DB0" },
{ name = "cowboy", version = "2.12.0", build_tools = ["make", "rebar3"], requirements = ["cowlib", "ranch"], otp_app = "cowboy", source = "hex", outer_checksum = "8A7ABE6D183372CEB21CAA2709BEC928AB2B72E18A3911AA1771639BEF82651E" },
{ name = "cowlib", version = "2.13.0", build_tools = ["make", "rebar3"], requirements = [], otp_app = "cowlib", source = "hex", outer_checksum = "E1E1284DC3FC030A64B1AD0D8382AE7E99DA46C3246B815318A4B848873800A4" },
{ name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
{ 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.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" },
Expand All @@ -22,7 +22,7 @@ packages = [
]

[requirements]
cowboy = { version = "~> 2.10.0" }
cowboy = { version = "~> 2.0" }
gleam_erlang = { version = "~> 0.22" }
gleam_hackney = { version = "~> 1.0" }
gleam_http = { version = "~> 3.0" }
Expand Down
15 changes: 13 additions & 2 deletions src/gleam/http/cowboy.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ import gleam/bytes_builder.{type BytesBuilder}
import gleam/dynamic.{type Dynamic}
import gleam/erlang/process.{type Pid}

type CowboyRequest
type CowboyRequest

@external(erlang, "gleam_cowboy_native", "start_link")
fn erlang_start_link(
handler: fn(CowboyRequest) -> CowboyRequest,
port: Int,
) -> Result(Pid, Dynamic)

@external(erlang, "gleam_cowboy_native", "set_headers")
fn erlang_set_headers(headers: Dict(String, Dynamic), request: CowboyRequest) -> CowboyRequest

fn set_headers(headers: Dict(String, Dynamic), request: CowboyRequest) -> CowboyRequest {
erlang_set_headers(headers, request)
}

@external(erlang, "cowboy_req", "reply")
fn cowboy_reply(
status: Int,
Expand Down Expand Up @@ -120,8 +127,12 @@ fn service_to_handler(
let status = response.status

let headers = cowboy_format_headers(response.headers)
// We set headers directly on the CowboyRequest as we cannot set cookie headers
// using cowboy_req:set_resp_headers as of 2.11.0.
// https://github.com/ninenines/cowboy/pull/1624#issuecomment-1915324578
let request = set_headers(headers, request)
let body = response.body
cowboy_reply(status, headers, body, request)
cowboy_reply(status, dict.new(), body, request)
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/gleam_cowboy_native.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-module(gleam_cowboy_native).

-export([init/2, start_link/2, read_entire_body/1]).
-export([init/2, start_link/2, read_entire_body/1, set_headers/2]).

start_link(Handler, Port) ->
RanchOptions = #{
Expand Down Expand Up @@ -29,3 +29,6 @@ read_entire_body(Body, Req0) ->
{ok, Chunk, Req1} -> {list_to_binary([Body, Chunk]), Req1};
{more, Chunk, Req1} -> read_entire_body([Body, Chunk], Req1)
end.

set_headers(Headers, Req) ->
Req#{resp_headers => Headers}.
24 changes: 23 additions & 1 deletion test/gleam/http/cowboy_test.gleam
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import gleam/http/cowboy
import gleam/bytes_builder.{type BytesBuilder}
import gleam/http.{Get, Head, Post}
import gleam/http.{Get, Head, Post, Http}
import gleam/http/request.{type Request}
import gleam/http/response.{type Response}
import gleam/http/cookie
import gleam/hackney
import gleam/list

pub fn echo_service(request: Request(BitArray)) -> Response(BytesBuilder) {
let body = case request.body {
Expand All @@ -12,6 +14,7 @@ pub fn echo_service(request: Request(BitArray)) -> Response(BytesBuilder) {
}
response.new(200)
|> response.prepend_header("made-with", "Gleam")
|> response.set_cookie("cookie_name", "cookie_value", cookie.defaults(Http))
|> response.set_body(body)
}

Expand Down Expand Up @@ -87,3 +90,22 @@ pub fn body_is_echoed_on_post_test() {
let assert Ok("Gleam") = response.get_header(resp, "made-with")
let assert "Ping" = resp.body
}

pub fn cookie_headers_are_handled_correctly_test() {
let port = 3082
let assert Ok(_) = cowboy.start(echo_service, on_port: port)

let req =
request.new()
|> request.set_method(Get)
|> request.set_host("0.0.0.0")
|> request.set_scheme(http.Http)
|> request.set_port(port)
|> request.set_header("name", "value")

let assert Ok(resp) = hackney.send(req)
let assert 200 = resp.status
let assert Ok("Gleam") = response.get_header(resp, "made-with")
let cookies = response.get_cookies(resp)
let assert True = list.contains(cookies, #("cookie_name", "cookie_value"))
}

0 comments on commit 0794048

Please sign in to comment.