@curveball/core
Advanced tools
Comparing version 0.11.3-0 to 0.12.0
Changelog | ||
========= | ||
0.12.0 (2020-03-22) | ||
------------------- | ||
* Both `Request` and `Response` are now typescript interfaces. This will allow | ||
plugins to extends them via interface declaration merging. | ||
* Everything is now compiled with the typescript 'strict' mode, which caused | ||
some internal refactoring. | ||
0.11.2 (2020-03-09) | ||
@@ -5,0 +14,0 @@ ------------------- |
@@ -0,122 +1,85 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http_errors_1 = require("@curveball/http-errors"); | ||
const events_1 = require("events"); | ||
const http_1 = __importDefault(require("http")); | ||
const base_context_1 = __importDefault(require("./base-context")); | ||
const memory_request_1 = __importDefault(require("./memory-request")); | ||
const memory_response_1 = __importDefault(require("./memory-response")); | ||
const not_found_1 = __importDefault(require("./middleware/not-found")); | ||
const http_utils_1 = require("./node/http-utils"); | ||
const request_1 = __importDefault(require("./node/request")); | ||
const response_1 = __importDefault(require("./node/response")); | ||
const pkg = require('../package.json'); | ||
/** | ||
* The middleware-call Symbol is a special symbol that might exist as a | ||
* property on an object. | ||
* | ||
* If it exists, the object can be used as a middleware. | ||
*/ | ||
const middlewareCall = Symbol('middleware-call'); | ||
exports.middlewareCall = middlewareCall; | ||
// Calls a series of middlewares, in order. | ||
async function invokeMiddlewares(ctx, fns) { | ||
if (fns.length === 0) { | ||
return; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "@curveball/http-errors", "events", "http", "./base-context", "./memory-request", "./memory-response", "./middleware/not-found", "./node/http-utils", "./node/request", "./node/response"], factory); | ||
const mw = fns[0]; | ||
let mwFunc; | ||
if (isMiddlewareObject(mw)) { | ||
mwFunc = mw[middlewareCall].bind(fns[0]); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http_errors_1 = require("@curveball/http-errors"); | ||
const events_1 = require("events"); | ||
const http_1 = __importDefault(require("http")); | ||
const base_context_1 = __importDefault(require("./base-context")); | ||
const memory_request_1 = __importDefault(require("./memory-request")); | ||
const memory_response_1 = __importDefault(require("./memory-response")); | ||
const not_found_1 = __importDefault(require("./middleware/not-found")); | ||
const http_utils_1 = require("./node/http-utils"); | ||
const request_1 = __importDefault(require("./node/request")); | ||
const response_1 = __importDefault(require("./node/response")); | ||
const pkg = require('../package.json'); | ||
else { | ||
mwFunc = mw; | ||
} | ||
return mwFunc(ctx, async () => { | ||
await invokeMiddlewares(ctx, fns.slice(1)); | ||
}); | ||
} | ||
exports.invokeMiddlewares = invokeMiddlewares; | ||
function isMiddlewareObject(input) { | ||
return (input[middlewareCall] !== undefined); | ||
} | ||
class Application extends events_1.EventEmitter { | ||
constructor() { | ||
super(...arguments); | ||
this.middlewares = []; | ||
} | ||
/** | ||
* The middleware-call Symbol is a special symbol that might exist as a | ||
* property on an object. | ||
* Add a middleware to the application. | ||
* | ||
* If it exists, the object can be used as a middleware. | ||
* Middlewares are called in the order they are added. | ||
*/ | ||
const middlewareCall = Symbol('middleware-call'); | ||
exports.middlewareCall = middlewareCall; | ||
// Calls a series of middlewares, in order. | ||
async function invokeMiddlewares(ctx, fns) { | ||
if (fns.length === 0) { | ||
return; | ||
} | ||
let mw; | ||
if (fns[0][middlewareCall] !== undefined) { | ||
mw = fns[0][middlewareCall].bind(fns[0]); | ||
} | ||
else { | ||
mw = fns[0]; | ||
} | ||
return mw(ctx, async () => { | ||
await invokeMiddlewares(ctx, fns.slice(1)); | ||
}); | ||
use(...middleware) { | ||
this.middlewares.push(...middleware); | ||
} | ||
exports.invokeMiddlewares = invokeMiddlewares; | ||
class Application extends events_1.EventEmitter { | ||
constructor() { | ||
super(...arguments); | ||
this.middlewares = []; | ||
} | ||
/** | ||
* Add a middleware to the application. | ||
* | ||
* Middlewares are called in the order they are added. | ||
*/ | ||
use(...middleware) { | ||
this.middlewares.push(...middleware); | ||
} | ||
/** | ||
* Handles a single request and calls all middleware. | ||
*/ | ||
async handle(ctx) { | ||
ctx.response.headers.set('Server', 'curveball/' + pkg.version); | ||
ctx.response.type = 'application/hal+json'; | ||
await invokeMiddlewares(ctx, [...this.middlewares, not_found_1.default]); | ||
} | ||
/** | ||
* Starts a HTTP server on the specified port. | ||
*/ | ||
listen(port) { | ||
const server = http_1.default.createServer(this.callback()); | ||
return server.listen(port); | ||
} | ||
/** | ||
* This function is a callback that can be used for Node's http.Server, | ||
* https.Server, or http2.Server. | ||
*/ | ||
callback() { | ||
return async (req, res) => { | ||
try { | ||
const ctx = this.buildContextFromHttp(req, res); | ||
await this.handle(ctx); | ||
// @ts-ignore - not sure why this line fails | ||
http_utils_1.sendBody(res, ctx.response.body); | ||
} | ||
catch (err) { | ||
// tslint:disable:no-console | ||
console.error(err); | ||
if (http_errors_1.isHttpError(err)) { | ||
res.statusCode = err.httpStatus; | ||
} | ||
else { | ||
res.statusCode = 500; | ||
} | ||
res.setHeader('Content-Type', 'text/plain'); | ||
res.end( | ||
// @ts-ignore | ||
'Uncaught exception. No middleware was defined to handle it. We got the following HTTP status: ' + | ||
res.statusCode); | ||
if (this.listenerCount('error')) { | ||
this.emit('error', err); | ||
} | ||
} | ||
}; | ||
} | ||
async subRequest(arg1, path, headers, body = '') { | ||
let request; | ||
if (typeof arg1 === 'string') { | ||
request = new memory_request_1.default(arg1, path, headers, body); | ||
} | ||
else { | ||
request = arg1; | ||
} | ||
const context = new base_context_1.default(request, new memory_response_1.default()); | ||
/** | ||
* Handles a single request and calls all middleware. | ||
*/ | ||
async handle(ctx) { | ||
ctx.response.headers.set('Server', 'curveball/' + pkg.version); | ||
ctx.response.type = 'application/hal+json'; | ||
await invokeMiddlewares(ctx, [...this.middlewares, not_found_1.default]); | ||
} | ||
/** | ||
* Starts a HTTP server on the specified port. | ||
*/ | ||
listen(port) { | ||
const server = http_1.default.createServer(this.callback()); | ||
return server.listen(port); | ||
} | ||
/** | ||
* This function is a callback that can be used for Node's http.Server, | ||
* https.Server, or http2.Server. | ||
*/ | ||
callback() { | ||
return async (req, res) => { | ||
try { | ||
await this.handle(context); | ||
const ctx = this.buildContextFromHttp(req, res); | ||
await this.handle(ctx); | ||
// @ts-ignore - not sure why this line fails | ||
http_utils_1.sendBody(res, ctx.response.body); | ||
} | ||
@@ -126,27 +89,58 @@ catch (err) { | ||
console.error(err); | ||
if (this.listenerCount('error')) { | ||
this.emit('error', err); | ||
} | ||
if (http_errors_1.isHttpError(err)) { | ||
context.response.status = err.httpStatus; | ||
res.statusCode = err.httpStatus; | ||
} | ||
else { | ||
context.response.status = 500; | ||
res.statusCode = 500; | ||
} | ||
context.response.body = | ||
'Uncaught exception. No middleware was defined to handle it. We got the following HTTP status: ' + | ||
context.response.status; | ||
res.setHeader('Content-Type', 'text/plain'); | ||
res.end( | ||
// @ts-ignore | ||
'Uncaught exception. No middleware was defined to handle it. We got the following HTTP status: ' + | ||
res.statusCode); | ||
if (this.listenerCount('error')) { | ||
this.emit('error', err); | ||
} | ||
} | ||
return context.response; | ||
}; | ||
} | ||
async subRequest(arg1, path, headers, body = '') { | ||
let request; | ||
if (typeof arg1 === 'string') { | ||
request = new memory_request_1.default(arg1, path, headers, body); | ||
} | ||
/** | ||
* Creates a Context object based on a node.js request and response object. | ||
*/ | ||
buildContextFromHttp(req, res) { | ||
const context = new base_context_1.default(new request_1.default(req), new response_1.default(res)); | ||
return context; | ||
else { | ||
request = arg1; | ||
} | ||
const context = new base_context_1.default(request, new memory_response_1.default()); | ||
try { | ||
await this.handle(context); | ||
} | ||
catch (err) { | ||
// tslint:disable:no-console | ||
console.error(err); | ||
if (this.listenerCount('error')) { | ||
this.emit('error', err); | ||
} | ||
if (http_errors_1.isHttpError(err)) { | ||
context.response.status = err.httpStatus; | ||
} | ||
else { | ||
context.response.status = 500; | ||
} | ||
context.response.body = | ||
'Uncaught exception. No middleware was defined to handle it. We got the following HTTP status: ' + | ||
context.response.status; | ||
} | ||
return context.response; | ||
} | ||
exports.default = Application; | ||
}); | ||
/** | ||
* Creates a Context object based on a node.js request and response object. | ||
*/ | ||
buildContextFromHttp(req, res) { | ||
const context = new base_context_1.default(new request_1.default(req), new response_1.default(res)); | ||
return context; | ||
} | ||
} | ||
exports.default = Application; | ||
//# sourceMappingURL=application.js.map |
@@ -1,138 +0,128 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* The Context object encapsulates a single HTTP request. | ||
* | ||
* It has references to the internal request and response object. | ||
*/ | ||
class BaseContext { | ||
constructor(req, res) { | ||
this.request = req; | ||
this.response = res; | ||
this.state = {}; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
/** | ||
* The Request path. | ||
* | ||
* Shortcut for request.path | ||
*/ | ||
get path() { | ||
return this.request.path; | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* The Context object encapsulates a single HTTP request. | ||
* HTTP method | ||
* | ||
* It has references to the internal request and response object. | ||
* Shortcut for request.method | ||
*/ | ||
class BaseContext { | ||
constructor(req, res) { | ||
this.request = req; | ||
this.response = res; | ||
this.state = {}; | ||
get method() { | ||
return this.request.method; | ||
} | ||
/** | ||
* This object contains parsed query string parameters. | ||
* | ||
* This is a shortcut for request.query | ||
*/ | ||
get query() { | ||
return this.request.query; | ||
} | ||
/** | ||
* accepts is used for negotation the Content-Type with a client. | ||
* | ||
* You can pass a content-type, or an array of content-types. | ||
* The Content-Types you provide are a list of types your application | ||
* supports. | ||
* | ||
* This function will then return the best possible type based on the Accept | ||
* header. | ||
* | ||
* If no compatible types are found, this function returns null. | ||
* | ||
* This is a shortcut to request.accepts() | ||
*/ | ||
accepts(...types) { | ||
return this.request.accepts(...types); | ||
} | ||
/** | ||
* HTTP status code. | ||
* | ||
* This is a shortcut for response.status | ||
*/ | ||
get status() { | ||
return this.response.status; | ||
} | ||
/** | ||
* Sets the HTTP response status code. | ||
* | ||
* This is a shortcut for response.status. | ||
*/ | ||
set status(value) { | ||
this.response.status = value; | ||
} | ||
/** | ||
* Sends an informational (1xx status code) response. | ||
* | ||
* This is a shortcut for response.sendInformational() | ||
*/ | ||
sendInformational(status, headers) { | ||
return this.response.sendInformational(status, headers); | ||
} | ||
/** | ||
* Sends a HTTP/2 push. | ||
* | ||
* The passed middleware will be called with a new Context object specific | ||
* for pushes. | ||
* | ||
* This is a shortcut for response.push() | ||
*/ | ||
push(callback) { | ||
return this.response.push(callback); | ||
} | ||
/** | ||
* returns the ip address of the client that's trying to connect. | ||
* | ||
* If 'trustProxy' is set to true, it means the server is running behind a | ||
* proxy, and the X-Forwarded-For header should be parsed instead. | ||
* | ||
* If there was no real HTTP client, this method will return null. | ||
*/ | ||
ip(trustProxy = false) { | ||
if (this.request.ip !== undefined) { | ||
return this.request.ip(trustProxy); | ||
} | ||
/** | ||
* The Request path. | ||
* | ||
* Shortcut for request.path | ||
*/ | ||
get path() { | ||
return this.request.path; | ||
return null; | ||
} | ||
/** | ||
* redirect redirects the response with an optionally provided HTTP status | ||
* code in the first position to the location provided in address. If no status | ||
* is provided, 303 See Other is used. | ||
* | ||
* It is a wrapper method for the underlying Response.redirect function. | ||
* | ||
* @param {(string|number)} addrOrStatus if passed a string, the string will | ||
* be used to set the Location header of the response object and the default status | ||
* of 303 See Other will be used. If a number, an addressed must be passed in the second | ||
* argument. | ||
* @param {string} address If addrOrStatus is passed a status code, this value is | ||
* set as the value of the response's Location header. | ||
*/ | ||
redirect(addrOrStatus, address = '') { | ||
if (typeof (addrOrStatus) === 'number') { | ||
return this.response.redirect(addrOrStatus, address); | ||
} | ||
/** | ||
* HTTP method | ||
* | ||
* Shortcut for request.method | ||
*/ | ||
get method() { | ||
return this.request.method; | ||
else { | ||
return this.response.redirect(addrOrStatus); | ||
} | ||
/** | ||
* This object contains parsed query string parameters. | ||
* | ||
* This is a shortcut for request.query | ||
*/ | ||
get query() { | ||
return this.request.query; | ||
} | ||
/** | ||
* accepts is used for negotation the Content-Type with a client. | ||
* | ||
* You can pass a content-type, or an array of content-types. | ||
* The Content-Types you provide are a list of types your application | ||
* supports. | ||
* | ||
* This function will then return the best possible type based on the Accept | ||
* header. | ||
* | ||
* If no compatible types are found, this function returns null. | ||
* | ||
* This is a shortcut to request.accepts() | ||
*/ | ||
accepts(...types) { | ||
return this.request.accepts(...types); | ||
} | ||
/** | ||
* HTTP status code. | ||
* | ||
* This is a shortcut for response.status | ||
*/ | ||
get status() { | ||
return this.response.status; | ||
} | ||
/** | ||
* Sets the HTTP response status code. | ||
* | ||
* This is a shortcut for response.status. | ||
*/ | ||
set status(value) { | ||
this.response.status = value; | ||
} | ||
/** | ||
* Sends an informational (1xx status code) response. | ||
* | ||
* This is a shortcut for response.sendInformational() | ||
*/ | ||
sendInformational(status, headers) { | ||
return this.response.sendInformational(status, headers); | ||
} | ||
/** | ||
* Sends a HTTP/2 push. | ||
* | ||
* The passed middleware will be called with a new Context object specific | ||
* for pushes. | ||
* | ||
* This is a shortcut for response.push() | ||
*/ | ||
push(callback) { | ||
return this.response.push(callback); | ||
} | ||
/** | ||
* returns the ip address of the client that's trying to connect. | ||
* | ||
* If 'trustProxy' is set to true, it means the server is running behind a | ||
* proxy, and the X-Forwarded-For header should be parsed instead. | ||
* | ||
* If there was no real HTTP client, this method will return null. | ||
*/ | ||
ip(trustProxy = false) { | ||
if (this.request.ip !== undefined) { | ||
return this.request.ip(trustProxy); | ||
} | ||
return null; | ||
} | ||
/** | ||
* redirect redirects the response with an optionally provided HTTP status | ||
* code in the first position to the location provided in address. If no status | ||
* is provided, 303 See Other is used. | ||
* | ||
* It is a wrapper method for the underlying Response.redirect function. | ||
* | ||
* @param {(string|number)} addrOrStatus if passed a string, the string will | ||
* be used to set the Location header of the response object and the default status | ||
* of 303 See Other will be used. If a number, an addressed must be passed in the second | ||
* argument. | ||
* @param {string} address If addrOrStatus is passed a status code, this value is | ||
* set as the value of the response's Location header. | ||
*/ | ||
redirect(addrOrStatus, address = '') { | ||
if (typeof (addrOrStatus) === 'number') { | ||
return this.response.redirect(addrOrStatus, address); | ||
} | ||
else { | ||
return this.response.redirect(addrOrStatus); | ||
} | ||
} | ||
} | ||
exports.default = BaseContext; | ||
}); | ||
} | ||
exports.default = BaseContext; | ||
//# sourceMappingURL=base-context.js.map |
@@ -1,108 +0,98 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This method will check the following request headers: | ||
* | ||
* 1. If-Match | ||
* 2. If-None-Match | ||
* 3. If-Modified-Since | ||
* 4. If-Unmodified-Since | ||
* | ||
* To check these headers,v alues for lastModified and etag should be passed. | ||
* | ||
* The result of this function will return a suggested HTTP status. It will | ||
* return | ||
* * '200' if all the preconditions passed. | ||
* * '304' is a 'Not Modified' status should be returned, and | ||
* * '412' if a 'Precondition failed' error should be returned. | ||
*/ | ||
function conditionalCheck(request, lastModified, etag) { | ||
validateETag(etag); | ||
if (request.headers.has('If-Match')) { | ||
return ifMatchCheck(request.headers.get('If-Match'), etag); | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
if (request.headers.has('If-None-Match')) { | ||
return ifNoneMatchCheck(request.method, request.headers.get('If-None-Match'), etag); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This method will check the following request headers: | ||
* | ||
* 1. If-Match | ||
* 2. If-None-Match | ||
* 3. If-Modified-Since | ||
* 4. If-Unmodified-Since | ||
* | ||
* To check these headers,v alues for lastModified and etag should be passed. | ||
* | ||
* The result of this function will return a suggested HTTP status. It will | ||
* return | ||
* * '200' if all the preconditions passed. | ||
* * '304' is a 'Not Modified' status should be returned, and | ||
* * '412' if a 'Precondition failed' error should be returned. | ||
*/ | ||
function conditionalCheck(request, lastModified, etag) { | ||
validateETag(etag); | ||
if (request.headers.has('If-Match')) { | ||
return ifMatchCheck(request.headers.get('If-Match'), etag); | ||
} | ||
if (request.headers.has('If-None-Match')) { | ||
return ifNoneMatchCheck(request.method, request.headers.get('If-None-Match'), etag); | ||
} | ||
if (request.headers.has('If-Modified-Since')) { | ||
return ifModifiedSinceCheck(request.method, request.headers.get('If-Modified-Since'), lastModified); | ||
} | ||
if (request.headers.has('If-Unmodified-Since')) { | ||
return ifUnmodifiedSinceCheck(request.headers.get('If-Unmodified-Since'), lastModified); | ||
} | ||
return 200; | ||
if (request.headers.has('If-Modified-Since')) { | ||
return ifModifiedSinceCheck(request.method, request.headers.get('If-Modified-Since'), lastModified); | ||
} | ||
exports.conditionalCheck = conditionalCheck; | ||
function ifMatchCheck(header, serverState) { | ||
if (header === '*') { | ||
return serverState !== null ? 200 : 412; | ||
} | ||
if (request.headers.has('If-Unmodified-Since')) { | ||
return ifUnmodifiedSinceCheck(request.headers.get('If-Unmodified-Since'), lastModified); | ||
} | ||
return 200; | ||
} | ||
exports.conditionalCheck = conditionalCheck; | ||
function ifMatchCheck(header, serverState) { | ||
if (header === '*') { | ||
return serverState !== null ? 200 : 412; | ||
} | ||
const headerTags = header.split(',').map(foo => foo.trim()); | ||
for (const tag of headerTags) { | ||
if (tag === serverState) | ||
return 200; | ||
} | ||
return 412; | ||
} | ||
function ifNoneMatchCheck(method, header, serverState) { | ||
let pass; | ||
if (header === '*') { | ||
pass = serverState === null; | ||
} | ||
else { | ||
const headerTags = header.split(',').map(foo => foo.trim()); | ||
pass = true; | ||
for (const tag of headerTags) { | ||
if (tag === serverState) | ||
return 200; | ||
if (tag === serverState) { | ||
pass = false; | ||
} | ||
} | ||
return 412; | ||
} | ||
function ifNoneMatchCheck(method, header, serverState) { | ||
let pass; | ||
if (header === '*') { | ||
pass = serverState === null; | ||
if (!pass) { | ||
if (method === 'GET' || method === 'HEAD') { | ||
return 304; | ||
} | ||
else { | ||
const headerTags = header.split(',').map(foo => foo.trim()); | ||
pass = true; | ||
for (const tag of headerTags) { | ||
if (tag === serverState) { | ||
pass = false; | ||
} | ||
} | ||
return 412; | ||
} | ||
if (!pass) { | ||
if (method === 'GET' || method === 'HEAD') { | ||
return 304; | ||
} | ||
else { | ||
return 412; | ||
} | ||
} | ||
} | ||
return 200; | ||
} | ||
function ifModifiedSinceCheck(method, header, serverState) { | ||
if (method !== 'GET' && method !== 'HEAD') { | ||
// Only GET and HEAD are supported, everything else is ignored | ||
return 200; | ||
} | ||
function ifModifiedSinceCheck(method, header, serverState) { | ||
if (method !== 'GET' && method !== 'HEAD') { | ||
// Only GET and HEAD are supported, everything else is ignored | ||
return 200; | ||
} | ||
if (serverState === null) { | ||
return 200; | ||
} | ||
const headerDate = new Date(header); | ||
return (serverState.getTime() > headerDate.getTime()) ? 200 : 304; | ||
if (serverState === null) { | ||
return 200; | ||
} | ||
function ifUnmodifiedSinceCheck(header, serverState) { | ||
// This is not in any spec, but I believe that we should reject requests | ||
// if we don't know that the Last-Modified value is. | ||
if (serverState === null) { | ||
return 412; | ||
} | ||
const headerDate = new Date(header); | ||
return (serverState.getTime() <= headerDate.getTime()) ? 200 : 412; | ||
const headerDate = new Date(header); | ||
return (serverState.getTime() > headerDate.getTime()) ? 200 : 304; | ||
} | ||
function ifUnmodifiedSinceCheck(header, serverState) { | ||
// This is not in any spec, but I believe that we should reject requests | ||
// if we don't know that the Last-Modified value is. | ||
if (serverState === null) { | ||
return 412; | ||
} | ||
function validateETag(etag) { | ||
if (etag === null) | ||
return; | ||
if (!/^(W\/)?"[\x21\x23-\x7e]+"$/.test(etag)) { | ||
throw new Error('Etags must be valid. Did you forget the double-quotes?'); | ||
} | ||
const headerDate = new Date(header); | ||
return (serverState.getTime() <= headerDate.getTime()) ? 200 : 412; | ||
} | ||
function validateETag(etag) { | ||
if (etag === null) | ||
return; | ||
if (!/^(W\/)?"[\x21\x23-\x7e]+"$/.test(etag)) { | ||
throw new Error('Etags must be valid. Did you forget the double-quotes?'); | ||
} | ||
}); | ||
} | ||
//# sourceMappingURL=conditional.js.map |
@@ -1,13 +0,3 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
}); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=context.js.map |
@@ -1,91 +0,81 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This method will return true or false if a Request or Response has a | ||
* Content-Type header that matches the argument. | ||
* | ||
* For example, if the Content-Type header has the value: application/hal+json, | ||
* then the arguments will all return true: | ||
* | ||
* * application/hal+json | ||
* * application/json | ||
* * hal+json | ||
* * json | ||
* * application/* | ||
*/ | ||
function is(message, type) { | ||
const messageType = message.type; | ||
// No Content-Type header | ||
if (!messageType) { | ||
return false; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
if (type === messageType) { | ||
// Matches application/hal+json | ||
return true; | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This method will return true or false if a Request or Response has a | ||
* Content-Type header that matches the argument. | ||
* | ||
* For example, if the Content-Type header has the value: application/hal+json, | ||
* then the arguments will all return true: | ||
* | ||
* * application/hal+json | ||
* * application/json | ||
* * hal+json | ||
* * json | ||
* * application/* | ||
*/ | ||
function is(message, type) { | ||
const messageType = message.type; | ||
// No Content-Type header | ||
if (!messageType) { | ||
return false; | ||
} | ||
if (type === messageType) { | ||
// Matches application/hal+json | ||
const [mainType, subType] = messageType.split('/', 2); | ||
if (subType === type) { | ||
// Matches hal+json | ||
return true; | ||
} | ||
if (type === mainType + '/*') { | ||
// matches application/* | ||
return true; | ||
} | ||
const subTypeParts = subType.split('+', 2); | ||
if (subTypeParts.length === 2) { | ||
if (subTypeParts[1] === type) { | ||
// Matches 'json' | ||
return true; | ||
} | ||
const [mainType, subType] = messageType.split('/', 2); | ||
if (subType === type) { | ||
// Matches hal+json | ||
if (mainType + '/' + subTypeParts[1] === type) { | ||
// matches application/json | ||
return true; | ||
} | ||
if (type === mainType + '/*') { | ||
// matches application/* | ||
return true; | ||
} | ||
const subTypeParts = subType.split('+', 2); | ||
if (subTypeParts.length === 2) { | ||
if (subTypeParts[1] === type) { | ||
// Matches 'json' | ||
return true; | ||
} | ||
if (mainType + '/' + subTypeParts[1] === type) { | ||
// matches application/json | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
exports.is = is; | ||
/** | ||
* Parses a RFC7240 Prefer header. | ||
* | ||
* It's a naive parser as it assumes a fairly simple subset of | ||
* Prefer. | ||
* | ||
* TODO: Make this parse every possible variation. | ||
*/ | ||
function parsePrefer(header) { | ||
if (!header) { | ||
return {}; | ||
} | ||
const result = {}; | ||
for (const headerItem of splitHeader(header)) { | ||
const [keyValue] = headerItem.split(';'); | ||
const [key, value] = keyValue.split('='); | ||
result[key.toLowerCase()] = value !== undefined ? value : true; | ||
} | ||
return result; | ||
return false; | ||
} | ||
exports.is = is; | ||
/** | ||
* Parses a RFC7240 Prefer header. | ||
* | ||
* It's a naive parser as it assumes a fairly simple subset of | ||
* Prefer. | ||
* | ||
* TODO: Make this parse every possible variation. | ||
*/ | ||
function parsePrefer(header) { | ||
if (!header) { | ||
return {}; | ||
} | ||
exports.parsePrefer = parsePrefer; | ||
/** | ||
* This function takes a multi-value comma-separated header and splits it | ||
* into multiple headers. | ||
* | ||
* TODO: In the future this function will respect comma's appearing within | ||
* quotes and ignore them. It doesn't right now. | ||
*/ | ||
function splitHeader(header) { | ||
return header.split(',').map(item => item.trim()); | ||
const result = {}; | ||
for (const headerItem of splitHeader(header)) { | ||
const [keyValue] = headerItem.split(';'); | ||
const [key, value] = keyValue.split('='); | ||
result[key.toLowerCase()] = value !== undefined ? value : true; | ||
} | ||
exports.splitHeader = splitHeader; | ||
}); | ||
return result; | ||
} | ||
exports.parsePrefer = parsePrefer; | ||
/** | ||
* This function takes a multi-value comma-separated header and splits it | ||
* into multiple headers. | ||
* | ||
* TODO: In the future this function will respect comma's appearing within | ||
* quotes and ignore them. It doesn't right now. | ||
*/ | ||
function splitHeader(header) { | ||
return header.split(',').map(item => item.trim()); | ||
} | ||
exports.splitHeader = splitHeader; | ||
//# sourceMappingURL=header-helpers.js.map |
@@ -1,93 +0,83 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
class Headers { | ||
constructor(headersObj = {}) { | ||
this.store = {}; | ||
for (const key of Object.keys(headersObj)) { | ||
this.set(key, headersObj[key]); | ||
} | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
/** | ||
* Sets a HTTP header name and value. | ||
*/ | ||
set(name, value) { | ||
// Storing the header name case-insenstive, but we retain the original | ||
// case as well. | ||
this.store[name.toLowerCase()] = [name, value]; | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
class Headers { | ||
constructor(headersObj = {}) { | ||
this.store = {}; | ||
for (const key of Object.keys(headersObj)) { | ||
this.set(key, headersObj[key]); | ||
} | ||
/** | ||
* Gets a HTTP header's value. | ||
* | ||
* This function will return null if the header did not exist. If it did | ||
* exist, it will return a string. | ||
* | ||
* If there were multiple headers with the same value, it will join the | ||
* headers with a comma. | ||
*/ | ||
get(name) { | ||
const tuple = this.store[name.toLowerCase()]; | ||
if (tuple === undefined) { | ||
return null; | ||
} | ||
/** | ||
* Sets a HTTP header name and value. | ||
*/ | ||
set(name, value) { | ||
// Storing the header name case-insenstive, but we retain the original | ||
// case as well. | ||
this.store[name.toLowerCase()] = [name, value]; | ||
const value = tuple[1]; | ||
if (typeof (value) === 'string') { | ||
return value; | ||
} | ||
/** | ||
* Gets a HTTP header's value. | ||
* | ||
* This function will return null if the header did not exist. If it did | ||
* exist, it will return a string. | ||
* | ||
* If there were multiple headers with the same value, it will join the | ||
* headers with a comma. | ||
*/ | ||
get(name) { | ||
const tuple = this.store[name.toLowerCase()]; | ||
if (tuple === undefined) { | ||
return null; | ||
} | ||
const value = tuple[1]; | ||
if (typeof (value) === 'string') { | ||
return value; | ||
} | ||
else if (Array.isArray(value)) { | ||
return value.join(', '); | ||
} | ||
else { | ||
return value.toString(); | ||
} | ||
else if (Array.isArray(value)) { | ||
return value.join(', '); | ||
} | ||
/** | ||
* Returns true or false depending on if a HTTP header exists. | ||
*/ | ||
has(name) { | ||
return this.store[name.toLowerCase()] !== undefined; | ||
else { | ||
return value.toString(); | ||
} | ||
/** | ||
* Returns all HTTP headers. | ||
* | ||
* Headernames are lowercased. Values may be either strings or arrays of | ||
* strings or numbers. | ||
*/ | ||
getAll() { | ||
const result = {}; | ||
for (const headerName of Object.keys(this.store)) { | ||
result[headerName] = this.store[headerName][1]; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Returns true or false depending on if a HTTP header exists. | ||
*/ | ||
has(name) { | ||
return this.store[name.toLowerCase()] !== undefined; | ||
} | ||
/** | ||
* Returns all HTTP headers. | ||
* | ||
* Headernames are lowercased. Values may be either strings or arrays of | ||
* strings or numbers. | ||
*/ | ||
getAll() { | ||
const result = {}; | ||
for (const headerName of Object.keys(this.store)) { | ||
result[headerName] = this.store[headerName][1]; | ||
} | ||
/** | ||
* Appends a new header, without removing an old one with the same name. | ||
*/ | ||
append(name, value) { | ||
const lowerName = name.toLowerCase(); | ||
if (this.store[lowerName] === undefined) { | ||
this.store[lowerName] = [name, value]; | ||
return; | ||
} | ||
const oldArray = Array.isArray(this.store[lowerName][1]) ? this.store[lowerName][1] : [this.store[lowerName][1].toString()]; | ||
this.store[lowerName][1] = oldArray.concat(value); | ||
return result; | ||
} | ||
/** | ||
* Appends a new header, without removing an old one with the same name. | ||
*/ | ||
append(name, value) { | ||
const lowerName = name.toLowerCase(); | ||
if (this.store[lowerName] === undefined) { | ||
this.store[lowerName] = [name, value]; | ||
return; | ||
} | ||
/** | ||
* Removes a HTTP header | ||
*/ | ||
delete(name) { | ||
delete this.store[name.toLowerCase()]; | ||
} | ||
const oldArray = Array.isArray(this.store[lowerName][1]) ? this.store[lowerName][1] : [this.store[lowerName][1].toString()]; | ||
this.store[lowerName][1] = oldArray.concat(value); | ||
} | ||
exports.Headers = Headers; | ||
exports.default = Headers; | ||
}); | ||
/** | ||
* Removes a HTTP header | ||
*/ | ||
delete(name) { | ||
delete this.store[name.toLowerCase()]; | ||
} | ||
} | ||
exports.Headers = Headers; | ||
exports.default = Headers; | ||
//# sourceMappingURL=headers.js.map |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
@@ -11,33 +12,18 @@ if (mod && mod.__esModule) return mod; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "./application", "./base-context", "./headers", "./memory-request", "./memory-response", "./request", "./response", "./conditional"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const application_1 = __importStar(require("./application")); | ||
exports.Application = application_1.default; | ||
exports.invokeMiddlewares = application_1.invokeMiddlewares; | ||
exports.middlewareCall = application_1.middlewareCall; | ||
const base_context_1 = __importDefault(require("./base-context")); | ||
exports.BaseContext = base_context_1.default; | ||
const headers_1 = __importDefault(require("./headers")); | ||
exports.Headers = headers_1.default; | ||
const memory_request_1 = __importDefault(require("./memory-request")); | ||
exports.MemoryRequest = memory_request_1.default; | ||
const memory_response_1 = __importDefault(require("./memory-response")); | ||
exports.MemoryResponse = memory_response_1.default; | ||
const request_1 = __importDefault(require("./request")); | ||
exports.Request = request_1.default; | ||
const response_1 = __importDefault(require("./response")); | ||
exports.Response = response_1.default; | ||
const conditional_1 = require("./conditional"); | ||
exports.conditionalCheck = conditional_1.conditionalCheck; | ||
exports.default = application_1.default; | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const application_1 = __importStar(require("./application")); | ||
exports.Application = application_1.default; | ||
exports.invokeMiddlewares = application_1.invokeMiddlewares; | ||
exports.middlewareCall = application_1.middlewareCall; | ||
const base_context_1 = __importDefault(require("./base-context")); | ||
exports.BaseContext = base_context_1.default; | ||
const headers_1 = __importDefault(require("./headers")); | ||
exports.Headers = headers_1.default; | ||
const memory_request_1 = __importDefault(require("./memory-request")); | ||
exports.MemoryRequest = memory_request_1.default; | ||
const memory_response_1 = __importDefault(require("./memory-response")); | ||
exports.MemoryResponse = memory_response_1.default; | ||
const conditional_1 = require("./conditional"); | ||
exports.conditionalCheck = conditional_1.conditionalCheck; | ||
exports.default = application_1.default; | ||
//# sourceMappingURL=index.js.map |
/// <reference types="node" /> | ||
import { Readable } from 'stream'; | ||
import { HeadersInterface, HeadersObject } from './headers'; | ||
import Request from './request'; | ||
export declare class MemoryRequest<T> extends Request<T> { | ||
import BaseRequest from './base-request'; | ||
export declare class MemoryRequest<T> extends BaseRequest<T> { | ||
/** | ||
@@ -7,0 +7,0 @@ * List of HTTP Headers |
@@ -0,80 +1,68 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const stream_1 = require("stream"); | ||
const headers_1 = require("./headers"); | ||
const base_request_1 = __importDefault(require("./base-request")); | ||
class MemoryRequest extends base_request_1.default { | ||
constructor(method, requestTarget, headers, body = null) { | ||
super(method, requestTarget); | ||
if (headers && headers.get !== undefined) { | ||
this.headers = headers; | ||
} | ||
else { | ||
this.headers = new headers_1.Headers(headers); | ||
} | ||
this.originalBody = body; | ||
// @ts-ignore: Typescript doesn't like null here because it might be | ||
// incompatible with T, but we're ignoring it as it's a good default. | ||
this.body = null; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "stream", "./headers", "./request"], factory); | ||
async rawBody(encoding, limit) { | ||
return this.getBody(encoding); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const stream_1 = require("stream"); | ||
const headers_1 = require("./headers"); | ||
const request_1 = __importDefault(require("./request")); | ||
class MemoryRequest extends request_1.default { | ||
constructor(method, requestTarget, headers, body = null) { | ||
super(); | ||
this.method = method; | ||
this.requestTarget = requestTarget; | ||
if (headers && headers.get !== undefined) { | ||
this.headers = headers; | ||
/** | ||
* getStream returns a Node.js readable stream. | ||
* | ||
* A stream can typically only be read once. | ||
*/ | ||
getStream() { | ||
const s = new stream_1.Readable(); | ||
s.push(this.getBody()); | ||
s.push(null); | ||
return s; | ||
} | ||
getBody(encoding) { | ||
if (!this.originalBody) { | ||
// Memoizing the null case | ||
this.originalBody = ''; | ||
} | ||
if (typeof this.originalBody === 'object' && !(this.originalBody instanceof Buffer)) { | ||
// Memoizing the JSON object case. | ||
this.originalBody = JSON.stringify(this.originalBody); | ||
} | ||
if (this.originalBody instanceof Buffer) { | ||
// The buffer case | ||
if (typeof encoding === 'undefined') { | ||
return this.originalBody; | ||
} | ||
else { | ||
this.headers = new headers_1.Headers(headers); | ||
return this.originalBody.toString(encoding); | ||
} | ||
this.originalBody = body; | ||
// @ts-ignore: Typescript doesn't like null here because it might be | ||
// incompatible with T, but we're ignoring it as it's a good default. | ||
this.body = null; | ||
} | ||
async rawBody(encoding, limit) { | ||
return this.getBody(encoding); | ||
} | ||
/** | ||
* getStream returns a Node.js readable stream. | ||
* | ||
* A stream can typically only be read once. | ||
*/ | ||
getStream() { | ||
const s = new stream_1.Readable(); | ||
s.push(this.getBody()); | ||
s.push(null); | ||
return s; | ||
} | ||
getBody(encoding) { | ||
if (!this.originalBody) { | ||
// Memoizing the null case | ||
this.originalBody = ''; | ||
else { | ||
// The string case | ||
if (typeof encoding === 'undefined') { | ||
return Buffer.from(this.originalBody); | ||
} | ||
if (typeof this.originalBody === 'object' && !(this.originalBody instanceof Buffer)) { | ||
// Memoizing the JSON object case. | ||
this.originalBody = JSON.stringify(this.originalBody); | ||
} | ||
if (this.originalBody instanceof Buffer) { | ||
// The buffer case | ||
if (typeof encoding === 'undefined') { | ||
return this.originalBody; | ||
} | ||
else { | ||
return this.originalBody.toString(encoding); | ||
} | ||
} | ||
else { | ||
// The string case | ||
if (typeof encoding === 'undefined') { | ||
return Buffer.from(this.originalBody); | ||
} | ||
else { | ||
return this.originalBody; | ||
} | ||
return this.originalBody; | ||
} | ||
} | ||
} | ||
exports.MemoryRequest = MemoryRequest; | ||
exports.default = MemoryRequest; | ||
}); | ||
} | ||
exports.MemoryRequest = MemoryRequest; | ||
exports.default = MemoryRequest; | ||
//# sourceMappingURL=memory-request.js.map |
import { Headers } from './headers'; | ||
import Response from './response'; | ||
export declare class MemoryResponse<T> extends Response<T> { | ||
import BaseResponse from './base-response'; | ||
export declare class MemoryResponse<T> extends BaseResponse<T> { | ||
constructor(); | ||
@@ -5,0 +5,0 @@ /** |
@@ -0,30 +1,20 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const headers_1 = require("./headers"); | ||
const base_response_1 = __importDefault(require("./base-response")); | ||
class MemoryResponse extends base_response_1.default { | ||
constructor() { | ||
super(); | ||
this.headers = new headers_1.Headers(); | ||
this.status = 200; | ||
// @ts-ignore: Typescript doesn't like null here because it might be | ||
// incompatible with T, but we're ignoring it as it's a good default. | ||
this.body = null; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "./headers", "./response"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const headers_1 = require("./headers"); | ||
const response_1 = __importDefault(require("./response")); | ||
class MemoryResponse extends response_1.default { | ||
constructor() { | ||
super(); | ||
this.headers = new headers_1.Headers(); | ||
this.status = 200; | ||
// @ts-ignore: Typescript doesn't like null here because it might be | ||
// incompatible with T, but we're ignoring it as it's a good default. | ||
this.body = null; | ||
} | ||
} | ||
exports.MemoryResponse = MemoryResponse; | ||
exports.default = MemoryResponse; | ||
}); | ||
} | ||
exports.MemoryResponse = MemoryResponse; | ||
exports.default = MemoryResponse; | ||
//# sourceMappingURL=memory-response.js.map |
@@ -1,36 +0,26 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This middleware simply triggers a 'NotFound' error. | ||
* | ||
* This is a utility middleware that's automatically added to the middleware | ||
* stack to be 'last'. | ||
* | ||
* The purpose of this mw is that if no other middlewares did anything to | ||
* handle a request, we automatically respond with a 404. | ||
*/ | ||
class NoHandler extends Error { | ||
constructor() { | ||
super(); | ||
this.type = 'https://curveballjs.org/errors/no-handler'; | ||
this.title = 'No handler for this request'; | ||
this.detail = 'This server doesn\'t know what to do with your request. Did you make a typo?'; | ||
this.message = this.title; | ||
this.httpStatus = 404; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This middleware simply triggers a 'NotFound' error. | ||
* | ||
* This is a utility middleware that's automatically added to the middleware | ||
* stack to be 'last'. | ||
* | ||
* The purpose of this mw is that if no other middlewares did anything to | ||
* handle a request, we automatically respond with a 404. | ||
*/ | ||
class NoHandler extends Error { | ||
constructor() { | ||
super(); | ||
this.type = 'https://curveballjs.org/errors/no-handler'; | ||
this.title = 'No handler for this request'; | ||
this.detail = 'This server doesn\'t know what to do with your request. Did you make a typo?'; | ||
this.message = this.title; | ||
this.httpStatus = 404; | ||
} | ||
} | ||
function mw(ctx) { | ||
throw new NoHandler(); | ||
} | ||
exports.default = mw; | ||
}); | ||
} | ||
function mw(ctx) { | ||
throw new NoHandler(); | ||
} | ||
exports.default = mw; | ||
//# sourceMappingURL=not-found.js.map |
@@ -0,51 +1,41 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const stream_1 = __importDefault(require("stream")); | ||
/** | ||
* A type guard to see if a Response object is a HTTP2 response. | ||
*/ | ||
function isHttp2Response(response) { | ||
return response.stream !== undefined; | ||
} | ||
exports.isHttp2Response = isHttp2Response; | ||
function sendBody(res, body) { | ||
if (body === null) { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(); | ||
return; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "stream"], factory); | ||
else if (typeof body === 'string') { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(body); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const stream_1 = __importDefault(require("stream")); | ||
/** | ||
* A type guard to see if a Response object is a HTTP2 response. | ||
*/ | ||
function isHttp2Response(response) { | ||
return response.stream !== undefined; | ||
else if (body instanceof Buffer) { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(body); | ||
} | ||
exports.isHttp2Response = isHttp2Response; | ||
function sendBody(res, body) { | ||
if (body === null) { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(); | ||
return; | ||
} | ||
else if (typeof body === 'string') { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(body); | ||
} | ||
else if (body instanceof Buffer) { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(body); | ||
} | ||
else if (body instanceof stream_1.default) { | ||
// @ts-ignore - not sure why this line fails | ||
body.pipe(res); | ||
} | ||
else if (typeof body === 'object') { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(JSON.stringify(body)); | ||
} | ||
else { | ||
throw new TypeError('Unsupported type for body: ' + typeof body); | ||
} | ||
else if (body instanceof stream_1.default) { | ||
// @ts-ignore - not sure why this line fails | ||
body.pipe(res); | ||
} | ||
exports.sendBody = sendBody; | ||
}); | ||
else if (typeof body === 'object') { | ||
// @ts-ignore - not sure why this line fails | ||
res.end(JSON.stringify(body)); | ||
} | ||
else { | ||
throw new TypeError('Unsupported type for body: ' + typeof body); | ||
} | ||
} | ||
exports.sendBody = sendBody; | ||
//# sourceMappingURL=http-utils.js.map |
@@ -0,61 +1,51 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http2_1 = __importDefault(require("http2")); | ||
const http_utils_1 = require("./http-utils"); | ||
/** | ||
* This is a utility for helping with HTTP/2 Push for node servers. | ||
*/ | ||
async function push(stream, pushCtx) { | ||
const requestHeaders = { | ||
':path': pushCtx.request.requestTarget, | ||
...pushCtx.request.headers.getAll(), | ||
}; | ||
let pushStream; | ||
try { | ||
pushStream = await getPushStream(stream, requestHeaders); | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "http2", "./http-utils"], factory); | ||
catch (err) { | ||
if (err.code === 'ERR_HTTP2_PUSH_DISABLED') { | ||
// HTTP/2 disabled pusing after all | ||
return; | ||
} | ||
throw err; | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http2_1 = __importDefault(require("http2")); | ||
const http_utils_1 = require("./http-utils"); | ||
/** | ||
* This is a utility for helping with HTTP/2 Push for node servers. | ||
*/ | ||
async function push(stream, pushCtx) { | ||
const requestHeaders = { | ||
':path': pushCtx.request.requestTarget, | ||
...pushCtx.request.headers.getAll(), | ||
}; | ||
let pushStream; | ||
try { | ||
pushStream = await getPushStream(stream, requestHeaders); | ||
pushStream.on('error', err => { | ||
const isRefusedStream = pushStream.rstCode === http2_1.default.constants.NGHTTP2_REFUSED_STREAM; | ||
if (!isRefusedStream) { | ||
throw err; | ||
} | ||
catch (err) { | ||
if (err.code === 'ERR_HTTP2_PUSH_DISABLED') { | ||
// HTTP/2 disabled pusing after all | ||
}); | ||
pushStream.respond({ | ||
':status': 200, | ||
...pushCtx.response.headers.getAll(), | ||
}); | ||
http_utils_1.sendBody(pushStream, pushCtx.response.body); | ||
} | ||
exports.default = push; | ||
function getPushStream(stream, requestHeaders) { | ||
return new Promise((res, rej) => { | ||
stream.pushStream(requestHeaders, (err, pushStream) => { | ||
if (err) { | ||
rej(err); | ||
return; | ||
} | ||
throw err; | ||
} | ||
pushStream.on('error', err => { | ||
const isRefusedStream = pushStream.rstCode === http2_1.default.constants.NGHTTP2_REFUSED_STREAM; | ||
if (!isRefusedStream) { | ||
throw err; | ||
} | ||
res(pushStream); | ||
}); | ||
pushStream.respond({ | ||
':status': 200, | ||
...pushCtx.response.headers.getAll(), | ||
}); | ||
http_utils_1.sendBody(pushStream, pushCtx.response.body); | ||
} | ||
exports.default = push; | ||
function getPushStream(stream, requestHeaders) { | ||
return new Promise((res, rej) => { | ||
stream.pushStream(requestHeaders, (err, pushStream) => { | ||
if (err) { | ||
rej(err); | ||
return; | ||
} | ||
res(pushStream); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
//# sourceMappingURL=push.js.map |
/// <reference types="node" /> | ||
import { Readable } from 'stream'; | ||
import { HeadersInterface } from '../headers'; | ||
import Request from '../request'; | ||
import BaseRequest from '../base-request'; | ||
import { NodeHttpRequest } from './http-utils'; | ||
export declare class NodeRequest<T> extends Request<T> { | ||
export declare class NodeRequest<T> extends BaseRequest<T> { | ||
/** | ||
* List of HTTP Headers | ||
*/ | ||
headers: HeadersInterface; | ||
/** | ||
* Contains a parsed, stored representation of the body. It's up to | ||
* middlewares to do the actual parsing. | ||
*/ | ||
body: T; | ||
/** | ||
* Node.js Request object | ||
@@ -22,32 +12,2 @@ */ | ||
/** | ||
* HTTP method | ||
* | ||
* For example: GET | ||
*/ | ||
get method(): string; | ||
/** | ||
* The request target. | ||
* | ||
* This contains the literal value after the HTTP method in the request. | ||
* So for: | ||
* | ||
* GET /foo HTTP/1.1 | ||
* | ||
* This would contain '/foo'. In many cases this is the same as the 'path' | ||
* property, but there's 3 other forms in the HTTP specificatio. Here's the | ||
* different formats: | ||
* | ||
* * origin-form - This is the most common. Example: /foo. | ||
* * absolute-form - Clients might sent an entire path. Also used by proxies. | ||
* Example: https://example.org/foo | ||
* * authority-form - Used by the CONNECT method. Example: example.org:1212 | ||
* * asterisk-form - Used by the OPTIONS method. Example: * | ||
* | ||
* In most cases users will want to use the 'path' property instead. only use | ||
* this if you know what you're doing. | ||
* | ||
* @see {@link https://tools.ietf.org/html/rfc7230#section-5.3} | ||
*/ | ||
get requestTarget(): string; | ||
/** | ||
* This function returns the request body. | ||
@@ -54,0 +14,0 @@ * |
@@ -0,100 +1,56 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const raw_body_1 = __importDefault(require("raw-body")); | ||
const headers_1 = require("../headers"); | ||
const base_request_1 = __importDefault(require("../base-request")); | ||
class NodeRequest extends base_request_1.default { | ||
constructor(inner) { | ||
super(inner.method, inner.url); | ||
this.inner = inner; | ||
// @ts-ignore ignoring that headers might be undefined | ||
this.headers = new headers_1.Headers(this.inner.headers); | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "raw-body", "../headers", "../request"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const raw_body_1 = __importDefault(require("raw-body")); | ||
const headers_1 = require("../headers"); | ||
const request_1 = __importDefault(require("../request")); | ||
class NodeRequest extends request_1.default { | ||
constructor(inner) { | ||
super(); | ||
this.inner = inner; | ||
// @ts-ignore ignoring that headers might be undefined | ||
this.headers = new headers_1.Headers(this.inner.headers); | ||
rawBody(encoding, limit) { | ||
const options = {}; | ||
if (limit) { | ||
options.limit = limit; | ||
} | ||
/** | ||
* HTTP method | ||
* | ||
* For example: GET | ||
*/ | ||
get method() { | ||
return this.inner.method; | ||
if (encoding) { | ||
options.encoding = encoding; | ||
} | ||
/** | ||
* The request target. | ||
* | ||
* This contains the literal value after the HTTP method in the request. | ||
* So for: | ||
* | ||
* GET /foo HTTP/1.1 | ||
* | ||
* This would contain '/foo'. In many cases this is the same as the 'path' | ||
* property, but there's 3 other forms in the HTTP specificatio. Here's the | ||
* different formats: | ||
* | ||
* * origin-form - This is the most common. Example: /foo. | ||
* * absolute-form - Clients might sent an entire path. Also used by proxies. | ||
* Example: https://example.org/foo | ||
* * authority-form - Used by the CONNECT method. Example: example.org:1212 | ||
* * asterisk-form - Used by the OPTIONS method. Example: * | ||
* | ||
* In most cases users will want to use the 'path' property instead. only use | ||
* this if you know what you're doing. | ||
* | ||
* @see {@link https://tools.ietf.org/html/rfc7230#section-5.3} | ||
*/ | ||
get requestTarget() { | ||
return this.inner.url; | ||
const length = this.headers.get('Content-Length'); | ||
if (length) { | ||
options.length = length; | ||
} | ||
rawBody(encoding, limit) { | ||
const options = {}; | ||
if (limit) { | ||
options.limit = limit; | ||
return raw_body_1.default(this.inner, options); | ||
} | ||
/** | ||
* getStream returns a Node.js readable stream. | ||
* | ||
* A stream can typically only be read once. | ||
*/ | ||
getStream() { | ||
return this.inner; | ||
} | ||
/** | ||
* Returns the IP address of the HTTP client. | ||
* | ||
* If trustProxy is set to true, it means this server is running behind a | ||
* proxy, and the X-Forwarded-For header should be used instead. | ||
*/ | ||
ip(trustProxy = false) { | ||
if (trustProxy) { | ||
const forwardedForHeader = this.headers.get('X-Forwarded-For'); | ||
if (forwardedForHeader) { | ||
return forwardedForHeader.split(',')[0].trim(); | ||
} | ||
if (encoding) { | ||
options.encoding = encoding; | ||
} | ||
const length = this.headers.get('Content-Length'); | ||
if (length) { | ||
options.length = length; | ||
} | ||
return raw_body_1.default(this.inner, options); | ||
} | ||
/** | ||
* getStream returns a Node.js readable stream. | ||
* | ||
* A stream can typically only be read once. | ||
*/ | ||
getStream() { | ||
return this.inner; | ||
} | ||
/** | ||
* Returns the IP address of the HTTP client. | ||
* | ||
* If trustProxy is set to true, it means this server is running behind a | ||
* proxy, and the X-Forwarded-For header should be used instead. | ||
*/ | ||
ip(trustProxy = false) { | ||
if (trustProxy) { | ||
const forwardedForHeader = this.headers.get('X-Forwarded-For'); | ||
if (forwardedForHeader) { | ||
return forwardedForHeader.split(',')[0].trim(); | ||
} | ||
} | ||
return this.inner.socket.remoteAddress; | ||
} | ||
return this.inner.socket.remoteAddress; | ||
} | ||
exports.NodeRequest = NodeRequest; | ||
exports.default = NodeRequest; | ||
}); | ||
} | ||
exports.NodeRequest = NodeRequest; | ||
exports.default = NodeRequest; | ||
//# sourceMappingURL=request.js.map |
@@ -1,89 +0,79 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This is a wrapper around the Node Response object, and handles creates a | ||
* nicer API around Headers access. | ||
*/ | ||
class NodeHeaders { | ||
constructor(inner) { | ||
this.inner = inner; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports"], factory); | ||
/** | ||
* Sets a HTTP header name and value | ||
*/ | ||
set(name, value) { | ||
this.inner.setHeader(name, value); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* This is a wrapper around the Node Response object, and handles creates a | ||
* nicer API around Headers access. | ||
* Gets a HTTP header's value. | ||
* | ||
* This function will return null if the header did not exist. If it did | ||
* exist, it will return a string. | ||
* | ||
* If there were multiple headers with the same value, it will join the | ||
* headers with a comma. | ||
*/ | ||
class NodeHeaders { | ||
constructor(inner) { | ||
this.inner = inner; | ||
get(name) { | ||
const value = this.inner.getHeader(name); | ||
if (value === undefined || value === null) { | ||
return null; | ||
} | ||
/** | ||
* Sets a HTTP header name and value | ||
*/ | ||
set(name, value) { | ||
this.inner.setHeader(name, value); | ||
else if (typeof (value) === 'string') { | ||
return value; | ||
} | ||
/** | ||
* Gets a HTTP header's value. | ||
* | ||
* This function will return null if the header did not exist. If it did | ||
* exist, it will return a string. | ||
* | ||
* If there were multiple headers with the same value, it will join the | ||
* headers with a comma. | ||
*/ | ||
get(name) { | ||
const value = this.inner.getHeader(name); | ||
if (value === undefined || value === null) { | ||
return null; | ||
} | ||
else if (typeof (value) === 'string') { | ||
return value; | ||
} | ||
else if (Array.isArray(value)) { | ||
return value.join(', '); | ||
} | ||
else { | ||
return value.toString(); | ||
} | ||
else if (Array.isArray(value)) { | ||
return value.join(', '); | ||
} | ||
/** | ||
* Returns true or false depending on if a HTTP header exists. | ||
*/ | ||
has(name) { | ||
return !!this.inner.getHeader(name); | ||
else { | ||
return value.toString(); | ||
} | ||
/** | ||
* Removes a HTTP header | ||
*/ | ||
delete(name) { | ||
this.inner.removeHeader(name); | ||
} | ||
/** | ||
* Returns true or false depending on if a HTTP header exists. | ||
*/ | ||
has(name) { | ||
return !!this.inner.getHeader(name); | ||
} | ||
/** | ||
* Removes a HTTP header | ||
*/ | ||
delete(name) { | ||
this.inner.removeHeader(name); | ||
} | ||
/** | ||
* Returns all HTTP headers. | ||
* | ||
* Headernames are not lowercased. Values may be either strings or arrays of | ||
* strings. | ||
*/ | ||
getAll() { | ||
// @ts-ignore typescript doesn't like that the getHeaders function can | ||
// have undefined values, so we're just ignoring that problem. | ||
return this.inner.getHeaders(); | ||
} | ||
/** | ||
* Appends a new header, without removing an old one with the same name. | ||
*/ | ||
append(name, value) { | ||
let oldValue = this.inner.getHeader(name); | ||
if (oldValue === undefined) { | ||
oldValue = []; | ||
} | ||
/** | ||
* Returns all HTTP headers. | ||
* | ||
* Headernames are not lowercased. Values may be either strings or arrays of | ||
* strings. | ||
*/ | ||
getAll() { | ||
// @ts-ignore typescript doesn't like that the getHeaders function can | ||
// have undefined values, so we're just ignoring that problem. | ||
return this.inner.getHeaders(); | ||
if (!Array.isArray(oldValue)) { | ||
oldValue = [oldValue.toString()]; | ||
} | ||
/** | ||
* Appends a new header, without removing an old one with the same name. | ||
*/ | ||
append(name, value) { | ||
let oldValue = this.inner.getHeader(name); | ||
if (oldValue === undefined) { | ||
oldValue = []; | ||
} | ||
if (!Array.isArray(oldValue)) { | ||
oldValue = [oldValue.toString()]; | ||
} | ||
this.inner.setHeader(name, oldValue.concat(value)); | ||
} | ||
this.inner.setHeader(name, oldValue.concat(value)); | ||
} | ||
exports.default = NodeHeaders; | ||
}); | ||
} | ||
exports.default = NodeHeaders; | ||
//# sourceMappingURL=response-headers.js.map |
@@ -6,5 +6,5 @@ import { Middleware } from '../application'; | ||
import NodeHeaders from './response-headers'; | ||
export declare class NodeResponse<T> extends Response<T> { | ||
export declare class NodeResponse<T> implements Response<T> { | ||
private inner; | ||
private bodyValue; | ||
private bodyValue?; | ||
private explicitStatus; | ||
@@ -27,7 +27,7 @@ constructor(inner: NodeHttpResponse); | ||
*/ | ||
set body(value: T); | ||
set body(value: T | undefined); | ||
/** | ||
* Returns the response body. | ||
*/ | ||
get body(): T; | ||
get body(): T | undefined; | ||
/** | ||
@@ -47,3 +47,30 @@ * Sends an informational response before the real response. | ||
push(callback: Middleware): Promise<void>; | ||
/** | ||
* Returns the value of the Content-Type header, with any additional | ||
* parameters such as charset= removed. | ||
* | ||
* If there was no Content-Type header, an empty string will be returned. | ||
*/ | ||
get type(): string; | ||
/** | ||
* Shortcut for setting the Content-Type header. | ||
*/ | ||
set type(value: string); | ||
/** | ||
* This method will return true or false if a Request or Response has a | ||
* Content-Type header that matches the argument. | ||
* | ||
* For example, if the Content-Type header has the value: application/hal+json, | ||
* then the arguments will all return true: | ||
* | ||
* * application/hal+json | ||
* * application/json | ||
* * hal+json | ||
* * json | ||
* * application/* | ||
*/ | ||
is(type: string): boolean; | ||
redirect(address: string): void; | ||
redirect(status: number, address: string): void; | ||
} | ||
export default NodeResponse; |
@@ -0,142 +1,191 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http_1 = __importDefault(require("http")); | ||
const util_1 = require("util"); | ||
const application_1 = require("../application"); | ||
const base_context_1 = __importDefault(require("../base-context")); | ||
const memory_request_1 = __importDefault(require("../memory-request")); | ||
const memory_response_1 = __importDefault(require("../memory-response")); | ||
const http_utils_1 = require("./http-utils"); | ||
const push_1 = __importDefault(require("./push")); | ||
const response_headers_1 = __importDefault(require("./response-headers")); | ||
const header_helpers_1 = require("./../header-helpers"); | ||
class NodeResponse { | ||
constructor(inner) { | ||
// The default response status is 404. | ||
this.inner = inner; | ||
// @ts-ignore: Typescript doesn't like null here because it might be | ||
// incompatible with T, but we're ignoring it as it's a good default. | ||
this.body = null; | ||
this.status = 404; | ||
this.explicitStatus = false; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "http", "util", "../application", "../base-context", "../memory-request", "../memory-response", "../response", "./http-utils", "./push", "./response-headers"], factory); | ||
/** | ||
* List of HTTP Headers | ||
*/ | ||
get headers() { | ||
return new response_headers_1.default(this.inner); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const http_1 = __importDefault(require("http")); | ||
const util_1 = require("util"); | ||
const application_1 = require("../application"); | ||
const base_context_1 = __importDefault(require("../base-context")); | ||
const memory_request_1 = __importDefault(require("../memory-request")); | ||
const memory_response_1 = __importDefault(require("../memory-response")); | ||
const response_1 = __importDefault(require("../response")); | ||
const http_utils_1 = require("./http-utils"); | ||
const push_1 = __importDefault(require("./push")); | ||
const response_headers_1 = __importDefault(require("./response-headers")); | ||
class NodeResponse extends response_1.default { | ||
constructor(inner) { | ||
super(); | ||
// The default response status is 404. | ||
this.inner = inner; | ||
// @ts-ignore: Typescript doesn't like null here because it might be | ||
// incompatible with T, but we're ignoring it as it's a good default. | ||
this.body = null; | ||
this.status = 404; | ||
this.explicitStatus = false; | ||
/** | ||
* HTTP status code. | ||
*/ | ||
get status() { | ||
return this.inner.statusCode; | ||
} | ||
/** | ||
* Updates the HTTP status code for this response. | ||
*/ | ||
set status(value) { | ||
this.explicitStatus = true; | ||
this.inner.statusCode = value; | ||
} | ||
/** | ||
* Updates the response body. | ||
*/ | ||
set body(value) { | ||
if (!this.explicitStatus) { | ||
// If no status was set earlier, we set it to 200. | ||
this.inner.statusCode = 200; | ||
} | ||
/** | ||
* List of HTTP Headers | ||
*/ | ||
get headers() { | ||
return new response_headers_1.default(this.inner); | ||
} | ||
/** | ||
* HTTP status code. | ||
*/ | ||
get status() { | ||
return this.inner.statusCode; | ||
} | ||
/** | ||
* Updates the HTTP status code for this response. | ||
*/ | ||
set status(value) { | ||
this.explicitStatus = true; | ||
this.inner.statusCode = value; | ||
} | ||
/** | ||
* Updates the response body. | ||
*/ | ||
set body(value) { | ||
if (!this.explicitStatus) { | ||
// If no status was set earlier, we set it to 200. | ||
this.inner.statusCode = 200; | ||
this.bodyValue = value; | ||
} | ||
/** | ||
* Returns the response body. | ||
*/ | ||
get body() { | ||
return this.bodyValue; | ||
} | ||
/** | ||
* Sends an informational response before the real response. | ||
* | ||
* This can be used to for example send a `100 Continue` or `103 Early Hints` | ||
* response. | ||
*/ | ||
async sendInformational(status, headers) { | ||
let outHeaders = {}; | ||
if (typeof headers !== 'undefined') { | ||
if (headers.getAll !== undefined) { | ||
outHeaders = headers.getAll(); | ||
} | ||
this.bodyValue = value; | ||
else { | ||
outHeaders = headers; | ||
} | ||
} | ||
/** | ||
* Returns the response body. | ||
* It's a HTTP2 connection. | ||
*/ | ||
get body() { | ||
return this.bodyValue; | ||
if (http_utils_1.isHttp2Response(this.inner)) { | ||
this.inner.stream.additionalHeaders({ | ||
':status': status, | ||
...outHeaders | ||
}); | ||
} | ||
/** | ||
* Sends an informational response before the real response. | ||
* | ||
* This can be used to for example send a `100 Continue` or `103 Early Hints` | ||
* response. | ||
*/ | ||
async sendInformational(status, headers) { | ||
let outHeaders = {}; | ||
if (typeof headers !== 'undefined') { | ||
if (headers.getAll !== undefined) { | ||
outHeaders = headers.getAll(); | ||
else { | ||
const rawHeaders = []; | ||
for (const headerName of Object.keys(outHeaders)) { | ||
const headerValue = outHeaders[headerName]; | ||
if (Array.isArray(headerValue)) { | ||
for (const headerVal of headerValue) { | ||
rawHeaders.push(`${headerName}: ${headerVal}\r\n`); | ||
} | ||
} | ||
else { | ||
outHeaders = headers; | ||
rawHeaders.push(`${headerName}: ${headerValue}\r\n`); | ||
} | ||
} | ||
/** | ||
* It's a HTTP2 connection. | ||
*/ | ||
if (http_utils_1.isHttp2Response(this.inner)) { | ||
this.inner.stream.additionalHeaders({ | ||
':status': status, | ||
...outHeaders | ||
}); | ||
} | ||
else { | ||
const rawHeaders = []; | ||
for (const headerName of Object.keys(outHeaders)) { | ||
const headerValue = outHeaders[headerName]; | ||
if (Array.isArray(headerValue)) { | ||
for (const headerVal of headerValue) { | ||
rawHeaders.push(`${headerName}: ${headerVal}\r\n`); | ||
} | ||
} | ||
else { | ||
rawHeaders.push(`${headerName}: ${headerValue}\r\n`); | ||
} | ||
} | ||
// @ts-ignore | ||
const writeRaw = util_1.promisify(this.inner._writeRaw.bind(this.inner)); | ||
const message = `HTTP/1.1 ${status} ${http_1.default.STATUS_CODES[status]}\r\n${rawHeaders.join('')}\r\n`; | ||
await writeRaw(message, 'ascii'); | ||
} | ||
// @ts-ignore | ||
const writeRaw = util_1.promisify(this.inner._writeRaw.bind(this.inner)); | ||
const message = `HTTP/1.1 ${status} ${http_1.default.STATUS_CODES[status]}\r\n${rawHeaders.join('')}\r\n`; | ||
await writeRaw(message, 'ascii'); | ||
} | ||
/** | ||
* Sends a HTTP/2 push. | ||
* | ||
* The passed middleware will be called with a new Context object specific | ||
* for pushes. | ||
*/ | ||
async push(callback) { | ||
if (!http_utils_1.isHttp2Response(this.inner)) { | ||
// Not HTTP2 | ||
return; | ||
} | ||
const stream = this.inner.stream; | ||
if (!stream.pushAllowed) { | ||
// Client doesn't want pushes | ||
return; | ||
} | ||
const pushCtx = new base_context_1.default(new memory_request_1.default('GET', '|||DELIBERATELY_INVALID|||'), new memory_response_1.default()); | ||
await application_1.invokeMiddlewares(pushCtx, [callback]); | ||
if (pushCtx.request.requestTarget === '|||DELIBERATELY_INVALID|||') { | ||
throw new Error('The "path" must be set in the push context\'s request'); | ||
} | ||
return push_1.default(stream, pushCtx); | ||
} | ||
/** | ||
* Sends a HTTP/2 push. | ||
* | ||
* The passed middleware will be called with a new Context object specific | ||
* for pushes. | ||
*/ | ||
async push(callback) { | ||
if (!http_utils_1.isHttp2Response(this.inner)) { | ||
// Not HTTP2 | ||
return; | ||
} | ||
const stream = this.inner.stream; | ||
if (!stream.pushAllowed) { | ||
// Client doesn't want pushes | ||
return; | ||
} | ||
const pushCtx = new base_context_1.default(new memory_request_1.default('GET', '|||DELIBERATELY_INVALID|||'), new memory_response_1.default()); | ||
await application_1.invokeMiddlewares(pushCtx, [callback]); | ||
if (pushCtx.request.requestTarget === '|||DELIBERATELY_INVALID|||') { | ||
throw new Error('The "path" must be set in the push context\'s request'); | ||
} | ||
return push_1.default(stream, pushCtx); | ||
} | ||
exports.NodeResponse = NodeResponse; | ||
exports.default = NodeResponse; | ||
}); | ||
/** | ||
* Returns the value of the Content-Type header, with any additional | ||
* parameters such as charset= removed. | ||
* | ||
* If there was no Content-Type header, an empty string will be returned. | ||
*/ | ||
get type() { | ||
const type = this.headers.get('content-type'); | ||
if (!type) { | ||
return ''; | ||
} | ||
return type.split(';')[0]; | ||
} | ||
/** | ||
* Shortcut for setting the Content-Type header. | ||
*/ | ||
set type(value) { | ||
this.headers.set('content-type', value); | ||
} | ||
/** | ||
* This method will return true or false if a Request or Response has a | ||
* Content-Type header that matches the argument. | ||
* | ||
* For example, if the Content-Type header has the value: application/hal+json, | ||
* then the arguments will all return true: | ||
* | ||
* * application/hal+json | ||
* * application/json | ||
* * hal+json | ||
* * json | ||
* * application/* | ||
*/ | ||
is(type) { | ||
return header_helpers_1.is(this, type); | ||
} | ||
/** | ||
* redirect redirects the response with an optionally provided HTTP status | ||
* code in the first position to the location provided in address. If no status | ||
* is provided, 303 See Other is used. | ||
* | ||
* @param {(string|number)} addrOrStatus if passed a string, the string will | ||
* be used to set the Location header of the response object and the default status | ||
* of 303 See Other will be used. If a number, an addressed must be passed in the second | ||
* argument. | ||
* @param {string} address If addrOrStatus is passed a status code, this value is | ||
* set as the value of the response's Location header. | ||
*/ | ||
redirect(addrOrStatus, address = '') { | ||
let status = 303; | ||
let addr; | ||
if (typeof (addrOrStatus) === 'number') { | ||
status = addrOrStatus; | ||
addr = address; | ||
} | ||
else { | ||
addr = addrOrStatus; | ||
} | ||
this.status = status; | ||
this.headers.set('Location', addr); | ||
} | ||
} | ||
exports.NodeResponse = NodeResponse; | ||
exports.default = NodeResponse; | ||
//# sourceMappingURL=response.js.map |
@@ -7,3 +7,3 @@ /// <reference types="node" /> | ||
*/ | ||
export declare abstract class Request<T = any> { | ||
export interface Request<T = any> { | ||
/** | ||
@@ -18,8 +18,4 @@ * List of HTTP Headers | ||
*/ | ||
get path(): string; | ||
path: string; | ||
/** | ||
* Sets the request path | ||
*/ | ||
set path(value: string); | ||
/** | ||
* HTTP method | ||
@@ -58,3 +54,3 @@ * | ||
*/ | ||
body: T; | ||
body?: T; | ||
/** | ||
@@ -69,4 +65,4 @@ * This function returns the request body. | ||
*/ | ||
abstract rawBody(encoding: string, limit?: string): Promise<string>; | ||
abstract rawBody(encoding?: undefined, limit?: string): Promise<Buffer>; | ||
rawBody(encoding: string, limit?: string): Promise<string>; | ||
rawBody(encoding?: undefined, limit?: string): Promise<Buffer>; | ||
/** | ||
@@ -77,7 +73,7 @@ * getStream returns a Node.js readable stream. | ||
*/ | ||
abstract getStream(): Readable; | ||
getStream(): Readable; | ||
/** | ||
* This object contains parsed query parameters. | ||
*/ | ||
get query(): { | ||
query: { | ||
[s: string]: string; | ||
@@ -91,3 +87,3 @@ }; | ||
*/ | ||
get type(): string; | ||
type: string; | ||
/** | ||
@@ -94,0 +90,0 @@ * accepts is used for negotation the Content-Type with a client. |
@@ -1,104 +0,3 @@ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "accepts", "url", "./header-helpers"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const accepts_1 = __importDefault(require("accepts")); | ||
const url_1 = __importDefault(require("url")); | ||
const header_helpers_1 = require("./header-helpers"); | ||
/** | ||
* This interface represents an incoming server request. | ||
*/ | ||
class Request { | ||
/** | ||
* path-part of the request. | ||
* | ||
* For example /hello/world | ||
*/ | ||
get path() { | ||
return url_1.default.parse(this.requestTarget).pathname; | ||
} | ||
/** | ||
* Sets the request path | ||
*/ | ||
set path(value) { | ||
this.requestTarget = value; | ||
} | ||
/** | ||
* This object contains parsed query parameters. | ||
*/ | ||
get query() { | ||
return url_1.default.parse(this.requestTarget, true).query; | ||
} | ||
/** | ||
* Returns the value of the Content-Type header, with any additional | ||
* parameters such as charset= removed. | ||
* | ||
* If there was no Content-Type header, an empty string will be returned. | ||
*/ | ||
get type() { | ||
const type = this.headers.get('content-type'); | ||
if (!type) { | ||
return ''; | ||
} | ||
return type.split(';')[0]; | ||
} | ||
/** | ||
* accepts is used for negotation the Content-Type with a client. | ||
* | ||
* You can pass a content-type, or an array of content-types. | ||
* The Content-Types you provide are a list of types your application | ||
* supports. | ||
* | ||
* This function will then return the best possible type based on the Accept | ||
* header. | ||
* | ||
* If no compatible types are found, this function returns null. | ||
*/ | ||
accepts(...types) { | ||
const mockRequestObj = { | ||
headers: { | ||
accept: this.headers.get('Accept') | ||
} | ||
}; | ||
const result = accepts_1.default(mockRequestObj).type(types); | ||
return result === false ? null : result; | ||
} | ||
/** | ||
* This method will return true or false if a Request or Response has a | ||
* Content-Type header that matches the argument. | ||
* | ||
* For example, if the Content-Type header has the value: application/hal+json, | ||
* then the arguments will all return true: | ||
* | ||
* * application/hal+json | ||
* * application/json | ||
* * hal+json | ||
* * json | ||
* * application/* | ||
*/ | ||
is(type) { | ||
return header_helpers_1.is(this, type); | ||
} | ||
prefer(preference) { | ||
const prefer = header_helpers_1.parsePrefer(this.headers.get('Prefer')); | ||
const val = prefer[preference]; | ||
if (val === undefined) { | ||
return false; | ||
} | ||
return val; | ||
} | ||
} | ||
exports.Request = Request; | ||
exports.default = Request; | ||
}); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=request.js.map |
@@ -6,3 +6,3 @@ import { Middleware } from './application'; | ||
*/ | ||
export declare abstract class Response<T = any> { | ||
export interface Response<T = any> { | ||
/** | ||
@@ -19,3 +19,3 @@ * List of HTTP Headers | ||
*/ | ||
body: T; | ||
body?: T; | ||
/** | ||
@@ -27,8 +27,4 @@ * Returns the value of the Content-Type header, with any additional | ||
*/ | ||
get type(): string; | ||
type: string; | ||
/** | ||
* Shortcut for setting the Content-Type header. | ||
*/ | ||
set type(value: string); | ||
/** | ||
* Sends an informational response before the real response. | ||
@@ -35,0 +31,0 @@ * |
@@ -1,99 +0,3 @@ | ||
(function (factory) { | ||
if (typeof module === "object" && typeof module.exports === "object") { | ||
var v = factory(require, exports); | ||
if (v !== undefined) module.exports = v; | ||
} | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "./header-helpers"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const header_helpers_1 = require("./header-helpers"); | ||
/** | ||
* This interface represents an incoming server request. | ||
*/ | ||
class Response { | ||
/** | ||
* Returns the value of the Content-Type header, with any additional | ||
* parameters such as charset= removed. | ||
* | ||
* If there was no Content-Type header, an empty string will be returned. | ||
*/ | ||
get type() { | ||
const type = this.headers.get('content-type'); | ||
if (!type) { | ||
return ''; | ||
} | ||
return type.split(';')[0]; | ||
} | ||
/** | ||
* Shortcut for setting the Content-Type header. | ||
*/ | ||
set type(value) { | ||
this.headers.set('content-type', value); | ||
} | ||
/** | ||
* Sends an informational response before the real response. | ||
* | ||
* This can be used to for example send a `100 Continue` or `103 Early Hints` | ||
* response. | ||
*/ | ||
async sendInformational(status, headers) { | ||
// No need to do anything | ||
} | ||
/** | ||
* Sends a HTTP/2 push. | ||
* | ||
* The passed middleware will be called with a new Context object specific | ||
* for pushes. | ||
*/ | ||
async push(callback) { | ||
// Don't do anything | ||
} | ||
/** | ||
* This method will return true or false if a Request or Response has a | ||
* Content-Type header that matches the argument. | ||
* | ||
* For example, if the Content-Type header has the value: application/hal+json, | ||
* then the arguments will all return true: | ||
* | ||
* * application/hal+json | ||
* * application/json | ||
* * hal+json | ||
* * json | ||
* * application/* | ||
*/ | ||
is(type) { | ||
return header_helpers_1.is(this, type); | ||
} | ||
/** | ||
* redirect redirects the response with an optionally provided HTTP status | ||
* code in the first position to the location provided in address. If no status | ||
* is provided, 303 See Other is used. | ||
* | ||
* @param {(string|number)} addrOrStatus if passed a string, the string will | ||
* be used to set the Location header of the response object and the default status | ||
* of 303 See Other will be used. If a number, an addressed must be passed in the second | ||
* argument. | ||
* @param {string} address If addrOrStatus is passed a status code, this value is | ||
* set as the value of the response's Location header. | ||
*/ | ||
redirect(addrOrStatus, address = '') { | ||
let status = 303; | ||
let addr; | ||
if (typeof (addrOrStatus) === 'number') { | ||
status = addrOrStatus; | ||
addr = address; | ||
} | ||
else { | ||
addr = addrOrStatus; | ||
} | ||
this.status = status; | ||
this.headers.set('Location', addr); | ||
} | ||
} | ||
exports.Response = Response; | ||
exports.default = Response; | ||
}); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=response.js.map |
{ | ||
"name": "@curveball/core", | ||
"version": "0.11.3-0", | ||
"version": "0.12.0", | ||
"description": "Curveball is a framework writting in Typescript for Node.js", | ||
@@ -36,3 +36,3 @@ "main": "dist/index.js", | ||
"@types/accepts": "^1.3.5", | ||
"@types/chai": "^4.2.10", | ||
"@types/chai": "^4.2.11", | ||
"@types/co-body": "0.0.3", | ||
@@ -44,8 +44,8 @@ "@types/mocha": "^7.0.2", | ||
"chai": "^4.2.0", | ||
"mocha": "^7.1.0", | ||
"mocha": "^7.1.1", | ||
"node-fetch": "^2.6.0", | ||
"nyc": "^15.0.0", | ||
"sinon": "^9.0.0", | ||
"ts-node": "^8.6.2", | ||
"tslint": "^6.0.0", | ||
"sinon": "^9.0.1", | ||
"ts-node": "^8.8.1", | ||
"tslint": "^6.1.0", | ||
"typescript": "^3.8.3" | ||
@@ -52,0 +52,0 @@ }, |
@@ -58,3 +58,3 @@ Curveball | ||
* [Controller](https://github.com/curveball/controller). | ||
* [Access logs](https://github.com/curveball/access-log). | ||
* [Access Logs](https://github.com/curveball/accesslog). | ||
* [Sessions](https://github.com/curveball/session). | ||
@@ -61,0 +61,0 @@ * [Generating application/problem+json responses](https://github.com/curveball/problem). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
3
121343
2323