From 1f659d4da0766ab0f59745e679401a77cfb9452b Mon Sep 17 00:00:00 2001 From: Austin Wright Date: Thu, 25 Jun 2020 23:15:45 -0700 Subject: [PATCH] test: verify request payload is uploaded consistently Node.js seems to change how it is uploaded based on the method, but HTTP doesn't make any distinction. Co-authored-by: Austin Wright Co-authored-by: Lenvin Gonsalves Co-authored-by: Antoine du Hamel --- ...st-http-clientrequest-end-contentlength.js | 51 ++++++++++++++++++ ...p-clientrequest-end-empty-response-body.js | 52 +++++++++++++++++++ .../test-http-clientrequest-write-chunked.js | 52 +++++++++++++++++++ ...test-http-request-method-delete-payload.js | 30 +++++++++++ 4 files changed, 185 insertions(+) create mode 100644 test/known_issues/test-http-clientrequest-end-contentlength.js create mode 100644 test/known_issues/test-http-clientrequest-end-empty-response-body.js create mode 100644 test/known_issues/test-http-clientrequest-write-chunked.js create mode 100644 test/parallel/test-http-request-method-delete-payload.js diff --git a/test/known_issues/test-http-clientrequest-end-contentlength.js b/test/known_issues/test-http-clientrequest-end-contentlength.js new file mode 100644 index 00000000000000..9d5370ad07a303 --- /dev/null +++ b/test/known_issues/test-http-clientrequest-end-contentlength.js @@ -0,0 +1,51 @@ +'use strict'; + +// Refs: https://github.com/nodejs/node/pull/34066 + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Test that ClientRequest#end with default options +// computes and sends a Content-Length header +const upload = 'PUT / HTTP/1.1\r\n\r\n'; +const response = 'content-length: 19\r\n'; + +// Test that the upload is properly received with the same headers, +// regardless of request method +const methods = [ 'GET', 'HEAD', 'DELETE', 'POST', 'PATCH', 'PUT', 'OPTIONS' ]; + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('data', function(chunk) { + assert.strictEqual(chunk, Buffer.from(upload)); + }); + res.setHeader('Content-Type', 'text/plain'); + let payload = `${req.method}\r\n`; + for (let i = 0; i < req.rawHeaders.length; i += 2) { + // Ignore a couple headers that may vary + if (req.rawHeaders[i].toLowerCase() === 'host') continue; + if (req.rawHeaders[i].toLowerCase() === 'connection') continue; + payload += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`; + } + res.end(payload); +}), methods.length); + +server.listen(0, function tryNextRequest() { + const method = methods.pop(); + if (method === undefined) return; + const port = server.address().port; + const req = http.request({ method, port }, function(res) { + const chunks = []; + res.on('data', function(chunk) { + chunks.push(chunk); + }); + res.on('end', function() { + const received = Buffer.concat(chunks).toString(); + const expected = method.toLowerCase() + '\r\n' + response; + assert.strictEqual(received.toLowerCase(), expected); + tryNextRequest(); + }); + }); + + req.end(upload); +}).unref(); diff --git a/test/known_issues/test-http-clientrequest-end-empty-response-body.js b/test/known_issues/test-http-clientrequest-end-empty-response-body.js new file mode 100644 index 00000000000000..2edbe239a3cae2 --- /dev/null +++ b/test/known_issues/test-http-clientrequest-end-empty-response-body.js @@ -0,0 +1,52 @@ +'use strict'; + +// Refs: https://github.com/nodejs/node/pull/34066 + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Test that ClientRequest#end with default options +// and empty payload sends neither Content-Length nor Transfer-Encoding. +// Sending Content-Length: 0 would be acceptable, but is unnecessary. +const upload = 'PUT / HTTP/1.1\r\n\r\n'; +const response = ''; + +// Test that the upload is properly received with the same headers, +// regardless of request method. +const methods = [ 'GET', 'HEAD', 'DELETE', 'POST', 'PATCH', 'PUT', 'OPTIONS' ]; + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('data', function(chunk) { + assert.strictEqual(chunk.toString(), upload); + }); + res.setHeader('Content-Type', 'text/plain'); + res.write(`${req.method}\r\n`); + for (let i = 0; i < req.rawHeaders.length; i += 2) { + // Ignore a couple headers that may vary + if (req.rawHeaders[i].toLowerCase() === 'host') continue; + if (req.rawHeaders[i].toLowerCase() === 'connection') continue; + res.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`); + } + res.end(); +}), methods.length); + +server.listen(0, function tryNextRequest() { + const method = methods.pop(); + if (method === undefined) return; + const port = server.address().port; + const req = http.request({ method, port }, function(res) { + const chunks = []; + res.on('data', function(chunk) { + chunks.push(chunk); + }); + res.on('end', function() { + const received = Buffer.concat(chunks).toString(); + const expected = method.toLowerCase() + '\r\n' + response; + assert.strictEqual(received.toLowerCase(), expected); + tryNextRequest(); + }); + }); + + req.end(); +}).unref(); diff --git a/test/known_issues/test-http-clientrequest-write-chunked.js b/test/known_issues/test-http-clientrequest-write-chunked.js new file mode 100644 index 00000000000000..2cf63a8be85297 --- /dev/null +++ b/test/known_issues/test-http-clientrequest-write-chunked.js @@ -0,0 +1,52 @@ +'use strict'; + +// Refs: https://github.com/nodejs/node/pull/34066 + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Test that ClientRequest#write with default options +// uses a chunked Transfer-Encoding +const upload = 'PUT / HTTP/1.1\r\n\r\n'; +const response = 'transfer-encoding: chunked\r\n'; + +// Test that the upload is properly received with the same headers, +// regardless of request method. +const methods = [ 'GET', 'HEAD', 'DELETE', 'POST', 'PATCH', 'PUT', 'OPTIONS' ]; + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('data', function(chunk) { + assert.strictEqual(chunk.toString(), upload); + }); + res.setHeader('Content-Type', 'text/plain'); + res.write(`${req.method}\r\n`); + for (let i = 0; i < req.rawHeaders.length; i += 2) { + // Ignore a couple headers that may vary + if (req.rawHeaders[i].toLowerCase() === 'host') continue; + if (req.rawHeaders[i].toLowerCase() === 'connection') continue; + res.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`); + } + res.end(); +}), methods.length); + +server.listen(0, function tryNextRequest() { + const method = methods.pop(); + if (method === undefined) return; + const port = server.address().port; + const req = http.request({ method, port }, function(res) { + const chunks = []; + res.on('data', function(chunk) { + chunks.push(chunk); + }); + res.on('end', function() { + const received = Buffer.concat(chunks).toString(); + const expected = method.toLowerCase() + '\r\n' + response; + assert.strictEqual(received.toLowerCase(), expected); + tryNextRequest(); + }); + }); + + req.write(upload); + req.end(); +}).unref(); diff --git a/test/parallel/test-http-request-method-delete-payload.js b/test/parallel/test-http-request-method-delete-payload.js new file mode 100644 index 00000000000000..03728846df69d6 --- /dev/null +++ b/test/parallel/test-http-request-method-delete-payload.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const http = require('http'); + +const data = 'PUT / HTTP/1.1\r\n\r\n'; + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('data', function(chunk) { + assert.strictEqual(chunk, Buffer.from(data)); + }); + res.setHeader('Content-Type', 'text/plain'); + for (let i = 0; i < req.rawHeaders.length; i += 2) { + if (req.rawHeaders[i].toLowerCase() === 'host') continue; + if (req.rawHeaders[i].toLowerCase() === 'connection') continue; + res.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`); + } + res.end(); +})).unref(); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const req = http.request({ method: 'DELETE', port }, function(res) { + res.resume(); + }); + + req.write(data); + req.end(); +}));