-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(gatsby-plugin-fastify): add customizable headers with sensible defaults for security #392
Conversation
- add initial work from original POC in #98\n- slightly modifications from original POC for missing vars/typos/etc
🦋 Changeset detectedLatest commit: 266d421 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@moonmeister what's the best way to test this out in a real site pre-release? For example, can we do an |
d246da3
to
233b2ae
Compare
aa5be3b
to
5643eba
Compare
5643eba
to
568e8ad
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start! Thanks for all your work on this. I'm sure a lot of my comments are against the code you copied and pasted from me, I'm aware. None of this is a comment on you, just me wanting to get us to a good point.
So the major question in the back of my head is if this can/should be implemented as a plugin using hooks or Decorators. I myself have been refactoring features as I go to use these features. It helps segment code logic into a single file so I don't have to reimplement things across ssr/static, etc.
Looks like your tests have focussed on static/ssr/dsg. Have you tested client-only routes or thought about how headers interact with client-only routes?
I'm sure I've forgotten some things but hopefully this gets you started?
Great question, It is possible, but I've never done it with Changesets, I'll try to make time to do some reading...though if you want to look into the docs and see what's possible that'd be helpful. |
I think I'll need to manually do a pre-release. LEt's get this a little further along then I can do that. |
Thanks @moonmeister - getting back to it today. FYI:
I think I figured out why this is failing, looking at a fix. Having to do with trying to read the |
Yeah. Maybe need to mock this. |
- refactor into headers plugin that handles add all necessary headers across all paths/modules - move headers related tests into new headers test suite - add additional tests for rev proxy, functions, client routes - add test coverage for `header-builder.ts` - rewrite documentation Resolves: - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392
- refactor into headers plugin that handles add all necessary headers across all paths/modules - move headers related tests into new headers test suite - add additional tests for rev proxy, functions, client routes - add test coverage for `header-builder.ts` - rewrite documentation Resolves: - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392
734259a
to
ce7d202
Compare
- refactor into headers plugin that handles add all necessary headers across all paths/modules - move headers related tests into new headers test suite - add additional tests for rev proxy, functions, client routes - add test coverage for `header-builder.ts` - rewrite documentation Resolves: - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392 - resolves #392
ce7d202
to
70e5443
Compare
@moonmeister ok, I think it's in a much better place now! Let me know what we need to do next. BenchmarksI ran the benchmarks and they most definitely called for a refactor.
RefactorI refactored it to add all of the actual page paths to the
@moonmeister says:
For this, I added Lastly, while refactoring/testing, I came across a bug in the reverse proxy implementation which I fixed in 0883b8f TestsI have written test cases to check all configurations in the feature table. I've updated/flipped some of the values you initially configured either because that's how Gatsby Cloud supports it or I think it's how we should support it - I've added explanations for them all in the footnotes. I also added Client-only Routes in the last column.
Footnotes
|
@moonmeister the test site with the updated version should build on gatsby cloud from the latest commit and I'll also get the |
- refactor `header-builder.ts` to loop through all possible pages/files and add their matching headers during `onPostBuild` - refactor `headers.ts` fastify plugin to look up headers for the path and add them, alongside default security, respecting overwrite precedence - add `reply.mode` and `reply.path` decorators to support adding headers to dynamic routes via the matched path rather than `request.url` - add tests to support feature table in #392, specifically: #392 (comment)
ab6ff63
to
3be7da1
Compare
|
- refactor `header-builder.ts` to loop through all possible pages/files and add their matching headers during `onPostBuild` - refactor `headers.ts` fastify plugin to look up headers for the path and add them, alongside default security, respecting overwrite precedence - add `reply.mode` and `reply.path` decorators to support adding headers to dynamic routes via the matched path rather than `request.url` - add tests to support feature table in #392, specifically: #392 (comment)
3be7da1
to
35bafd2
Compare
- refactor `header-builder.ts` to loop through all possible pages/files and add their matching headers during `onPostBuild` - refactor `headers.ts` fastify plugin to look up headers for the path and add them, alongside default security, respecting overwrite precedence - add `reply.mode` and `reply.path` decorators to support adding headers to dynamic routes via the matched path rather than `request.url` - add tests to support feature table in #392, specifically: #392 (comment)
35bafd2
to
56511ae
Compare
- refactor `header-builder.ts` to loop through all possible pages/files and add their matching headers during `onPostBuild` - refactor `headers.ts` fastify plugin to look up headers for the path and add them, alongside default security, respecting overwrite precedence - add `reply.mode` and `reply.path` decorators to support adding headers to dynamic routes via the matched path rather than `request.url` - add tests to support feature table in #392, specifically: #392 (comment)
56511ae
to
897e7d5
Compare
@moonmeister didn't get much time to work on this today...thoughts on #392 (comment) before I get back to it tomorrow? |
Hey sorry, been a busy weekend and haven't had much time. I read it briefly. Haven't been able to look at code. I should have time tonight. |
Definitely seems like we're on the right track. |
RE: Benchmarks - Glad we did some testing to validate this is the correct path forward. RE: Table - Thanks for validating and giving feedback. I agree with your assessments. Only one question, re: Functions and Security headers, What does Gatsby Cloud do? RE: Refactor - Thanks, I'll do a code review and provide feedback there |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I have the literal bandwidth to make a release tonight...I'll try in the next couple of days when I have time and a decent cell conection.
} = require("gatsby-plugin-fastify/dist/utils/config"); | ||
const { serveGatsby } = require("gatsby-plugin-fastify/dist/plugins/gatsby"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was getting gatsby-plugin-fastify module not found
(iirc on the exact error)...I tried a few things and only this worked. I'll try again while I'm making changes and put in the exact error, maybe you have another fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird...it kinda makes sense...but why has it been working is just as strange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya know what...this isn't automated...it probably hasn't been run since I made this change.
@@ -29,6 +30,7 @@ | |||
"gatsby-source-faker": "^5.7.0", | |||
"gatsby-source-filesystem": "^5.7.0", | |||
"gatsby-transformer-sharp": "^5.7.0", | |||
"pino-pretty": "^10.0.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not seeing this used anywhere... also http-status-codes
is being used now but it hasn't been added to deps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you open a PR for this, let's just get it out of this one now.
@@ -49,6 +49,8 @@ export const handleFunctions: FastifyPluginAsync<{ | |||
handler: async function (request, reply) { | |||
try { | |||
reply.appendModuleHeader("Functions"); | |||
reply.mode = "FUNCTION"; | |||
reply.path = `${FUNCTIONS_PREFIX}${path}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we stay consistent with our use of path.join
, or the POSIX version, which ever is correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can switch it to POSIX - just path.join
does \\api\\test
(on Windows).
// add configured headers | ||
reply.headers(pageHeaders); | ||
|
||
// check pageHeaders for "undefined" values and remove those headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree we shouldn't be sending undefined headers...this seems like a type checking issue somewhere else and should not need to be cleaned at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a type checking issue.
Perhaps you can add some input on a better way to handle this but in order to support being able to remove headers I made it so if you pass the string
value "undefined"
to the config object (or any of the default constants) it will properly remove the header with reply.removeHeader
.
I originally needed this functionality to match gatsby hosting as for some routes they do not return a cache-control
header at all, whereas Fastify was defaulting to public, max-age: 0
if we didn't explicitly set or remove it. Afterwards, I realized it could be helpful for customHeaders
as well to "unset" headers on specific paths and documented it in the README.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah...okay. hmmm. I don't love the "undefined"
but maybe there is no other good option. what about just using js undefined
? or false
? or "unset"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I think we can do this in the onPostBuild
...The only exception would be default security. but that could be cleaned up to be faster probably. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah...okay. hmmm. I don't love the
"undefined"
but maybe there is no other good option. what about just using jsundefined
? orfalse
? or"unset"
?
IIRC, setting the object like { "cache-control": undefined }
with the js undefined
makes the object not have the cache-control
property (it is undefined) so we can't check for it and remove the headers. I went back and forth with "undefined"
and "unset"
and ended on using undefined because the end result is that the header is not defined (vs the default or previously matched path headers). I think unset works too, it's just a more specific/made up term for our use-case. If you want to change it, I would lean back toward unset.
Also, I think we can do this in the onPostBuild...The only exception would be default security. but that could be cleaned up to be faster probably. Thoughts?
onPostBuild
doesn't actually set or remove the headers, just puts the entry into the headers
object, we could check previously configured defaults/customs to remove them here instead of adding the unset/undefined value - but we still have to check and removeHeader
at runtime to cover cases where it's getting set by fastify outside of our own code, such as the cache-control
default or other headers the user may want to remove. These lines are only looking at the specific headers object for the matched path, which is only 5-10 entries, generally, so I don't think it will make a performance difference to do some in onPostBuild
if we still have to check/remove at runtime anyway. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I'm guessing that @fastify/static
is adding this header, see: https://github.com/fastify/fastify-static#send-options. If we disable that then we should not have to deal with any headers set by Fastify. There are a couple of others like Etag
and LastModified
but I don't see any reason these would need to be overridden. If someone brings one to light, we can address the issue then. This means the only possible things we'd need to undefine would be things the user is setting. or gatsby defaults...which is kinda why we have those as selectable options. Unless you have an immediate use-case for this I think we should shelve this as a feature. Wait till there's a clear need.
useDefaultSecurity: boolean; | ||
}> = fp(async (fastify, { headers, useDefaultSecurity }) => { | ||
fastify.addHook("onSend", async function (request, reply: FastifyReply) { | ||
let pageHeaders = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of mutating this, I think Object.assign
can clean this up.
Co-authored-by: Alex Moon <[email protected]>
Co-authored-by: Alex Moon <[email protected]>
export const handleHeaders: FastifyPluginAsync<{ | ||
headers: Headers; | ||
useDefaultSecurity: boolean; | ||
}> = fp(async (fastify, { headers, useDefaultSecurity }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also thought about logging...not a rush but before we merge we'll want a couple of debug logs noting the use of default security headers, default caching headers, and custom headers. custom headers can probably just say hey there are custom headers or aren't. see https://github.com/gatsby-uc/plugins/blob/main/packages/gatsby-plugin-fastify/src/plugins/client-routes.ts#L16 for an example.
@tsdexter You're not waiting on me are you? |
@moonmeister haven't forgot about this, just been having some other production issues and deadlines. Will hopefully get back to it later this week or next. |
Nope, no worries. Just had to get some other stuff done in the meantime. See above. |
Gatsby officially has an Adapter spec on the way 🥳 . It specifically notes the inclusion of headers in the spec to eliminate our very need to do this crazy implementation per adapter. 🥳 |
And I had it on the schedule to finally get back to this after my vacation in July. I wonder how long until it's stable - looks great so far, been playing around with the demo repo. |
Yeah but also this is going to make life so easy. We can close every issue ever and it'll just work...hopefully. |
With the announcement of the Gatsby Adapters spec, we'll be discontinuing any work on this plugin. Should the need for a performant node server still be needed, this work can continue under the official spec as |
@moonmeister this makes sense. Just one comment. I think there is still a bit of uncertainty around adapters, but to me it seems they are intended to be more platform specific. I think there is probably still a path for something generic like For example, If my thinking is correct, perhaps the more generic Thoughts? |
Description
@moonmeister I think I've got this working as expected now. Please take a look and let me know.
options.headers
plugin configuration added for applying custom headers to specific files/pathsDocumentation
Headers section at the bottom of the README
Related Issues
closes #98
fixes #59
@moonmeister test
Gatsby Node API › Should Build Config
is failing - I'm not sure why. Can you take a look?