-
-
Save cramforce/12e58aea2920ae80159f42be451e8027 to your computer and use it in GitHub Desktop.
import { NextResponse } from "next/server"; | |
import type { NextRequest } from "next/server"; | |
const SCRIPT_TAG = '<script async src="/feedback-bootstrap.js"></script>'; | |
export async function middleware(request: NextRequest) { | |
// Skip requests not looking for html. | |
const accept = request.headers.get("accept") || ""; | |
if (!accept) { | |
console.log("Request headers", request.headers); | |
} | |
if (!accept || !accept.startsWith("text/html")) { | |
return NextResponse.next(); | |
} | |
const middlewareResponse = NextResponse.next(); | |
// Skip redirects, etc. | |
if (middlewareResponse.status > 200) { | |
return middlewareResponse; | |
} | |
// We want to change the response, and that means we have to fetch it ourselves. | |
const originResponse = await fetch(request); | |
// Response doesn't look like HTML, skip! | |
const contentType = originResponse.headers.get("content-type"); | |
if (!contentType) { | |
console.log("Origin response headers", originResponse.headers); | |
} | |
if (!contentType || !contentType.startsWith("text/html")) { | |
return originResponse; | |
} | |
let rewritten; | |
if (originResponse.body.getReader) { | |
console.log("Using the streaming API"); | |
const reader = originResponse.body.getReader(); | |
rewritten = new ReadableStream({ | |
start(controller) { | |
function push() { | |
return reader.read().then(({ done, value }) => { | |
if (done) { | |
// Append the script tag. | |
controller.enqueue(new TextEncoder().encode(SCRIPT_TAG)); | |
controller.close(); | |
return; | |
} | |
controller.enqueue(value); | |
push(); | |
}); | |
} | |
push(); | |
}, | |
}); | |
} else { | |
// It appears that originResponse.body.getReader is null and so we cannot stream the | |
// addition. | |
const text = await originResponse.text(); | |
// Dumping it to the end is simple and works. | |
rewritten = text + SCRIPT_TAG; | |
} | |
return new Response(rewritten, { | |
headers: new Headers(originResponse.headers), | |
status: originResponse.status, | |
statusText: originResponse.statusText, | |
}); | |
} |
The idea is that if other middlewares do a redirect, then this one should do nothing. It would likely also check whether there is a body in case the middleware actually responded.
But NextResponse.next()
doesn't actually run anything else. It's just a static object (so that check will always be false):
static next() {
return new NextResponse(null, {
headers: {
'x-middleware-next': '1',
},
})
}
It's used to check the response at the end of the middleware call to know what to do next. I think it's mostly meant to be used if you want to let the request fall through, but attach some extra headers.
As far as I can see the streaming interface on the fetch response is broken.
Yeah, it's a middleware issue, we have a workaround cleanResponseBody
method in Live to get the correct ReadableStream (currently it returns a Node.js stream)
--
The rest looks good. Only worry here is if the user has an existing top-level middleware, I don't know how this will play out (how do we merge them)
This is pretty much exactly the way I implemented it locally as well, only difference was the streaming
I think that is actually exactly what we want. We do need to monitor when
location.href
changes and update the comment state.The idea is that if other middlewares do a redirect, then this one should do nothing. It would likely also check whether there is a body in case the middleware actually responded.
See comment. As far as I can see the streaming interface on the fetch response is broken. Otherwise we should absolutely do this.