about summary refs log tree commit diff
path: root/nixpkgs/pkgs/servers/http/nginx/nix-etag-1.15.4.patch
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/servers/http/nginx/nix-etag-1.15.4.patch')
-rw-r--r--nixpkgs/pkgs/servers/http/nginx/nix-etag-1.15.4.patch106
1 files changed, 106 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/servers/http/nginx/nix-etag-1.15.4.patch b/nixpkgs/pkgs/servers/http/nginx/nix-etag-1.15.4.patch
new file mode 100644
index 000000000000..8c8c8ce74b21
--- /dev/null
+++ b/nixpkgs/pkgs/servers/http/nginx/nix-etag-1.15.4.patch
@@ -0,0 +1,106 @@
+This patch makes it possible to serve static content from Nix store paths, by
+using the hash of the store path for the ETag header.
+
+diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
+index 97a91aee2..2d07d71e6 100644
+--- a/src/http/ngx_http_core_module.c
++++ b/src/http/ngx_http_core_module.c
+@@ -1676,6 +1676,8 @@ ngx_http_set_etag(ngx_http_request_t *r)
+ {
+     ngx_table_elt_t           *etag;
+     ngx_http_core_loc_conf_t  *clcf;
++    u_char                    *real, *ptr1, *ptr2;
++    ngx_err_t                 err;
+ 
+     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ 
+@@ -1692,16 +1694,82 @@ ngx_http_set_etag(ngx_http_request_t *r)
+     etag->next = NULL;
+     ngx_str_set(&etag->key, "ETag");
+ 
+-    etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);
+-    if (etag->value.data == NULL) {
+-        etag->hash = 0;
+-        return NGX_ERROR;
++    // Upstream nginx uses file mod timestamp and content-length for Etag, but
++    // files in the Nix store have their timestamps reset, so that doesn't work.
++    // Instead, when serving from the Nix store, we use the hash from the store
++    // path and content-length.
++    //
++    // Every file in under the given store path will share the same store path
++    // hash. It is fine to serve different resources with the same Etag, but
++    // different representations of the same resource (eg the same file, but
++    // gzip-compressed) should have different Etags. Thus, we also append
++    // content-length, which should be different when the response is compressed
++
++    err = ngx_errno;
++    real = ngx_realpath(clcf->root.data, NULL);
++    ngx_set_errno(err);
++
++    #define NIX_STORE_DIR "@nixStoreDir@"
++    #define NIX_STORE_LEN @nixStoreDirLen@
++
++    if (r->headers_out.last_modified_time == 1
++        && real != NULL
++        && !ngx_strncmp(real, NIX_STORE_DIR, NIX_STORE_LEN)
++        && real[NIX_STORE_LEN] == '/'
++        && real[NIX_STORE_LEN + 1] != '\0')
++    {
++        // extract the hash from a path formatted like
++        // /nix/store/hashhere1234-pname-1.0.0
++        // +1 to skip the leading /
++        ptr1 = real + NIX_STORE_LEN + 1;
++
++        ptr2 = (u_char *) ngx_strchr(ptr1, '-');
++
++        if (ptr2 == NULL) {
++            ngx_free(real);
++            etag->hash = 0;
++            return NGX_ERROR;
++        }
++
++        *ptr2 = '\0';
++
++        // hash + content-length + quotes and hyphen. Note that the
++        // content-length part of the string can vary in length.
++        etag->value.data = ngx_pnalloc(r->pool, ngx_strlen(ptr1) + NGX_OFF_T_LEN + 3);
++
++        if (etag->value.data == NULL) {
++            ngx_free(real);
++            etag->hash = 0;
++            return NGX_ERROR;
++        }
++
++
++        // set value.data content to "{hash}-{content-length}" (including quote
++        // marks), and set value.len to the length of the resulting string
++        etag->value.len = ngx_sprintf(etag->value.data, "\"\%s-%xO\"",
++                                      ptr1,
++                                      r->headers_out.content_length_n)
++                          - etag->value.data;
++
++        ngx_http_clear_last_modified(r);
++    } else {
++        // outside of Nix store, use the upstream Nginx logic for etags
++
++        etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);
++
++        if (etag->value.data == NULL) {
++            ngx_free(real);
++            etag->hash = 0;
++            return NGX_ERROR;
++        }
++
++        etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
++                                      r->headers_out.last_modified_time,
++                                      r->headers_out.content_length_n)
++                          - etag->value.data;
+     }
+ 
+-    etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
+-                                  r->headers_out.last_modified_time,
+-                                  r->headers_out.content_length_n)
+-                      - etag->value.data;
++    ngx_free(real);
+ 
+     r->headers_out.etag = etag;