Sindbad~EG File Manager
'use strict';
const Stream = require('stream');
const Boom = require('@hapi/boom');
const Bounce = require('@hapi/bounce');
const Hoek = require('@hapi/hoek');
const Podium = require('@hapi/podium');
const Streams = require('./streams');
const internals = {
events: Podium.validate(['finish', { name: 'peek', spread: true }]),
hopByHop: {
connection: true,
'keep-alive': true,
'proxy-authenticate': true,
'proxy-authorization': true,
'te': true,
'trailer': true,
'transfer-encoding': true,
'upgrade': true
},
reserved: ['app', 'headers', 'plugins', 'request', 'source', 'statusCode', 'variety',
'settings', 'events', 'code', 'message', 'header', 'vary', 'etag', 'type', 'contentType',
'bytes', 'location', 'created', 'compressed', 'replacer', 'space', 'suffix', 'escape',
'passThrough', 'redirect', 'temporary', 'permanent', 'rewritable', 'encoding', 'charset',
'ttl', 'state', 'unstate', 'takeover']
};
exports = module.exports = internals.Response = class {
constructor(source, request, options = {}) {
this.app = {};
this.headers = {}; // Incomplete as some headers are stored in flags
this.plugins = {};
this.request = request;
this.source = null;
this.statusCode = null;
this.variety = null;
this.settings = {
charset: 'utf-8', // '-' required by IANA
compressed: null,
encoding: 'utf8',
message: null,
passThrough: true,
stringify: null, // JSON.stringify options
ttl: null,
varyEtag: false
};
this._events = null;
this._payload = null; // Readable stream
this._error = options.error ?? null; // The boom object when created from an error (used for logging)
this._contentType = null; // Used if no explicit content-type is set and type is known
this._takeover = false;
this._statusCode = false; // true when code() called
this._state = this._error ? 'prepare' : 'init'; // One of 'init', 'prepare', 'marshall', 'close'
this._processors = {
marshal: options.marshal,
prepare: options.prepare,
close: options.close
};
this._setSource(source, options.variety);
}
static wrap(result, request) {
if (result instanceof request._core.Response ||
typeof result === 'symbol') {
return result;
}
if (result instanceof Error) {
return Boom.boomify(result);
}
return new request._core.Response(result, request);
}
_setSource(source, variety) {
// Method must not set any headers or other properties as source can change later
this.variety = variety ?? 'plain';
if (source === null ||
source === undefined) {
source = null;
}
else if (Buffer.isBuffer(source)) {
this.variety = 'buffer';
this._contentType = 'application/octet-stream';
}
else if (Streams.isStream(source)) {
this.variety = 'stream';
this._contentType = 'application/octet-stream';
}
this.source = source;
if (this.variety === 'plain' &&
this.source !== null) {
this._contentType = typeof this.source === 'string' ? 'text/html' : 'application/json';
}
}
get events() {
if (!this._events) {
this._events = new Podium.Podium(internals.events);
}
return this._events;
}
code(statusCode) {
Hoek.assert(Number.isSafeInteger(statusCode), 'Status code must be an integer');
this.statusCode = statusCode;
this._statusCode = true;
return this;
}
message(httpMessage) {
this.settings.message = httpMessage;
return this;
}
header(key, value, options) {
key = key.toLowerCase();
if (key === 'vary') {
return this.vary(value);
}
return this._header(key, value, options);
}
_header(key, value, options = {}) {
const append = options.append ?? false;
const separator = options.separator || ',';
const override = options.override !== false;
const duplicate = options.duplicate !== false;
if (!append && override ||
!this.headers[key]) {
this.headers[key] = value;
}
else if (override) {
if (key === 'set-cookie') {
this.headers[key] = [].concat(this.headers[key], value);
}
else {
const existing = this.headers[key];
if (!duplicate) {
const values = existing.split(separator);
for (const v of values) {
if (v === value) {
return this;
}
}
}
this.headers[key] = existing + separator + value;
}
}
return this;
}
vary(value) {
if (value === '*') {
this.headers.vary = '*';
}
else if (!this.headers.vary) {
this.headers.vary = value;
}
else if (this.headers.vary !== '*') {
this._header('vary', value, { append: true, duplicate: false });
}
return this;
}
etag(tag, options) {
const entity = this.request._core.Response.entity(tag, options);
this._header('etag', entity.etag);
this.settings.varyEtag = entity.vary;
return this;
}
static entity(tag, options = {}) {
Hoek.assert(tag !== '*', 'ETag cannot be *');
return {
etag: (options.weak ? 'W/' : '') + '"' + tag + '"',
vary: options.vary !== false && !options.weak, // vary defaults to true
modified: options.modified
};
}
static unmodified(request, entity) {
if (request.method !== 'get' &&
request.method !== 'head') {
return false;
}
// Strong verifier
if (entity.etag &&
request.headers['if-none-match']) {
const ifNoneMatch = request.headers['if-none-match'].split(/\s*,\s*/);
for (const etag of ifNoneMatch) {
// Compare tags (https://tools.ietf.org/html/rfc7232#section-2.3.2)
if (etag === entity.etag) { // Strong comparison
return true;
}
if (!entity.vary) {
continue;
}
if (etag === `W/${entity.etag}`) { // Weak comparison
return etag;
}
const etagBase = entity.etag.slice(0, -1);
const encoders = request._core.compression.encodings;
for (const encoder of encoders) {
if (etag === etagBase + `-${encoder}"`) {
return true;
}
}
}
return false;
}
// Weak verifier
if (!entity.modified) {
return false;
}
const ifModifiedSinceHeader = request.headers['if-modified-since'];
if (!ifModifiedSinceHeader) {
return false;
}
const ifModifiedSince = internals.parseDate(ifModifiedSinceHeader);
if (!ifModifiedSince) {
return false;
}
const lastModified = internals.parseDate(entity.modified);
if (!lastModified) {
return false;
}
return ifModifiedSince >= lastModified;
}
type(type) {
this._header('content-type', type);
return this;
}
get contentType() {
let type = this.headers['content-type'];
if (type) {
type = type.trim();
if (this.settings.charset &&
type.match(/^(?:text\/)|(?:application\/(?:json)|(?:javascript))/) &&
!type.match(/; *charset=/)) {
const semi = type[type.length - 1] === ';';
return type + (semi ? ' ' : '; ') + 'charset=' + this.settings.charset;
}
return type;
}
if (this._contentType) {
const charset = this.settings.charset && this._contentType !== 'application/octet-stream' ? '; charset=' + this.settings.charset : '';
return this._contentType + charset;
}
return null;
}
bytes(bytes) {
this._header('content-length', bytes);
return this;
}
location(uri) {
this._header('location', uri);
return this;
}
created(location) {
Hoek.assert(this.request.method === 'post' ||
this.request.method === 'put' ||
this.request.method === 'patch', 'Cannot return 201 status codes for ' + this.request.method.toUpperCase());
this.statusCode = 201;
this.location(location);
return this;
}
compressed(encoding) {
Hoek.assert(encoding && typeof encoding === 'string', 'Invalid content-encoding');
this.settings.compressed = encoding;
return this;
}
replacer(method) {
this.settings.stringify = this.settings.stringify ?? {};
this.settings.stringify.replacer = method;
return this;
}
spaces(count) {
this.settings.stringify = this.settings.stringify ?? {};
this.settings.stringify.space = count;
return this;
}
suffix(suffix) {
this.settings.stringify = this.settings.stringify ?? {};
this.settings.stringify.suffix = suffix;
return this;
}
escape(escape) {
this.settings.stringify = this.settings.stringify ?? {};
this.settings.stringify.escape = escape;
return this;
}
passThrough(enabled) {
this.settings.passThrough = enabled !== false; // Defaults to true
return this;
}
redirect(location) {
this.statusCode = 302;
this.location(location);
return this;
}
temporary(isTemporary) {
Hoek.assert(this.headers.location, 'Cannot set redirection mode without first setting a location');
this._setTemporary(isTemporary !== false); // Defaults to true
return this;
}
permanent(isPermanent) {
Hoek.assert(this.headers.location, 'Cannot set redirection mode without first setting a location');
this._setTemporary(isPermanent === false); // Defaults to true
return this;
}
rewritable(isRewritable) {
Hoek.assert(this.headers.location, 'Cannot set redirection mode without first setting a location');
this._setRewritable(isRewritable !== false); // Defaults to true
return this;
}
_isTemporary() {
return this.statusCode === 302 || this.statusCode === 307;
}
_isRewritable() {
return this.statusCode === 301 || this.statusCode === 302;
}
_setTemporary(isTemporary) {
if (isTemporary) {
if (this._isRewritable()) {
this.statusCode = 302;
}
else {
this.statusCode = 307;
}
}
else {
if (this._isRewritable()) {
this.statusCode = 301;
}
else {
this.statusCode = 308;
}
}
}
_setRewritable(isRewritable) {
if (isRewritable) {
if (this._isTemporary()) {
this.statusCode = 302;
}
else {
this.statusCode = 301;
}
}
else {
if (this._isTemporary()) {
this.statusCode = 307;
}
else {
this.statusCode = 308;
}
}
}
encoding(encoding) {
this.settings.encoding = encoding;
return this;
}
charset(charset) {
this.settings.charset = charset ?? null;
return this;
}
ttl(ttl) {
this.settings.ttl = ttl;
return this;
}
state(name, value, options) {
this.request._setState(name, value, options);
return this;
}
unstate(name, options) {
this.request._clearState(name, options);
return this;
}
takeover() {
this._takeover = true;
return this;
}
_prepare() {
Hoek.assert(this._state === 'init');
this._state = 'prepare';
this._passThrough();
if (!this._processors.prepare) {
return this;
}
try {
return this._processors.prepare(this);
}
catch (err) {
throw Boom.boomify(err);
}
}
_passThrough() {
if (this.variety === 'stream' &&
this.settings.passThrough) {
if (this.source.statusCode &&
!this.statusCode) {
this.statusCode = this.source.statusCode; // Stream is an HTTP response
}
if (this.source.headers) {
let headerKeys = Object.keys(this.source.headers);
if (headerKeys.length) {
const localHeaders = this.headers;
this.headers = {};
const connection = this.source.headers.connection;
const byHop = {};
if (connection) {
connection.split(/\s*,\s*/).forEach((header) => {
byHop[header] = true;
});
}
for (const key of headerKeys) {
const lower = key.toLowerCase();
if (!internals.hopByHop[lower] &&
!byHop[lower]) {
this.header(lower, Hoek.clone(this.source.headers[key])); // Clone arrays
}
}
headerKeys = Object.keys(localHeaders);
for (const key of headerKeys) {
this.header(key, localHeaders[key], { append: key === 'set-cookie' });
}
}
}
}
this.statusCode = this.statusCode ?? 200;
}
async _marshal() {
Hoek.assert(this._state === 'prepare');
this._state = 'marshall';
// Processor marshal
let source = this.source;
if (this._processors.marshal) {
try {
source = await this._processors.marshal(this);
}
catch (err) {
throw Boom.boomify(err);
}
}
// Stream source
if (Streams.isStream(source)) {
this._payload = source;
return;
}
// Plain source (non string or null)
const jsonify = this.variety === 'plain' && source !== null && typeof source !== 'string';
if (!jsonify &&
this.settings.stringify) {
throw Boom.badImplementation('Cannot set formatting options on non object response');
}
let payload = source;
if (jsonify) {
const options = this.settings.stringify ?? {};
const space = options.space ?? this.request.route.settings.json.space;
const replacer = options.replacer ?? this.request.route.settings.json.replacer;
const suffix = options.suffix ?? this.request.route.settings.json.suffix ?? '';
const escape = this.request.route.settings.json.escape;
try {
if (replacer || space) {
payload = JSON.stringify(payload, replacer, space);
}
else {
payload = JSON.stringify(payload);
}
}
catch (err) {
throw Boom.boomify(err);
}
if (suffix) {
payload = payload + suffix;
}
if (escape) {
payload = Hoek.escapeJson(payload);
}
}
this._payload = new internals.Response.Payload(payload, this.settings);
}
_tap() {
if (!this._events) {
return null;
}
if (this._events.hasListeners('peek') ||
this._events.hasListeners('finish')) {
return new internals.Response.Peek(this._events);
}
return null;
}
_close() {
if (this._state === 'close') {
return;
}
this._state = 'close';
if (this._processors.close) {
try {
this._processors.close(this);
}
catch (err) {
Bounce.rethrow(err, 'system');
this.request._log(['response', 'cleanup', 'error'], err);
}
}
const stream = this._payload || this.source;
if (Streams.isStream(stream)) {
internals.Response.drain(stream);
}
}
_isPayloadSupported() {
return this.request.method !== 'head' && this.statusCode !== 304 && this.statusCode !== 204;
}
static drain(stream) {
stream.destroy();
}
};
internals.Response.reserved = internals.reserved;
internals.parseDate = function (string) {
try {
return Date.parse(string);
}
catch (errIgnore) { }
};
internals.Response.Payload = class extends Stream.Readable {
constructor(payload, options) {
super();
this._data = payload;
this._encoding = options.encoding;
}
_read(size) {
if (this._data) {
this.push(this._data, this._encoding);
}
this.push(null);
}
size() {
if (!this._data) {
return 0;
}
return Buffer.isBuffer(this._data) ? this._data.length : Buffer.byteLength(this._data, this._encoding);
}
writeToStream(stream) {
if (this._data) {
stream.write(this._data, this._encoding);
}
stream.end();
}
};
internals.Response.Peek = class extends Stream.Transform {
constructor(podium) {
super();
this._podium = podium;
this.on('finish', () => podium.emit('finish'));
}
_transform(chunk, encoding, callback) {
this._podium.emit('peek', [chunk, encoding]);
this.push(chunk, encoding);
callback();
}
};
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists