Skip to content

Commit

Permalink
Add defined behavior for HTTP Upgrade requests
Browse files Browse the repository at this point in the history
closes #12
  • Loading branch information
dougwilson committed May 26, 2015
1 parent 962bbe9 commit 2099462
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 0 deletions.
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ unreleased
==========

* Add defined behavior for HTTP `CONNECT` requests
* Add defined behavior for HTTP `Upgrade` requests
* deps: [email protected]

2.2.1 / 2015-04-22
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ the request will be considered "finished" even before it has been read.
There is no such thing as a response object to a `CONNECT` request in
Node.js, so there is no support for for one.

### HTTP Upgrade request

The meaning of the `Upgrade` header from RFC 7230, section 6.1:

> The "Upgrade" header field is intended to provide a simple mechanism
> for transitioning from HTTP/1.1 to some other protocol on the same
> connection.
In Node.js, these request objects come from the `'upgrade'` event on
the HTTP server.

When this module is used on a HTTP request with an `Upgrade` header, the
request is considered "finished" immediately, **due to limitations in the
Node.js interface**. This means if the `Upgrade` request contains a request
entity, the request will be considered "finished" even before it has been
read.

There is no such thing as a response object to a `Upgrade` request in
Node.js, so there is no support for for one.

## Example

The following code ensures that file descriptors are always closed
Expand Down
203 changes: 203 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,111 @@ describe('onFinished(req, listener)', function () {
})
})
})

describe('when Upgrade request', function () {
it('should fire when request finishes', function (done) {
var client
var server = http.createServer(function (req, res) {
res.statusCode = 405
res.end()
})
server.on('upgrade', function (req, socket, bodyHead) {
var data = [bodyHead]

onFinished(req, function (err) {
assert.ifError(err)
assert.equal(Buffer.concat(data).toString(), 'knock, knock')

socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'ping')
socket.end('pong')
})
socket.write('HTTP/1.1 101 Switching Protocols\r\n')
socket.write('Connection: Upgrade\r\n')
socket.write('Upgrade: Raw\r\n')
socket.write('\r\n')
})

req.on('data', function (chunk) {
data.push(chunk)
})
})

server.listen(function () {
client = http.request({
headers: {
'Connection': 'Upgrade',
'Upgrade': 'Raw'
},
hostname: '127.0.0.1',
port: this.address().port
})

client.on('upgrade', function (res, socket, bodyHead) {
socket.write('ping')
socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'pong')
socket.end()
server.close(done)
})
})
client.end('knock, knock')
})
})

it('should fire when called after finish', function (done) {
var client
var server = http.createServer(function (req, res) {
res.statusCode = 405
res.end()
})
server.on('upgrade', function (req, socket, bodyHead) {
var data = [bodyHead]

onFinished(req, function (err) {
assert.ifError(err)
assert.equal(Buffer.concat(data).toString(), 'knock, knock')

socket.write('HTTP/1.1 101 Switching Protocols\r\n')
socket.write('Connection: Upgrade\r\n')
socket.write('Upgrade: Raw\r\n')
socket.write('\r\n')
})

socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'ping')
onFinished(req, function (err) {
socket.end('pong')
})
})

req.on('data', function (chunk) {
data.push(chunk)
})
})

server.listen(function () {
client = http.request({
headers: {
'Connection': 'Upgrade',
'Upgrade': 'Raw'
},
hostname: '127.0.0.1',
port: this.address().port
})

client.on('upgrade', function (res, socket, bodyHead) {
socket.write('ping')
socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'pong')
socket.end()
server.close(done)
})
})
client.end('knock, knock')
})
})
})
})

describe('isFinished(req)', function () {
Expand Down Expand Up @@ -787,6 +892,104 @@ describe('isFinished(req)', function () {
})
})
})

describe('when Upgrade request', function () {
it('should be true immediately', function (done) {
var client
var server = http.createServer(function (req, res) {
res.statusCode = 405
res.end()
})

server.on('upgrade', function (req, socket, bodyHead) {
assert.ok(onFinished.isFinished(req))
assert.equal(bodyHead.length, 0)
req.resume()

socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'ping')
socket.end('pong')
})
socket.write('HTTP/1.1 101 Switching Protocols\r\n')
socket.write('Connection: Upgrade\r\n')
socket.write('Upgrade: Raw\r\n')
socket.write('\r\n')
})

server.listen(function () {
client = http.request({
headers: {
'Connection': 'Upgrade',
'Upgrade': 'Raw'
},
hostname: '127.0.0.1',
port: this.address().port
})

client.on('upgrade', function (res, socket, bodyHead) {
socket.write('ping')
socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'pong')
socket.end()
server.close(done)
})
})
client.end()
})
})

it('should be true after request finishes', function (done) {
var client
var server = http.createServer(function (req, res) {
res.statusCode = 405
res.end()
})
server.on('upgrade', function (req, socket, bodyHead) {
var data = [bodyHead]

onFinished(req, function (err) {
assert.ifError(err)
assert.ok(onFinished.isFinished(req))
assert.equal(Buffer.concat(data).toString(), 'knock, knock')

socket.write('HTTP/1.1 101 Switching Protocols\r\n')
socket.write('Connection: Upgrade\r\n')
socket.write('Upgrade: Raw\r\n')
socket.write('\r\n')
})

socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'ping')
socket.end('pong')
})

req.on('data', function (chunk) {
data.push(chunk)
})
})

server.listen(function () {
client = http.request({
headers: {
'Connection': 'Upgrade',
'Upgrade': 'Raw'
},
hostname: '127.0.0.1',
port: this.address().port
})

client.on('upgrade', function (res, socket, bodyHead) {
socket.write('ping')
socket.on('data', function (chunk) {
assert.equal(chunk.toString(), 'pong')
socket.end()
server.close(done)
})
})
client.end('knock, knock')
})
})
})
})

function captureStderr(fn) {
Expand Down

0 comments on commit 2099462

Please sign in to comment.