html() loses earlier cookie() headers
jdudmesh opened this issue · comments
Thehtml()
plugin appears to drop headers that have been set earlier in the call lifecycle. Consider the following code where a derive()
plugin is used to set a session cookie on all incoming requests:
import { Elysia } from 'elysia'
import { html } from '@elysiajs/html';
import { cookie } from '@elysiajs/cookie';
const app = new Elysia()
.use(cookie())
.use(html())
.derive(({ setCookie }) => {
const sessionID = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
setCookie("session", sessionID, {
httpOnly: true,
sameSite: "lax"
})
return { sessionID }
})
.get('/', ({ sessionID, html }) => {
return html(`<html><body><div>SessionID: ${sessionID}</div></body></html>`)
})
.get('/cookie-ok', ({ sessionID, set }) => {
set.headers["content-type"] = "text/html; charset=utf8"
return `<html><body><div>SessionID: ${sessionID}</div></body></html>`
})
.listen(3001)
console.log(`Elysia is running at ${app.server?.hostname}:${app.server?.port}`)
Requests to /
do not return the cookie:
> curl http://localhost:3001/ -v
* Trying 127.0.0.1:3001...
* Connected to localhost (127.0.0.1) port 3001 (#0)
> GET / HTTP/1.1
> Host: localhost:3001
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf8
< Date: Fri, 15 Sep 2023 09:13:29 GMT
< Content-Length: 70
<
* Connection #0 to host localhost left intact
<html><body><div>SessionID: gcjg4c01blszjs3fiirnhc</div></body></html>
Calls to the second endpoint do return the cookie:
> curl http://localhost:3001/cookie-ok -v
* Trying 127.0.0.1:3001...
* Connected to localhost (127.0.0.1) port 3001 (#0)
> GET /cookie-ok HTTP/1.1
> Host: localhost:3001
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< set-cookie: session=mer7j274vpibxf6j3d94nd; Path=/; HttpOnly; SameSite=Lax
< Content-Type: text/html; charset=utf8
< Date: Fri, 15 Sep 2023 09:14:22 GMT
< Content-Length: 70
<
* Connection #0 to host localhost left intact
<html><body><div>SessionID: mer7j274vpibxf6j3d94nd</div></body></html>
Looking at the code, the plugin simply returns a new Response
object without merging the existing headers from the set
parameters.
https://github.com/elysiajs/elysia-html/blob/main/src/index.ts#L18-L24
Something like this works well:
html(value: string) {
const headers: Record<string, string> = {
"content-type": "text/html; charset=utf8",
}
Object.entries(set.headers).forEach(([k, v]) => {
if (Array.isArray(v)) {
headers[k] = v.join(" ")
} else {
headers[k] = v
}
})
return new Response(value, {
headers: headers,
})