blob: 0e1648f533422af8433ba86da0c6de8286431c44 [file] [log] [blame]
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
'use strict'
const assert = require('node:assert')
const http = require('node:http')
const url = require('node:url')
const HttpClient = require('selenium-webdriver/http').HttpClient
const HttpRequest = require('selenium-webdriver/lib/http').Request
const Server = require('../../lib/test/httpserver').Server
describe('HttpClient', function () {
const server = new Server(function (req, res) {
// eslint-disable-next-line n/no-deprecated-api
const parsedUrl = url.parse(req.url)
if (req.method === 'GET' && req.url === '/echo') {
res.writeHead(200)
res.end(JSON.stringify(req.headers))
} else if (req.method === 'GET' && req.url === '/redirect') {
res.writeHead(303, { Location: server.url('/hello') })
res.end()
} else if (req.method === 'GET' && req.url === '/hello') {
res.writeHead(200, { 'content-type': 'text/plain' })
res.end('hello, world!')
} else if (req.method === 'GET' && req.url === '/chunked') {
res.writeHead(200, {
'content-type': 'text/html; charset=utf-8',
'transfer-encoding': 'chunked',
})
res.write('<!DOCTYPE html>')
setTimeout(() => res.end('<h1>Hello, world!</h1>'), 20)
} else if (req.method === 'GET' && req.url === '/badredirect') {
res.writeHead(303, {})
res.end()
} else if (req.method === 'GET' && req.url === '/protected') {
const denyAccess = function () {
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="test"' })
res.end('Access denied')
}
// eslint-disable-next-line no-useless-escape
const basicAuthRegExp = /^\s*basic\s+([a-z0-9\-\._~\+\/]+)=*\s*$/i
const auth = req.headers.authorization
const match = basicAuthRegExp.exec(auth || '')
if (!match) {
denyAccess()
return
}
const userNameAndPass = Buffer.from(match[1], 'base64').toString()
const parts = userNameAndPass.split(':', 2)
if (parts[0] !== 'genie' && parts[1] !== 'bottle') {
denyAccess()
return
}
res.writeHead(200, { 'content-type': 'text/plain' })
res.end('Access granted!')
} else if (req.method === 'GET' && parsedUrl.pathname && parsedUrl.pathname.endsWith('/proxy')) {
let headers = Object.assign({}, req.headers)
headers['x-proxy-request-uri'] = req.url
res.writeHead(200, headers)
res.end()
} else if (req.method === 'GET' && parsedUrl.pathname && parsedUrl.pathname.endsWith('/proxy/redirect')) {
let path = `/proxy${parsedUrl.search || ''}${parsedUrl.hash || ''}`
res.writeHead(303, { Location: path })
res.end()
} else {
res.writeHead(404, {})
res.end()
}
})
before(function () {
return server.start()
})
after(function () {
return server.stop()
})
it('can send a basic HTTP request', function () {
const request = new HttpRequest('GET', '/echo')
request.headers.set('Foo', 'Bar')
const agent = new http.Agent()
agent.maxSockets = 1 // Only making 1 request.
const client = new HttpClient(server.url(), agent)
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
const headers = JSON.parse(response.body)
assert.strictEqual(headers['content-length'], '0')
assert.strictEqual(headers['connection'], 'keep-alive')
assert.strictEqual(headers['host'], server.host())
const regex = /^selenium\/.* \(js (windows|mac|linux)\)$/
assert.ok(regex.test(headers['user-agent']), `${headers['user-agent']} does not match ${regex}`)
assert.strictEqual(request.headers.get('Foo'), 'Bar')
assert.strictEqual(request.headers.get('Accept'), 'application/json; charset=utf-8')
assert.strictEqual(agent.keepAlive, false)
})
})
it('can send a basic HTTP request with custom user-agent via client_options', function () {
const request = new HttpRequest('GET', '/echo')
request.headers.set('Foo', 'Bar')
const agent = new http.Agent()
agent.maxSockets = 1 // Only making 1 request.
const client = new HttpClient(server.url(), agent, null, {
'user-agent': 'test',
})
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
const headers = JSON.parse(response.body)
assert.strictEqual(headers['content-length'], '0')
assert.strictEqual(headers['connection'], 'keep-alive')
assert.strictEqual(headers['host'], server.host())
assert.strictEqual(headers['user-agent'], 'test')
assert.strictEqual(request.headers.get('Foo'), 'Bar')
assert.strictEqual(agent.keepAlive, false)
assert.strictEqual(request.headers.get('Accept'), 'application/json; charset=utf-8')
})
})
it('can send a basic HTTP request with keep-alive being set to true via client_options', function () {
const request = new HttpRequest('GET', '/echo')
request.headers.set('Foo', 'Bar')
const agent = new http.Agent()
agent.maxSockets = 1 // Only making 1 request.
const client = new HttpClient(server.url(), agent, null, {
'keep-alive': 'true',
})
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
const headers = JSON.parse(response.body)
assert.strictEqual(headers['content-length'], '0')
assert.strictEqual(headers['connection'], 'keep-alive')
assert.strictEqual(headers['host'], server.host())
const regex = /^selenium\/.* \(js (windows|mac|linux)\)$/
assert.ok(regex.test(headers['user-agent']), `${headers['user-agent']} does not match ${regex}`)
assert.strictEqual(request.headers.get('Foo'), 'Bar')
assert.strictEqual(agent.keepAlive, true)
assert.strictEqual(request.headers.get('Accept'), 'application/json; charset=utf-8')
})
})
it('handles chunked responses', function () {
let request = new HttpRequest('GET', '/chunked')
let client = new HttpClient(server.url())
return client.send(request).then((response) => {
assert.strictEqual(200, response.status)
assert.strictEqual(response.body, '<!DOCTYPE html><h1>Hello, world!</h1>')
})
})
it('can use basic auth', function () {
// eslint-disable-next-line n/no-deprecated-api
const parsed = url.parse(server.url())
parsed.auth = 'genie:bottle'
const client = new HttpClient(url.format(parsed))
const request = new HttpRequest('GET', '/protected')
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
assert.strictEqual(response.headers.get('content-type'), 'text/plain')
assert.strictEqual(response.body, 'Access granted!')
})
})
it('fails requests missing required basic auth', function () {
const client = new HttpClient(server.url())
const request = new HttpRequest('GET', '/protected')
return client.send(request).then(function (response) {
assert.strictEqual(401, response.status)
assert.strictEqual(response.body, 'Access denied')
})
})
it('automatically follows redirects', function () {
const request = new HttpRequest('GET', '/redirect')
const client = new HttpClient(server.url())
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
assert.strictEqual(response.headers.get('content-type'), 'text/plain')
assert.strictEqual(response.body, 'hello, world!')
})
})
it('handles malformed redirect responses', function () {
const request = new HttpRequest('GET', '/badredirect')
const client = new HttpClient(server.url())
return client.send(request).then(assert.fail, function (err) {
assert.ok(/Failed to parse "Location"/.test(err.message), 'Not the expected error: ' + err.message)
})
})
describe('with proxy', function () {
it('sends request to proxy with absolute URI', function () {
const request = new HttpRequest('GET', '/proxy')
const client = new HttpClient('http://another.server.com', undefined, server.url())
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
assert.strictEqual(response.headers.get('host'), 'another.server.com')
assert.strictEqual(response.headers.get('x-proxy-request-uri'), 'http://another.server.com/proxy')
})
})
it('uses proxy when following redirects', function () {
const request = new HttpRequest('GET', '/proxy/redirect')
const client = new HttpClient('http://another.server.com', undefined, server.url())
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
assert.strictEqual(response.headers.get('host'), 'another.server.com')
assert.strictEqual(response.headers.get('x-proxy-request-uri'), 'http://another.server.com/proxy')
})
})
it('includes search and hash in redirect URI', function () {
const request = new HttpRequest('GET', '/proxy/redirect?foo#bar')
const client = new HttpClient('http://another.server.com', undefined, server.url())
return client.send(request).then(function (response) {
assert.strictEqual(200, response.status)
assert.strictEqual(response.headers.get('host'), 'another.server.com')
assert.strictEqual(response.headers.get('x-proxy-request-uri'), 'http://another.server.com/proxy?foo#bar')
})
})
})
})