libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

precompressed.ts (3668B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 import type {Context} from 'netlify:edge';
      7 
      8 // This lambda is executed whenever request URL matches.
      9 export default async (request: Request, context: Context) => {
     10   // Measure time for debugging purpose.
     11   let t0 = Date.now();
     12   // Get resource path (i.e. ignore query parameters).
     13   let url = request.url.split('?')[0];
     14   // Pick request headers; fallback to empty string if header is not set.
     15   let acceptEncodingHeader = request.headers.get('Accept-Encoding') || '';
     16   let acceptHeader = request.headers.get('Accept') || '';
     17   let etag = request.headers.get('If-None-Match') || '';
     18   // Roughly parse encodings list; this ignores "quality"; no modern browsers
     19   // use it -> don't care.
     20   let splitter = /[,;]/;
     21   let supportedEncodings =
     22       acceptEncodingHeader.split(splitter).map(v => v.trimStart());
     23   let supportsBr = supportedEncodings.includes('br');
     24   let supportedMedia = acceptHeader.split(splitter).map(v => v.trimStart());
     25   let supportsJxl = supportedMedia.includes('image/jxl');
     26   // Dump basic request info (we care about).
     27   context.log(
     28       'URL: ' + url + '; acceptEncodingHeader: ' + acceptEncodingHeader +
     29       '; supportsBr: ' + supportsBr + '; supportsJxl: ' + supportsJxl +
     30       '; etag: ' + etag);
     31 
     32   // If browser does not support Brotli/Jxl - just process request normally.
     33 
     34   if (!supportsBr && !supportsJxl) {
     35     return;
     36   }
     37 
     38   // Jxl processing is higher priority, because images are (usually) transferred
     39   // with 'identity' content encoding.
     40   let isJxlWorkflow = supportsJxl;
     41   let suffix = isJxlWorkflow ? '.jxl' : '.br';
     42 
     43   // Request pre-compressed resource (with a suffix).
     44   let response = await context.rewrite(url + suffix);
     45   context.log('Response status: ' + response.status);
     46   // First latency checkpoint (as we synchronously wait for resource fetch).
     47   let t1 = Date.now();
     48   // If pre-compressed resource does not exist - pass.
     49   if (response.status == 404) {
     50     return;
     51   }
     52   // Get resource ETag.
     53   let responseEtag = response.headers.get('ETag') || '';
     54   context.log('Response etag: ' + responseEtag);
     55   // We rely on platform to check ETag; add debugging info just in case.
     56   if (etag.length >= 4 && responseEtag === etag) {
     57     console.log('Match; status: ' + response.status);
     58   }
     59   // Status 200 is regular "OK" - fetch resource; in such a case we need to
     60   // craft response with the response contents.
     61   // Status 3xx likely means "use cache"; pass response as is.
     62   // Status 4xx is unlikely (404 has been already processed).
     63   // Status 5xx is server error - nothing we could do around it.
     64   if (response.status != 200) return response;
     65   // Second time consuming operation - wait for resource contents.
     66   let data = await response.arrayBuffer();
     67   let fixedHeaders = new Headers(response.headers);
     68 
     69   if (isJxlWorkflow) {
     70     fixedHeaders.set('Content-Type', 'image/jxl');
     71   } else {  // is Brotli workflow
     72     // Set "Content-Type" based on resource suffix;
     73     // otherwise browser will complain.
     74     let contentEncoding = 'text/html; charset=UTF-8';
     75     if (url.endsWith('.js')) {
     76       contentEncoding = 'application/javascript';
     77     } else if (url.endsWith('.wasm')) {
     78       contentEncoding = 'application/wasm';
     79     }
     80     fixedHeaders.set('Content-Type', contentEncoding);
     81     // Inform browser that data stream is compressed.
     82     fixedHeaders.set('Content-Encoding', 'br');
     83   }
     84   let t2 = Date.now();
     85   console.log('Timing: ' + (t1 - t0) + ' ' + (t2 - t1));
     86   return new Response(data, {headers: fixedHeaders});
     87 };