Skip to content

Commit

Permalink
test: verify request payload is uploaded consistently
Browse files Browse the repository at this point in the history
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 <aaa@bzfx.net>
Co-authored-by: Lenvin Gonsalves <lenvingonsalves@gmail.com>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
PR-URL: nodejs#34066
Refs: nodejs#27880
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
3 people authored and bmeck committed Jun 22, 2024
1 parent f55d83a commit 3f17bd2
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
51 changes: 51 additions & 0 deletions test/known_issues/test-http-clientrequest-end-contentlength.js
Original file line number Diff line number Diff line change
@@ -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();
Original file line number Diff line number Diff line change
@@ -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();
52 changes: 52 additions & 0 deletions test/known_issues/test-http-clientrequest-write-chunked.js
Original file line number Diff line number Diff line change
@@ -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();
30 changes: 30 additions & 0 deletions test/parallel/test-http-request-method-delete-payload.js
Original file line number Diff line number Diff line change
@@ -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();
}));

0 comments on commit 3f17bd2

Please sign in to comment.