Browse Source

test: unflake some net specs (#18782)

Jeremy Apthorp 5 years ago
parent
commit
7b26048d9e
3 changed files with 1304 additions and 1716 deletions
  1. 15 15
      lib/browser/api/net.js
  2. 1289 0
      spec-main/api-net-spec.ts
  3. 0 1701
      spec/api-net-spec.js

+ 15 - 15
lib/browser/api/net.js

@@ -96,11 +96,11 @@ class IncomingMessage extends Readable {
   }
 
   get rawTrailers () {
-    throw new Error('HTTP trailers are not supported.')
+    throw new Error('HTTP trailers are not supported')
   }
 
   get trailers () {
-    throw new Error('HTTP trailers are not supported.')
+    throw new Error('HTTP trailers are not supported')
   }
 
   _storeInternalData (chunk) {
@@ -161,7 +161,7 @@ class ClientRequest extends EventEmitter {
       const urlObj = {}
       const protocol = options.protocol || 'http:'
       if (!kSupportedProtocols.has(protocol)) {
-        throw new Error('Protocol "' + protocol + '" not supported. ')
+        throw new Error('Protocol "' + protocol + '" not supported')
       }
       urlObj.protocol = protocol
 
@@ -186,7 +186,7 @@ class ClientRequest extends EventEmitter {
         // well, and b) possibly too restrictive for real-world usage. That's
         // why it only scans for spaces because those are guaranteed to create
         // an invalid request.
-        throw new TypeError('Request path contains unescaped characters.')
+        throw new TypeError('Request path contains unescaped characters')
       }
       const pathObj = url.parse(options.path || '/')
       urlObj.pathname = pathObj.pathname
@@ -209,13 +209,13 @@ class ClientRequest extends EventEmitter {
       if (options.session instanceof Session) {
         urlRequestOptions.session = options.session
       } else {
-        throw new TypeError('`session` should be an instance of the Session class.')
+        throw new TypeError('`session` should be an instance of the Session class')
       }
     } else if (options.partition) {
       if (typeof options.partition === 'string') {
         urlRequestOptions.partition = options.partition
       } else {
-        throw new TypeError('`partition` should be an a string.')
+        throw new TypeError('`partition` should be a string')
       }
     }
 
@@ -276,20 +276,20 @@ class ClientRequest extends EventEmitter {
 
   set chunkedEncoding (value) {
     if (!this.urlRequest.notStarted) {
-      throw new Error('Can\'t set the transfer encoding, headers have been sent.')
+      throw new Error('Can\'t set the transfer encoding, headers have been sent')
     }
     this.chunkedEncodingEnabled = value
   }
 
   setHeader (name, value) {
     if (typeof name !== 'string') {
-      throw new TypeError('`name` should be a string in setHeader(name, value).')
+      throw new TypeError('`name` should be a string in setHeader(name, value)')
     }
     if (value == null) {
-      throw new Error('`value` required in setHeader("' + name + '", value).')
+      throw new Error('`value` required in setHeader("' + name + '", value)')
     }
     if (!this.urlRequest.notStarted) {
-      throw new Error('Can\'t set headers after they are sent.')
+      throw new Error('Can\'t set headers after they are sent')
     }
 
     const key = name.toLowerCase()
@@ -299,7 +299,7 @@ class ClientRequest extends EventEmitter {
 
   getHeader (name) {
     if (name == null) {
-      throw new Error('`name` is required for getHeader(name).')
+      throw new Error('`name` is required for getHeader(name)')
     }
 
     if (!this.extraHeaders) {
@@ -312,11 +312,11 @@ class ClientRequest extends EventEmitter {
 
   removeHeader (name) {
     if (name == null) {
-      throw new Error('`name` is required for removeHeader(name).')
+      throw new Error('`name` is required for removeHeader(name)')
     }
 
     if (!this.urlRequest.notStarted) {
-      throw new Error('Can\'t remove headers after they are sent.')
+      throw new Error('Can\'t remove headers after they are sent')
     }
 
     const key = name.toLowerCase()
@@ -328,7 +328,7 @@ class ClientRequest extends EventEmitter {
     const chunkIsString = typeof chunk === 'string'
     const chunkIsBuffer = chunk instanceof Buffer
     if (!chunkIsString && !chunkIsBuffer) {
-      throw new TypeError('First argument must be a string or Buffer.')
+      throw new TypeError('First argument must be a string or Buffer')
     }
 
     if (chunkIsString) {
@@ -357,7 +357,7 @@ class ClientRequest extends EventEmitter {
 
   write (data, encoding, callback) {
     if (this.urlRequest.finished) {
-      const error = new Error('Write after end.')
+      const error = new Error('Write after end')
       process.nextTick(writeAfterEndNT, this, error, callback)
       return true
     }

+ 1289 - 0
spec-main/api-net-spec.ts

@@ -0,0 +1,1289 @@
+import { expect } from 'chai'
+import { net, session, ClientRequest } from 'electron'
+import * as http from 'http'
+import * as url from 'url'
+import { AddressInfo } from 'net'
+
+const kOneKiloByte = 1024
+const kOneMegaByte = kOneKiloByte * kOneKiloByte
+
+function randomBuffer (size: number, start: number = 0, end: number = 255) {
+  const range = 1 + end - start
+  const buffer = Buffer.allocUnsafe(size)
+  for (let i = 0; i < size; ++i) {
+    buffer[i] = start + Math.floor(Math.random() * range)
+  }
+  return buffer
+}
+
+function randomString (length: number) {
+  const buffer = randomBuffer(length, '0'.charCodeAt(0), 'z'.charCodeAt(0))
+  return buffer.toString()
+}
+
+function respondOnce(fn: http.RequestListener): Promise<string> {
+  return new Promise((resolve) => {
+    const server = http.createServer((request, response) => {
+      fn(request, response)
+      // don't close if a redirect was returned
+      if (response.statusCode < 300 || response.statusCode >= 399)
+        server.close()
+    })
+    server.listen(0, '127.0.0.1', () => {
+      resolve(`http://127.0.0.1:${(server.address() as AddressInfo).port}`)
+    })
+  })
+}
+
+respondOnce.toRoutes = (routes: Record<string, http.RequestListener>) => {
+  return respondOnce((request, response) => {
+    if (routes.hasOwnProperty(request.url || '')) {
+      routes[request.url || ''](request, response)
+    } else {
+      response.statusCode = 500
+      response.end()
+      expect.fail(`Unexpected URL: ${request.url}`)
+    }
+  })
+}
+
+respondOnce.toURL = (url: string, fn: http.RequestListener) => {
+  return respondOnce.toRoutes({[url]: fn})
+}
+
+respondOnce.toSingleURL = (fn: http.RequestListener) => {
+  const requestUrl = '/requestUrl'
+  return respondOnce.toURL(requestUrl, fn).then(url => `${url}${requestUrl}`)
+}
+
+describe('net module', () => {
+  describe('HTTP basics', () => {
+    it('should be able to issue a basic GET request', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.method).to.equal('GET')
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to issue a basic POST request', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.method).to.equal('POST')
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          method: 'POST',
+          url: serverUrl
+        })
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          response.on('data', () => { })
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should fetch correct data in a GET request', (done) => {
+      const bodyData = 'Hello World!'
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.method).to.equal('GET')
+        response.end(bodyData)
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          let expectedBodyData = ''
+          expect(response.statusCode).to.equal(200)
+          response.on('data', (chunk) => {
+            expectedBodyData += chunk.toString()
+          })
+          response.on('end', () => {
+            expect(expectedBodyData).to.equal(bodyData)
+            done()
+          })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should post the correct data in a POST request', (done) => {
+      const bodyData = 'Hello World!'
+      respondOnce.toSingleURL((request, response) => {
+        let postedBodyData = ''
+        expect(request.method).to.equal('POST')
+        request.on('data', (chunk: Buffer) => {
+          postedBodyData += chunk.toString()
+        })
+        request.on('end', () => {
+          expect(postedBodyData).to.equal(bodyData)
+          response.end()
+        })
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          method: 'POST',
+          url: serverUrl
+        })
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.write(bodyData)
+        urlRequest.end()
+      })
+    })
+
+    it('should support chunked encoding', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.chunkedEncoding = true
+        expect(request.method).to.equal('POST')
+        expect(request.headers['transfer-encoding']).to.equal('chunked')
+        expect(request.headers['content-length']).to.equal(undefined)
+        request.on('data', (chunk: Buffer) => {
+          response.write(chunk)
+        })
+        request.on('end', (chunk: Buffer) => {
+          response.end(chunk)
+        })
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          method: 'POST',
+          url: serverUrl
+        })
+
+        let chunkIndex = 0
+        const chunkCount = 100
+        const sentChunks: Array<Buffer> = []
+        const receivedChunks: Array<Buffer> = []
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          response.on('data', (chunk) => {
+            receivedChunks.push(chunk)
+          })
+          response.on('end', () => {
+            const sentData = Buffer.concat(sentChunks)
+            const receivedData = Buffer.concat(receivedChunks)
+            expect(sentData.toString()).to.equal(receivedData.toString())
+            expect(chunkIndex).to.be.equal(chunkCount)
+            done()
+          })
+        })
+        urlRequest.chunkedEncoding = true
+        while (chunkIndex < chunkCount) {
+          chunkIndex += 1
+          const chunk = randomBuffer(kOneKiloByte)
+          sentChunks.push(chunk)
+          expect(urlRequest.write(chunk)).to.equal(true)
+        }
+        urlRequest.end()
+      })
+    })
+  })
+
+  describe('ClientRequest API', () => {
+    it('request/response objects should emit expected events', (done) => {
+      const bodyData = randomString(kOneKiloByte)
+      respondOnce.toSingleURL((request, response) => {
+        response.end(bodyData)
+      }).then(serverUrl => {
+        let requestResponseEventEmitted = false
+        let requestFinishEventEmitted = false
+        let requestCloseEventEmitted = false
+        let responseDataEventEmitted = false
+        let responseEndEventEmitted = false
+
+        function maybeDone (done: () => void) {
+          if (!requestCloseEventEmitted || !responseEndEventEmitted) {
+            return
+          }
+
+          expect(requestResponseEventEmitted).to.equal(true)
+          expect(requestFinishEventEmitted).to.equal(true)
+          expect(requestCloseEventEmitted).to.equal(true)
+          expect(responseDataEventEmitted).to.equal(true)
+          expect(responseEndEventEmitted).to.equal(true)
+          done()
+        }
+
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          requestResponseEventEmitted = true
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          const buffers: Buffer[] = []
+          response.on('data', (chunk) => {
+            buffers.push(chunk)
+            responseDataEventEmitted = true
+          })
+          response.on('end', () => {
+            const receivedBodyData = Buffer.concat(buffers)
+            expect(receivedBodyData.toString()).to.equal(bodyData)
+            responseEndEventEmitted = true
+            maybeDone(done)
+          })
+          response.on('error', (error: Error) => {
+            expect(error).to.be.an('Error')
+          })
+          response.on('aborted', () => {
+            expect.fail('response aborted')
+          })
+        })
+        urlRequest.on('finish', () => {
+          requestFinishEventEmitted = true
+        })
+        urlRequest.on('error', (error) => {
+          expect(error).to.be.an('Error')
+        })
+        urlRequest.on('abort', () => {
+          expect.fail('request aborted')
+        })
+        urlRequest.on('close', () => {
+          requestCloseEventEmitted = true
+          maybeDone(done)
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to set a custom HTTP request header before first write', (done) => {
+      const customHeaderName = 'Some-Custom-Header-Name'
+      const customHeaderValue = 'Some-Customer-Header-Value'
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue)
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', () => {
+          })
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.setHeader(customHeaderName, customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
+        urlRequest.write('')
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to set a non-string object as a header value', (done) => {
+      const customHeaderName = 'Some-Integer-Value'
+      const customHeaderValue = 900
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue.toString())
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.setHeader(customHeaderName, customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
+        urlRequest.write('')
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
+        urlRequest.end()
+      })
+    })
+
+    it('should not be able to set a custom HTTP request header after first write', (done) => {
+      const customHeaderName = 'Some-Custom-Header-Name'
+      const customHeaderValue = 'Some-Customer-Header-Value'
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.headers[customHeaderName.toLowerCase()]).to.equal(undefined)
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.write('')
+        expect(() => {
+          urlRequest.setHeader(customHeaderName, customHeaderValue)
+        }).to.throw()
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(undefined)
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to remove a custom HTTP request header before first write', (done) => {
+      const customHeaderName = 'Some-Custom-Header-Name'
+      const customHeaderValue = 'Some-Customer-Header-Value'
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.headers[customHeaderName.toLowerCase()]).to.equal(undefined)
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.setHeader(customHeaderName, customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        urlRequest.removeHeader(customHeaderName)
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(undefined)
+        urlRequest.write('')
+        urlRequest.end()
+      })
+    })
+
+    it('should not be able to remove a custom HTTP request header after first write', (done) => {
+      const customHeaderName = 'Some-Custom-Header-Name'
+      const customHeaderValue = 'Some-Customer-Header-Value'
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue)
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.setHeader(customHeaderName, customHeaderValue)
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        urlRequest.write('')
+        expect(() => {
+          urlRequest.removeHeader(customHeaderName)
+        }).to.throw()
+        expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to set cookie header line', (done) => {
+      const cookieHeaderName = 'Cookie'
+      const cookieHeaderValue = 'test=12345'
+      const customSession = session.fromPartition('test-cookie-header')
+      respondOnce.toSingleURL((request, response) => {
+        expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue)
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrl => {
+        customSession.cookies.set({
+          url: `${serverUrl}`,
+          name: 'test',
+          value: '11111'
+        }).then(() => { // resolved
+          const urlRequest = net.request({
+            method: 'GET',
+            url: serverUrl,
+            session: customSession
+          })
+          urlRequest.on('response', (response) => {
+            const statusCode = response.statusCode
+            expect(statusCode).to.equal(200)
+            response.on('data', () => {})
+            response.on('end', () => {
+              done()
+            })
+          })
+          urlRequest.setHeader(cookieHeaderName, cookieHeaderValue)
+          expect(urlRequest.getHeader(cookieHeaderName)).to.equal(cookieHeaderValue)
+          urlRequest.end()
+        }, (error) => {
+          done(error)
+        })
+      })
+    })
+
+    it('should be able to abort an HTTP request before first write', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        response.end()
+        expect.fail('Unexpected request event')
+      }).then(serverUrl => {
+        let requestAbortEventEmitted = false
+
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          expect.fail('Unexpected response event')
+        })
+        urlRequest.on('finish', () => {
+          expect.fail('Unexpected finish event')
+        })
+        urlRequest.on('error', () => {
+          expect.fail('Unexpected error event')
+        })
+        urlRequest.on('abort', () => {
+          requestAbortEventEmitted = true
+        })
+        urlRequest.on('close', () => {
+          expect(requestAbortEventEmitted).to.equal(true)
+          done()
+        })
+        urlRequest.abort()
+        expect(urlRequest.write('')).to.equal(false)
+        urlRequest.end()
+      })
+    })
+
+    it('it should be able to abort an HTTP request before request end', (done) => {
+      let requestReceivedByServer = false
+      let urlRequest: ClientRequest | null = null
+      respondOnce.toSingleURL((request, response) => {
+        requestReceivedByServer = true
+        urlRequest!.abort()
+      }).then(serverUrl => {
+        let requestAbortEventEmitted = false
+
+        urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          expect.fail('Unexpected response event')
+        })
+        urlRequest.on('finish', () => {
+          expect.fail('Unexpected finish event')
+        })
+        urlRequest.on('error', () => {
+          expect.fail('Unexpected error event')
+        })
+        urlRequest.on('abort', () => {
+          requestAbortEventEmitted = true
+        })
+        urlRequest.on('close', () => {
+          expect(requestReceivedByServer).to.equal(true)
+          expect(requestAbortEventEmitted).to.equal(true)
+          done()
+        })
+
+        urlRequest.chunkedEncoding = true
+        urlRequest.write(randomString(kOneKiloByte))
+      })
+    })
+
+    it('it should be able to abort an HTTP request after request end and before response', (done) => {
+      let requestReceivedByServer = false
+      let urlRequest: ClientRequest | null = null
+      respondOnce.toSingleURL((request, response) => {
+        requestReceivedByServer = true
+        urlRequest!.abort()
+        process.nextTick(() => {
+          response.statusCode = 200
+          response.statusMessage = 'OK'
+          response.end()
+        })
+      }).then(serverUrl => {
+        let requestAbortEventEmitted = false
+        let requestFinishEventEmitted = false
+
+        urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          expect.fail('Unexpected response event')
+        })
+        urlRequest.on('finish', () => {
+          requestFinishEventEmitted = true
+        })
+        urlRequest.on('error', () => {
+          expect.fail('Unexpected error event')
+        })
+        urlRequest.on('abort', () => {
+          requestAbortEventEmitted = true
+        })
+        urlRequest.on('close', () => {
+          expect(requestFinishEventEmitted).to.equal(true)
+          expect(requestReceivedByServer).to.equal(true)
+          expect(requestAbortEventEmitted).to.equal(true)
+          done()
+        })
+
+        urlRequest.end(randomString(kOneKiloByte))
+      })
+    })
+
+    it('it should be able to abort an HTTP request after response start', (done) => {
+      let requestReceivedByServer = false
+      respondOnce.toSingleURL((request, response) => {
+        requestReceivedByServer = true
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.write(randomString(kOneKiloByte))
+      }).then(serverUrl => {
+        let requestFinishEventEmitted = false
+        let requestResponseEventEmitted = false
+        let requestAbortEventEmitted = false
+        let responseAbortedEventEmitted = false
+
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          requestResponseEventEmitted = true
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', (chunk) => {
+          })
+          response.on('end', () => {
+            expect.fail('Unexpected end event')
+          })
+          response.on('error', () => {
+            expect.fail('Unexpected error event')
+          })
+          response.on('aborted', () => {
+            responseAbortedEventEmitted = true
+          })
+          urlRequest.abort()
+        })
+        urlRequest.on('finish', () => {
+          requestFinishEventEmitted = true
+        })
+        urlRequest.on('error', () => {
+          expect.fail('Unexpected error event')
+        })
+        urlRequest.on('abort', () => {
+          requestAbortEventEmitted = true
+        })
+        urlRequest.on('close', () => {
+          expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event')
+          expect(requestReceivedByServer).to.be.true('request should be received by the server')
+          expect(requestResponseEventEmitted).to.be.true('"response" event should be emitted')
+          expect(requestAbortEventEmitted).to.be.true('request should emit "abort" event')
+          expect(responseAbortedEventEmitted).to.be.true('response should emit "aborted" event')
+          done()
+        })
+        urlRequest.end(randomString(kOneKiloByte))
+      })
+    })
+
+    it('abort event should be emitted at most once', (done) => {
+      let requestReceivedByServer = false
+      let urlRequest: ClientRequest | null = null
+      respondOnce.toSingleURL((request, response) => {
+        requestReceivedByServer = true
+        urlRequest!.abort()
+        urlRequest!.abort()
+      }).then(serverUrl => {
+        let requestFinishEventEmitted = false
+        let abortEmitted = false
+
+        urlRequest = net.request(serverUrl)
+        urlRequest.on('response', () => {
+          expect.fail('Unexpected response event')
+        })
+        urlRequest.on('finish', () => {
+          requestFinishEventEmitted = true
+        })
+        urlRequest.on('error', () => {
+          expect.fail('Unexpected error event')
+        })
+        urlRequest.on('abort', () => {
+          expect(abortEmitted).to.be.false('abort event should not be emitted more than once')
+          abortEmitted = true
+          urlRequest!.abort()
+        })
+        urlRequest.on('close', () => {
+          expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event')
+          expect(requestReceivedByServer).to.be.true('request should be received by server')
+          expect(abortEmitted).to.be.true('request should emit "abort" event')
+          done()
+        })
+
+        urlRequest.end(randomString(kOneKiloByte))
+      })
+    })
+
+    describe('webRequest', () => {
+      afterEach(() => {
+        session.defaultSession.webRequest.onBeforeRequest(null)
+      })
+
+      it('Requests should be intercepted by webRequest module', (done) => {
+        const requestUrl = '/requestUrl'
+        const redirectUrl = '/redirectUrl'
+        let requestIsRedirected = false
+        respondOnce.toURL(redirectUrl, (request, response) => {
+          requestIsRedirected = true
+          response.end()
+        }).then(serverUrl => {
+          let requestIsIntercepted = false
+          session.defaultSession.webRequest.onBeforeRequest(
+            (details, callback) => {
+              if (details.url === `${serverUrl}${requestUrl}`) {
+                requestIsIntercepted = true
+                // Disabled due to false positive in StandardJS
+                // eslint-disable-next-line standard/no-callback-literal
+                callback({
+                  redirectURL: `${serverUrl}${redirectUrl}`
+                })
+              } else {
+                callback({
+                  cancel: false
+                })
+              }
+            })
+
+          const urlRequest = net.request(`${serverUrl}${requestUrl}`)
+
+          urlRequest.on('response', (response) => {
+            expect(response.statusCode).to.equal(200)
+            response.on('data', () => {})
+            response.on('end', () => {
+              expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL')
+              expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module')
+              done()
+            })
+          })
+          urlRequest.end()
+        })
+      })
+
+      it('should to able to create and intercept a request using a custom session object', (done) => {
+        const requestUrl = '/requestUrl'
+        const redirectUrl = '/redirectUrl'
+        const customPartitionName = 'custom-partition'
+        let requestIsRedirected = false
+        respondOnce.toURL(redirectUrl, (request, response) => {
+          requestIsRedirected = true
+          response.end()
+        }).then(serverUrl => {
+          session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
+            expect.fail('Request should not be intercepted by the default session')
+          })
+
+          const customSession = session.fromPartition(customPartitionName, { cache: false })
+          let requestIsIntercepted = false
+          customSession.webRequest.onBeforeRequest((details, callback) => {
+            if (details.url === `${serverUrl}${requestUrl}`) {
+              requestIsIntercepted = true
+              // Disabled due to false positive in StandardJS
+              // eslint-disable-next-line standard/no-callback-literal
+              callback({
+                redirectURL: `${serverUrl}${redirectUrl}`
+              })
+            } else {
+              callback({
+                cancel: false
+              })
+            }
+          })
+
+          const urlRequest = net.request({
+            url: `${serverUrl}${requestUrl}`,
+            session: customSession
+          })
+          urlRequest.on('response', (response) => {
+            expect(response.statusCode).to.equal(200)
+            response.on('data', (chunk) => {
+            })
+            response.on('end', () => {
+              expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL')
+              expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module')
+              done()
+            })
+          })
+          urlRequest.end()
+        })
+      })
+
+      it('should to able to create and intercept a request using a custom session object', (done) => {
+        const requestUrl = '/requestUrl'
+        const redirectUrl = '/redirectUrl'
+        const customPartitionName = 'custom-partition'
+        let requestIsRedirected = false
+        respondOnce.toURL(redirectUrl, (request, response) => {
+          requestIsRedirected = true
+          response.end()
+        }).then(serverUrl => {
+          session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
+            expect.fail('Request should not be intercepted by the default session')
+          })
+
+          const customSession = session.fromPartition(customPartitionName, { cache: false })
+          let requestIsIntercepted = false
+          customSession.webRequest.onBeforeRequest((details, callback) => {
+            if (details.url === `${serverUrl}${requestUrl}`) {
+              requestIsIntercepted = true
+              // Disabled due to false positive in StandardJS
+              // eslint-disable-next-line standard/no-callback-literal
+              callback({
+                redirectURL: `${serverUrl}${redirectUrl}`
+              })
+            } else {
+              callback({
+                cancel: false
+              })
+            }
+          })
+
+          const urlRequest = net.request({
+            url: `${serverUrl}${requestUrl}`,
+            partition: customPartitionName
+          })
+          urlRequest.on('response', (response) => {
+            expect(response.statusCode).to.equal(200)
+            response.on('data', (chunk) => {
+            })
+            response.on('end', () => {
+              expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL')
+              expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module')
+              done()
+            })
+          })
+          urlRequest.end()
+        })
+      })
+    })
+
+    it('should throw if given an invalid redirect mode', () => {
+      expect(() => {
+        net.request({
+          url: 'https://test',
+          redirect: 'custom'
+        })
+      }).to.throw('redirect mode should be one of follow, error or manual')
+    })
+
+    it('should throw when calling getHeader without a name', () => {
+      expect(() => {
+        (net.request({ url: 'https://test' }).getHeader as any)()
+      }).to.throw(/`name` is required for getHeader\(name\)/)
+
+      expect(() => {
+        net.request({ url: 'https://test' }).getHeader(null as any)
+      }).to.throw(/`name` is required for getHeader\(name\)/)
+    })
+
+    it('should throw when calling removeHeader without a name', () => {
+      expect(() => {
+        (net.request({ url: 'https://test' }).removeHeader as any)()
+      }).to.throw(/`name` is required for removeHeader\(name\)/)
+
+      expect(() => {
+        net.request({ url: 'https://test' }).removeHeader(null as any)
+      }).to.throw(/`name` is required for removeHeader\(name\)/)
+    })
+
+    it('should follow redirect when no redirect mode is provided', (done) => {
+      const requestUrl = '/301'
+      respondOnce.toRoutes({
+        '/301': (request, response) => {
+          response.statusCode = 301
+          response.setHeader('Location', '/200')
+          response.end()
+        },
+        '/200': (request, response) => {
+          response.statusCode = 200
+          response.end()
+        },
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          url: `${serverUrl}${requestUrl}`
+        })
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          done()
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should follow redirect chain when no redirect mode is provided', (done) => {
+      respondOnce.toRoutes({
+        '/redirectChain': (request, response) => {
+          response.statusCode = 301
+          response.setHeader('Location', '/301')
+          response.end()
+        },
+        '/301': (request, response) => {
+          response.statusCode = 301
+          response.setHeader('Location', '/200')
+          response.end()
+        },
+        '/200': (request, response) => {
+          response.statusCode = 200
+          response.end()
+        },
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          url: `${serverUrl}/redirectChain`
+        })
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          done()
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should not follow redirect when mode is error', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        response.statusCode = 301
+        response.setHeader('Location', '/200')
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          url: serverUrl,
+          redirect: 'error'
+        })
+        urlRequest.on('error', (error) => {
+          expect(error.message).to.equal('Request cannot follow redirect with the current redirect mode')
+        })
+        urlRequest.on('close', () => {
+          done()
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should allow follow redirect when mode is manual', (done) => {
+      respondOnce.toRoutes({
+        '/redirectChain': (request, response) => {
+          response.statusCode = 301
+          response.setHeader('Location', '/301')
+          response.end()
+        },
+        '/301': (request, response) => {
+          response.statusCode = 301
+          response.setHeader('Location', '/200')
+          response.end()
+        },
+        '/200': (request, response) => {
+          response.statusCode = 200
+          response.end()
+        },
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          url: `${serverUrl}/redirectChain`,
+          redirect: 'manual'
+        })
+        let redirectCount = 0
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          expect(redirectCount).to.equal(2)
+          done()
+        })
+        urlRequest.on('redirect', (status, method, url) => {
+          if (url === `${serverUrl}/301` || url === `${serverUrl}/200`) {
+            redirectCount += 1
+            urlRequest.followRedirect()
+          }
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should allow cancelling redirect when mode is manual', (done) => {
+      respondOnce.toRoutes({
+        '/redirect': (request, response) => {
+          response.statusCode = 301
+          response.setHeader('Location', '/200')
+          response.end()
+        },
+        '/200': (request, response) => {
+          response.statusCode = 200
+          response.end()
+        },
+      }).then(serverUrl => {
+        const urlRequest = net.request({
+          url: `${serverUrl}/redirect`,
+          redirect: 'manual'
+        })
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).that.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            urlRequest.abort()
+          })
+        })
+        let redirectCount = 0
+        urlRequest.on('close', () => {
+          expect(redirectCount).to.equal(1)
+          done()
+        })
+        urlRequest.on('redirect', (status, method, url) => {
+          if (url === `${serverUrl}/200`) {
+            redirectCount += 1
+            urlRequest.followRedirect()
+          }
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should throw if given an invalid session option', () => {
+      expect(() => {
+        net.request({
+          url: 'https://foo',
+          session: 1
+        })
+      }).to.throw("`session` should be an instance of the Session class")
+    })
+
+    it('should throw if given an invalid partition option', () => {
+      expect(() => {
+        net.request({
+          url: 'https://foo',
+          partition: 1
+        })
+      }).to.throw("`partition` should be a string")
+    })
+
+    it('should be able to create a request with options', (done) => {
+      const customHeaderName = 'Some-Custom-Header-Name'
+      const customHeaderValue = 'Some-Customer-Header-Value'
+      respondOnce.toURL('/', (request, response) => {
+        expect(request.method).to.equal('GET')
+        expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue)
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.end()
+      }).then(serverUrlUnparsed => {
+        const serverUrl = url.parse(serverUrlUnparsed)
+        const options = {
+          port: serverUrl.port,
+          hostname: '127.0.0.1',
+          headers: { [customHeaderName]: customHeaderValue }
+        }
+        const urlRequest = net.request(options)
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.be.equal(200)
+          response.on('data', () => {})
+          response.on('end', () => {
+            done()
+          })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to pipe a readable stream into a net request', (done) => {
+      const bodyData = randomString(kOneMegaByte)
+      let netRequestReceived = false
+      let netRequestEnded = false
+
+      Promise.all([
+        respondOnce.toSingleURL((request, response) => response.end(bodyData)),
+        respondOnce.toSingleURL((request, response) => {
+          netRequestReceived = true
+          let receivedBodyData = ''
+          request.on('data', (chunk) => {
+            receivedBodyData += chunk.toString()
+          })
+          request.on('end', (chunk: Buffer | undefined) => {
+            netRequestEnded = true
+            if (chunk) {
+              receivedBodyData += chunk.toString()
+            }
+            expect(receivedBodyData).to.be.equal(bodyData)
+            response.end()
+          })
+        })
+      ]).then(([nodeServerUrl, netServerUrl]) => {
+        const nodeRequest = http.request(nodeServerUrl)
+        nodeRequest.on('response', (nodeResponse) => {
+          const netRequest = net.request(netServerUrl)
+          netRequest.on('response', (netResponse) => {
+            expect(netResponse.statusCode).to.equal(200)
+            netResponse.on('data', (chunk) => {})
+            netResponse.on('end', () => {
+              expect(netRequestReceived).to.be.true('net request received')
+              expect(netRequestEnded).to.be.true('net request ended')
+              done()
+            })
+          })
+          nodeResponse.pipe(netRequest)
+        })
+        nodeRequest.end()
+      })
+    })
+
+    it('should emit error event on server socket close', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        request.socket.destroy()
+      }).then(serverUrl => {
+        let requestErrorEventEmitted = false
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('error', (error) => {
+          expect(error).to.be.an('Error')
+          requestErrorEventEmitted = true
+        })
+        urlRequest.on('close', () => {
+          expect(requestErrorEventEmitted).to.be.true('request error event was emitted')
+          done()
+        })
+        urlRequest.end()
+      })
+    })
+  })
+
+  describe('IncomingMessage API', () => {
+    it('response object should implement the IncomingMessage API', (done) => {
+      const customHeaderName = 'Some-Custom-Header-Name'
+      const customHeaderValue = 'Some-Customer-Header-Value'
+
+      respondOnce.toSingleURL((request, response) => {
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.setHeader(customHeaderName, customHeaderValue)
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+
+        urlRequest.on('response', (response) => {
+          expect(response.statusCode).to.equal(200)
+          expect(response.statusMessage).to.equal('OK')
+
+          const headers = response.headers
+          expect(headers).to.be.an('object')
+          const headerValue = headers[customHeaderName.toLowerCase()]
+          expect(headerValue).to.equal(customHeaderValue)
+
+          const httpVersion = response.httpVersion
+          expect(httpVersion).to.be.a('string').and.to.have.lengthOf.at.least(1)
+
+          const httpVersionMajor = response.httpVersionMajor
+          expect(httpVersionMajor).to.be.a('number').and.to.be.at.least(1)
+
+          const httpVersionMinor = response.httpVersionMinor
+          expect(httpVersionMinor).to.be.a('number').and.to.be.at.least(0)
+
+          response.on('data', chunk => {})
+          response.on('end', () => { done() })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should discard duplicate headers', (done) => {
+      const includedHeader = 'max-forwards'
+      const discardableHeader = 'Max-Forwards'
+
+      const includedHeaderValue = 'max-fwds-val'
+      const discardableHeaderValue = 'max-fwds-val-two'
+
+      respondOnce.toSingleURL((request, response) => {
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.setHeader(discardableHeader, discardableHeaderValue)
+        response.setHeader(includedHeader, includedHeaderValue)
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+
+        urlRequest.on('response', response => {
+          expect(response.statusCode).to.equal(200)
+          expect(response.statusMessage).to.equal('OK')
+
+          const headers = response.headers
+          expect(headers).to.be.an('object')
+
+          expect(headers).to.have.property(includedHeader)
+          expect(headers).to.not.have.property(discardableHeader)
+          expect(headers[includedHeader]).to.equal(includedHeaderValue)
+
+          response.on('data', chunk => {})
+          response.on('end', () => { done() })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should join repeated non-discardable value with ,', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        response.statusCode = 200
+        response.statusMessage = 'OK'
+        response.setHeader('referrer-policy', ['first-text', 'second-text'])
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+
+        urlRequest.on('response', response => {
+          expect(response.statusCode).to.equal(200)
+          expect(response.statusMessage).to.equal('OK')
+
+          const headers = response.headers
+          expect(headers).to.be.an('object')
+          expect(headers).to.have.property('referrer-policy')
+          expect(headers['referrer-policy']).to.equal('first-text, second-text')
+
+          response.on('data', chunk => {})
+          response.on('end', () => { done() })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should be able to pipe a net response into a writable stream', (done) => {
+      const bodyData = randomString(kOneKiloByte)
+      Promise.all([
+        respondOnce.toSingleURL((request, response) => response.end(bodyData)),
+        respondOnce.toSingleURL((request, response) => {
+          let receivedBodyData = ''
+          request.on('data', (chunk) => {
+            receivedBodyData += chunk.toString()
+          })
+          request.on('end', (chunk: Buffer | undefined) => {
+            if (chunk) {
+              receivedBodyData += chunk.toString()
+            }
+            expect(receivedBodyData).to.be.equal(bodyData)
+            response.end()
+          })
+        })
+      ]).then(([netServerUrl, nodeServerUrl]) => {
+        const netRequest = net.request(netServerUrl)
+        netRequest.on('response', (netResponse) => {
+          const serverUrl = url.parse(nodeServerUrl)
+          const nodeOptions = {
+            method: 'POST',
+            path: serverUrl.path,
+            port: serverUrl.port
+          }
+          const nodeRequest = http.request(nodeOptions, res => {
+            res.on('data', (chunk) => {})
+            res.on('end', () => {
+              done()
+            })
+          });
+          // TODO: IncomingMessage should properly extend ReadableStream in the
+          // docs
+          (netResponse as any).pipe(nodeRequest)
+        })
+        netRequest.end()
+      })
+    })
+
+    it('should not emit any event after close', (done) => {
+      const bodyData = randomString(kOneKiloByte)
+      respondOnce.toSingleURL((request, response) => {
+        response.end(bodyData)
+      }).then(serverUrl => {
+        let requestCloseEventEmitted = false
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          expect(requestCloseEventEmitted).to.be.false('request close event emitted')
+          const statusCode = response.statusCode
+          expect(statusCode).to.equal(200)
+          response.on('data', () => { })
+          response.on('end', () => { })
+          response.on('error', () => {
+            expect(requestCloseEventEmitted).to.be.false('request close event emitted')
+          })
+          response.on('aborted', () => {
+            expect(requestCloseEventEmitted).to.be.false('request close event emitted')
+          })
+        })
+        urlRequest.on('finish', () => {
+          expect(requestCloseEventEmitted).to.be.false('request close event emitted')
+        })
+        urlRequest.on('error', () => {
+          expect(requestCloseEventEmitted).to.be.false('request close event emitted')
+        })
+        urlRequest.on('abort', () => {
+          expect(requestCloseEventEmitted).to.be.false('request close event emitted')
+        })
+        urlRequest.on('close', () => {
+          requestCloseEventEmitted = true
+          // Wait so that all async events get scheduled.
+          setTimeout(() => {
+            done()
+          }, 100)
+        })
+        urlRequest.end()
+      })
+    })
+  })
+
+  describe('Stability and performance', () => {
+    it('should free unreferenced, never-started request objects without crash', (done) => {
+      net.request('https://test')
+      process.nextTick(() => {
+        const v8Util = process.electronBinding('v8_util')
+        v8Util.requestGarbageCollectionForTesting()
+        done()
+      })
+    })
+
+    it('should collect on-going requests without crash', (done) => {
+      let finishResponse: (() => void) | null = null
+      respondOnce.toSingleURL((request, response) => {
+        response.write(randomString(kOneKiloByte))
+        finishResponse = () => {
+          response.write(randomString(kOneKiloByte))
+          response.end()
+        }
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          response.on('data', () => { })
+          response.on('end', () => {
+            done()
+          })
+          process.nextTick(() => {
+            // Trigger a garbage collection.
+            const v8Util = process.electronBinding('v8_util')
+            v8Util.requestGarbageCollectionForTesting()
+            finishResponse!()
+          })
+        })
+        urlRequest.end()
+      })
+    })
+
+    it('should collect unreferenced, ended requests without crash', (done) => {
+      respondOnce.toSingleURL((request, response) => {
+        response.end()
+      }).then(serverUrl => {
+        const urlRequest = net.request(serverUrl)
+        urlRequest.on('response', (response) => {
+          response.on('data', () => {})
+          response.on('end', () => {})
+        })
+        urlRequest.on('close', () => {
+          process.nextTick(() => {
+            const v8Util = process.electronBinding('v8_util')
+            v8Util.requestGarbageCollectionForTesting()
+            done()
+          })
+        })
+        urlRequest.end()
+      })
+    })
+  })
+})

+ 0 - 1701
spec/api-net-spec.js

@@ -1,1701 +0,0 @@
-const chai = require('chai')
-const dirtyChai = require('dirty-chai')
-
-const { remote } = require('electron')
-const { ipcRenderer } = require('electron')
-const http = require('http')
-const url = require('url')
-const { net } = remote
-const { session } = remote
-
-const { expect } = chai
-chai.use(dirtyChai)
-
-/* The whole net API doesn't use standard callbacks */
-/* eslint-disable standard/no-callback-literal */
-
-function randomBuffer (size, start, end) {
-  start = start || 0
-  end = end || 255
-  const range = 1 + end - start
-  const buffer = Buffer.allocUnsafe(size)
-  for (let i = 0; i < size; ++i) {
-    buffer[i] = start + Math.floor(Math.random() * range)
-  }
-  return buffer
-}
-
-function randomString (length) {
-  const buffer = randomBuffer(length, '0'.charCodeAt(0), 'z'.charCodeAt(0))
-  return buffer.toString()
-}
-
-const kOneKiloByte = 1024
-const kOneMegaByte = kOneKiloByte * kOneKiloByte
-
-describe('net module', () => {
-  let server
-  const connections = new Set()
-
-  beforeEach((done) => {
-    server = http.createServer()
-    server.listen(0, '127.0.0.1', () => {
-      server.url = `http://127.0.0.1:${server.address().port}`
-      done()
-    })
-    server.on('connection', (connection) => {
-      connections.add(connection)
-      connection.once('close', () => {
-        connections.delete(connection)
-      })
-    })
-  })
-
-  afterEach((done) => {
-    for (const connection of connections) {
-      connection.destroy()
-    }
-    server.close(() => {
-      server = null
-      done()
-    })
-  })
-
-  describe('HTTP basics', () => {
-    it('should be able to issue a basic GET request', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.method).to.equal('GET')
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request(`${server.url}${requestUrl}`)
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should be able to issue a basic POST request', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.method).to.equal('POST')
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'POST',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should fetch correct data in a GET request', (done) => {
-      const requestUrl = '/requestUrl'
-      const bodyData = 'Hello World!'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.method).to.equal('GET')
-            response.write(bodyData)
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request(`${server.url}${requestUrl}`)
-      urlRequest.on('response', (response) => {
-        let expectedBodyData = ''
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-          expectedBodyData += chunk.toString()
-        })
-        response.on('end', () => {
-          expect(expectedBodyData).to.equal(bodyData)
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should post the correct data in a POST request', (done) => {
-      const requestUrl = '/requestUrl'
-      const bodyData = 'Hello World!'
-      server.on('request', (request, response) => {
-        let postedBodyData = ''
-        switch (request.url) {
-          case requestUrl:
-            expect(request.method).to.equal('POST')
-            request.on('data', (chunk) => {
-              postedBodyData += chunk.toString()
-            })
-            request.on('end', () => {
-              expect(postedBodyData).to.equal(bodyData)
-              response.end()
-            })
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'POST',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {})
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.write(bodyData)
-      urlRequest.end()
-    })
-
-    it('should support chunked encoding', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.chunkedEncoding = true
-            expect(request.method).to.equal('POST')
-            expect(request.headers['transfer-encoding']).to.equal('chunked')
-            expect(request.headers['content-length']).to.be.undefined()
-            request.on('data', (chunk) => {
-              response.write(chunk)
-            })
-            request.on('end', (chunk) => {
-              response.end(chunk)
-            })
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'POST',
-        url: `${server.url}${requestUrl}`
-      })
-
-      let chunkIndex = 0
-      const chunkCount = 100
-      const sentChunks = []
-      const receivedChunks = []
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-          receivedChunks.push(chunk)
-        })
-        response.on('end', () => {
-          const sentData = Buffer.concat(sentChunks)
-          const receivedData = Buffer.concat(receivedChunks)
-          expect(sentData.toString()).to.equal(receivedData.toString())
-          expect(chunkIndex).to.be.equal(chunkCount)
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.chunkedEncoding = true
-      while (chunkIndex < chunkCount) {
-        chunkIndex += 1
-        const chunk = randomBuffer(kOneKiloByte)
-        sentChunks.push(chunk)
-        expect(urlRequest.write(chunk)).to.be.true()
-      }
-      urlRequest.end()
-    })
-  })
-
-  describe('ClientRequest API', () => {
-    afterEach(() => {
-      session.defaultSession.webRequest.onBeforeRequest(null)
-    })
-
-    it('request/response objects should emit expected events', (done) => {
-      const requestUrl = '/requestUrl'
-      const bodyData = randomString(kOneMegaByte)
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.write(bodyData)
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      let requestResponseEventEmitted = false
-      let requestFinishEventEmitted = false
-      let requestCloseEventEmitted = false
-      let responseDataEventEmitted = false
-      let responseEndEventEmitted = false
-
-      function maybeDone (done) {
-        if (!requestCloseEventEmitted || !responseEndEventEmitted) {
-          return
-        }
-
-        expect(requestResponseEventEmitted).to.be.true()
-        expect(requestFinishEventEmitted).to.be.true()
-        expect(requestCloseEventEmitted).to.be.true()
-        expect(responseDataEventEmitted).to.be.true()
-        expect(responseEndEventEmitted).to.be.true()
-        done()
-      }
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        requestResponseEventEmitted = true
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        const buffers = []
-        response.pause()
-        response.on('data', (chunk) => {
-          buffers.push(chunk)
-          responseDataEventEmitted = true
-        })
-        response.on('end', () => {
-          const receivedBodyData = Buffer.concat(buffers)
-          expect(receivedBodyData.toString()).to.equal(bodyData)
-          responseEndEventEmitted = true
-          maybeDone(done)
-        })
-        response.resume()
-        response.on('error', (error) => {
-          expect(error).to.be.an('Error')
-        })
-        response.on('aborted', () => {
-          expect.fail('response aborted')
-        })
-      })
-      urlRequest.on('finish', () => {
-        requestFinishEventEmitted = true
-      })
-      urlRequest.on('error', (error) => {
-        expect(error).to.be.an('Error')
-      })
-      urlRequest.on('abort', () => {
-        expect.fail('request aborted')
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        maybeDone(done)
-      })
-      urlRequest.end()
-    })
-
-    it('should be able to set a custom HTTP request header before first write', (done) => {
-      const requestUrl = '/requestUrl'
-      const customHeaderName = 'Some-Custom-Header-Name'
-      const customHeaderValue = 'Some-Customer-Header-Value'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue)
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.setHeader(customHeaderName, customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
-      urlRequest.write('')
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
-      urlRequest.end()
-    })
-
-    it('should be able to set a non-string object as a header value', (done) => {
-      const requestUrl = '/requestUrl'
-      const customHeaderName = 'Some-Integer-Value'
-      const customHeaderValue = 900
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue.toString())
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            expect(request.url).to.equal(requestUrl)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.setHeader(customHeaderName, customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
-      urlRequest.write('')
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue)
-      urlRequest.end()
-    })
-
-    it('should not be able to set a custom HTTP request header after first write', (done) => {
-      const requestUrl = '/requestUrl'
-      const customHeaderName = 'Some-Custom-Header-Name'
-      const customHeaderValue = 'Some-Customer-Header-Value'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.headers[customHeaderName.toLowerCase()]).to.be.undefined()
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.write('')
-      expect(() => {
-        urlRequest.setHeader(customHeaderName, customHeaderValue)
-      }).to.throw()
-      expect(urlRequest.getHeader(customHeaderName)).to.be.undefined()
-      urlRequest.end()
-    })
-
-    it('should be able to remove a custom HTTP request header before first write', (done) => {
-      const requestUrl = '/requestUrl'
-      const customHeaderName = 'Some-Custom-Header-Name'
-      const customHeaderValue = 'Some-Customer-Header-Value'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.headers[customHeaderName.toLowerCase()]).to.be.undefined()
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.setHeader(customHeaderName, customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      urlRequest.removeHeader(customHeaderName)
-      expect(urlRequest.getHeader(customHeaderName)).to.be.undefined()
-      urlRequest.write('')
-      urlRequest.end()
-    })
-
-    it('should not be able to remove a custom HTTP request header after first write', (done) => {
-      const requestUrl = '/requestUrl'
-      const customHeaderName = 'Some-Custom-Header-Name'
-      const customHeaderValue = 'Some-Customer-Header-Value'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue)
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.setHeader(customHeaderName, customHeaderValue)
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      urlRequest.write('')
-      expect(() => {
-        urlRequest.removeHeader(customHeaderName)
-      }).to.throw()
-      expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue)
-      urlRequest.end()
-    })
-
-    it('should be able to set cookie header line', (done) => {
-      const requestUrl = '/requestUrl'
-      const cookieHeaderName = 'Cookie'
-      const cookieHeaderValue = 'test=12345'
-      const customSession = session.fromPartition('test-cookie-header')
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue)
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      customSession.cookies.set({
-        url: `${server.url}`,
-        name: 'test',
-        value: '11111'
-      }).then(() => { // resolved
-        const urlRequest = net.request({
-          method: 'GET',
-          url: `${server.url}${requestUrl}`,
-          session: customSession
-        })
-        urlRequest.on('response', (response) => {
-          const statusCode = response.statusCode
-          expect(statusCode).to.equal(200)
-          response.pause()
-          response.on('data', (chunk) => {})
-          response.on('end', () => {
-            done()
-          })
-          response.resume()
-        })
-        urlRequest.setHeader(cookieHeaderName, cookieHeaderValue)
-        expect(urlRequest.getHeader(cookieHeaderName)).to.equal(cookieHeaderValue)
-        urlRequest.end()
-      }, (error) => {
-        done(error)
-      })
-    })
-
-    it('should be able to abort an HTTP request before first write', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        response.end()
-        expect.fail('Unexpected request event')
-      })
-
-      let requestAbortEventEmitted = false
-      let requestCloseEventEmitted = false
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect.fail('Unexpected response event')
-      })
-      urlRequest.on('finish', () => {
-        expect.fail('Unexpected finish event')
-      })
-      urlRequest.on('error', () => {
-        expect.fail('Unexpected error event')
-      })
-      urlRequest.on('abort', () => {
-        requestAbortEventEmitted = true
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        expect(requestAbortEventEmitted).to.be.true()
-        expect(requestCloseEventEmitted).to.be.true()
-        done()
-      })
-      urlRequest.abort()
-      expect(urlRequest.write('')).to.be.false()
-      urlRequest.end()
-    })
-
-    it('it should be able to abort an HTTP request before request end', (done) => {
-      const requestUrl = '/requestUrl'
-      let requestReceivedByServer = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            requestReceivedByServer = true
-            cancelRequest()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      let requestAbortEventEmitted = false
-      let requestCloseEventEmitted = false
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect.fail('Unexpected response event')
-      })
-      urlRequest.on('finish', () => {
-        expect.fail('Unexpected finish event')
-      })
-      urlRequest.on('error', () => {
-        expect.fail('Unexpected error event')
-      })
-      urlRequest.on('abort', () => {
-        requestAbortEventEmitted = true
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        expect(requestReceivedByServer).to.be.true()
-        expect(requestAbortEventEmitted).to.be.true()
-        expect(requestCloseEventEmitted).to.be.true()
-        done()
-      })
-
-      urlRequest.chunkedEncoding = true
-      urlRequest.write(randomString(kOneKiloByte))
-      function cancelRequest () {
-        urlRequest.abort()
-      }
-    })
-
-    it('it should be able to abort an HTTP request after request end and before response', (done) => {
-      const requestUrl = '/requestUrl'
-      let requestReceivedByServer = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            requestReceivedByServer = true
-            cancelRequest()
-            process.nextTick(() => {
-              response.statusCode = 200
-              response.statusMessage = 'OK'
-              response.end()
-            })
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      let requestAbortEventEmitted = false
-      let requestFinishEventEmitted = false
-      let requestCloseEventEmitted = false
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect.fail('Unexpected response event')
-      })
-      urlRequest.on('finish', () => {
-        requestFinishEventEmitted = true
-      })
-      urlRequest.on('error', () => {
-        expect.fail('Unexpected error event')
-      })
-      urlRequest.on('abort', () => {
-        requestAbortEventEmitted = true
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        expect(requestFinishEventEmitted).to.be.true()
-        expect(requestReceivedByServer).to.be.true()
-        expect(requestAbortEventEmitted).to.be.true()
-        expect(requestCloseEventEmitted).to.be.true()
-        done()
-      })
-
-      urlRequest.end(randomString(kOneKiloByte))
-      function cancelRequest () {
-        urlRequest.abort()
-      }
-    })
-
-    it('it should be able to abort an HTTP request after response start', (done) => {
-      const requestUrl = '/requestUrl'
-      let requestReceivedByServer = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            requestReceivedByServer = true
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.write(randomString(kOneKiloByte))
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      let requestFinishEventEmitted = false
-      let requestResponseEventEmitted = false
-      let requestAbortEventEmitted = false
-      let requestCloseEventEmitted = false
-      let responseAbortedEventEmitted = false
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        requestResponseEventEmitted = true
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          expect.fail('Unexpected end event')
-        })
-        response.resume()
-        response.on('error', () => {
-          expect.fail('Unexpected error event')
-        })
-        response.on('aborted', () => {
-          responseAbortedEventEmitted = true
-        })
-        urlRequest.abort()
-      })
-      urlRequest.on('finish', () => {
-        requestFinishEventEmitted = true
-      })
-      urlRequest.on('error', () => {
-        expect.fail('Unexpected error event')
-      })
-      urlRequest.on('abort', () => {
-        requestAbortEventEmitted = true
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event')
-        expect(requestReceivedByServer).to.be.true('request should be received by the server')
-        expect(requestResponseEventEmitted).to.be.true('"response" event should be emitted')
-        expect(requestAbortEventEmitted).to.be.true('request should emit "abort" event')
-        expect(responseAbortedEventEmitted).to.be.true('response should emit "aborted" event')
-        expect(requestCloseEventEmitted).to.be.true('request should emit "close" event')
-        done()
-      })
-      urlRequest.end(randomString(kOneKiloByte))
-    })
-
-    it('abort event should be emitted at most once', (done) => {
-      const requestUrl = '/requestUrl'
-      let requestReceivedByServer = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            requestReceivedByServer = true
-            cancelRequest()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      let requestFinishEventEmitted = false
-      let requestAbortEventCount = 0
-      let requestCloseEventEmitted = false
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', () => {
-        expect.fail('Unexpected response event')
-      })
-      urlRequest.on('finish', () => {
-        requestFinishEventEmitted = true
-      })
-      urlRequest.on('error', () => {
-        expect.fail('Unexpected error event')
-      })
-      urlRequest.on('abort', () => {
-        ++requestAbortEventCount
-        urlRequest.abort()
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        // Let all pending async events to be emitted
-        setTimeout(() => {
-          expect(requestFinishEventEmitted).to.be.true()
-          expect(requestReceivedByServer).to.be.true()
-          expect(requestAbortEventCount).to.equal(1)
-          expect(requestCloseEventEmitted).to.be.true()
-          done()
-        }, 500)
-      })
-
-      urlRequest.end(randomString(kOneKiloByte))
-      function cancelRequest () {
-        urlRequest.abort()
-        urlRequest.abort()
-      }
-    })
-
-    it('Requests should be intercepted by webRequest module', (done) => {
-      const requestUrl = '/requestUrl'
-      const redirectUrl = '/redirectUrl'
-      let requestIsRedirected = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case redirectUrl:
-            requestIsRedirected = true
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      let requestIsIntercepted = false
-      session.defaultSession.webRequest.onBeforeRequest(
-        (details, callback) => {
-          if (details.url === `${server.url}${requestUrl}`) {
-            requestIsIntercepted = true
-            // Disabled due to false positive in StandardJS
-            // eslint-disable-next-line standard/no-callback-literal
-            callback({
-              redirectURL: `${server.url}${redirectUrl}`
-            })
-          } else {
-            callback({
-              cancel: false
-            })
-          }
-        })
-
-      const urlRequest = net.request(`${server.url}${requestUrl}`)
-
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL')
-          expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module')
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should to able to create and intercept a request using a custom session object', (done) => {
-      const requestUrl = '/requestUrl'
-      const redirectUrl = '/redirectUrl'
-      const customPartitionName = 'custom-partition'
-      let requestIsRedirected = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case redirectUrl:
-            requestIsRedirected = true
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
-        expect.fail('Request should not be intercepted by the default session')
-      })
-
-      const customSession = session.fromPartition(customPartitionName, { cache: false })
-      let requestIsIntercepted = false
-      customSession.webRequest.onBeforeRequest((details, callback) => {
-        if (details.url === `${server.url}${requestUrl}`) {
-          requestIsIntercepted = true
-          // Disabled due to false positive in StandardJS
-          // eslint-disable-next-line standard/no-callback-literal
-          callback({
-            redirectURL: `${server.url}${redirectUrl}`
-          })
-        } else {
-          callback({
-            cancel: false
-          })
-        }
-      })
-
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`,
-        session: customSession
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL')
-          expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module')
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should throw if given an invalid redirect mode', () => {
-      const requestUrl = '/requestUrl'
-      const options = {
-        url: `${server.url}${requestUrl}`,
-        redirect: 'custom'
-      }
-      expect(() => {
-        net.request(options)
-      }).to.throw('redirect mode should be one of follow, error or manual')
-    })
-
-    it('should throw when calling getHeader without a name', () => {
-      expect(() => {
-        net.request({ url: `${server.url}/requestUrl` }).getHeader()
-      }).to.throw(/`name` is required for getHeader\(name\)\./)
-
-      expect(() => {
-        net.request({ url: `${server.url}/requestUrl` }).getHeader(null)
-      }).to.throw(/`name` is required for getHeader\(name\)\./)
-    })
-
-    it('should throw when calling removeHeader without a name', () => {
-      expect(() => {
-        net.request({ url: `${server.url}/requestUrl` }).removeHeader()
-      }).to.throw(/`name` is required for removeHeader\(name\)\./)
-
-      expect(() => {
-        net.request({ url: `${server.url}/requestUrl` }).removeHeader(null)
-      }).to.throw(/`name` is required for removeHeader\(name\)\./)
-    })
-
-    it('should follow redirect when no redirect mode is provided', (done) => {
-      const requestUrl = '/301'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case '/301':
-            response.statusCode = '301'
-            response.setHeader('Location', '/200')
-            response.end()
-            break
-          case '/200':
-            response.statusCode = '200'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        done()
-      })
-      urlRequest.end()
-    })
-
-    it('should follow redirect chain when no redirect mode is provided', (done) => {
-      const requestUrl = '/redirectChain'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case '/redirectChain':
-            response.statusCode = '301'
-            response.setHeader('Location', '/301')
-            response.end()
-            break
-          case '/301':
-            response.statusCode = '301'
-            response.setHeader('Location', '/200')
-            response.end()
-            break
-          case '/200':
-            response.statusCode = '200'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        done()
-      })
-      urlRequest.end()
-    })
-
-    it('should not follow redirect when mode is error', (done) => {
-      const requestUrl = '/301'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case '/301':
-            response.statusCode = '301'
-            response.setHeader('Location', '/200')
-            response.end()
-            break
-          case '/200':
-            response.statusCode = '200'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`,
-        redirect: 'error'
-      })
-      urlRequest.on('error', (error) => {
-        expect(error.message).to.equal('Request cannot follow redirect with the current redirect mode')
-      })
-      urlRequest.on('close', () => {
-        done()
-      })
-      urlRequest.end()
-    })
-
-    it('should allow follow redirect when mode is manual', (done) => {
-      const requestUrl = '/redirectChain'
-      let redirectCount = 0
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case '/redirectChain':
-            response.statusCode = '301'
-            response.setHeader('Location', '/301')
-            response.end()
-            break
-          case '/301':
-            response.statusCode = '301'
-            response.setHeader('Location', '/200')
-            response.end()
-            break
-          case '/200':
-            response.statusCode = '200'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`,
-        redirect: 'manual'
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        expect(redirectCount).to.equal(2)
-        done()
-      })
-      urlRequest.on('redirect', (status, method, url) => {
-        if (url === `${server.url}/301` || url === `${server.url}/200`) {
-          redirectCount += 1
-          urlRequest.followRedirect()
-        }
-      })
-      urlRequest.end()
-    })
-
-    it('should allow cancelling redirect when mode is manual', (done) => {
-      const requestUrl = '/redirectChain'
-      let redirectCount = 0
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case '/redirectChain':
-            response.statusCode = '301'
-            response.setHeader('Location', '/redirect/1')
-            response.end()
-            break
-          case '/redirect/1':
-            response.statusCode = '200'
-            response.setHeader('Location', '/redirect/2')
-            response.end()
-            break
-          case '/redirect/2':
-            response.statusCode = '200'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`,
-        redirect: 'manual'
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).that.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          urlRequest.abort()
-        })
-        response.resume()
-      })
-      urlRequest.on('close', () => {
-        expect(redirectCount).to.equal(1)
-        done()
-      })
-      urlRequest.on('redirect', (status, method, url) => {
-        if (url === `${server.url}/redirect/1`) {
-          redirectCount += 1
-          urlRequest.followRedirect()
-        }
-      })
-      urlRequest.end()
-    })
-
-    it('should throw if given an invalid session option', (done) => {
-      const requestUrl = '/requestUrl'
-      try {
-        const urlRequest = net.request({
-          url: `${server.url}${requestUrl}`,
-          session: 1
-        })
-
-        // eslint-disable-next-line
-        urlRequest
-      } catch (exception) {
-        done()
-      }
-    })
-
-    it('should to able to create and intercept a request using a custom partition name', (done) => {
-      const requestUrl = '/requestUrl'
-      const redirectUrl = '/redirectUrl'
-      const customPartitionName = 'custom-partition'
-      let requestIsRedirected = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case redirectUrl:
-            requestIsRedirected = true
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
-        expect.fail('Request should not be intercepted by the default session')
-      })
-
-      const customSession = session.fromPartition(customPartitionName, {
-        cache: false
-      })
-      let requestIsIntercepted = false
-      customSession.webRequest.onBeforeRequest((details, callback) => {
-        if (details.url === `${server.url}${requestUrl}`) {
-          requestIsIntercepted = true
-          callback({
-            redirectURL: `${server.url}${redirectUrl}`
-          })
-        } else {
-          callback({
-            cancel: false
-          })
-        }
-      })
-
-      const urlRequest = net.request({
-        url: `${server.url}${requestUrl}`,
-        partition: customPartitionName
-      })
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL')
-          expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module')
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should throw if given an invalid partition option', (done) => {
-      const requestUrl = '/requestUrl'
-      try {
-        const urlRequest = net.request({
-          url: `${server.url}${requestUrl}`,
-          partition: 1
-        })
-
-        // eslint-disable-next-line
-        urlRequest
-      } catch (exception) {
-        done()
-      }
-    })
-
-    it('should be able to create a request with options', (done) => {
-      const requestUrl = '/'
-      const customHeaderName = 'Some-Custom-Header-Name'
-      const customHeaderValue = 'Some-Customer-Header-Value'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            expect(request.method).to.equal('GET')
-            expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue)
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      const serverUrl = url.parse(server.url)
-      const options = {
-        port: serverUrl.port,
-        hostname: '127.0.0.1',
-        headers: {}
-      }
-      options.headers[customHeaderName] = customHeaderValue
-      const urlRequest = net.request(options)
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.be.equal(200)
-        response.pause()
-        response.on('data', (chunk) => {
-        })
-        response.on('end', () => {
-          done()
-        })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should be able to pipe a readable stream into a net request', (done) => {
-      const nodeRequestUrl = '/nodeRequestUrl'
-      const netRequestUrl = '/netRequestUrl'
-      const bodyData = randomString(kOneMegaByte)
-      let netRequestReceived = false
-      let netRequestEnded = false
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case nodeRequestUrl:
-            response.write(bodyData)
-            response.end()
-            break
-          case netRequestUrl:
-            netRequestReceived = true
-            let receivedBodyData = ''
-            request.on('data', (chunk) => {
-              receivedBodyData += chunk.toString()
-            })
-            request.on('end', (chunk) => {
-              netRequestEnded = true
-              if (chunk) {
-                receivedBodyData += chunk.toString()
-              }
-              expect(receivedBodyData).to.be.equal(bodyData)
-              response.end()
-            })
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      const nodeRequest = http.request(`${server.url}${nodeRequestUrl}`)
-      nodeRequest.on('response', (nodeResponse) => {
-        const netRequest = net.request(`${server.url}${netRequestUrl}`)
-        netRequest.on('response', (netResponse) => {
-          expect(netResponse.statusCode).to.be.equal(200)
-          netResponse.pause()
-          netResponse.on('data', (chunk) => {})
-          netResponse.on('end', () => {
-            expect(netRequestReceived).to.be.true()
-            expect(netRequestEnded).to.be.true()
-            done()
-          })
-          netResponse.resume()
-        })
-        nodeResponse.pipe(netRequest)
-      })
-      nodeRequest.end()
-    })
-
-    it('should emit error event on server socket close', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            request.socket.destroy()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      let requestErrorEventEmitted = false
-      const urlRequest = net.request(`${server.url}${requestUrl}`)
-      urlRequest.on('error', (error) => {
-        expect(error).to.be.an('Error')
-        requestErrorEventEmitted = true
-      })
-      urlRequest.on('close', () => {
-        expect(requestErrorEventEmitted).to.be.true()
-        done()
-      })
-      urlRequest.end()
-    })
-  })
-
-  describe('IncomingMessage API', () => {
-    it('response object should implement the IncomingMessage API', (done) => {
-      const requestUrl = '/requestUrl'
-      const customHeaderName = 'Some-Custom-Header-Name'
-      const customHeaderValue = 'Some-Customer-Header-Value'
-
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.setHeader(customHeaderName, customHeaderValue)
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-
-      urlRequest.on('response', (response) => {
-        expect(response.statusCode).to.equal(200)
-        expect(response.statusMessage).to.equal('OK')
-
-        const headers = response.headers
-        expect(headers).to.be.an('object')
-        const headerValue = headers[customHeaderName.toLowerCase()]
-        expect(headerValue).to.equal(customHeaderValue)
-
-        const httpVersion = response.httpVersion
-        expect(httpVersion).to.be.a('string').and.to.have.lengthOf.at.least(1)
-
-        const httpVersionMajor = response.httpVersionMajor
-        expect(httpVersionMajor).to.be.a('number').and.to.be.at.least(1)
-
-        const httpVersionMinor = response.httpVersionMinor
-        expect(httpVersionMinor).to.be.a('number').and.to.be.at.least(0)
-
-        response.pause()
-        response.on('data', chunk => {})
-        response.on('end', () => { done() })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should discard duplicate headers', (done) => {
-      const requestUrl = '/duplicateRequestUrl'
-      const includedHeader = 'max-forwards'
-      const discardableHeader = 'Max-Forwards'
-
-      const includedHeaderValue = 'max-fwds-val'
-      const discardableHeaderValue = 'max-fwds-val-two'
-
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.setHeader(discardableHeader, discardableHeaderValue)
-            response.setHeader(includedHeader, includedHeaderValue)
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-
-      urlRequest.on('response', response => {
-        expect(response.statusCode).to.equal(200)
-        expect(response.statusMessage).to.equal('OK')
-
-        const headers = response.headers
-        expect(headers).to.be.an('object')
-
-        expect(headers).to.have.property(includedHeader)
-        expect(headers).to.not.have.property(discardableHeader)
-        expect(headers[includedHeader]).to.equal(includedHeaderValue)
-
-        response.pause()
-        response.on('data', chunk => {})
-        response.on('end', () => { done() })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should join repeated non-discardable value with ,', (done) => {
-      const requestUrl = '/requestUrl'
-
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.setHeader('referrer-policy', ['first-text', 'second-text'])
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-
-      urlRequest.on('response', response => {
-        expect(response.statusCode).to.equal(200)
-        expect(response.statusMessage).to.equal('OK')
-
-        const headers = response.headers
-        expect(headers).to.be.an('object')
-        expect(headers).to.have.a.property('referrer-policy')
-        expect(headers['referrer-policy']).to.equal('first-text, second-text')
-
-        response.pause()
-        response.on('data', chunk => {})
-        response.on('end', () => { done() })
-        response.resume()
-      })
-      urlRequest.end()
-    })
-
-    it('should be able to pipe a net response into a writable stream', (done) => {
-      const nodeRequestUrl = '/nodeRequestUrl'
-      const netRequestUrl = '/netRequestUrl'
-      const bodyData = randomString(kOneMegaByte)
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case netRequestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.write(bodyData)
-            response.end()
-            break
-          case nodeRequestUrl:
-            let receivedBodyData = ''
-            request.on('data', (chunk) => {
-              receivedBodyData += chunk.toString()
-            })
-            request.on('end', (chunk) => {
-              if (chunk) {
-                receivedBodyData += chunk.toString()
-              }
-              expect(receivedBodyData).to.equal(bodyData)
-              response.end()
-            })
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      ipcRenderer.once('api-net-spec-done', () => {
-        done()
-      })
-      // Execute below code directly within the browser context without
-      // using the remote module.
-      ipcRenderer.send('eval', `
-        const {net} = require('electron')
-        const http = require('http')
-        const url = require('url')
-        const netRequest = net.request('${server.url}${netRequestUrl}')
-        netRequest.on('response', function (netResponse) {
-          const serverUrl = url.parse('${server.url}')
-          const nodeOptions = {
-            method: 'POST',
-            path: '${nodeRequestUrl}',
-            port: serverUrl.port
-          }
-          let nodeRequest = http.request(nodeOptions)
-          nodeRequest.on('response', function (nodeResponse) {
-            nodeResponse.on('data', (chunk) => {
-            })
-            nodeResponse.on('end', (chunk) => {
-              event.sender.send('api-net-spec-done')
-            })
-          })
-          netResponse.pipe(nodeRequest)
-        })
-        netRequest.end()
-      `)
-    })
-
-    it('should not emit any event after close', (done) => {
-      const requestUrl = '/requestUrl'
-      const bodyData = randomString(kOneKiloByte)
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.write(bodyData)
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      let requestCloseEventEmitted = false
-      const urlRequest = net.request({
-        method: 'GET',
-        url: `${server.url}${requestUrl}`
-      })
-      urlRequest.on('response', (response) => {
-        expect(requestCloseEventEmitted).to.be.false()
-        const statusCode = response.statusCode
-        expect(statusCode).to.equal(200)
-        response.pause()
-        response.on('data', () => {
-        })
-        response.on('end', () => {
-        })
-        response.resume()
-        response.on('error', () => {
-          expect(requestCloseEventEmitted).to.be.false()
-        })
-        response.on('aborted', () => {
-          expect(requestCloseEventEmitted).to.be.false()
-        })
-      })
-      urlRequest.on('finish', () => {
-        expect(requestCloseEventEmitted).to.be.false()
-      })
-      urlRequest.on('error', () => {
-        expect(requestCloseEventEmitted).to.be.false()
-      })
-      urlRequest.on('abort', () => {
-        expect(requestCloseEventEmitted).to.be.false()
-      })
-      urlRequest.on('close', () => {
-        requestCloseEventEmitted = true
-        // Wait so that all async events get scheduled.
-        setTimeout(() => {
-          done()
-        }, 100)
-      })
-      urlRequest.end()
-    })
-  })
-
-  describe('Stability and performance', () => {
-    it('should free unreferenced, never-started request objects without crash', (done) => {
-      const requestUrl = '/requestUrl'
-      ipcRenderer.once('api-net-spec-done', () => {
-        done()
-      })
-      ipcRenderer.send('eval', `
-        const {net} = require('electron')
-        const urlRequest = net.request('${server.url}${requestUrl}')
-        process.nextTick(() => {
-          const v8Util = process.electronBinding('v8_util')
-          v8Util.requestGarbageCollectionForTesting()
-          event.sender.send('api-net-spec-done')
-        })
-      `)
-    })
-
-    it('should not collect on-going requests without crash', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.write(randomString(kOneKiloByte))
-            ipcRenderer.once('api-net-spec-resume', () => {
-              response.write(randomString(kOneKiloByte))
-              response.end()
-            })
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      ipcRenderer.once('api-net-spec-done', () => {
-        done()
-      })
-      // Execute below code directly within the browser context without
-      // using the remote module.
-      ipcRenderer.send('eval', `
-        const {net} = require('electron')
-        const urlRequest = net.request('${server.url}${requestUrl}')
-        urlRequest.on('response', (response) => {
-          response.on('data', () => {
-          })
-          response.on('end', () => {
-            event.sender.send('api-net-spec-done')
-          })
-          process.nextTick(() => {
-            // Trigger a garbage collection.
-            const v8Util = process.electronBinding('v8_util')
-            v8Util.requestGarbageCollectionForTesting()
-            event.sender.send('api-net-spec-resume')
-          })
-        })
-        urlRequest.end()
-      `)
-    })
-
-    it('should collect unreferenced, ended requests without crash', (done) => {
-      const requestUrl = '/requestUrl'
-      server.on('request', (request, response) => {
-        switch (request.url) {
-          case requestUrl:
-            response.statusCode = 200
-            response.statusMessage = 'OK'
-            response.end()
-            break
-          default:
-            handleUnexpectedURL(request, response)
-        }
-      })
-      ipcRenderer.once('api-net-spec-done', () => {
-        done()
-      })
-      ipcRenderer.send('eval', `
-        const {net} = require('electron')
-        const urlRequest = net.request('${server.url}${requestUrl}')
-        urlRequest.on('response', (response) => {
-          response.on('data', () => {
-          })
-          response.on('end', () => {
-          })
-        })
-        urlRequest.on('close', () => {
-          process.nextTick(() => {
-            const v8Util = process.electronBinding('v8_util')
-            v8Util.requestGarbageCollectionForTesting()
-            event.sender.send('api-net-spec-done')
-          })
-        })
-        urlRequest.end()
-      `)
-    })
-  })
-})
-
-function handleUnexpectedURL (request, response) {
-  response.statusCode = '500'
-  response.end()
-  expect.fail(`Unexpected URL: ${request.url}`)
-}