blob: fb78da41f02a3575e0816e73c5e0c7ef7bc888ce [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('assert')
const http = require('http')
const url = require('url')
const HttpClient = require('../../http').HttpClient
const HttpRequest = require('../../lib/http').Request
const Server = require('../../lib/test/httpserver').Server
describe('HttpClient', function () {
const server = new Server(function (req, res) {
// eslint-disable-next-line node/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 node/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'
)
})
})
})
})