Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

nginx: change etags for statically compressed files served from store #278380

Merged
merged 1 commit into from Jan 14, 2024

Conversation

DeeUnderscore
Copy link
Contributor

Description of changes

Per RFC 9110, section 8.8.1, different representations of the same resource should have different Etags:

A strong validator is unique across all versions of all representations associated with a particular resource over time. However, there is no implication of uniqueness across representations of different resources (i.e., the same strong validator might be in use for representations of multiple resources at the same time and does not imply that those representations are equivalent)

When serving statically compressed files (ie, when there is an existing corresponding .gz/.br/etc. file on disk), Nginx sends the Etag marked as strong. These tags should be different for each compressed format (as shown in an explicit example in section 8.8.3.3 of the RFC). Upstream Etags are composed of the file modification timestamp and content length, and the latter generally changes between these representations.

Previous implementation of Nix-specific Etags for things served from store used the store hash. This is fine to share between different files, but it becomes a problem for statically compressed versions of the same file, as it means Nginx was serving different representations of the same resource with the same Etag, marked as strong.

This patch addresses this by imitating the upstream Nginx behavior, and appending the value of content length to the store hash.

Alternatives

I've considered appending something like -gzip or -br to the Etag, but both the gzip and br static modules set Content-type after Etag, so this is more tricky.

It might also be possible to mark the Etag as weak, if it is compressed (ie, prefix it with W/). This is what the filter (dynamic compression) modules do, reusing the original file's Etag, marked as weak. I think using strong Etags where possible is preferable, though.

Other options are dropping the Etag entirely for statically compressed resources, or leaving things as they are now. The former is the safer option. The latter generally seems to work, although it goes against the standard, so it might break in some non-obvious edge case somewhere.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.05 Release Notes (or backporting 23.05 and 23.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 馃憤 reaction to pull requests you find important.

@Izorkin
Copy link
Contributor

Izorkin commented Jan 3, 2024

Theoretically this change can be adopted in the upstream nginx?
I recently tried to find out from them whether an existing patch would be suitable for adding to the upstream, but they said that the patch was not suitable.
Maybe somehow it will be possible to come to an agreement with them and solve this problem immediately upstream.
And would it be suitable to generate an etag based on the file path + file size + file modification time?

@RaitoBezarius
Copy link
Member

Theoretically this change can be adopted in the upstream nginx? I recently tried to find out from them whether an existing patch would be suitable for adding to the upstream, but they said that the patch was not suitable. Maybe somehow it will be possible to come to an agreement with them and solve this problem immediately upstream. And would it be suitable to generate an etag based on the file path + file size + file modification time?

I'm not sure I see how they would agree to adding a prefix stripping logic for the path calculation :/. Do you have a thread where you asked them?

@Izorkin
Copy link
Contributor

Izorkin commented Jan 3, 2024

Do you have a thread where you asked them?

Yes, there is a topic, but in a different language.

Copy link
Member

@RaitoBezarius RaitoBezarius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly got around the review of the patch, I agree on the principle and concept but I need the patch to get commented so people can understand what is going on.

pkgs/servers/http/nginx/nix-etag-1.15.4.patch Show resolved Hide resolved
@Izorkin
Copy link
Contributor

Izorkin commented Jan 13, 2024

https://trac.nginx.org/nginx/ticket/2351 - this variant is being considered by developers,

@DeeUnderscore
Copy link
Contributor Author

https://trac.nginx.org/nginx/ticket/2351 - this variant is being considered by developers,

That sounds like a more robust and generic solution. The Nixpkgs solution is more narrow in scope, so I don't really think it would work for upstream as is.

@Izorkin
Copy link
Contributor

Izorkin commented Jan 13, 2024

That sounds like a more robust and generic solution. The Nixpkgs solution is more narrow in scope, so I don't really think it would work for upstream as is.

I hope they make a universal variant :)

Per RFC 9110, [section 8.8.1][1], different representations of the same
resource should have different Etags:

> A strong validator is unique across all versions of all
> representations associated with a particular resource over time.
> However, there is no implication of uniqueness across representations
> of different resources (i.e., the same strong validator might be in
> use for representations of multiple resources at the same time and
> does not imply that those representations are equivalent)

When serving statically compressed files (ie, when there is an existing
corresponding .gz/.br/etc. file on disk), Nginx sends the Etag marked
as strong. These tags should be different for each compressed format
(as shown in  an explicit example in section [8.8.3.3][2] of the RFC).
Upstream Etags are composed of the file modification timestamp and
content length, and the latter generally changes between these
representations.

Previous implementation of Nix-specific Etags for things served from
store used the store hash. This is fine to share between different
files, but it becomes a problem for statically compressed versions of
the same file, as it means Nginx was serving different representations
of the same resource with the same Etag, marked as strong.

This patch addresses this by imitating the upstream Nginx behavior, and
appending the value of content length to the store hash.

[1]: https://www.rfc-editor.org/rfc/rfc9110.html#name-validator-fields
[2]:
https://www.rfc-editor.org/rfc/rfc9110.html#name-example-entity-tags-varying
@DeeUnderscore
Copy link
Contributor Author

Added some comments that hopefully make things clearer.

@ofborg ofborg bot requested a review from RaitoBezarius January 14, 2024 00:42
@RaitoBezarius RaitoBezarius merged commit dd5621d into NixOS:master Jan 14, 2024
23 of 24 checks passed
@RaitoBezarius
Copy link
Member

Thank you @DeeUnderscore !

@DeeUnderscore DeeUnderscore deleted the fix/nginx-etag branch January 14, 2024 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants