From e8c1af13e500d070def952ccb97a246baddde2e5 Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 15:07:38 -0700
Subject: [PATCH 01/10] fix!: immutable headers guards for incoming response
 and downstream request

---
 .../js-compute/fixtures/app/src/request-headers.js         | 6 ++++++
 integration-tests/js-compute/fixtures/app/src/response.js  | 7 ++++++-
 runtime/fastly/builtins/fetch/request-response.cpp         | 4 ++--
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/integration-tests/js-compute/fixtures/app/src/request-headers.js b/integration-tests/js-compute/fixtures/app/src/request-headers.js
index 9bac7c6748..25f6242e62 100644
--- a/integration-tests/js-compute/fixtures/app/src/request-headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/request-headers.js
@@ -1,4 +1,5 @@
 /* eslint-env serviceworker */
+import { assertThrows } from './assertions.js';
 import { routes } from './routes.js';
 
 {
@@ -7,6 +8,11 @@ import { routes } from './routes.js';
      * @type {Request} request
      **/
     const request = event.request;
+
+    assertThrows(() => {
+      request.headers.set('should-be', 'immutable');
+    }, TypeError);
+
     const headers = {};
     for (const [name, value] of request.headers.entries()) {
       if (!headers[name]) {
diff --git a/integration-tests/js-compute/fixtures/app/src/response.js b/integration-tests/js-compute/fixtures/app/src/response.js
index 0742fb7f74..5a37cdf3e1 100644
--- a/integration-tests/js-compute/fixtures/app/src/response.js
+++ b/integration-tests/js-compute/fixtures/app/src/response.js
@@ -1,7 +1,7 @@
 /* eslint-env serviceworker */
 
 import { routes } from './routes.js';
-import { assert } from './assertions.js';
+import { assert, assertThrows } from './assertions.js';
 import { allowDynamicBackends } from 'fastly:experimental';
 
 routes.set('/response/stall', async (event) => {
@@ -64,6 +64,11 @@ routes.set('/response/request-body-init', async () => {
       accept: 'image/webp',
     },
   });
+
+  assertThrows(() => {
+    downloadResp.headers.set('should-be', 'immutable');
+  }, TypeError);
+
   // stream it through an echo proxy
   const postResp = await fetch(
     new Request('https://httpbin.org/anything', {
diff --git a/runtime/fastly/builtins/fetch/request-response.cpp b/runtime/fastly/builtins/fetch/request-response.cpp
index c04e183c49..43f85b3905 100644
--- a/runtime/fastly/builtins/fetch/request-response.cpp
+++ b/runtime/fastly/builtins/fetch/request-response.cpp
@@ -459,7 +459,7 @@ JSObject *Request::headers(JSContext *cx, JS::HandleObject obj) {
   if (!headers) {
     MOZ_ASSERT(is_instance(obj));
     if (is_downstream(obj)) {
-      headers = Headers::create(cx, request_handle(obj).headers(), Headers::HeadersGuard::Request);
+      headers = Headers::create(cx, request_handle(obj).headers(), Headers::HeadersGuard::Immutable);
     } else {
       headers = Headers::create(cx, Headers::HeadersGuard::Request);
     }
@@ -481,7 +481,7 @@ JSObject *Response::headers(JSContext *cx, JS::HandleObject obj) {
       headers =
           Headers::create(cx, response_handle(obj).headers(), Headers::HeadersGuard::Response);
     } else {
-      headers = Headers::create(cx, Headers::HeadersGuard::Response);
+      headers = Headers::create(cx, Headers::HeadersGuard::Immutable);
     }
     if (!headers) {
       return nullptr;

From ae639b51d94d3b2a5036bcdbd736d8c5b85c034f Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 15:09:31 -0700
Subject: [PATCH 02/10] fixup

---
 runtime/fastly/builtins/fetch/request-response.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/runtime/fastly/builtins/fetch/request-response.cpp b/runtime/fastly/builtins/fetch/request-response.cpp
index 43f85b3905..6e66225e2e 100644
--- a/runtime/fastly/builtins/fetch/request-response.cpp
+++ b/runtime/fastly/builtins/fetch/request-response.cpp
@@ -459,7 +459,8 @@ JSObject *Request::headers(JSContext *cx, JS::HandleObject obj) {
   if (!headers) {
     MOZ_ASSERT(is_instance(obj));
     if (is_downstream(obj)) {
-      headers = Headers::create(cx, request_handle(obj).headers(), Headers::HeadersGuard::Immutable);
+      headers =
+          Headers::create(cx, request_handle(obj).headers(), Headers::HeadersGuard::Immutable);
     } else {
       headers = Headers::create(cx, Headers::HeadersGuard::Request);
     }

From dc33a132f9f68a47ae2def2236b4f918f261c4d4 Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 15:18:50 -0700
Subject: [PATCH 03/10] fixup

---
 runtime/fastly/builtins/fetch/request-response.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/runtime/fastly/builtins/fetch/request-response.cpp b/runtime/fastly/builtins/fetch/request-response.cpp
index 6e66225e2e..381c147cb4 100644
--- a/runtime/fastly/builtins/fetch/request-response.cpp
+++ b/runtime/fastly/builtins/fetch/request-response.cpp
@@ -480,9 +480,9 @@ JSObject *Response::headers(JSContext *cx, JS::HandleObject obj) {
     MOZ_ASSERT(is_instance(obj));
     if (is_upstream(obj)) {
       headers =
-          Headers::create(cx, response_handle(obj).headers(), Headers::HeadersGuard::Response);
+          Headers::create(cx, response_handle(obj).headers(), Headers::HeadersGuard::Immutable);
     } else {
-      headers = Headers::create(cx, Headers::HeadersGuard::Immutable);
+      headers = Headers::create(cx, Headers::HeadersGuard::Response);
     }
     if (!headers) {
       return nullptr;

From b2e9d1538de27c84bd5d773643b62e2d2487e368 Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 15:58:04 -0700
Subject: [PATCH 04/10] fixups

---
 .../js-compute/fixtures/app/src/headers.js    | 20 ++++++++++++-------
 .../js-compute/fixtures/app/src/index.js      |  5 ++++-
 .../js-compute/fixtures/app/tests.json        |  5 ++++-
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/integration-tests/js-compute/fixtures/app/src/headers.js b/integration-tests/js-compute/fixtures/app/src/headers.js
index 3376b83ffc..26061c4b66 100644
--- a/integration-tests/js-compute/fixtures/app/src/headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/headers.js
@@ -26,8 +26,11 @@ routes.set('/headers/from-response/set', async () => {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  response.headers.set('cuStom', 'test');
-  return response;
+  return new Response(response.body, {
+    headers: {
+      cuStom: 'test',
+    },
+  });
 });
 
 routes.set('/headers/from-response/delete-invalid', async () => {
@@ -35,8 +38,9 @@ routes.set('/headers/from-response/delete-invalid', async () => {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  response.headers.delete('none');
-  return response;
+  const outResponse = new Response(response);
+  outResponse.headers.delete('none');
+  return outResponse;
 });
 
 routes.set('/headers/from-response/set-delete', async () => {
@@ -44,7 +48,9 @@ routes.set('/headers/from-response/set-delete', async () => {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  response.headers.set('custom', 'test');
-  response.headers.delete('access-control-allow-origin');
-  return response;
+  const outResponse = new Response(response);
+  outResponse.headers.set('custom', 'test');
+  outResponse.headers.set('another', 'test');
+  outResponse.headers.delete('access-control-allow-origin');
+  return outResponse;
 });
diff --git a/integration-tests/js-compute/fixtures/app/src/index.js b/integration-tests/js-compute/fixtures/app/src/index.js
index c097fa8064..cdacc67930 100644
--- a/integration-tests/js-compute/fixtures/app/src/index.js
+++ b/integration-tests/js-compute/fixtures/app/src/index.js
@@ -92,7 +92,10 @@ async function app(event) {
       }
     }
   } finally {
-    res.headers.set('fastly_service_version', FASTLY_SERVICE_VERSION);
+    try {
+      // this will fail if headers were same headers object from original upstream
+      res.headers.set('fastly_service_version', FASTLY_SERVICE_VERSION);
+    } catch {}
   }
 
   return res;
diff --git a/integration-tests/js-compute/fixtures/app/tests.json b/integration-tests/js-compute/fixtures/app/tests.json
index c25379912d..d08e5a4fb9 100644
--- a/integration-tests/js-compute/fixtures/app/tests.json
+++ b/integration-tests/js-compute/fixtures/app/tests.json
@@ -1503,7 +1503,10 @@
   "GET /headers/from-response/set-delete": {
     "downstream_response": {
       "status": 200,
-      "headers": [["cuStom", "test"]]
+      "headers": [
+        ["cuStom", "test"],
+        ["another", "test"]
+      ]
     }
   },
 

From bff45296e88a15c0c65ab23525a07694c16ed8ab Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 16:11:39 -0700
Subject: [PATCH 05/10] fixup

---
 .../fixtures/app/src/manual-framing-headers.js           | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
index 30bb32b20d..a2a73fd6d7 100644
--- a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
@@ -224,10 +224,11 @@ async function responseMethod(setManualFramingHeaders) {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  response.setManualFramingHeaders(setManualFramingHeaders);
-  response.headers.set('content-length', '11');
-  response.headers.delete('transfer-encoding');
-  return response;
+  const outResponse = new Response(response);
+  outResponse.setManualFramingHeaders(setManualFramingHeaders);
+  outResponse.headers.set('content-length', '11');
+  outResponse.headers.delete('transfer-encoding');
+  return outResponse;
 }
 
 routes.set('/override-content-length/response/method/false', async () => {

From dab45a6d97bb8f22c815a262bf8d9a697de55cc6 Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 17:31:14 -0700
Subject: [PATCH 06/10] fixup

---
 .../js-compute/fixtures/app/src/manual-framing-headers.js  | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
index a2a73fd6d7..5e8c434c85 100644
--- a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
@@ -224,8 +224,11 @@ async function responseMethod(setManualFramingHeaders) {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  const outResponse = new Response(response);
-  outResponse.setManualFramingHeaders(setManualFramingHeaders);
+  const outResponse = new Response(response.body, {
+    headers: response.headers,
+    status: response.status,
+  });
+  response.setManualFramingHeaders(setManualFramingHeaders);
   outResponse.headers.set('content-length', '11');
   outResponse.headers.delete('transfer-encoding');
   return outResponse;

From 913751b55e9ec0eae86cf097e68efb1552305c1f Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 17:47:52 -0700
Subject: [PATCH 07/10] fixup

---
 .../js-compute/fixtures/app/src/manual-framing-headers.js        | 1 +
 1 file changed, 1 insertion(+)

diff --git a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
index 5e8c434c85..b2c017b7d7 100644
--- a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
@@ -229,6 +229,7 @@ async function responseMethod(setManualFramingHeaders) {
     status: response.status,
   });
   response.setManualFramingHeaders(setManualFramingHeaders);
+  outResponse.setManualFramingHeaders(setManualFramingHeaders);
   outResponse.headers.set('content-length', '11');
   outResponse.headers.delete('transfer-encoding');
   return outResponse;

From 3d579ca8b169073e795fe46c56f11ce384a6eb25 Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 17:59:00 -0700
Subject: [PATCH 08/10] fixup

---
 .../js-compute/fixtures/app/src/manual-framing-headers.js        | 1 -
 1 file changed, 1 deletion(-)

diff --git a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
index b2c017b7d7..ad717e5e17 100644
--- a/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/manual-framing-headers.js
@@ -228,7 +228,6 @@ async function responseMethod(setManualFramingHeaders) {
     headers: response.headers,
     status: response.status,
   });
-  response.setManualFramingHeaders(setManualFramingHeaders);
   outResponse.setManualFramingHeaders(setManualFramingHeaders);
   outResponse.headers.set('content-length', '11');
   outResponse.headers.delete('transfer-encoding');

From b5dae628478be1ea029d8fe98c96407157c6ea21 Mon Sep 17 00:00:00 2001
From: Guy Bedford <gbedford@fastly.com>
Date: Fri, 11 Oct 2024 17:59:51 -0700
Subject: [PATCH 09/10] fixup

---
 integration-tests/js-compute/fixtures/app/src/headers.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/integration-tests/js-compute/fixtures/app/src/headers.js b/integration-tests/js-compute/fixtures/app/src/headers.js
index 26061c4b66..d11a8f7342 100644
--- a/integration-tests/js-compute/fixtures/app/src/headers.js
+++ b/integration-tests/js-compute/fixtures/app/src/headers.js
@@ -38,7 +38,7 @@ routes.set('/headers/from-response/delete-invalid', async () => {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  const outResponse = new Response(response);
+  const outResponse = new Response(response.body, response);
   outResponse.headers.delete('none');
   return outResponse;
 });
@@ -48,7 +48,7 @@ routes.set('/headers/from-response/set-delete', async () => {
     backend: 'httpbin',
     cacheOverride: new CacheOverride('pass'),
   });
-  const outResponse = new Response(response);
+  const outResponse = new Response(response.body, response);
   outResponse.headers.set('custom', 'test');
   outResponse.headers.set('another', 'test');
   outResponse.headers.delete('access-control-allow-origin');

From 3cd3cd72e1f700eaae111abedbd7c41a303ebc80 Mon Sep 17 00:00:00 2001
From: Chris Fallin <chris@cfallin.org>
Date: Mon, 4 Nov 2024 16:25:58 -0800
Subject: [PATCH 10/10] AOT compilation: rename flag; no longer experimental.
 (#1033)

---
 integration-tests/cli/help.test.js   | 4 ++--
 integration-tests/js-compute/test.js | 2 +-
 src/parseInputs.js                   | 8 ++++++++
 src/printHelp.js                     | 2 +-
 4 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/integration-tests/cli/help.test.js b/integration-tests/cli/help.test.js
index 8219516316..001c379ade 100644
--- a/integration-tests/cli/help.test.js
+++ b/integration-tests/cli/help.test.js
@@ -32,7 +32,7 @@ test('--help should return help on stdout and zero exit code', async function (t
     '--engine-wasm <engine-wasm>                             The JS engine Wasm file path',
     '--module-mode                            [experimental] Run all sources as native modules,',
     'with full error stack support.',
-    '--enable-experimental-aot                               Enable experimental AOT compilation for performance',
+    '--enable-aot                                            Enable AOT compilation for performance',
     '--enable-experimental-high-resolution-time-methods      Enable experimental high-resolution fastly.now() method',
     '--enable-experimental-top-level-await                   Enable experimental top level await',
     'ARGS:',
@@ -61,7 +61,7 @@ test('-h should return help on stdout and zero exit code', async function (t) {
     '--engine-wasm <engine-wasm>                             The JS engine Wasm file path',
     '--module-mode                            [experimental] Run all sources as native modules,',
     'with full error stack support.',
-    '--enable-experimental-aot                               Enable experimental AOT compilation for performance',
+    '--enable-aot                                            Enable AOT compilation for performance',
     '--enable-experimental-high-resolution-time-methods      Enable experimental high-resolution fastly.now() method',
     '--enable-experimental-top-level-await                   Enable experimental top level await',
     'ARGS:',
diff --git a/integration-tests/js-compute/test.js b/integration-tests/js-compute/test.js
index b4c5e8f3ed..a2b1516ec2 100755
--- a/integration-tests/js-compute/test.js
+++ b/integration-tests/js-compute/test.js
@@ -85,7 +85,7 @@ const config = TOML.parse(
 config.name = serviceName;
 if (aot) {
   const buildArgs = config.scripts.build.split(' ');
-  buildArgs.splice(-1, null, '--enable-experimental-aot');
+  buildArgs.splice(-1, null, '--enable-aot');
   config.scripts.build = buildArgs.join(' ');
 }
 if (debugBuild) {
diff --git a/src/parseInputs.js b/src/parseInputs.js
index 609a10d547..c5e674196d 100644
--- a/src/parseInputs.js
+++ b/src/parseInputs.js
@@ -39,7 +39,15 @@ export async function parseInputs(cliInputs) {
         bundle = true;
         break;
       }
+      case '--enable-aot': {
+        enableAOT = true;
+        break;
+      }
       case '--enable-experimental-aot': {
+        console.error(
+          'Warning: --enable-experimental-aot flag is now --enable-aot. The old flag continues\n' +
+            'to work for now, but please update your build invocation!',
+        );
         enableAOT = true;
         break;
       }
diff --git a/src/printHelp.js b/src/printHelp.js
index 0ee51b0495..3e35cb6b3b 100644
--- a/src/printHelp.js
+++ b/src/printHelp.js
@@ -16,7 +16,7 @@ OPTIONS:
     --engine-wasm <engine-wasm>                             The JS engine Wasm file path
     --module-mode                            [experimental] Run all sources as native modules,
                                                             with full error stack support.
-    --enable-experimental-aot                               Enable experimental AOT compilation for performance
+    --enable-aot                                            Enable AOT compilation for performance
     --enable-experimental-high-resolution-time-methods      Enable experimental high-resolution fastly.now() method
     --enable-experimental-top-level-await                   Enable experimental top level await