Cookies
ZIO HTTP has special support for Cookie headers using the Cookie
Domain to add and invalidate cookies. Adding a
cookie will generate the correct Set-Cookie
headers
Create a Request Cookie
A request cookie consists of name
and content
and can be created with Cookie.Request
:
import zio._
import zio.http._
val cookie: Cookie = Cookie.Request("id", "abc")
// cookie: Cookie = Request(name = "id", content = "abc")
Updating a request cookie
withContent
updates the content of cookie
cookie.withContent("def")
// res0: Cookie = Request(name = "id", content = "def")
withName
updates the name of cookie
cookie.withName("id2")
// res1: Cookie = Request(name = "id2", content = "abc")
Create a Response Cookie
A Response Cookie
can be created with
params name
, content
, expires
, domain
, path
, isSecure
, isHttpOnly
, maxAge
, sameSite
and secret
according to HTTP Set-Cookie
The below snippet creates a response cookie from the above request cookie:
val responseCookie = cookie.toResponse
// responseCookie: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = None,
// path = None,
// isSecure = false,
// isHttpOnly = false,
// maxAge = None,
// sameSite = None
// )
Update a Response Cookie
Cookie.Response
is a case class so it can be updated by its copy
method:
maxAge
updates the max-age of the cookie
responseCookie.copy(maxAge = Some(5.days))
// res2: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = None,
// path = None,
// isSecure = false,
// isHttpOnly = false,
// maxAge = Some(value = PT120H),
// sameSite = None
// )
domain
updates the host to which the cookie will be sent
responseCookie.copy(domain = Some("example.com"))
// res3: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = Some(value = "example.com"),
// path = None,
// isSecure = false,
// isHttpOnly = false,
// maxAge = None,
// sameSite = None
// )
path
updates the path of the cookie
responseCookie.copy(path = Some(Root / "cookie"))
// res4: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = None,
// path = Some(value = Path(segments = Vector(Root, Text(text = "cookie")))),
// isSecure = false,
// isHttpOnly = false,
// maxAge = None,
// sameSite = None
// )
isSecure
enables cookie only on https server
responseCookie.copy(isSecure = true)
// res5: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = None,
// path = None,
// isSecure = true,
// isHttpOnly = false,
// maxAge = None,
// sameSite = None
// )
isHttpOnly
forbids JavaScript from accessing the cookie
responseCookie.copy(isHttpOnly = true)
// res6: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = None,
// path = None,
// isSecure = false,
// isHttpOnly = true,
// maxAge = None,
// sameSite = None
// )
sameSite
updates whether or not a cookie is sent with cross-origin requests
responseCookie.copy(sameSite = Some(Cookie.SameSite.Strict))
// res7: Cookie.Response = Response(
// name = "id",
// content = "abc",
// domain = None,
// path = None,
// isSecure = false,
// isHttpOnly = false,
// maxAge = None,
// sameSite = Some(value = Strict)
// )
Sign a Cookie
The cookies can be signed with a signature:
- Using
sign
To sign a cookie, you can usesign
val cookie2 = Cookie.Response("key", "hello", maxAge = Some(5.days))
// cookie2: Cookie.Response = Response(
// name = "key",
// content = "hello",
// domain = None,
// path = None,
// isSecure = false,
// isHttpOnly = false,
// maxAge = Some(value = PT120H),
// sameSite = None
// )
val app = Http.collect[Request] { case Method.GET -> Root / "cookie" =>
Response.ok.addCookie(cookie2.sign("secret"))
}
// app: Http[Any, Nothing, Request, Response] = zio.http.Http$CollectHandler$$anon$17@65880400
- Using
signCookies
middleware
To sign all the cookies in your HttpApp
, you can use signCookies
middleware:
import RequestHandlerMiddlewares.signCookies
private val app2 = Http.collect[Request] {
case Method.GET -> Root / "cookie" => Response.ok.addCookie(cookie2)
case Method.GET -> Root / "secure-cookie" => Response.ok.addCookie(cookie2.copy(isSecure = true))
}
// app2: Http[Any, Nothing, Request, Response] = zio.http.Http$CollectHandler$$anon$17@415419a4
// Run it like any simple app
def run(args: List[String]): ZIO[Any, Throwable, Nothing] =
Server.serve(app2 @@ signCookies("secret"))
.provide(Server.default)
Adding Cookie in Response
The cookies can be added in Response
headers:
val res = Response.ok.addCookie(responseCookie)
// res: Response = zio.http.Response$BasicResponse$$anon$1@d83c305
It updates the response header Set-Cookie
as Set-Cookie: <cookie-name>=<cookie-value>
Getting Cookie from Request
In HTTP requests, cookies are stored in the cookie
header. cookiesDecoded
can be used to get all the cookies in the
request:
private val app3 = Http.collect[Request] {
case req@Method.GET -> Root / "cookie" =>
Response.text(req.header(Header.Cookie).map(_.value.toChunk).getOrElse(Chunk.empty).mkString(""))
}
// app3: Http[Any, Nothing, Request, Response] = zio.http.Http$CollectHandler$$anon$17@1784a296