elysiajs / elysia-html

A plugin for Elysia that add support for returning html
MIT License
25 stars 18 forks source link

html() loses earlier cookie() headers #17

Closed jdudmesh closed 1 year ago

jdudmesh commented 1 year ago

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,
      })
arthurfiorette commented 1 year ago

Hey @jdudmesh is this still a problem with 0.6.6? You did not specified whats your elysia & elysia html version...

https://github.com/elysiajs/elysia-html/blob/5dee1438c0f19b1de6eed243e65b81fc39a3bf4c/src/html.ts#L23-L27

arthurfiorette commented 1 year ago

I can confirm its working after 0.6.6.

image