bare-http1
Advanced tools
32
index.js
exports.IncomingMessage = require('./lib/incoming-message') | ||
exports.OutgoingMessage = require('./lib/outgoing-message') | ||
const Agent = exports.Agent = require('./lib/agent') | ||
exports.globalAgent = Agent.global | ||
exports.Agent = require('./lib/agent') | ||
exports.globalAgent = exports.Agent.global | ||
const Server = exports.Server = require('./lib/server') | ||
exports.Server = require('./lib/server') | ||
exports.ServerResponse = require('./lib/server-response') | ||
exports.ServerConnection = require('./lib/server-connection') | ||
const Request = exports.ClientRequest = require('./lib/client-request') | ||
exports.ClientRequest = require('./lib/client-request') | ||
exports.ClientConnection = require('./lib/client-connection') | ||
@@ -18,7 +18,7 @@ | ||
exports.createServer = function createServer (opts, onrequest) { | ||
return new Server(opts, onrequest) | ||
exports.createServer = function createServer(opts, onrequest) { | ||
return new exports.Server(opts, onrequest) | ||
} | ||
exports.request = function request (url, opts, onresponse) { | ||
exports.request = function request(url, opts, onresponse) { | ||
if (typeof opts === 'function') { | ||
@@ -42,16 +42,20 @@ onresponse = opts | ||
opts.host = opts.hostname || opts.host | ||
opts.port = typeof opts.port === 'string' ? parseInt(opts.port, 10) : opts.port | ||
opts.port = | ||
typeof opts.port === 'string' ? parseInt(opts.port, 10) : opts.port | ||
} | ||
return new Request(opts, onresponse) | ||
return new exports.ClientRequest(opts, onresponse) | ||
} | ||
// https://url.spec.whatwg.org/#default-port | ||
function defaultPort (url) { | ||
function defaultPort(url) { | ||
switch (url.protocol) { | ||
case 'ftp:': return 21 | ||
case 'ftp:': | ||
return 21 | ||
case 'http:': | ||
case 'ws:': return 80 | ||
case 'ws:': | ||
return 80 | ||
case 'https:': | ||
case 'wss:': return 443 | ||
case 'wss:': | ||
return 443 | ||
} | ||
@@ -63,3 +67,3 @@ | ||
// https://url.spec.whatwg.org/#api | ||
function isURL (url) { | ||
function isURL(url) { | ||
return ( | ||
@@ -66,0 +70,0 @@ url !== null && |
@@ -5,7 +5,4 @@ const tcp = require('bare-tcp') | ||
module.exports = class HTTPAgent { | ||
constructor (opts = {}) { | ||
const { | ||
keepAlive = false, | ||
keepAliveMsecs = 1000 | ||
} = opts | ||
constructor(opts = {}) { | ||
const { keepAlive = false, keepAliveMsecs = 1000 } = opts | ||
@@ -15,3 +12,8 @@ this._sockets = new Map() | ||
this._keepAlive = typeof keepAlive === 'number' ? keepAlive : keepAlive ? keepAliveMsecs : -1 | ||
this._keepAlive = | ||
typeof keepAlive === 'number' | ||
? keepAlive | ||
: keepAlive | ||
? keepAliveMsecs | ||
: -1 | ||
@@ -21,11 +23,11 @@ this._opts = { ...opts } | ||
createConnection (opts) { | ||
createConnection(opts) { | ||
return tcp.createConnection(opts) | ||
} | ||
reuseSocket (socket, req) { | ||
reuseSocket(socket, req) { | ||
socket.ref() | ||
} | ||
keepSocketAlive (socket) { | ||
keepSocketAlive(socket) { | ||
if (this._keepAlive === -1) return false | ||
@@ -39,7 +41,7 @@ | ||
getName (opts) { | ||
getName(opts) { | ||
return `${opts.host}:${opts.port}` | ||
} | ||
addRequest (req, opts) { | ||
addRequest(req, opts) { | ||
opts = { ...opts, ...this._opts } | ||
@@ -82,3 +84,3 @@ | ||
destroy () { | ||
destroy() { | ||
for (const set of [this._sockets, this._freeSockets]) { | ||
@@ -91,3 +93,3 @@ for (const [, sockets] of set) { | ||
_onfree (socket, name) { | ||
_onfree(socket, name) { | ||
if (this.keepSocketAlive(socket)) { | ||
@@ -108,4 +110,6 @@ this._onremove(socket, name, false) | ||
_onremove (socket, name, all = true) { | ||
for (const set of all ? [this._sockets, this._freeSockets] : [this._sockets]) { | ||
_onremove(socket, name, all = true) { | ||
for (const set of all | ||
? [this._sockets, this._freeSockets] | ||
: [this._sockets]) { | ||
const sockets = set.get(name) | ||
@@ -119,3 +123,3 @@ if (sockets === undefined) continue | ||
_ontimeout (socket, name) { | ||
_ontimeout(socket, name) { | ||
const sockets = this._freeSockets.get(name) | ||
@@ -122,0 +126,0 @@ if (!sockets) return |
@@ -10,14 +10,12 @@ const HTTPIncomingMessage = require('./incoming-message') | ||
static for (socket) { | ||
static for(socket) { | ||
return this._connections.get(socket) || null | ||
} | ||
static from (socket, opts) { | ||
static from(socket, opts) { | ||
return this.for(socket) || new this(socket, opts) | ||
} | ||
constructor (socket, opts = {}) { | ||
const { | ||
IncomingMessage = HTTPIncomingMessage | ||
} = opts | ||
constructor(socket, opts = {}) { | ||
const { IncomingMessage = HTTPIncomingMessage } = opts | ||
@@ -55,19 +53,19 @@ this.socket = socket | ||
get idle () { | ||
get idle() { | ||
return this._idle | ||
} | ||
_onerror (err) { | ||
_onerror(err) { | ||
if (this.req) this.req.destroy(err) | ||
} | ||
_onclose () { | ||
_onclose() { | ||
if (this.req) this.req._continueFinal() | ||
} | ||
_onend () { | ||
_onend() { | ||
if (this.req) this.req.destroy(errors.CONNECTION_LOST()) | ||
} | ||
_ondata (data) { | ||
_ondata(data) { | ||
this._idle = false | ||
@@ -95,3 +93,6 @@ | ||
const head = this._buffer.subarray(0, i - 1) | ||
this._buffer = i + 1 === this._buffer.byteLength ? null : this._buffer.subarray(i + 1) | ||
this._buffer = | ||
i + 1 === this._buffer.byteLength | ||
? null | ||
: this._buffer.subarray(i + 1) | ||
i = 0 | ||
@@ -110,3 +111,6 @@ hits = 0 | ||
this._buffer = i + 1 === this._buffer.byteLength ? null : this._buffer.subarray(i + 1) | ||
this._buffer = | ||
i + 1 === this._buffer.byteLength | ||
? null | ||
: this._buffer.subarray(i + 1) | ||
i = 0 | ||
@@ -123,3 +127,6 @@ hits = 0 | ||
const head = this._buffer.subarray(0, i - 3) | ||
this._buffer = i + 1 === this._buffer.byteLength ? null : this._buffer.subarray(i + 1) | ||
this._buffer = | ||
i + 1 === this._buffer.byteLength | ||
? null | ||
: this._buffer.subarray(i + 1) | ||
i = 0 | ||
@@ -137,3 +144,3 @@ hits = 0 | ||
_onhead (data) { | ||
_onhead(data) { | ||
this._state = constants.state.IN_HEAD | ||
@@ -154,7 +161,15 @@ | ||
this.req.on('close', () => { this.req = null }) | ||
this.req.on('close', () => { | ||
this.req = null | ||
}) | ||
this.res = new this._IncomingMessage(this.socket, headers, { statusCode: parseInt(statusCode, 10), statusMessage: statusMessage.join(' ') }) | ||
this.res = new this._IncomingMessage(this.socket, headers, { | ||
statusCode: parseInt(statusCode, 10), | ||
statusMessage: statusMessage.join(' ') | ||
}) | ||
this.res.on('close', () => { this.res = null; this._onreset() }) | ||
this.res.on('close', () => { | ||
this.res = null | ||
this._onreset() | ||
}) | ||
@@ -186,3 +201,3 @@ if (headers.connection && headers.connection.toLowerCase() === 'upgrade') { | ||
_onchunklength (data) { | ||
_onchunklength(data) { | ||
this._length = parseInt(data.toString(), 16) | ||
@@ -194,3 +209,3 @@ | ||
_onchunk (data) { | ||
_onchunk(data) { | ||
this._read += data.byteLength | ||
@@ -203,3 +218,3 @@ | ||
_onbody (data) { | ||
_onbody(data) { | ||
this._read += data.byteLength | ||
@@ -212,3 +227,3 @@ | ||
_onupgrade (head) { | ||
_onupgrade(head) { | ||
this._ondetach() | ||
@@ -226,7 +241,7 @@ | ||
_ontimeout () { | ||
_ontimeout() { | ||
if (this.req) this.req.emit('timeout') | ||
} | ||
_onfinished () { | ||
_onfinished() { | ||
if (this.res) this.res.push(null) | ||
@@ -236,3 +251,3 @@ if (this.req) this.req._continueFinal() | ||
_onreset () { | ||
_onreset() { | ||
this._state = constants.state.BEFORE_HEAD | ||
@@ -247,7 +262,7 @@ this._length = -1 | ||
_ondrain () { | ||
_ondrain() { | ||
if (this.req) this.req._continueWrite() | ||
} | ||
_ondetach () { | ||
_ondetach() { | ||
this.socket | ||
@@ -254,0 +269,0 @@ .off('error', this._onerror) |
@@ -5,3 +5,3 @@ const HTTPAgent = require('./agent') | ||
module.exports = class HTTPClientRequest extends HTTPOutgoingMessage { | ||
constructor (opts = {}, onresponse = null) { | ||
constructor(opts = {}, onresponse = null) { | ||
if (typeof opts === 'function') { | ||
@@ -14,7 +14,8 @@ onresponse = opts | ||
const agent = opts.agent === false ? new HTTPAgent() : opts.agent || HTTPAgent.global | ||
const agent = | ||
opts.agent === false ? new HTTPAgent() : opts.agent || HTTPAgent.global | ||
const method = opts.method || 'GET' | ||
const path = opts.path || '/' | ||
const host = opts.host = opts.host || 'localhost' | ||
const port = opts.port = opts.port || 80 | ||
const host = (opts.host = opts.host || 'localhost') | ||
const port = (opts.port = opts.port || 80) | ||
@@ -36,3 +37,3 @@ super() | ||
_header () { | ||
_header() { | ||
let h = `${this.method} ${this.path} HTTP/1.1\r\n` | ||
@@ -47,3 +48,4 @@ | ||
if (n === 'content-length') this._chunked = false | ||
if (n === 'connection' && v && v.toLowerCase() === 'upgrade') upgrade = true | ||
if (n === 'connection' && v && v.toLowerCase() === 'upgrade') | ||
upgrade = true | ||
@@ -62,3 +64,3 @@ h += `${httpCase(n)}: ${v}\r\n` | ||
_write (data, encoding, cb) { | ||
_write(data, encoding, cb) { | ||
if (this.headersSent === false) this.flushHeaders() | ||
@@ -78,3 +80,3 @@ | ||
_final (cb) { | ||
_final(cb) { | ||
if (this.headersSent === false) this.flushHeaders() | ||
@@ -87,3 +89,3 @@ | ||
_predestroy () { | ||
_predestroy() { | ||
if (this.upgrade) return this._continueFinal() | ||
@@ -94,3 +96,3 @@ | ||
_continueWrite () { | ||
_continueWrite() { | ||
if (this._pendingWrite === null) return | ||
@@ -102,3 +104,3 @@ const cb = this._pendingWrite | ||
_continueFinal () { | ||
_continueFinal() { | ||
if (this._pendingFinal === null) return | ||
@@ -111,3 +113,3 @@ const cb = this._pendingFinal | ||
function httpCase (n) { | ||
function httpCase(n) { | ||
let s = '' | ||
@@ -114,0 +116,0 @@ for (const part of n.split('-')) { |
module.exports = class HTTPError extends Error { | ||
constructor (msg, code, fn = HTTPError) { | ||
constructor(msg, code, fn = HTTPError) { | ||
super(`${code}: ${msg}`) | ||
@@ -11,13 +11,13 @@ this.code = code | ||
get name () { | ||
get name() { | ||
return 'HTTPError' | ||
} | ||
static NOT_IMPLEMENTED (msg = 'Method not implemented') { | ||
static NOT_IMPLEMENTED(msg = 'Method not implemented') { | ||
return new HTTPError(msg, 'NOT_IMPLEMENTED', HTTPError.NOT_IMPLEMENTED) | ||
} | ||
static CONNECTION_LOST (msg = 'Socket hung up') { | ||
static CONNECTION_LOST(msg = 'Socket hung up') { | ||
return new HTTPError(msg, 'CONNECTION_LOST', HTTPError.CONNECTION_LOST) | ||
} | ||
} |
const { Readable } = require('bare-stream') | ||
module.exports = class HTTPIncomingMessage extends Readable { | ||
constructor (socket = null, headers = {}, opts = {}) { | ||
constructor(socket = null, headers = {}, opts = {}) { | ||
super() | ||
@@ -20,19 +20,19 @@ | ||
get httpVersion () { | ||
get httpVersion() { | ||
return '1.1' | ||
} | ||
getHeader (name) { | ||
getHeader(name) { | ||
return this.headers[name.toLowerCase()] | ||
} | ||
getHeaders () { | ||
getHeaders() { | ||
return { ...this.headers } | ||
} | ||
hasHeader (name) { | ||
hasHeader(name) { | ||
return name.toLowerCase() in this.headers | ||
} | ||
setTimeout (ms, ontimeout) { | ||
setTimeout(ms, ontimeout) { | ||
if (ontimeout) this.once('timeout', ontimeout) | ||
@@ -45,5 +45,5 @@ | ||
_predestroy () { | ||
_predestroy() { | ||
if (this.upgrade === false && this.socket !== null) this.socket.destroy() | ||
} | ||
} |
@@ -5,3 +5,3 @@ const { Writable } = require('bare-stream') | ||
module.exports = class HTTPOutgoingMessage extends Writable { | ||
constructor (socket = null) { | ||
constructor(socket = null) { | ||
super() | ||
@@ -15,19 +15,19 @@ | ||
getHeader (name) { | ||
getHeader(name) { | ||
return this.headers[name.toLowerCase()] | ||
} | ||
getHeaders () { | ||
getHeaders() { | ||
return { ...this.headers } | ||
} | ||
hasHeader (name) { | ||
hasHeader(name) { | ||
return name.toLowerCase() in this.headers | ||
} | ||
setHeader (name, value) { | ||
setHeader(name, value) { | ||
this.headers[name.toLowerCase()] = value | ||
} | ||
flushHeaders () { | ||
flushHeaders() { | ||
if (this.headersSent === true || this.socket === null) return | ||
@@ -39,3 +39,3 @@ | ||
setTimeout (ms, ontimeout) { | ||
setTimeout(ms, ontimeout) { | ||
if (ontimeout) this.once('timeout', ontimeout) | ||
@@ -48,9 +48,9 @@ | ||
_header () { | ||
_header() { | ||
throw errors.NOT_IMPLEMENTED() | ||
} | ||
_predestroy () { | ||
_predestroy() { | ||
if (this.upgrade === false && this.socket !== null) this.socket.destroy() | ||
} | ||
} |
@@ -12,7 +12,7 @@ const tcp = require('bare-tcp') | ||
static for (socket) { | ||
static for(socket) { | ||
return this._connections.get(socket) || null | ||
} | ||
constructor (server, socket, opts = {}) { | ||
constructor(server, socket, opts = {}) { | ||
const { | ||
@@ -55,7 +55,7 @@ IncomingMessage = HTTPIncomingMessage, | ||
get idle () { | ||
get idle() { | ||
return this._idle | ||
} | ||
_onclose () { | ||
_onclose() { | ||
if (this.req && !isEnded(this.req)) this.req.destroy() | ||
@@ -67,3 +67,3 @@ if (this.res && !isFinished(this.res)) this.res.destroy() | ||
_ondata (data) { | ||
_ondata(data) { | ||
this._idle = false | ||
@@ -91,3 +91,6 @@ | ||
const head = this._buffer.subarray(0, i - 1) | ||
this._buffer = i + 1 === this._buffer.byteLength ? null : this._buffer.subarray(i + 1) | ||
this._buffer = | ||
i + 1 === this._buffer.byteLength | ||
? null | ||
: this._buffer.subarray(i + 1) | ||
i = 0 | ||
@@ -106,3 +109,6 @@ hits = 0 | ||
this._buffer = i + 1 === this._buffer.byteLength ? null : this._buffer.subarray(i + 1) | ||
this._buffer = | ||
i + 1 === this._buffer.byteLength | ||
? null | ||
: this._buffer.subarray(i + 1) | ||
i = 0 | ||
@@ -119,3 +125,6 @@ hits = 0 | ||
const head = this._buffer.subarray(0, i - 3) | ||
this._buffer = i + 1 === this._buffer.byteLength ? null : this._buffer.subarray(i + 1) | ||
this._buffer = | ||
i + 1 === this._buffer.byteLength | ||
? null | ||
: this._buffer.subarray(i + 1) | ||
i = 0 | ||
@@ -133,3 +142,3 @@ hits = 0 | ||
_onhead (data) { | ||
_onhead(data) { | ||
this._state = constants.state.IN_HEAD | ||
@@ -152,3 +161,6 @@ | ||
this.req.on('close', () => { this.req = null; this._onreset() }) | ||
this.req.on('close', () => { | ||
this.req = null | ||
this._onreset() | ||
}) | ||
@@ -161,5 +173,11 @@ if (headers.connection && headers.connection.toLowerCase() === 'upgrade') { | ||
this.res = new this._ServerResponse(this.socket, this.req, headers.connection === 'close') | ||
this.res = new this._ServerResponse( | ||
this.socket, | ||
this.req, | ||
headers.connection === 'close' | ||
) | ||
this.res.on('close', () => { this.res = null }) | ||
this.res.on('close', () => { | ||
this.res = null | ||
}) | ||
@@ -185,3 +203,3 @@ this.server.emit('request', this.req, this.res) | ||
_onchunklength (data) { | ||
_onchunklength(data) { | ||
this._length = parseInt(data.toString(), 16) | ||
@@ -193,3 +211,3 @@ | ||
_onchunk (data) { | ||
_onchunk(data) { | ||
this._read += data.byteLength | ||
@@ -202,3 +220,3 @@ | ||
_onbody (data) { | ||
_onbody(data) { | ||
this._read += data.byteLength | ||
@@ -211,3 +229,3 @@ | ||
_onupgrade (head) { | ||
_onupgrade(head) { | ||
this._ondetach() | ||
@@ -223,3 +241,3 @@ | ||
_ontimeout () { | ||
_ontimeout() { | ||
const reqTimeout = this.req && this.req.emit('timeout') | ||
@@ -232,7 +250,7 @@ const resTimeout = this.res && this.res.emit('timeout') | ||
_onfinished () { | ||
_onfinished() { | ||
if (this.req) this.req.push(null) | ||
} | ||
_onreset () { | ||
_onreset() { | ||
this._state = constants.state.BEFORE_HEAD | ||
@@ -249,7 +267,7 @@ this._length = -1 | ||
_ondrain () { | ||
_ondrain() { | ||
if (this.res) this.res._continueWrite() | ||
} | ||
_ondetach () { | ||
_ondetach() { | ||
this.socket | ||
@@ -266,2 +284,2 @@ .off('error', noop) | ||
function noop () {} | ||
function noop() {} |
@@ -5,3 +5,3 @@ const HTTPOutgoingMessage = require('./outgoing-message') | ||
module.exports = class HTTPServerResponse extends HTTPOutgoingMessage { | ||
constructor (socket, req, close) { | ||
constructor(socket, req, close) { | ||
super(socket) | ||
@@ -22,3 +22,3 @@ | ||
end (data) { | ||
end(data) { | ||
this._finishing = true | ||
@@ -28,3 +28,3 @@ return super.end(data) | ||
writeHead (statusCode, statusMessage = null, headers = {}) { | ||
writeHead(statusCode, statusMessage = null, headers = {}) { | ||
if (typeof statusMessage === 'object' && statusMessage !== null) { | ||
@@ -40,4 +40,11 @@ headers = statusMessage | ||
_header () { | ||
let h = 'HTTP/1.1 ' + this.statusCode + ' ' + (this.statusMessage === null ? constants.status[this.statusCode] : this.statusMessage) + '\r\n' | ||
_header() { | ||
let h = | ||
'HTTP/1.1 ' + | ||
this.statusCode + | ||
' ' + | ||
(this.statusMessage === null | ||
? constants.status[this.statusCode] | ||
: this.statusMessage) + | ||
'\r\n' | ||
@@ -49,3 +56,4 @@ for (const name of Object.keys(this.headers)) { | ||
if (n === 'content-length') this._chunked = false | ||
if (n === 'connection' && v && v.toLowerCase() === 'close') this._close = true | ||
if (n === 'connection' && v && v.toLowerCase() === 'close') | ||
this._close = true | ||
@@ -62,6 +70,9 @@ h += httpCase(n) + ': ' + v + '\r\n' | ||
_write (data, encoding, cb) { | ||
_write(data, encoding, cb) { | ||
if (this.headersSent === false) { | ||
if (this._finishing) { | ||
this.setHeader('Content-Length', (data.byteLength + this._writableState.buffered).toString()) | ||
this.setHeader( | ||
'Content-Length', | ||
(data.byteLength + this._writableState.buffered).toString() | ||
) | ||
} | ||
@@ -86,3 +97,3 @@ | ||
_final (cb) { | ||
_final(cb) { | ||
if (this.headersSent === false) { | ||
@@ -93,3 +104,4 @@ this.setHeader('Content-Length', '0') | ||
if (this._chunked && this._onlyHeaders === false) this.socket.write(Buffer.from('0\r\n\r\n')) | ||
if (this._chunked && this._onlyHeaders === false) | ||
this.socket.write(Buffer.from('0\r\n\r\n')) | ||
if (this._close) this.socket.end() | ||
@@ -100,3 +112,3 @@ | ||
_predestroy () { | ||
_predestroy() { | ||
super._predestroy() | ||
@@ -107,3 +119,3 @@ this.req.destroy() | ||
_continueWrite () { | ||
_continueWrite() { | ||
if (this._pendingWrite === null) return | ||
@@ -116,3 +128,3 @@ const cb = this._pendingWrite | ||
function httpCase (n) { | ||
function httpCase(n) { | ||
let s = '' | ||
@@ -119,0 +131,0 @@ for (const part of n.split('-')) { |
@@ -5,3 +5,3 @@ const TCPServer = require('bare-tcp').Server | ||
module.exports = class HTTPServer extends TCPServer { | ||
constructor (opts = {}, onrequest) { | ||
constructor(opts = {}, onrequest) { | ||
if (typeof opts === 'function') { | ||
@@ -16,3 +16,6 @@ onrequest = opts | ||
this.on('connection', (socket) => new HTTPServerConnection(this, socket, opts)) | ||
this.on( | ||
'connection', | ||
(socket) => new HTTPServerConnection(this, socket, opts) | ||
) | ||
@@ -22,7 +25,7 @@ if (onrequest) this.on('request', onrequest) | ||
get timeout () { | ||
get timeout() { | ||
return this._timeout || undefined // For Node.js compatibility | ||
} | ||
setTimeout (ms = 0, ontimeout) { | ||
setTimeout(ms = 0, ontimeout) { | ||
if (ontimeout) this.on('timeout', ontimeout) | ||
@@ -35,3 +38,3 @@ | ||
close (onclose) { | ||
close(onclose) { | ||
super.close(onclose) | ||
@@ -38,0 +41,0 @@ |
{ | ||
"name": "bare-http1", | ||
"version": "3.8.2", | ||
"version": "4.0.0", | ||
"description": "Native HTTP/1 library for JavaScript", | ||
@@ -15,3 +15,3 @@ "exports": { | ||
"scripts": { | ||
"test": "standard && bare test.js" | ||
"test": "prettier . --check && bare test.js" | ||
}, | ||
@@ -31,8 +31,9 @@ "repository": { | ||
"bare-stream": "^2.3.0", | ||
"bare-tcp": "^1.8.0" | ||
"bare-tcp": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"brittle": "^3.3.0", | ||
"standard": "^17.0.0" | ||
"prettier": "^3.4.1", | ||
"prettier-config-standard": "^7.0.0" | ||
} | ||
} |
41077
1.12%952
6.49%3
50%