2024-10-04 09:38:27 +02:00
/ * !
* express
* Copyright ( c ) 2009 - 2013 TJ Holowaychuk
* Copyright ( c ) 2014 - 2015 Douglas Christopher Wilson
* MIT Licensed
* /
'use strict' ;
2023-12-06 11:59:35 +01:00
/ * *
* Module dependencies .
2024-10-04 09:38:27 +02:00
* @ private
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
var Buffer = require ( 'safe-buffer' ) . Buffer
var contentDisposition = require ( 'content-disposition' ) ;
var createError = require ( 'http-errors' )
var deprecate = require ( 'depd' ) ( 'express' ) ;
var encodeUrl = require ( 'encodeurl' ) ;
2023-12-06 11:59:35 +01:00
var escapeHtml = require ( 'escape-html' ) ;
var http = require ( 'http' ) ;
2024-10-04 09:38:27 +02:00
var isAbsolute = require ( './utils' ) . isAbsolute ;
var onFinished = require ( 'on-finished' ) ;
2023-12-06 11:59:35 +01:00
var path = require ( 'path' ) ;
2024-10-04 09:38:27 +02:00
var statuses = require ( 'statuses' )
var merge = require ( 'utils-merge' ) ;
2023-12-06 11:59:35 +01:00
var sign = require ( 'cookie-signature' ) . sign ;
var normalizeType = require ( './utils' ) . normalizeType ;
var normalizeTypes = require ( './utils' ) . normalizeTypes ;
var setCharset = require ( './utils' ) . setCharset ;
var cookie = require ( 'cookie' ) ;
var send = require ( 'send' ) ;
var extname = path . extname ;
var mime = send . mime ;
2024-10-04 09:38:27 +02:00
var resolve = path . resolve ;
2023-12-06 11:59:35 +01:00
var vary = require ( 'vary' ) ;
/ * *
* Response prototype .
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
var res = Object . create ( http . ServerResponse . prototype )
/ * *
* Module exports .
* @ public
* /
module . exports = res
/ * *
* Module variables .
* @ private
* /
var charsetRegExp = /;\s*charset\s*=/ ;
2023-12-06 11:59:35 +01:00
/ * *
* Set status ` code ` .
*
* @ param { Number } code
* @ return { ServerResponse }
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . status = function status ( code ) {
if ( ( typeof code === 'string' || Math . floor ( code ) !== code ) && code > 99 && code < 1000 ) {
deprecate ( 'res.status(' + JSON . stringify ( code ) + '): use res.status(' + Math . floor ( code ) + ') instead' )
}
2023-12-06 11:59:35 +01:00
this . statusCode = code ;
return this ;
} ;
/ * *
* Set Link header field with the given ` links ` .
*
* Examples :
*
* res . links ( {
* next : 'http://api.example.com/users?page=2' ,
* last : 'http://api.example.com/users?page=5'
* } ) ;
*
* @ param { Object } links
* @ return { ServerResponse }
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
res . links = function ( links ) {
var link = this . get ( 'Link' ) || '' ;
if ( link ) link += ', ' ;
return this . set ( 'Link' , link + Object . keys ( links ) . map ( function ( rel ) {
return '<' + links [ rel ] + '>; rel="' + rel + '"' ;
} ) . join ( ', ' ) ) ;
} ;
/ * *
* Send a response .
*
* Examples :
*
2024-10-04 09:38:27 +02:00
* res . send ( Buffer . from ( 'wahoo' ) ) ;
2023-12-06 11:59:35 +01:00
* res . send ( { some : 'json' } ) ;
* res . send ( '<p>some html</p>' ) ;
*
2024-10-04 09:38:27 +02:00
* @ param { string | number | boolean | object | Buffer } body
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . send = function send ( body ) {
var chunk = body ;
var encoding ;
2023-12-06 11:59:35 +01:00
var req = this . req ;
var type ;
// settings
var app = this . app ;
// allow status / body
2024-10-04 09:38:27 +02:00
if ( arguments . length === 2 ) {
2023-12-06 11:59:35 +01:00
// res.send(body, status) backwards compat
2024-10-04 09:38:27 +02:00
if ( typeof arguments [ 0 ] !== 'number' && typeof arguments [ 1 ] === 'number' ) {
deprecate ( 'res.send(body, status): Use res.status(status).send(body) instead' ) ;
2023-12-06 11:59:35 +01:00
this . statusCode = arguments [ 1 ] ;
} else {
2024-10-04 09:38:27 +02:00
deprecate ( 'res.send(status, body): Use res.status(status).send(body) instead' ) ;
this . statusCode = arguments [ 0 ] ;
chunk = arguments [ 1 ] ;
2023-12-06 11:59:35 +01:00
}
}
2024-10-04 09:38:27 +02:00
// disambiguate res.send(status) and res.send(status, num)
if ( typeof chunk === 'number' && arguments . length === 1 ) {
// res.send(status) will set status message as text string
if ( ! this . get ( 'Content-Type' ) ) {
this . type ( 'txt' ) ;
}
deprecate ( 'res.send(status): Use res.sendStatus(status) instead' ) ;
this . statusCode = chunk ;
chunk = statuses . message [ chunk ]
}
switch ( typeof chunk ) {
2023-12-06 11:59:35 +01:00
// string defaulting to html
case 'string' :
2024-10-04 09:38:27 +02:00
if ( ! this . get ( 'Content-Type' ) ) {
this . type ( 'html' ) ;
}
2023-12-06 11:59:35 +01:00
break ;
case 'boolean' :
2024-10-04 09:38:27 +02:00
case 'number' :
2023-12-06 11:59:35 +01:00
case 'object' :
2024-10-04 09:38:27 +02:00
if ( chunk === null ) {
chunk = '' ;
} else if ( Buffer . isBuffer ( chunk ) ) {
if ( ! this . get ( 'Content-Type' ) ) {
this . type ( 'bin' ) ;
}
2023-12-06 11:59:35 +01:00
} else {
2024-10-04 09:38:27 +02:00
return this . json ( chunk ) ;
2023-12-06 11:59:35 +01:00
}
break ;
}
// write strings in utf-8
2024-10-04 09:38:27 +02:00
if ( typeof chunk === 'string' ) {
2023-12-06 11:59:35 +01:00
encoding = 'utf8' ;
type = this . get ( 'Content-Type' ) ;
// reflect this in content-type
2024-10-04 09:38:27 +02:00
if ( typeof type === 'string' ) {
2023-12-06 11:59:35 +01:00
this . set ( 'Content-Type' , setCharset ( type , 'utf-8' ) ) ;
}
}
2024-10-04 09:38:27 +02:00
// determine if ETag should be generated
var etagFn = app . get ( 'etag fn' )
var generateETag = ! this . get ( 'ETag' ) && typeof etagFn === 'function'
2023-12-06 11:59:35 +01:00
// populate Content-Length
2024-10-04 09:38:27 +02:00
var len
if ( chunk !== undefined ) {
if ( Buffer . isBuffer ( chunk ) ) {
// get length of Buffer
len = chunk . length
} else if ( ! generateETag && chunk . length < 1000 ) {
// just calculate length when no ETag + small chunk
len = Buffer . byteLength ( chunk , encoding )
} else {
// convert chunk to Buffer and calculate
chunk = Buffer . from ( chunk , encoding )
encoding = undefined ;
len = chunk . length
}
2023-12-06 11:59:35 +01:00
this . set ( 'Content-Length' , len ) ;
}
2024-10-04 09:38:27 +02:00
// populate ETag
var etag ;
if ( generateETag && len !== undefined ) {
if ( ( etag = etagFn ( chunk , encoding ) ) ) {
this . set ( 'ETag' , etag ) ;
2023-12-06 11:59:35 +01:00
}
}
// freshness
if ( req . fresh ) this . statusCode = 304 ;
// strip irrelevant headers
2024-10-04 09:38:27 +02:00
if ( 204 === this . statusCode || 304 === this . statusCode ) {
2023-12-06 11:59:35 +01:00
this . removeHeader ( 'Content-Type' ) ;
this . removeHeader ( 'Content-Length' ) ;
this . removeHeader ( 'Transfer-Encoding' ) ;
2024-10-04 09:38:27 +02:00
chunk = '' ;
2023-12-06 11:59:35 +01:00
}
2024-10-04 09:38:27 +02:00
// alter headers for 205
if ( this . statusCode === 205 ) {
this . set ( 'Content-Length' , '0' )
this . removeHeader ( 'Transfer-Encoding' )
chunk = ''
}
if ( req . method === 'HEAD' ) {
// skip body for HEAD
this . end ( ) ;
} else {
// respond
this . end ( chunk , encoding ) ;
}
2023-12-06 11:59:35 +01:00
return this ;
} ;
/ * *
* Send JSON response .
*
* Examples :
*
* res . json ( null ) ;
* res . json ( { user : 'tj' } ) ;
*
2024-10-04 09:38:27 +02:00
* @ param { string | number | boolean | object } obj
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . json = function json ( obj ) {
var val = obj ;
2023-12-06 11:59:35 +01:00
// allow status / body
2024-10-04 09:38:27 +02:00
if ( arguments . length === 2 ) {
2023-12-06 11:59:35 +01:00
// res.json(body, status) backwards compat
2024-10-04 09:38:27 +02:00
if ( typeof arguments [ 1 ] === 'number' ) {
deprecate ( 'res.json(obj, status): Use res.status(status).json(obj) instead' ) ;
2023-12-06 11:59:35 +01:00
this . statusCode = arguments [ 1 ] ;
} else {
2024-10-04 09:38:27 +02:00
deprecate ( 'res.json(status, obj): Use res.status(status).json(obj) instead' ) ;
this . statusCode = arguments [ 0 ] ;
val = arguments [ 1 ] ;
2023-12-06 11:59:35 +01:00
}
}
// settings
var app = this . app ;
2024-10-04 09:38:27 +02:00
var escape = app . get ( 'json escape' )
2023-12-06 11:59:35 +01:00
var replacer = app . get ( 'json replacer' ) ;
var spaces = app . get ( 'json spaces' ) ;
2024-10-04 09:38:27 +02:00
var body = stringify ( val , replacer , spaces , escape )
2023-12-06 11:59:35 +01:00
// content-type
2024-10-04 09:38:27 +02:00
if ( ! this . get ( 'Content-Type' ) ) {
this . set ( 'Content-Type' , 'application/json' ) ;
}
2023-12-06 11:59:35 +01:00
return this . send ( body ) ;
} ;
/ * *
* Send JSON response with JSONP callback support .
*
* Examples :
*
* res . jsonp ( null ) ;
* res . jsonp ( { user : 'tj' } ) ;
*
2024-10-04 09:38:27 +02:00
* @ param { string | number | boolean | object } obj
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . jsonp = function jsonp ( obj ) {
var val = obj ;
2023-12-06 11:59:35 +01:00
// allow status / body
2024-10-04 09:38:27 +02:00
if ( arguments . length === 2 ) {
// res.jsonp(body, status) backwards compat
if ( typeof arguments [ 1 ] === 'number' ) {
deprecate ( 'res.jsonp(obj, status): Use res.status(status).jsonp(obj) instead' ) ;
2023-12-06 11:59:35 +01:00
this . statusCode = arguments [ 1 ] ;
} else {
2024-10-04 09:38:27 +02:00
deprecate ( 'res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead' ) ;
this . statusCode = arguments [ 0 ] ;
val = arguments [ 1 ] ;
2023-12-06 11:59:35 +01:00
}
}
// settings
var app = this . app ;
2024-10-04 09:38:27 +02:00
var escape = app . get ( 'json escape' )
2023-12-06 11:59:35 +01:00
var replacer = app . get ( 'json replacer' ) ;
var spaces = app . get ( 'json spaces' ) ;
2024-10-04 09:38:27 +02:00
var body = stringify ( val , replacer , spaces , escape )
2023-12-06 11:59:35 +01:00
var callback = this . req . query [ app . get ( 'jsonp callback name' ) ] ;
// content-type
2024-10-04 09:38:27 +02:00
if ( ! this . get ( 'Content-Type' ) ) {
this . set ( 'X-Content-Type-Options' , 'nosniff' ) ;
this . set ( 'Content-Type' , 'application/json' ) ;
}
2023-12-06 11:59:35 +01:00
// fixup callback
if ( Array . isArray ( callback ) ) {
callback = callback [ 0 ] ;
}
// jsonp
2024-10-04 09:38:27 +02:00
if ( typeof callback === 'string' && callback . length !== 0 ) {
this . set ( 'X-Content-Type-Options' , 'nosniff' ) ;
2023-12-06 11:59:35 +01:00
this . set ( 'Content-Type' , 'text/javascript' ) ;
2024-10-04 09:38:27 +02:00
// restrict callback charset
callback = callback . replace ( /[^\[\]\w$.]/g , '' ) ;
if ( body === undefined ) {
// empty argument
body = ''
} else if ( typeof body === 'string' ) {
// replace chars not allowed in JavaScript that are in JSON
body = body
. replace ( /\u2028/g , '\\u2028' )
. replace ( /\u2029/g , '\\u2029' )
}
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');' ;
2023-12-06 11:59:35 +01:00
}
return this . send ( body ) ;
} ;
2024-10-04 09:38:27 +02:00
/ * *
* Send given HTTP status code .
*
* Sets the response status to ` statusCode ` and the body of the
* response to the standard description from node ' s http . STATUS _CODES
* or the statusCode number if no description .
*
* Examples :
*
* res . sendStatus ( 200 ) ;
*
* @ param { number } statusCode
* @ public
* /
res . sendStatus = function sendStatus ( statusCode ) {
var body = statuses . message [ statusCode ] || String ( statusCode )
this . statusCode = statusCode ;
this . type ( 'txt' ) ;
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
return this . send ( body ) ;
} ;
2023-12-06 11:59:35 +01:00
/ * *
* Transfer the file at the given ` path ` .
*
* Automatically sets the _Content - Type _ response header field .
2024-10-04 09:38:27 +02:00
* The callback ` callback(err) ` is invoked when the transfer is complete
* or when an error occurs . Be sure to check ` res.headersSent `
2023-12-06 11:59:35 +01:00
* if you wish to attempt responding , as the header and some data
* may have already been transferred .
*
* Options :
*
2024-10-04 09:38:27 +02:00
* - ` maxAge ` defaulting to 0 ( can be string converted by ` ms ` )
* - ` root ` root directory for relative filenames
* - ` headers ` object of headers to serve with file
* - ` dotfiles ` serve dotfiles , defaulting to false ; can be ` "allow" ` to send them
2023-12-06 11:59:35 +01:00
*
* Other options are passed along to ` send ` .
*
* Examples :
*
2024-10-04 09:38:27 +02:00
* The following example illustrates how ` res.sendFile() ` may
2023-12-06 11:59:35 +01:00
* be used as an alternative for the ` static() ` middleware for
2024-10-04 09:38:27 +02:00
* dynamic situations . The code backing ` res.sendFile() ` is actually
2023-12-06 11:59:35 +01:00
* the same code , so HTTP cache support etc is identical .
*
* app . get ( '/user/:uid/photos/:file' , function ( req , res ) {
* var uid = req . params . uid
* , file = req . params . file ;
*
* req . user . mayViewFilesFrom ( uid , function ( yes ) {
* if ( yes ) {
2024-10-04 09:38:27 +02:00
* res . sendFile ( '/uploads/' + uid + '/' + file ) ;
2023-12-06 11:59:35 +01:00
* } else {
* res . send ( 403 , 'Sorry! you cant see that.' ) ;
* }
* } ) ;
* } ) ;
*
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . sendFile = function sendFile ( path , options , callback ) {
var done = callback ;
var req = this . req ;
var res = this ;
var next = req . next ;
var opts = options || { } ;
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
if ( ! path ) {
throw new TypeError ( 'path argument is required to res.sendFile' ) ;
}
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
if ( typeof path !== 'string' ) {
throw new TypeError ( 'path must be a string to res.sendFile' )
2023-12-06 11:59:35 +01:00
}
2024-10-04 09:38:27 +02:00
// support function as second arg
if ( typeof options === 'function' ) {
done = options ;
opts = { } ;
}
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
if ( ! opts . root && ! isAbsolute ( path ) ) {
throw new TypeError ( 'path must be absolute or specify root to res.sendFile' ) ;
}
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
// create file stream
var pathname = encodeURI ( path ) ;
var file = send ( req , pathname , opts ) ;
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
// transfer
sendfile ( res , file , opts , function ( err ) {
if ( done ) return done ( err ) ;
if ( err && err . code === 'EISDIR' ) return next ( ) ;
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
// next() all but write errors
if ( err && err . code !== 'ECONNABORTED' && err . syscall !== 'write' ) {
next ( err ) ;
}
} ) ;
} ;
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
/ * *
* Transfer the file at the given ` path ` .
*
* Automatically sets the _Content - Type _ response header field .
* The callback ` callback(err) ` is invoked when the transfer is complete
* or when an error occurs . Be sure to check ` res.headersSent `
* if you wish to attempt responding , as the header and some data
* may have already been transferred .
*
* Options :
*
* - ` maxAge ` defaulting to 0 ( can be string converted by ` ms ` )
* - ` root ` root directory for relative filenames
* - ` headers ` object of headers to serve with file
* - ` dotfiles ` serve dotfiles , defaulting to false ; can be ` "allow" ` to send them
*
* Other options are passed along to ` send ` .
*
* Examples :
*
* The following example illustrates how ` res.sendfile() ` may
* be used as an alternative for the ` static() ` middleware for
* dynamic situations . The code backing ` res.sendfile() ` is actually
* the same code , so HTTP cache support etc is identical .
*
* app . get ( '/user/:uid/photos/:file' , function ( req , res ) {
* var uid = req . params . uid
* , file = req . params . file ;
*
* req . user . mayViewFilesFrom ( uid , function ( yes ) {
* if ( yes ) {
* res . sendfile ( '/uploads/' + uid + '/' + file ) ;
* } else {
* res . send ( 403 , 'Sorry! you cant see that.' ) ;
* }
* } ) ;
* } ) ;
*
* @ public
* /
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
res . sendfile = function ( path , options , callback ) {
var done = callback ;
var req = this . req ;
var res = this ;
var next = req . next ;
var opts = options || { } ;
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
// support function as second arg
if ( typeof options === 'function' ) {
done = options ;
opts = { } ;
2023-12-06 11:59:35 +01:00
}
2024-10-04 09:38:27 +02:00
// create file stream
var file = send ( req , path , opts ) ;
2023-12-06 11:59:35 +01:00
// transfer
2024-10-04 09:38:27 +02:00
sendfile ( res , file , opts , function ( err ) {
if ( done ) return done ( err ) ;
if ( err && err . code === 'EISDIR' ) return next ( ) ;
// next() all but write errors
if ( err && err . code !== 'ECONNABORTED' && err . syscall !== 'write' ) {
next ( err ) ;
}
} ) ;
2023-12-06 11:59:35 +01:00
} ;
2024-10-04 09:38:27 +02:00
res . sendfile = deprecate . function ( res . sendfile ,
'res.sendfile: Use res.sendFile instead' ) ;
2023-12-06 11:59:35 +01:00
/ * *
* Transfer the file at the given ` path ` as an attachment .
*
* Optionally providing an alternate attachment ` filename ` ,
2024-10-04 09:38:27 +02:00
* and optional callback ` callback(err) ` . The callback is invoked
2023-12-06 11:59:35 +01:00
* when the data transfer is complete , or when an error has
2024-10-04 09:38:27 +02:00
* occurred . Be sure to check ` res.headersSent ` if you plan to respond .
2023-12-06 11:59:35 +01:00
*
2024-10-04 09:38:27 +02:00
* Optionally providing an ` options ` object to use with ` res.sendFile() ` .
* This function will set the ` Content-Disposition ` header , overriding
* any ` Content-Disposition ` header passed as header options in order
* to set the attachment and filename .
2023-12-06 11:59:35 +01:00
*
2024-10-04 09:38:27 +02:00
* This method uses ` res.sendFile() ` .
*
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . download = function download ( path , filename , options , callback ) {
var done = callback ;
var name = filename ;
var opts = options || null
// support function as second or third arg
if ( typeof filename === 'function' ) {
done = filename ;
name = null ;
opts = null
} else if ( typeof options === 'function' ) {
done = options
opts = null
2023-12-06 11:59:35 +01:00
}
2024-10-04 09:38:27 +02:00
// support optional filename, where options may be in it's place
if ( typeof filename === 'object' &&
( typeof options === 'function' || options === undefined ) ) {
name = null
opts = filename
}
// set Content-Disposition when file is sent
var headers = {
'Content-Disposition' : contentDisposition ( name || path )
} ;
// merge user-provided headers
if ( opts && opts . headers ) {
var keys = Object . keys ( opts . headers )
for ( var i = 0 ; i < keys . length ; i ++ ) {
var key = keys [ i ]
if ( key . toLowerCase ( ) !== 'content-disposition' ) {
headers [ key ] = opts . headers [ key ]
}
}
}
// merge user-provided options
opts = Object . create ( opts )
opts . headers = headers
// Resolve the full path for sendFile
var fullPath = ! opts . root
? resolve ( path )
: path
// send file
return this . sendFile ( fullPath , opts , done )
2023-12-06 11:59:35 +01:00
} ;
/ * *
* Set _Content - Type _ response header with ` type ` through ` mime.lookup() `
* when it does not contain "/" , or set the Content - Type to ` type ` otherwise .
*
* Examples :
*
* res . type ( '.html' ) ;
* res . type ( 'html' ) ;
* res . type ( 'json' ) ;
* res . type ( 'application/json' ) ;
* res . type ( 'png' ) ;
*
* @ param { String } type
* @ return { ServerResponse } for chaining
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
res . contentType =
2024-10-04 09:38:27 +02:00
res . type = function contentType ( type ) {
var ct = type . indexOf ( '/' ) === - 1
? mime . lookup ( type )
: type ;
return this . set ( 'Content-Type' , ct ) ;
2023-12-06 11:59:35 +01:00
} ;
/ * *
* Respond to the Acceptable formats using an ` obj `
* of mime - type callbacks .
*
* This method uses ` req.accepted ` , an array of
* acceptable types ordered by their quality values .
* When "Accept" is not present the _first _ callback
* is invoked , otherwise the first match is used . When
* no match is performed the server responds with
* 406 "Not Acceptable" .
*
* Content - Type is set for you , however if you choose
* you may alter this within the callback using ` res.type() `
* or ` res.set('Content-Type', ...) ` .
*
* res . format ( {
* 'text/plain' : function ( ) {
* res . send ( 'hey' ) ;
* } ,
*
* 'text/html' : function ( ) {
* res . send ( '<p>hey</p>' ) ;
* } ,
*
2024-10-04 09:38:27 +02:00
* 'application/json' : function ( ) {
2023-12-06 11:59:35 +01:00
* res . send ( { message : 'hey' } ) ;
* }
* } ) ;
*
* In addition to canonicalized MIME types you may
* also use extnames mapped to these types :
*
* res . format ( {
* text : function ( ) {
* res . send ( 'hey' ) ;
* } ,
*
* html : function ( ) {
* res . send ( '<p>hey</p>' ) ;
* } ,
*
* json : function ( ) {
* res . send ( { message : 'hey' } ) ;
* }
* } ) ;
*
* By default Express passes an ` Error `
* with a ` .status ` of 406 to ` next(err) `
* if a match is not made . If you provide
* a ` .default ` callback it will be invoked
* instead .
*
* @ param { Object } obj
* @ return { ServerResponse } for chaining
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
res . format = function ( obj ) {
var req = this . req ;
var next = req . next ;
2024-10-04 09:38:27 +02:00
var keys = Object . keys ( obj )
. filter ( function ( v ) { return v !== 'default' } )
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
var key = keys . length > 0
? req . accepts ( keys )
: false ;
2023-12-06 11:59:35 +01:00
this . vary ( "Accept" ) ;
if ( key ) {
this . set ( 'Content-Type' , normalizeType ( key ) . value ) ;
obj [ key ] ( req , this , next ) ;
2024-10-04 09:38:27 +02:00
} else if ( obj . default ) {
obj . default ( req , this , next )
2023-12-06 11:59:35 +01:00
} else {
2024-10-04 09:38:27 +02:00
next ( createError ( 406 , {
types : normalizeTypes ( keys ) . map ( function ( o ) { return o . value } )
} ) )
2023-12-06 11:59:35 +01:00
}
return this ;
} ;
/ * *
* Set _Content - Disposition _ header to _attachment _ with optional ` filename ` .
*
* @ param { String } filename
* @ return { ServerResponse }
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . attachment = function attachment ( filename ) {
if ( filename ) {
this . type ( extname ( filename ) ) ;
}
2023-12-06 11:59:35 +01:00
this . set ( 'Content-Disposition' , contentDisposition ( filename ) ) ;
2024-10-04 09:38:27 +02:00
2023-12-06 11:59:35 +01:00
return this ;
} ;
2024-10-04 09:38:27 +02:00
/ * *
* Append additional header ` field ` with value ` val ` .
*
* Example :
*
* res . append ( 'Link' , [ '<http://localhost/>' , '<http://localhost:3000/>' ] ) ;
* res . append ( 'Set-Cookie' , 'foo=bar; Path=/; HttpOnly' ) ;
* res . append ( 'Warning' , '199 Miscellaneous warning' ) ;
*
* @ param { String } field
* @ param { String | Array } val
* @ return { ServerResponse } for chaining
* @ public
* /
res . append = function append ( field , val ) {
var prev = this . get ( field ) ;
var value = val ;
if ( prev ) {
// concat the new and prev vals
value = Array . isArray ( prev ) ? prev . concat ( val )
: Array . isArray ( val ) ? [ prev ] . concat ( val )
: [ prev , val ]
}
return this . set ( field , value ) ;
} ;
2023-12-06 11:59:35 +01:00
/ * *
* Set header ` field ` to ` val ` , or pass
* an object of header fields .
*
* Examples :
*
* res . set ( 'Foo' , [ 'bar' , 'baz' ] ) ;
* res . set ( 'Accept' , 'application/json' ) ;
* res . set ( { Accept : 'text/plain' , 'X-API-Key' : 'tobi' } ) ;
*
* Aliased as ` res.header() ` .
*
2024-10-04 09:38:27 +02:00
* @ param { String | Object } field
* @ param { String | Array } val
2023-12-06 11:59:35 +01:00
* @ return { ServerResponse } for chaining
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
res . set =
2024-10-04 09:38:27 +02:00
res . header = function header ( field , val ) {
if ( arguments . length === 2 ) {
var value = Array . isArray ( val )
? val . map ( String )
: String ( val ) ;
// add charset to content-type
if ( field . toLowerCase ( ) === 'content-type' ) {
if ( Array . isArray ( value ) ) {
throw new TypeError ( 'Content-Type cannot be set to an Array' ) ;
}
if ( ! charsetRegExp . test ( value ) ) {
var charset = mime . charsets . lookup ( value . split ( ';' ) [ 0 ] ) ;
if ( charset ) value += '; charset=' + charset . toLowerCase ( ) ;
}
2023-12-06 11:59:35 +01:00
}
2024-10-04 09:38:27 +02:00
this . setHeader ( field , value ) ;
2023-12-06 11:59:35 +01:00
} else {
for ( var key in field ) {
this . set ( key , field [ key ] ) ;
}
}
return this ;
} ;
/ * *
* Get value for header ` field ` .
*
* @ param { String } field
* @ return { String }
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
res . get = function ( field ) {
return this . getHeader ( field ) ;
} ;
/ * *
* Clear cookie ` name ` .
*
* @ param { String } name
2024-10-04 09:38:27 +02:00
* @ param { Object } [ options ]
* @ return { ServerResponse } for chaining
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . clearCookie = function clearCookie ( name , options ) {
if ( options ) {
if ( options . maxAge ) {
deprecate ( 'res.clearCookie: Passing "options.maxAge" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.' ) ;
}
if ( options . expires ) {
deprecate ( 'res.clearCookie: Passing "options.expires" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.' ) ;
}
}
var opts = merge ( { expires : new Date ( 1 ) , path : '/' } , options ) ;
return this . cookie ( name , '' , opts ) ;
2023-12-06 11:59:35 +01:00
} ;
/ * *
2024-10-04 09:38:27 +02:00
* Set cookie ` name ` to ` value ` , with the given ` options ` .
2023-12-06 11:59:35 +01:00
*
* Options :
*
* - ` maxAge ` max - age in milliseconds , converted to ` expires `
* - ` signed ` sign the cookie
* - ` path ` defaults to "/"
*
* Examples :
*
* // "Remember Me" for 15 minutes
* res . cookie ( 'rememberme' , '1' , { expires : new Date ( Date . now ( ) + 900000 ) , httpOnly : true } ) ;
*
2024-10-04 09:38:27 +02:00
* // same as above
2023-12-06 11:59:35 +01:00
* res . cookie ( 'rememberme' , '1' , { maxAge : 900000 , httpOnly : true } )
*
* @ param { String } name
2024-10-04 09:38:27 +02:00
* @ param { String | Object } value
* @ param { Object } [ options ]
* @ return { ServerResponse } for chaining
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . cookie = function ( name , value , options ) {
var opts = merge ( { } , options ) ;
2023-12-06 11:59:35 +01:00
var secret = this . req . secret ;
2024-10-04 09:38:27 +02:00
var signed = opts . signed ;
if ( signed && ! secret ) {
throw new Error ( 'cookieParser("secret") required for signed cookies' ) ;
}
var val = typeof value === 'object'
? 'j:' + JSON . stringify ( value )
: String ( value ) ;
if ( signed ) {
val = 's:' + sign ( val , secret ) ;
}
if ( opts . maxAge != null ) {
var maxAge = opts . maxAge - 0
if ( ! isNaN ( maxAge ) ) {
opts . expires = new Date ( Date . now ( ) + maxAge )
opts . maxAge = Math . floor ( maxAge / 1000 )
2023-12-06 11:59:35 +01:00
}
}
2024-10-04 09:38:27 +02:00
if ( opts . path == null ) {
opts . path = '/' ;
}
this . append ( 'Set-Cookie' , cookie . serialize ( name , String ( val ) , opts ) ) ;
2023-12-06 11:59:35 +01:00
return this ;
} ;
/ * *
* Set the location header to ` url ` .
*
* The given ` url ` can also be "back" , which redirects
* to the _Referrer _ or _Referer _ headers or "/" .
*
* Examples :
*
* res . location ( '/foo/bar' ) . ;
* res . location ( 'http://example.com' ) ;
* res . location ( '../login' ) ;
*
* @ param { String } url
2024-10-04 09:38:27 +02:00
* @ return { ServerResponse } for chaining
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . location = function location ( url ) {
var loc ;
2023-12-06 11:59:35 +01:00
// "back" is an alias for the referrer
2024-10-04 09:38:27 +02:00
if ( url === 'back' ) {
deprecate ( 'res.location("back"): use res.location(req.get("Referrer") || "/") and refer to https://dub.sh/security-redirect for best practices' ) ;
loc = this . req . get ( 'Referrer' ) || '/' ;
} else {
loc = String ( url ) ;
}
2023-12-06 11:59:35 +01:00
2024-10-04 09:38:27 +02:00
return this . set ( 'Location' , encodeUrl ( loc ) ) ;
2023-12-06 11:59:35 +01:00
} ;
/ * *
* Redirect to the given ` url ` with optional response ` status `
* defaulting to 302.
*
* The resulting ` url ` is determined by ` res.location() ` , so
* it will play nicely with mounted apps , relative paths ,
* ` "back" ` etc .
*
* Examples :
*
* res . redirect ( '/foo/bar' ) ;
* res . redirect ( 'http://example.com' ) ;
* res . redirect ( 301 , 'http://example.com' ) ;
* res . redirect ( '../login' ) ; // /blog/post/1 -> /blog/login
*
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . redirect = function redirect ( url ) {
var address = url ;
2023-12-06 11:59:35 +01:00
var body ;
2024-10-04 09:38:27 +02:00
var status = 302 ;
2023-12-06 11:59:35 +01:00
// allow status / url
2024-10-04 09:38:27 +02:00
if ( arguments . length === 2 ) {
if ( typeof arguments [ 0 ] === 'number' ) {
status = arguments [ 0 ] ;
address = arguments [ 1 ] ;
2023-12-06 11:59:35 +01:00
} else {
2024-10-04 09:38:27 +02:00
deprecate ( 'res.redirect(url, status): Use res.redirect(status, url) instead' ) ;
2023-12-06 11:59:35 +01:00
status = arguments [ 1 ] ;
}
}
// Set location header
2024-10-04 09:38:27 +02:00
address = this . location ( address ) . get ( 'Location' ) ;
2023-12-06 11:59:35 +01:00
// Support text/{plain,html} by default
this . format ( {
text : function ( ) {
2024-10-04 09:38:27 +02:00
body = statuses . message [ status ] + '. Redirecting to ' + address
2023-12-06 11:59:35 +01:00
} ,
html : function ( ) {
2024-10-04 09:38:27 +02:00
var u = escapeHtml ( address ) ;
body = '<p>' + statuses . message [ status ] + '. Redirecting to ' + u + '</p>'
2023-12-06 11:59:35 +01:00
} ,
default : function ( ) {
body = '' ;
}
} ) ;
// Respond
this . statusCode = status ;
this . set ( 'Content-Length' , Buffer . byteLength ( body ) ) ;
2024-10-04 09:38:27 +02:00
if ( this . req . method === 'HEAD' ) {
this . end ( ) ;
} else {
this . end ( body ) ;
}
2023-12-06 11:59:35 +01:00
} ;
/ * *
* Add ` field ` to Vary . If already present in the Vary set , then
* this call is simply ignored .
*
* @ param { Array | String } field
2024-10-04 09:38:27 +02:00
* @ return { ServerResponse } for chaining
* @ public
2023-12-06 11:59:35 +01:00
* /
res . vary = function ( field ) {
// checks for back-compat
2024-10-04 09:38:27 +02:00
if ( ! field || ( Array . isArray ( field ) && ! field . length ) ) {
deprecate ( 'res.vary(): Provide a field name' ) ;
return this ;
}
2023-12-06 11:59:35 +01:00
vary ( this , field ) ;
return this ;
} ;
/ * *
* Render ` view ` with the given ` options ` and optional callback ` fn ` .
* When a callback function is given a response will _not _ be made
* automatically , otherwise a response of _200 _ and _text / html _ is given .
*
* Options :
*
* - ` cache ` boolean hinting to the engine it should cache
* - ` filename ` filename of the view being rendered
*
2024-10-04 09:38:27 +02:00
* @ public
2023-12-06 11:59:35 +01:00
* /
2024-10-04 09:38:27 +02:00
res . render = function render ( view , options , callback ) {
var app = this . req . app ;
var done = callback ;
var opts = options || { } ;
2023-12-06 11:59:35 +01:00
var req = this . req ;
2024-10-04 09:38:27 +02:00
var self = this ;
2023-12-06 11:59:35 +01:00
// support callback function as second arg
2024-10-04 09:38:27 +02:00
if ( typeof options === 'function' ) {
done = options ;
opts = { } ;
2023-12-06 11:59:35 +01:00
}
// merge res.locals
2024-10-04 09:38:27 +02:00
opts . _locals = self . locals ;
2023-12-06 11:59:35 +01:00
// default callback to respond
2024-10-04 09:38:27 +02:00
done = done || function ( err , str ) {
2023-12-06 11:59:35 +01:00
if ( err ) return req . next ( err ) ;
self . send ( str ) ;
} ;
// render
2024-10-04 09:38:27 +02:00
app . render ( view , opts , done ) ;
2023-12-06 11:59:35 +01:00
} ;
2024-10-04 09:38:27 +02:00
// pipe the send file stream
function sendfile ( res , file , options , callback ) {
var done = false ;
var streaming ;
// request aborted
function onaborted ( ) {
if ( done ) return ;
done = true ;
var err = new Error ( 'Request aborted' ) ;
err . code = 'ECONNABORTED' ;
callback ( err ) ;
}
// directory
function ondirectory ( ) {
if ( done ) return ;
done = true ;
var err = new Error ( 'EISDIR, read' ) ;
err . code = 'EISDIR' ;
callback ( err ) ;
}
// errors
function onerror ( err ) {
if ( done ) return ;
done = true ;
callback ( err ) ;
}
// ended
function onend ( ) {
if ( done ) return ;
done = true ;
callback ( ) ;
}
// file
function onfile ( ) {
streaming = false ;
}
// finished
function onfinish ( err ) {
if ( err && err . code === 'ECONNRESET' ) return onaborted ( ) ;
if ( err ) return onerror ( err ) ;
if ( done ) return ;
setImmediate ( function ( ) {
if ( streaming !== false && ! done ) {
onaborted ( ) ;
return ;
}
if ( done ) return ;
done = true ;
callback ( ) ;
} ) ;
}
// streaming
function onstream ( ) {
streaming = true ;
}
file . on ( 'directory' , ondirectory ) ;
file . on ( 'end' , onend ) ;
file . on ( 'error' , onerror ) ;
file . on ( 'file' , onfile ) ;
file . on ( 'stream' , onstream ) ;
onFinished ( res , onfinish ) ;
if ( options . headers ) {
// set headers on successful transfer
file . on ( 'headers' , function headers ( res ) {
var obj = options . headers ;
var keys = Object . keys ( obj ) ;
for ( var i = 0 ; i < keys . length ; i ++ ) {
var k = keys [ i ] ;
res . setHeader ( k , obj [ k ] ) ;
}
} ) ;
}
// pipe
file . pipe ( res ) ;
}
/ * *
* Stringify JSON , like JSON . stringify , but v8 optimized , with the
* ability to escape characters that can trigger HTML sniffing .
*
* @ param { * } value
* @ param { function } replacer
* @ param { number } spaces
* @ param { boolean } escape
* @ returns { string }
* @ private
* /
function stringify ( value , replacer , spaces , escape ) {
// v8 checks arguments.length for optimizing simple call
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
var json = replacer || spaces
? JSON . stringify ( value , replacer , spaces )
: JSON . stringify ( value ) ;
if ( escape && typeof json === 'string' ) {
json = json . replace ( /[<>&]/g , function ( c ) {
switch ( c . charCodeAt ( 0 ) ) {
case 0x3c :
return '\\u003c'
case 0x3e :
return '\\u003e'
case 0x26 :
return '\\u0026'
/* istanbul ignore next: unreachable default */
default :
return c
}
} )
}
return json
}