Repository: nightwatchjs/nightwatch
Pull request #4286 (2024-10-25 18:03:58)

fix: HTTP request agent not changes on protocol change.
When the `keep-alive` property of the `webdriver` config is set to `true`, Nightwatch uses the same HTTP agent for all the requests made during the entirety of the test run (a feature that was added in PR #3748).

This led to an issue where if we needed to switch to a different protocol (between `https` and `https`) for certain requests during the test run, those requests would fail due to an invalid agent and gave out the following error:

```
✖ TypeError
   Error while trying to create HTTP request for "/wd/hub/session/765c4e2a8c629d29ce778b2886bfef5d6634e050/url": Protocol "http:" not supported. Expected "https:"
```

This PR fixes this by allowing the agent to be changed whenever the protocol for the request changes.

github-actions[bot]: 
## Status
* ❌ No modified files found in the **types** directory.
Please make sure to include types for any changes you have made. Thank you!.

Patch:
diff --git a/lib/http/request.js b/lib/http/request.js
index 37f4fb00..c815e337 100644
--- a/lib/http/request.js
+++ b/lib/http/request.js
@@ -64,12 +64,17 @@ class HttpRequest extends EventEmitter {
   }
 
   static getAgent({secure, keepAliveMsecs, maxSockets}) {
-    if (__httpKeepAliveAgent__) {
+    const expectedProtocol = secure ? 'https:' : 'http:';
+    if (__httpKeepAliveAgent__ && __httpKeepAliveAgent__.protocol === expectedProtocol) {
       return __httpKeepAliveAgent__;
     }
 
     const protocol = secure ? https : http;
 
+    // might also consider storing http and https user agents separately
+    // and use the same agent for the entirety of test instead of creating
+    // a new user-agent every time the protocol changes.
+    // 1st commit: https://github.com/nightwatchjs/nightwatch/pull/3748
     return __httpKeepAliveAgent__ =  new protocol.Agent({
       keepAlive: true,
       keepAliveMsecs,
@@ -160,8 +165,9 @@ class HttpRequest extends EventEmitter {
       }
 
       if (enabled) {
+        const port = reqOptions.port || this.httpOpts.port;
         reqOptions.agent = HttpRequest.getAgent({
-          secure: this.httpOpts.port === 443,
+          secure: port === 443,
           keepAliveMsecs,
           maxSockets: 1
         });


Test patch:
diff --git a/test/src/index/testRequest.js b/test/src/index/testRequest.js
index 7a7dfe3a..a0b72a8a 100644
--- a/test/src/index/testRequest.js
+++ b/test/src/index/testRequest.js
@@ -320,6 +320,69 @@ describe('test HttpRequest', function() {
     assert.strictEqual(secondRequest.reqOptions.agent, opts.agent);
   });
 
+  it.only('keep alive user agent changes when request protocol changes',  function() {
+    const optionsHttp = {
+      path: '/session',
+      method: 'POST',
+      port: 4444,
+      data: {
+        desiredCapabilities: {
+          browserName: 'firefox'
+        }
+      }
+    };
+
+    const optionsHttps = {
+      path: '/session',
+      method: 'POST',
+      port: 443,
+      data: {
+        desiredCapabilities: {
+          browserName: 'chrome'
+        }
+      }
+    };
+
+    HttpRequest.globalSettings = {
+      default_path: '/wd/hub',
+      port: 4444,
+      keep_alive: {
+        keepAliveMsecs: 1000,
+        enabled: true
+      }
+    };
+
+    const httpRequest = new HttpRequest(optionsHttp);
+    const secondHttpRequest = new HttpRequest(optionsHttp);
+    const httpsRequest = new HttpRequest(optionsHttps);
+    const secondHttpsRequest = new HttpRequest(optionsHttps);
+    const thirdHttpRequest = new HttpRequest(optionsHttp);
+
+    const optsHttp = httpRequest.reqOptions;
+    assert.ok('agent' in optsHttp);
+    assert.ok(optsHttp.agent.protocol === 'http:');
+    assert.strictEqual(optsHttp.agent.keepAliveMsecs, 1000);
+
+    // first and second HTTP requests should share the same agent
+    assert.strictEqual(secondHttpRequest.reqOptions.agent, optsHttp.agent);
+
+    const optsHttps = httpsRequest.reqOptions;
+    assert.ok('agent' in optsHttps);
+    assert.ok(optsHttps.agent.protocol === 'https:');
+    // HTTPS agent should be different from HTTP agent
+    assert.notStrictEqual(optsHttps.agent, optsHttp.agent);
+
+    // first and second HTTPS requests should share the same agent
+    assert.strictEqual(secondHttpsRequest.reqOptions.agent, optsHttps.agent);
+    assert.notStrictEqual(secondHttpsRequest.reqOptions.agent, optsHttp.agent);
+
+    // while making HTTP request again after HTTPS request, it does
+    // not re-use the old HTTP agent (probably it should?)
+    assert.notStrictEqual(thirdHttpRequest.reqOptions.agent, optsHttp.agent);
+    assert.ok('agent' in thirdHttpRequest.reqOptions);
+    assert.ok(thirdHttpRequest.reqOptions.agent.protocol === 'http:');
+  });
+
   it('test send post request with keep alive extended - disabled', function (done) {
     const options = {
       path: '/session',