Repository: googlemaps/google-maps-services-js (ts)

Issue:
Client utilizes HTTP response compression by default
 **Is your feature request related to a problem? Please describe.**
Google Maps Services APIs support response compression, taking advantage of this mechanism provides latency and bandwidth improvements, the JavaScript client library currently does not by default .

 **Describe the solution you'd like**
Let's add an Accept-Encoding header for "gzip" to signal to the server that our client can handle compressed content.  Axios, the underlying HTTP client used by this library supports gzip compression transparently.

 **Describe alternatives you've considered**
Having usage of gzip compression be opt-in which a user of this library could do currently by using a custom Axios client instead of the default one provided (See: Client.ts).

The opt in approach is not preferred for two reasons:
1. It assumes that users of this client library will discover that the compression mechanism exists on their own which is poorly communicated.
2. It's fair to assume most applications are IO bound and *not* CPU bound and thus the additional CPU overhead of decompressing is worth the outsize benefit or reducing data sent over the wire.

 **Additional context**
The Go and Java libraries already take advantage of response compression and this would make the JS client more consistent with these other language variants.

Pull request:
feat: Request gzip response compression by default
Adds an "Accept-Encoding: gzip" header to all requests to improve
transfer speed and bandwidth utilization via server response
compression.

Most (all?) of the Maps APIs support serving compressed content and
this library's HTTP client (Axios) will transparently handle
decompression.

Requesting compressed content is already the default behavior of the Go
and Java google-maps-services client libraries.

A user can opt-out of compression by specifying a custom
Accept-Encoding ("identity") via config.headers at Client instantiation
or by instantiating a Client with an Axios client.

Fixes #356 


Patch:
diff --git a/src/client.ts b/src/client.ts
index 7776f946d..c0a2c2345 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -70,12 +70,16 @@ import {
 export const defaultHttpsAgent = new HttpsAgent({ keepAlive: true });
 export const defaultTimeout = 10000;
 export const userAgent = `google-maps-services-node-${version}`;
+export const acceptEncoding = "gzip";
 export const X_GOOG_MAPS_EXPERIENCE_ID = "X-GOOG-MAPS-EXPERIENCE-ID";
 
 const defaultConfig = {
   timeout: defaultTimeout,
   httpsAgent: defaultHttpsAgent,
-  headers: { "User-Agent": userAgent }
+  headers: {
+    "User-Agent": userAgent,
+    "Accept-Encoding": acceptEncoding
+  }
 };
 
 export const defaultAxiosInstance = axios.create(defaultConfig);


Test patch:
diff --git a/e2e/compression.test.ts b/e2e/compression.test.ts
new file mode 100644
index 000000000..078df5271
--- /dev/null
+++ b/e2e/compression.test.ts
@@ -0,0 +1,29 @@
+import { directions } from "../src/directions";
+
+test("server responds with compressed content", async () => {
+  const params = {
+    origin: "Seattle, WA",
+    destination: "San Francisco, CA",
+    waypoints: [{ lat: 40, lng: -120 }],
+    key: process.env.GOOGLE_MAPS_API_KEY
+  };
+
+  // Use of directions here is entirely arbitrary and any API that supports
+  // result compression could be substituted.
+
+  const r = await directions({ params: params });
+  expect(r.data.status).toEqual("OK");
+
+  // Axios removes conent-encoding from the response header set once it takes
+  // care of piping the stream through a zlib unzip instance.  So verifying
+  // that the server responds with a compressed response must be done via the
+  // raw headers.
+
+  const {rawHeaders} = r.request.res;
+  const contentEncodingIndex = rawHeaders
+      .findIndex(i => i.toLowerCase() === "content-encoding");
+
+  expect(contentEncodingIndex).not.toBe(-1);
+  expect(rawHeaders[contentEncodingIndex + 1]).toBe("gzip");
+  expect(r.headers["Content-Encoding"]).toBeUndefined();
+});
diff --git a/src/client.test.ts b/src/client.test.ts
index 759c9fc24..0055a6df9 100644
--- a/src/client.test.ts
+++ b/src/client.test.ts
@@ -1,6 +1,7 @@
 import {
   Client,
   userAgent,
+  acceptEncoding,
   DirectionsRequest,
   DistanceMatrixRequest,
   ElevationRequest,
@@ -38,6 +39,7 @@ test("client can be instantiated with axiosInstance has correct defaults", () =>
   expect(client["axiosInstance"].defaults.headers["User-Agent"]).toEqual(
     userAgent
   );
+  expect(client["axiosInstance"].defaults.headers["Accept-Encoding"]).toBeUndefined();
   expect(client["axiosInstance"].defaults.timeout).toEqual(
     axios.defaults.timeout
   );
@@ -59,6 +61,21 @@ test("client can be instantiated with header options", () => {
   expect(client["axiosInstance"].defaults.headers["User-Agent"]).toEqual(
     userAgent
   );
+  expect(client["axiosInstance"].defaults.headers["Accept-Encoding"]).toBe(
+      acceptEncoding
+  );
+});
+
+test("client can be override Accept-Encoding with header options", () => {
+  const client = new Client({ config: { headers: { "x-foo": "bar", "Accept-Encoding": "identity" } } });
+  expect(client["axiosInstance"]).toBeDefined();
+  expect(client["axiosInstance"].defaults.headers["x-foo"]).toEqual("bar");
+  expect(client["axiosInstance"].defaults.headers["User-Agent"]).toEqual(
+      userAgent
+  );
+  expect(client["axiosInstance"].defaults.headers["Accept-Encoding"]).toBe(
+      "identity"
+  );
 });
 
 test("client can be instantiated without header options", () => {
@@ -68,6 +85,9 @@ test("client can be instantiated without header options", () => {
   expect(client["axiosInstance"].defaults.headers["User-Agent"]).toEqual(
     userAgent
   );
+  expect(client["axiosInstance"].defaults.headers["Accept-Encoding"]).toBe(
+      acceptEncoding
+  );
 });
 
 test("client can be instantiated with experienceId", () => {