jrf0110 / 8track

A service worker router with async middleware and neato type-inference inspired by Koa
56 stars 6 forks source link

ctx.end(...) causes afterware effects to be ignored #43

Closed jrf0110 closed 3 years ago

jrf0110 commented 3 years ago

Edit

I previously thought this was only the case for ctx.end(new Response()). However, this applies to any afterware effects

For instance:

app.get(`(.*)`).use(async (ctx, next) => {
  await next()
  ctx.response.headers.append('Hello', 'World')
}).handle(async ctx => ctx.end(new Response('Hi'))

The 'Hello: World' Header will not make it into the final response. This is due to ctx being a different instance from middleware to middleware.

I originally had this ticket slated for a V2 release, but I think it's buggy enough to make it into 1.x as well.

Original

Suppose you have some middleware which adds a header and some route handler which calls .end() with a new Response::

app.get(`(.*)`).use(async (ctx, next) => {
  ctx.response.headers.append('Hello', 'World')
  return next()
}).handle(async ctx => ctx.end(new Response('Hi'))

This is confusing but not entirely surprising since the new Response will override the old response and not get any headers. There should be a way for middlewares and handlers to compose nicely and have responses merged together.

8track behaves a bit differently than koa in that the response is not sent until after all middleware chains have unwound. This should be fixed in #42.

Until then, one could add the header appending after an await next(), but this confusing to me - Why should the response be editable after that point? Similarly, HTMLRewriter becomes unviable after response becomes immutable after await next().

I propose we do a couple of things to make this nicer:

  1. Add a new property headers on ctx which will collect headers in a context and merge them with the final response
  2. Add a new method .transform() which accepts an HTMLRewriter instance. Transforms will be collected and merged with the final response