mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 05:12:29 +00:00
compat(node:http) more compatibility improvements (#19063)
This commit is contained in:
156
test/js/node/test/parallel/test-http-1.0-keep-alive.js
Normal file
156
test/js/node/test/parallel/test-http-1.0-keep-alive.js
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
require('../common');
|
||||
const http = require('http');
|
||||
const net = require('net');
|
||||
|
||||
// Check that our HTTP server correctly handles HTTP/1.0 keep-alive requests.
|
||||
check([{
|
||||
name: 'keep-alive, no TE header',
|
||||
requests: [{
|
||||
expectClose: true,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'Connection: keep-alive\r\n' +
|
||||
'\r\n'
|
||||
}, {
|
||||
expectClose: true,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'Connection: keep-alive\r\n' +
|
||||
'\r\n'
|
||||
}],
|
||||
responses: [{
|
||||
headers: { 'Connection': 'keep-alive' },
|
||||
chunks: ['OK']
|
||||
}, {
|
||||
chunks: []
|
||||
}]
|
||||
}, {
|
||||
name: 'keep-alive, with TE: chunked',
|
||||
requests: [{
|
||||
expectClose: false,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'Connection: keep-alive\r\n' +
|
||||
'TE: chunked\r\n' +
|
||||
'\r\n'
|
||||
}, {
|
||||
expectClose: true,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'\r\n'
|
||||
}],
|
||||
responses: [{
|
||||
headers: { 'Connection': 'keep-alive' },
|
||||
chunks: ['OK']
|
||||
}, {
|
||||
chunks: []
|
||||
}]
|
||||
}, {
|
||||
name: 'keep-alive, with Transfer-Encoding: chunked',
|
||||
requests: [{
|
||||
expectClose: false,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'Connection: keep-alive\r\n' +
|
||||
'\r\n'
|
||||
}, {
|
||||
expectClose: true,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'\r\n'
|
||||
}],
|
||||
responses: [{
|
||||
headers: { 'Connection': 'keep-alive',
|
||||
'Transfer-Encoding': 'chunked' },
|
||||
chunks: ['OK']
|
||||
}, {
|
||||
chunks: []
|
||||
}]
|
||||
}, {
|
||||
name: 'keep-alive, with Content-Length',
|
||||
requests: [{
|
||||
expectClose: false,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'Connection: keep-alive\r\n' +
|
||||
'\r\n'
|
||||
}, {
|
||||
expectClose: true,
|
||||
data: 'POST / HTTP/1.0\r\n' +
|
||||
'\r\n'
|
||||
}],
|
||||
responses: [{
|
||||
headers: { 'Connection': 'keep-alive',
|
||||
'Content-Length': '2' },
|
||||
chunks: ['OK']
|
||||
}, {
|
||||
chunks: []
|
||||
}]
|
||||
}]);
|
||||
|
||||
function check(tests) {
|
||||
const test = tests[0];
|
||||
let server;
|
||||
if (test) {
|
||||
server = http.createServer(serverHandler).listen(0, '127.0.0.1', client);
|
||||
}
|
||||
let current = 0;
|
||||
|
||||
function next() {
|
||||
check(tests.slice(1));
|
||||
}
|
||||
|
||||
function serverHandler(req, res) {
|
||||
if (current + 1 === test.responses.length) this.close();
|
||||
const ctx = test.responses[current];
|
||||
console.error('< SERVER SENDING RESPONSE', ctx);
|
||||
res.writeHead(200, ctx.headers);
|
||||
ctx.chunks.slice(0, -1).forEach(function(chunk) { res.write(chunk); });
|
||||
res.end(ctx.chunks[ctx.chunks.length - 1]);
|
||||
}
|
||||
|
||||
function client() {
|
||||
if (current === test.requests.length) return next();
|
||||
const port = server.address().port;
|
||||
const conn = net.createConnection(port, '127.0.0.1', connected);
|
||||
|
||||
function connected() {
|
||||
const ctx = test.requests[current];
|
||||
console.error(' > CLIENT SENDING REQUEST', ctx);
|
||||
conn.setEncoding('utf8');
|
||||
conn.write(ctx.data);
|
||||
|
||||
function onclose() {
|
||||
console.error(' > CLIENT CLOSE');
|
||||
if (!ctx.expectClose) throw new Error('unexpected close');
|
||||
client();
|
||||
}
|
||||
conn.on('close', onclose);
|
||||
|
||||
function ondata(s) {
|
||||
console.error(' > CLIENT ONDATA %j %j', s.length, s.toString());
|
||||
current++;
|
||||
if (ctx.expectClose) return;
|
||||
conn.removeListener('close', onclose);
|
||||
conn.removeListener('data', ondata);
|
||||
connected();
|
||||
}
|
||||
conn.on('data', ondata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const ClientRequest = require('http').ClientRequest;
|
||||
|
||||
{
|
||||
assert.throws(() => {
|
||||
new ClientRequest({ insecureHTTPParser: 'wrongValue' });
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: /insecureHTTPParser/
|
||||
}, 'http request should throw when passing invalid insecureHTTPParser');
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
|
||||
const server = http.createServer();
|
||||
|
||||
server.on('request', function(req, res) {
|
||||
res.writeHead(200, { 'foo': 'bar' });
|
||||
res.flushHeaders();
|
||||
res.flushHeaders(); // Should be idempotent.
|
||||
});
|
||||
server.listen(0, common.localhostIPv4, function() {
|
||||
const req = http.request({
|
||||
method: 'GET',
|
||||
host: common.localhostIPv4,
|
||||
port: this.address().port,
|
||||
}, onResponse);
|
||||
|
||||
req.end();
|
||||
|
||||
function onResponse(res) {
|
||||
assert.strictEqual(res.headers.foo, 'bar');
|
||||
res.destroy();
|
||||
server.closeAllConnections();
|
||||
}
|
||||
});
|
||||
64
test/js/node/test/parallel/test-http-request-methods.js
Normal file
64
test/js/node/test/parallel/test-http-request-methods.js
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
const http = require('http');
|
||||
|
||||
// Test that the DELETE, PATCH and PURGE verbs get passed through correctly
|
||||
|
||||
['DELETE', 'PATCH', 'PURGE'].forEach(function(method, index) {
|
||||
const server = http.createServer(common.mustCall(function(req, res) {
|
||||
assert.strictEqual(req.method, method);
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('hello ');
|
||||
res.write('world\n');
|
||||
res.end();
|
||||
}));
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(function() {
|
||||
const c = net.createConnection(this.address().port);
|
||||
let server_response = '';
|
||||
|
||||
c.setEncoding('utf8');
|
||||
|
||||
c.on('connect', function() {
|
||||
c.write(`${method} / HTTP/1.0\r\n\r\n`);
|
||||
});
|
||||
|
||||
c.on('data', function(chunk) {
|
||||
server_response += chunk;
|
||||
});
|
||||
|
||||
c.on('end', common.mustCall(function() {
|
||||
const m = server_response.split('\r\n\r\n');
|
||||
assert.strictEqual(m[1], 'hello world\n');
|
||||
c.end();
|
||||
}));
|
||||
|
||||
c.on('close', function() {
|
||||
server.close();
|
||||
});
|
||||
}));
|
||||
});
|
||||
27
test/js/node/test/parallel/test-http-server-method.query.js
Normal file
27
test/js/node/test/parallel/test-http-server-method.query.js
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const { strictEqual } = require('assert');
|
||||
const { createServer, request } = require('http');
|
||||
|
||||
const server = createServer(common.mustCall((req, res) => {
|
||||
strictEqual(req.method, 'QUERY');
|
||||
res.end('OK');
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const req = request({ port: server.address().port, method: 'QUERY' }, common.mustCall((res) => {
|
||||
strictEqual(res.statusCode, 200);
|
||||
|
||||
let buffer = '';
|
||||
res.setEncoding('utf-8');
|
||||
|
||||
res.on('data', (c) => buffer += c);
|
||||
res.on('end', common.mustCall(() => {
|
||||
strictEqual(buffer, 'OK');
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
|
||||
req.end();
|
||||
}));
|
||||
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const { createServer } = require('http');
|
||||
|
||||
// This test validates that the HTTP server timeouts are properly validated and set.
|
||||
|
||||
{
|
||||
const server = createServer();
|
||||
assert.strictEqual(server.headersTimeout, 60000);
|
||||
assert.strictEqual(server.requestTimeout, 300000);
|
||||
}
|
||||
|
||||
{
|
||||
const server = createServer({ headersTimeout: 10000, requestTimeout: 20000 });
|
||||
assert.strictEqual(server.headersTimeout, 10000);
|
||||
assert.strictEqual(server.requestTimeout, 20000);
|
||||
}
|
||||
|
||||
{
|
||||
const server = createServer({ headersTimeout: 10000, requestTimeout: 10000 });
|
||||
assert.strictEqual(server.headersTimeout, 10000);
|
||||
assert.strictEqual(server.requestTimeout, 10000);
|
||||
}
|
||||
|
||||
{
|
||||
const server = createServer({ headersTimeout: 10000 });
|
||||
assert.strictEqual(server.headersTimeout, 10000);
|
||||
assert.strictEqual(server.requestTimeout, 300000);
|
||||
}
|
||||
|
||||
{
|
||||
const server = createServer({ requestTimeout: 20000 });
|
||||
assert.strictEqual(server.headersTimeout, 20000);
|
||||
assert.strictEqual(server.requestTimeout, 20000);
|
||||
}
|
||||
|
||||
{
|
||||
const server = createServer({ requestTimeout: 100000 });
|
||||
assert.strictEqual(server.headersTimeout, 60000);
|
||||
assert.strictEqual(server.requestTimeout, 100000);
|
||||
}
|
||||
|
||||
{
|
||||
assert.throws(
|
||||
() => createServer({ headersTimeout: 10000, requestTimeout: 1000 }),
|
||||
{ code: 'ERR_OUT_OF_RANGE' }
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const { createServer } = require('https');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const options = {
|
||||
key: fixtures.readKey('agent1-key.pem'),
|
||||
cert: fixtures.readKey('agent1-cert.pem'),
|
||||
};
|
||||
|
||||
const server = createServer(options);
|
||||
|
||||
// 60000 seconds is the default
|
||||
assert.strictEqual(server.headersTimeout, 60000);
|
||||
const headersTimeout = common.platformTimeout(1000);
|
||||
server.headersTimeout = headersTimeout;
|
||||
assert.strictEqual(server.headersTimeout, headersTimeout);
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
const assert = require('assert');
|
||||
const { createServer } = require('https');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const options = {
|
||||
key: fixtures.readKey('agent1-key.pem'),
|
||||
cert: fixtures.readKey('agent1-cert.pem')
|
||||
};
|
||||
|
||||
const server = createServer(options);
|
||||
|
||||
// 300 seconds is the default
|
||||
assert.strictEqual(server.requestTimeout, 300000);
|
||||
const requestTimeout = common.platformTimeout(1000);
|
||||
server.requestTimeout = requestTimeout;
|
||||
assert.strictEqual(server.requestTimeout, requestTimeout);
|
||||
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
|
||||
// This test assesses whether long-running writes can complete
|
||||
// or timeout because the socket is not aware that the backing
|
||||
// stream is still writing.
|
||||
|
||||
const writeSize = 3000000;
|
||||
let socket;
|
||||
|
||||
const server = http.createServer(common.mustCall((req, res) => {
|
||||
server.close();
|
||||
const content = Buffer.alloc(writeSize, 0x44);
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': content.length.toString(),
|
||||
'Vary': 'Accept-Encoding'
|
||||
});
|
||||
|
||||
socket = res.socket;
|
||||
const onTimeout = socket._onTimeout;
|
||||
socket._onTimeout = common.mustCallAtLeast(() => onTimeout.call(socket), 1);
|
||||
res.write(content);
|
||||
res.end();
|
||||
}));
|
||||
server.on('timeout', () => {
|
||||
// TODO(apapirovski): This test is faulty on certain Windows systems
|
||||
// as no queue is ever created
|
||||
assert(!socket._handle || socket._handle.writeQueueSize === 0,
|
||||
'Should not timeout');
|
||||
});
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
http.get({
|
||||
path: '/',
|
||||
port: server.address().port
|
||||
}, (res) => {
|
||||
res.once('data', () => {
|
||||
socket._onTimeout();
|
||||
res.on('data', () => {});
|
||||
});
|
||||
res.on('end', () => server.close());
|
||||
});
|
||||
}));
|
||||
Reference in New Issue
Block a user