@leansdk/leanrc
Version:
LeanRC is a MVC framework for creating graceful applications
344 lines (294 loc) • 9.73 kB
text/coffeescript
# 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
###
Stream = require 'stream'
module.exports = (Module)->
{
AnyT, NilT
FuncG, UnionG, MaybeG
ResponseInterface, SwitchInterface, ContextInterface
CoreObject
Utils: { _, statuses }
} = Module::
class Response extends CoreObject
()
ResponseInterface
Module
res: Object, # native response object
get: -> .res
switch: SwitchInterface,
get: -> .switch
ctx: ContextInterface
socket: MaybeG(Object),
get: -> .req.socket
header: Object,
get: ->
headers: Object,
get: ->
if _.isFunction .getHeaders
.getHeaders()
else
._headers ? {}
status: MaybeG(Number),
get: -> .statusCode
set: (code)->
assert = require 'assert'
assert _.isNumber(code), 'status code must be a number'
assert statuses[code], "invalid status code: #{code}"
assert not .headersSent, 'headers have already been sent'
= yes
.statusCode = code
.statusMessage = statuses[code]
if Boolean( and statuses.empty[code])
= null
return code
message: String,
get: -> .statusMessage ? statuses[]
set: (msg)->
.statusMessage = msg
return msg
body: MaybeG(UnionG String, Buffer, Object, Array, Number, Boolean, Stream),
get: ->
set: (val)->
original =
= val
return if .headersSent
unless val?
unless statuses.empty[]
= 204
'Content-Type'
'Content-Length'
'Transfer-Encoding'
return
unless
= 200
setType = not ['content-type']
if _.isString val
if setType
= if /^\s*</.test val then 'html' else 'text'
= Buffer.byteLength val
return
if _.isBuffer val
if setType
= 'bin'
= val.length
return
if _.isFunction val.pipe
onFinish = require 'on-finished'
destroy = require 'destroy'
onFinish , destroy.bind null, val
ensureErrorHandler = require 'error-inject'
ensureErrorHandler val, (err)=> .onerror err
if original? and original isnt val
'Content-Length'
if setType
= 'bin'
return
'Content-Length'
= 'json'
return val
# body: [String, Buffer]
# locals: Object
# headers: Object
# statusCode: Number
# cookie: Function,
# default: (name, value, options = null)->
# download: Function,
# default: (path, filename)->
# json: Function,
# default: (data)->
# removeHeader: Function,
# default: (name)->
#
# send: Function,
# args: [[Buffer, String, Object, Array, Number, Boolean]]
# default: (data)->
# sendFile: Function,
# default: (path, options = null)->
# sendStatus: Function,
# default: (status)->
# throw: Function,
# return: NILL
# default: (status, reason, options = null)->
# write: Function,
# args: [[String, Buffer]]
# default: (data)->
length: Number,
get: ->
len = ['content-length']
unless len?
return 0 unless
if _.isString
return Buffer.byteLength
if _.isBuffer
return .length
if _.isObjectLike
return Buffer.byteLength JSON.stringify
return 0
~~Number len
set: (n)->
'Content-Length', n
return n
headerSent: MaybeG(Boolean),
get: -> .headersSent
vary: FuncG(String),
default: (field)->
vary = require 'vary'
vary , field
return
redirect: FuncG([String, MaybeG String]),
default: (url, alt)->
if 'back' is url
url = .get('Referrer') or alt or '/'
'Location', url
unless statuses.redirect[]
= 302
if .accepts 'html'
escape = require 'escape-html'
url = escape url
= 'text/html; charset=utf-8'
= "Redirecting to <a href=\"#{url}\">#{url}</a>."
return
= 'text/plain; charset=utf-8'
= "Redirecting to #{url}"
return
attachment: FuncG(String),
default: (filename)->
if filename
extname = require('path').extname
= extname filename
contentDisposition = require 'content-disposition'
'Content-Disposition', contentDisposition filename
return
lastModified: MaybeG(Date),
get: ->
date = 'last-modified'
if date
new Date date
set: (val)->
if _.isString val
val = new Date val
'Last-Modified', val.toUTCString()
return val
etag: String,
get: -> 'ETag'
set: (val)->
val = "\"#{val}\"" unless /^(W\/)?"/.test val
'ETag', val
return val
type: MaybeG(String),
get: ->
type = 'Content-Type'
return '' unless type
type.split(';')[0]
set: (_type)->
getType = require('mime-types').contentType
type = getType _type
if type
'Content-Type', type
else
'Content-Type'
return _type
'is': FuncG([UnionG String, Array], UnionG String, Boolean, NilT),
default: (args...)->
[types] = args
return or no unless types
unless _.isArray types
types = args
typeis = require('type-is').is
typeis , types
get: FuncG(String, UnionG String, Array),
default: (field)->
[field.toLowerCase()] ? ''
set: FuncG([UnionG(String, Object), MaybeG AnyT]),
default: (args...)->
[field, val] = args
if 2 is args.length
if _.isArray val
val = val.map String
else
val = String val
.setHeader field, val
else
for own key, value of field
key, value
return
append: FuncG([String, UnionG String, Array]),
default: (field, val)->
prev = field
if prev
if _.isArray prev
val = prev.concat val
else
val = [prev].concat val
field, val
return
remove: FuncG(String),
default: (field)->
.removeHeader field
return
writable: Boolean,
get: ->
return no if .finished
socket = .socket
return yes unless socket
socket.writable
flushHeaders: Function,
default: ->
if _.isFunction .flushHeaders
.flushHeaders()
else
headerNames = if _.isFunction .getHeaderNames
.getHeaderNames()
else
Object.keys ._headers
for header in headerNames
.removeHeader header
return
# inspect: FuncG([], Object),
# default: ->
# return unless
# o = ()
# o.body =
# o
# toJSON: FuncG([], Object),
# default: -> _.pick @, ['status', 'message', 'header']
restoreObject: Function,
default: ->
throw new Error "restoreObject method not supported for #{@name}"
yield return
replicateObject: Function,
default: ->
throw new Error "replicateObject method not supported for #{@name}"
yield return
init: FuncG(ContextInterface),
default: (context)->
()
= context
return
()