Repository: ProjectEvergreen/greenwood (js)

Issue:
devServer proxy not returning content (when proxied response includes a `content-encoding` header)
## Type of Change
Bug

## Summary
Noticed as part of the **v0.29.0** alpha release line that in the projects using`devServer.proxy` configuration, those proxied requests started not returning content anymore.
- https://github.com/AnalogStudiosRI/www.analogstudios.net/pull/89
- https://github.com/ContributaryCommunity/www.contributary.community/pull/124

An example endpoint trying to be proxied to is - https://www.contributary.community/api/topology
![Screenshot 2023-09-30 at 1 43 05 PM](https://github.com/ProjectEvergreen/greenwood/assets/895923/5f0d0596-ceb9-425d-89fb-b102056daa14)
![Screenshot 2023-09-30 at 1 51 39 PM](https://github.com/ProjectEvergreen/greenwood/assets/895923/c065e807-734f-477c-8036-071c07c75261)
![Screenshot 2023-09-30 at 1 51 52 PM](https://github.com/ProjectEvergreen/greenwood/assets/895923/8acb6769-8a20-4316-8085-08c514227e4e)

## Details
Interestingly, I noticed the endpoint worked when using `curl` locally and bypassing the browser entirely.
```sh
curl -v localhost:1984/api/topology
*   Trying 127.0.0.1:1984...
* Connected to localhost (127.0.0.1) port 1984 (#0)
> GET /api/topology HTTP/1.1
> Host: localhost:1984
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: application/json
< age: 4057
< connection: keep-alive
< content-encoding: gzip
< date: Sat, 07 Oct 2023 14:46:16 GMT
< transfer-encoding: chunked
< vary: Accept-Encoding
< via: 1.1 c772176b119045d2ed52ef4f42db5fe0.cloudfront.net (CloudFront), 1.1 26ba6c8d559dffe51c3f7d9239642222.cloudfront.net (CloudFront)
< x-amz-apigw-id: Mb5QoFtVIAMFf6A=
< x-amz-cf-id: qI75WVTcNy_jlZbR83KTqGA8UenHavmQBUDlWJJHviRgxgIQiYltuw==
< x-amz-cf-pop: IAD79-C2, BOS50-P3
< x-amzn-requestid: b9587ddc-dead-4a24-8dbd-fd5e9db6c97e
< x-amzn-trace-id: Root=1-65216f36-4738a36b702d7cc40c57513c;Sampled=0;lineage=edc229bf:0
< x-cache: Hit from cloudfront
<
* Connection #0 to host localhost left intact
{"language":{"c":{"label":"C","projects":[{"name":"Git","type":"org","repositories":["*"]},{"name":"Torvalds","type":"user","repositories":[{"name":"linux"}]}]},"go":{"label":"Go","projects":[{"name":"evanw","type":"user","repositories":[{"name":"esbuild"}]},{"name":"Docker","type":"org","repositories":["*"]}]},"java":{"label":"Java","projects":[{"name":"JenkinsCI","type":"org","repositories":["*"]},{"name":"KenzanLabs","type":"org","repositories":["*"]},{"name":"Netflix","type":"org","repositories":["*"]}]},"javascript":{"label":"JavaScript","projects":[{"name":"Babel","type":"org","repositories":["*"]},{"name":"ContributaryCommunity","type":"org","repositories":["*"]},{"name":"DevelopIt","type":"user","repositories":["*"]},{"name":"expressjs","type":"org","repositories":["*"]},{"name":"Facebook","type":"org","repositories":["*"]},{"name":"GatsbyJS","type":"org","repositories":["*"]},{"name":"cutenode","type":"org","repositories":["*"]},{"name":"jamiebuilds","type":"user","repositories":["*"]},{"name":"Microsoft","type":"org","repositories":["*"]},{"name":"NodeJS","type":"org","repositories":["*"]},{"name":"npm","type":"org","repositories":["*"]},{"name":"open-wc","type":"org","repositories":["*"]},{"name":"PayPal","type":"org","repositories":["*"]},{"name":"Polymer","type":"org","repositories":["*"]},{"name":"ProjectEvergreen","type":"org","repositories":["*"]},{"name":"ProvidenceGeeks","type":"org","repositories":["*"]},{"name":"TheGreenhouseIO","type":"org","repositories":["*"]},{"name":"thescientist13","type":"user","repositories":[{"name":"copy-dots"},{"name":"gallinago"},{"name":"git-explorer"},{"name":"github-dashboard"}]},{"name":"VueJS","type":"org","repositories":["*"]},{"name":"webpack","type":"org","repositories":["*"]},{"name":"YarnPkg","type":"org","repositories":["*"]}]},"php":{"label":"PHP","projects":[{"name":"composer","type":"org","repositories":["*"]},{"name":"laravel","type":"org","repositories":["*"]}]},"python":{"label":"Python","projects":[{"name":"django","type":"org","repositories":["*"]},{"name":"python","type":"org","repositories":["*"]}]},"rust":{"label":"Rust","projects":[{"name":"entropic-dev","type":"org","repositories":["*"]},{"name":"swc-project","type":"org","repositories":["*"]}]}}}
```

I dug into the headers a bit and started filtering them out and noticed that the one that fixed was when omitting the [`content-encoding` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding)
```
< content-encoding: gzip
```

Which makes sense, as we are processing the request server side first and returning the full body, so telling the client the response is encoded is probably making it think the browser needs to decode it, when it's not necessary.

----

Haven't quite pinpointed the exact release this broke on (given some periodic rebasing in the release branch, but it does seem to break on the v0.29.0-alpha.0 release which only had this change - https://github.com/ProjectEvergreen/greenwood/pull/1110

Pull request:
Bug/issue 1159 devServer proxy routing broken with content encoding header
<!--
## Submitting a Pull Request
We love contributions and appreciate any help you can offer!
-->

## Related Issue
resolves #1159 

## Summary of Changes
1. Filter out `content-encoding` fgrom dev server proxy requests

## TODO
1. [ ] Would be nice to narrow down the regression (my guess is in #1048 ?)
1. [x] Would be nice to get a test case to reproduce this 

> _Somewhat related, but should we rename it to just `devServer`?  Now that we're full stack, does it even matter?_ 🤔 

Patch:
diff --git a/packages/cli/src/lifecycles/serve.js b/packages/cli/src/lifecycles/serve.js
index f15107da..da386eaa 100644
--- a/packages/cli/src/lifecycles/serve.js
+++ b/packages/cli/src/lifecycles/serve.js
@@ -200,6 +200,8 @@ async function getStaticServer(compilation, composable) {
     await next();
   });
 
+  // TODO devServer.proxy is not really just for dev
+  // should it be renamed?  should this be a middleware?
   app.use(async (ctx, next) => {
     try {
       const url = new URL(`http://localhost:${port}${ctx.url}`);
diff --git a/packages/cli/src/plugins/resource/plugin-dev-proxy.js b/packages/cli/src/plugins/resource/plugin-dev-proxy.js
index a85b1d5c..37800a81 100644
--- a/packages/cli/src/plugins/resource/plugin-dev-proxy.js
+++ b/packages/cli/src/plugins/resource/plugin-dev-proxy.js
@@ -31,8 +31,18 @@ class DevProxyResource extends ResourceInterface {
       method: request.method,
       headers: request.header
     });
+    const response = await fetch(requestProxied);
+    const filteredResponseHeaders = new Headers();
 
-    return await fetch(requestProxied);
+    // filter out content-encoding to make sure browsers do not try and decode responses
+    // https://github.com/ProjectEvergreen/greenwood/issues/1159
+    response.headers.forEach((value, key) => {
+      if (key !== 'content-encoding') {
+        filteredResponseHeaders.set(key, value);
+      }
+    });
+
+    return new Response(response.body, { headers: filteredResponseHeaders });
   }
 }
 


Test patch:
diff --git a/packages/cli/test/cases/develop.default/develop.default.spec.js b/packages/cli/test/cases/develop.default/develop.default.spec.js
index 84837a0e..d6845298 100644
--- a/packages/cli/test/cases/develop.default/develop.default.spec.js
+++ b/packages/cli/test/cases/develop.default/develop.default.spec.js
@@ -1321,6 +1321,12 @@ describe('Develop Greenwood With: ', function() {
         done();
       });
 
+      // https://github.com/ProjectEvergreen/greenwood/issues/1159
+      it('should not return a content-encoding header', function(done) {
+        expect(response.headers['content-encoding']).to.equal(undefined);
+        done();
+      });
+
       it('should return the correct response body', function(done) {
         expect(response.body).to.have.lengthOf(1);
         done();