@leansdk/leanrc
Version:
LeanRC is a MVC framework for creating graceful applications
531 lines (480 loc) • 14.4 kB
JavaScript
(function() {
// This file is part of LeanRC.
// LeanRC is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// LeanRC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public License
// along with LeanRC. If not, see <https://www.gnu.org/licenses/>.
// contentDisposition = require 'content-disposition'
// ensureErrorHandler = require 'error-inject'
// getType = require('mime-types').contentType
// onFinish = require 'on-finished'
// escape = require 'escape-html'
// typeis = require('type-is').is
// destroy = require 'destroy'
// assert = require 'assert'
// extname = require('path').extname
// vary = require 'vary'
// Stream = require 'stream'
/*
Идеи взяты из https://github.com/koajs/koa/blob/master/lib/response.js
*/
var Stream,
hasProp = {}.hasOwnProperty;
Stream = require('stream');
module.exports = function(Module) {
var AnyT, ContextInterface, CoreObject, FuncG, MaybeG, NilT, Response, ResponseInterface, SwitchInterface, UnionG, _, statuses;
({
AnyT,
NilT,
FuncG,
UnionG,
MaybeG,
ResponseInterface,
SwitchInterface,
ContextInterface,
CoreObject,
Utils: {_, statuses}
} = Module.prototype);
return Response = (function() {
class Response extends CoreObject {};
Response.inheritProtected();
Response.implements(ResponseInterface);
Response.module(Module);
Response.public({
res: Object // native response object
}, {
get: function() {
return this.ctx.res;
}
});
Response.public({
switch: SwitchInterface
}, {
get: function() {
return this.ctx.switch;
}
});
Response.public({
ctx: ContextInterface
});
Response.public({
socket: MaybeG(Object)
}, {
get: function() {
return this.ctx.req.socket;
}
});
Response.public({
header: Object
}, {
get: function() {
return this.headers;
}
});
Response.public({
headers: Object
}, {
get: function() {
var ref;
if (_.isFunction(this.res.getHeaders)) {
return this.res.getHeaders();
} else {
return (ref = this.res._headers) != null ? ref : {};
}
}
});
Response.public({
status: MaybeG(Number)
}, {
get: function() {
return this.res.statusCode;
},
set: function(code) {
var assert;
assert = require('assert');
assert(_.isNumber(code), 'status code must be a number');
assert(statuses[code], `invalid status code: ${code}`);
assert(!this.res.headersSent, 'headers have already been sent');
this._explicitStatus = true;
this.res.statusCode = code;
this.res.statusMessage = statuses[code];
if (Boolean(this.body && statuses.empty[code])) {
this.body = null;
}
return code;
}
});
Response.public({
message: String
}, {
get: function() {
var ref;
return (ref = this.res.statusMessage) != null ? ref : statuses[this.status];
},
set: function(msg) {
this.res.statusMessage = msg;
return msg;
}
});
Response.public({
body: MaybeG(UnionG(String, Buffer, Object, Array, Number, Boolean, Stream))
}, {
get: function() {
return this._body;
},
set: function(val) {
var destroy, ensureErrorHandler, onFinish, original, setType;
original = this._body;
this._body = val;
if (this.res.headersSent) {
return;
}
if (val == null) {
if (!statuses.empty[this.status]) {
this.status = 204;
}
this.remove('Content-Type');
this.remove('Content-Length');
this.remove('Transfer-Encoding');
return;
}
if (!this._explicitStatus) {
this.status = 200;
}
setType = !this.headers['content-type'];
if (_.isString(val)) {
if (setType) {
this.type = /^\s*</.test(val) ? 'html' : 'text';
}
this.length = Buffer.byteLength(val);
return;
}
if (_.isBuffer(val)) {
if (setType) {
this.type = 'bin';
}
this.length = val.length;
return;
}
if (_.isFunction(val.pipe)) {
onFinish = require('on-finished');
destroy = require('destroy');
onFinish(this.res, destroy.bind(null, val));
ensureErrorHandler = require('error-inject');
ensureErrorHandler(val, (err) => {
return this.ctx.onerror(err);
});
if ((original != null) && original !== val) {
this.remove('Content-Length');
}
if (setType) {
this.type = 'bin';
}
return;
}
this.remove('Content-Length');
this.type = 'json';
return val;
}
});
// @public body: [String, Buffer]
// @public locals: Object
// @public headers: Object
// @public statusCode: Number
// @public cookie: Function,
// default: (name, value, options = null)->
// @public download: Function,
// default: (path, filename)->
// @public json: Function,
// default: (data)->
// @public removeHeader: Function,
// default: (name)->
// @public send: Function,
// args: [[Buffer, String, Object, Array, Number, Boolean]]
// default: (data)->
// @public sendFile: Function,
// default: (path, options = null)->
// @public sendStatus: Function,
// default: (status)->
// @public throw: Function,
// return: NILL
// default: (status, reason, options = null)->
// @public write: Function,
// args: [[String, Buffer]]
// default: (data)->
Response.public({
length: Number
}, {
get: function() {
var len;
len = this.headers['content-length'];
if (len == null) {
if (!this.body) {
return 0;
}
if (_.isString(this.body)) {
return Buffer.byteLength(this.body);
}
if (_.isBuffer(this.body)) {
return this.body.length;
}
if (_.isObjectLike(this.body)) {
return Buffer.byteLength(JSON.stringify(this.body));
}
return 0;
}
return ~~Number(len);
},
set: function(n) {
this.set('Content-Length', n);
return n;
}
});
Response.public({
headerSent: MaybeG(Boolean)
}, {
get: function() {
return this.res.headersSent;
}
});
Response.public({
vary: FuncG(String)
}, {
default: function(field) {
var vary;
vary = require('vary');
vary(this.res, field);
}
});
Response.public({
redirect: FuncG([String, MaybeG(String)])
}, {
default: function(url, alt) {
var escape;
if ('back' === url) {
url = this.ctx.get('Referrer') || alt || '/';
}
this.set('Location', url);
if (!statuses.redirect[this.status]) {
this.status = 302;
}
if (this.ctx.accepts('html')) {
escape = require('escape-html');
url = escape(url);
this.type = 'text/html; charset=utf-8';
this.body = `Redirecting to <a href=\"${url}\">${url}</a>.`;
return;
}
this.type = 'text/plain; charset=utf-8';
this.body = `Redirecting to ${url}`;
}
});
Response.public({
attachment: FuncG(String)
}, {
default: function(filename) {
var contentDisposition, extname;
if (filename) {
extname = require('path').extname;
this.type = extname(filename);
}
contentDisposition = require('content-disposition');
this.set('Content-Disposition', contentDisposition(filename));
}
});
Response.public({
lastModified: MaybeG(Date)
}, {
get: function() {
var date;
date = this.get('last-modified');
if (date) {
return new Date(date);
}
},
set: function(val) {
if (_.isString(val)) {
val = new Date(val);
}
this.set('Last-Modified', val.toUTCString());
return val;
}
});
Response.public({
etag: String
}, {
get: function() {
return this.get('ETag');
},
set: function(val) {
if (!/^(W\/)?"/.test(val)) {
val = `\"${val}\"`;
}
this.set('ETag', val);
return val;
}
});
Response.public({
type: MaybeG(String)
}, {
get: function() {
var type;
type = this.get('Content-Type');
if (!type) {
return '';
}
return type.split(';')[0];
},
set: function(_type) {
var getType, type;
getType = require('mime-types').contentType;
type = getType(_type);
if (type) {
this.set('Content-Type', type);
} else {
this.remove('Content-Type');
}
return _type;
}
});
Response.public({
'is': FuncG([UnionG(String, Array)], UnionG(String, Boolean, NilT))
}, {
default: function(...args) {
var typeis, types;
[types] = args;
if (!types) {
return this.type || false;
}
if (!_.isArray(types)) {
types = args;
}
typeis = require('type-is').is;
return typeis(this.type, types);
}
});
Response.public({
get: FuncG(String, UnionG(String, Array))
}, {
default: function(field) {
var ref;
return (ref = this.headers[field.toLowerCase()]) != null ? ref : '';
}
});
Response.public({
set: FuncG([UnionG(String, Object), MaybeG(AnyT)])
}, {
default: function(...args) {
var field, key, val, value;
[field, val] = args;
if (2 === args.length) {
if (_.isArray(val)) {
val = val.map(String);
} else {
val = String(val);
}
this.res.setHeader(field, val);
} else {
for (key in field) {
if (!hasProp.call(field, key)) continue;
value = field[key];
this.set(key, value);
}
}
}
});
Response.public({
append: FuncG([String, UnionG(String, Array)])
}, {
default: function(field, val) {
var prev;
prev = this.get(field);
if (prev) {
if (_.isArray(prev)) {
val = prev.concat(val);
} else {
val = [prev].concat(val);
}
}
this.set(field, val);
}
});
Response.public({
remove: FuncG(String)
}, {
default: function(field) {
this.res.removeHeader(field);
}
});
Response.public({
writable: Boolean
}, {
get: function() {
var socket;
if (this.res.finished) {
return false;
}
socket = this.res.socket;
if (!socket) {
return true;
}
return socket.writable;
}
});
Response.public({
flushHeaders: Function
}, {
default: function() {
var header, headerNames, i, len1;
if (_.isFunction(this.res.flushHeaders)) {
this.res.flushHeaders();
} else {
headerNames = _.isFunction(this.res.getHeaderNames) ? this.res.getHeaderNames() : Object.keys(this.res._headers);
for (i = 0, len1 = headerNames.length; i < len1; i++) {
header = headerNames[i];
this.res.removeHeader(header);
}
}
}
});
// @public inspect: FuncG([], Object),
// default: ->
// return unless @res
// o = @toJSON()
// o.body = @body
// o
// @public toJSON: FuncG([], Object),
// default: -> _.pick @, ['status', 'message', 'header']
Response.public(Response.static(Response.async({
restoreObject: Function
}, {
default: function*() {
throw new Error(`restoreObject method not supported for ${this.name}`);
}
})));
Response.public(Response.static(Response.async({
replicateObject: Function
}, {
default: function*() {
throw new Error(`replicateObject method not supported for ${this.name}`);
}
})));
Response.public({
init: FuncG(ContextInterface)
}, {
default: function(context) {
this.super();
this.ctx = context;
}
});
Response.initialize();
return Response;
}).call(this);
};
}).call(this);