Skip to content

Commit

Permalink
Merge pull request #69 from afharo/handle-malformed-head-responses
Browse files Browse the repository at this point in the history
  • Loading branch information
afharo authored Aug 10, 2023
2 parents fb841e0 + 4a0ca7b commit dafdc56
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/connection/HttpConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const debug = Debug('elasticsearch')
const INVALID_PATH_REGEX = /[^\u0021-\u00ff]/
const MAX_BUFFER_LENGTH = buffer.constants.MAX_LENGTH
const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH
const noop = (): void => {}

export default class HttpConnection extends BaseConnection {
agent?: http.Agent | https.Agent | hpagent.HttpProxyAgent | hpagent.HttpsProxyAgent
Expand Down Expand Up @@ -128,6 +129,7 @@ export default class HttpConnection extends BaseConnection {

const onResponse = (response: http.IncomingMessage): void => {
cleanListeners()
request.on('error', noop) // There are some edge cases where the request emits an error while processing the response.
this._openRequests--

if (options.asStream === true) {
Expand Down Expand Up @@ -193,6 +195,7 @@ export default class HttpConnection extends BaseConnection {
response.removeListener('end', onEnd)
response.removeListener('error', onEnd)
response.removeListener('aborted', onAbort)
request.removeListener('error', noop)

if (err != null) {
if (err.name === 'RequestAbortedError') {
Expand Down
48 changes: 48 additions & 0 deletions test/unit/http-connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import intoStream from 'into-stream'
import { AbortController } from 'node-abort-controller'
import { buildServer } from '../utils'
import { HttpConnection, errors, ConnectionOptions } from '../../'
import net from "net";

const {
TimeoutError,
Expand Down Expand Up @@ -1268,3 +1269,50 @@ test('Cleanup abort listener', async t => {
t.equal(controller.signal.eventEmitter.listeners('abort').length, 0)
server.stop()
})

test('Handles malformed HTML responses (HEAD response with body)', async t => {
t.plan(2)

// Creating a custom TCP server because `http.createServer` handles
// the method accordingly and skips sending the body if the request is HEAD
function createTcpServer() {
const server = net.createServer();
server.on('connection', (socket) => {
socket.write(`HTTP/1.1 200 OK\r\n`)
socket.write(`Content-Type: text/html\r\n`)
socket.write(`Content-Length: 155\r\n`)
socket.write(`\r\n`)
socket.write(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Hi there</h1>
<p>This is a bad implementation of an HTTP server</p></body>
</html>`)

socket.end()
})
return new Promise<{port: number, server: net.Server}>((resolve) => server.listen(0, () => {
const port = (server.address() as net.AddressInfo).port
resolve({port, server})
}));
}

const {port, server} = await createTcpServer()
const connection = new HttpConnection({
url: new URL(`http://localhost:${port}`)
})

const res = await connection.request({
path: '/hello',
method: 'HEAD',
headers: {
'X-Custom-Test': 'true'
}
}, options)
t.match(res.headers, { 'content-length': 155 })
t.equal(res.body, '')
server.close()
})

0 comments on commit dafdc56

Please sign in to comment.