export function sendHTML (req, res, html, method, { dev, generateEtags }) { if (isResSent(res)) return const etag = generateEtags && generateETag(html) if (fresh(req.headers, { etag })) { res.statusCode = 304 res.end() return } if (dev) { // In dev, we should not cache pages for any reason. // That's why we do this. res.setHeader('Cache-Control', 'no-store, must-revalidate') } if (etag) { res.setHeader('ETag', etag) } if (!res.getHeader('Content-Type')) { res.setHeader('Content-Type', 'text/html; charset=utf-8') } res.setHeader('Content-Length', Buffer.byteLength(html)) res.end(method === 'HEAD' ? null : html) }
/** * Middleware that is returned with public expiry call. * Looks up incoming request url in lookup hash and, if found, * sets cache headers according to settings * * @api private */ function middleware(req, res, next) { var headerInfo = expiry.assetCache[req.url] , options = expiry.options; if (headerInfo) { var cacheControl = (options.cacheControl === 'cookieless' && (req.get('cookie') || req.get('authorization'))) ? 'private' : options.cacheControl || ''; if (options.unconditional === 'both' || options.unconditional === 'max-age') { if (cacheControl.length) cacheControl += ', '; cacheControl += 'max-age=' + options.duration; } if (options.unconditional === 'both' || options.unconditional === 'expires') { var now = new Date(); now.setSeconds(now.getSeconds() + options.duration); res.set({ 'Expires' : now.toUTCString() }); } if (options.conditional === 'both' || options.conditional === 'etag') { res.set({ 'ETag' : '"' + headerInfo.etag + '"' }); } if (options.conditional === 'both' || options.conditional === 'last-modified') { res.set({ 'Last-Modified' : headerInfo.lastModified }); } if (cacheControl.length) res.set({ 'Cache-Control' : cacheControl }); if (fresh(req, res)) return res.send(304); req.originalUrl = req.url; req.url = headerInfo.assetUrl; } next(); };
function send(req, res, icon){ var _fresh = fresh(req.headers, icon.headers); var buf = _fresh ? '' : icon.body; var status = _fresh ? 304 : 200; res.writeHead(status, icon.headers); res.end(buf); }
function respondFromCache(req, res, cacheEntry) { _$jscoverage['middleware/staticCache.js'][152]++; var status = cacheEntry[0], headers = utils.merge({}, cacheEntry[1]), content = cacheEntry.slice(2); _$jscoverage['middleware/staticCache.js'][156]++; headers.age = (new Date() - new Date(headers.date)) / 1000 || 0; _$jscoverage['middleware/staticCache.js'][158]++; switch (req.method) { case "HEAD": _$jscoverage['middleware/staticCache.js'][160]++; res.writeHead(status, headers); _$jscoverage['middleware/staticCache.js'][161]++; res.end(); _$jscoverage['middleware/staticCache.js'][162]++; break; case "GET": _$jscoverage['middleware/staticCache.js'][164]++; if (utils.conditionalGET(req) && fresh(req.headers, headers)) { _$jscoverage['middleware/staticCache.js'][165]++; headers["content-length"] = 0; _$jscoverage['middleware/staticCache.js'][166]++; res.writeHead(304, headers); _$jscoverage['middleware/staticCache.js'][167]++; res.end(); } else { _$jscoverage['middleware/staticCache.js'][169]++; res.writeHead(status, headers); _$jscoverage['middleware/staticCache.js'][171]++; function write() { _$jscoverage['middleware/staticCache.js'][172]++; while (content.length) { _$jscoverage['middleware/staticCache.js'][173]++; if (false === res.write(content.shift())) { _$jscoverage['middleware/staticCache.js'][174]++; res.once("drain", write); _$jscoverage['middleware/staticCache.js'][175]++; return; } } _$jscoverage['middleware/staticCache.js'][178]++; res.end(); } _$jscoverage['middleware/staticCache.js'][181]++; write(); } _$jscoverage['middleware/staticCache.js'][183]++; break; default: _$jscoverage['middleware/staticCache.js'][186]++; res.writeHead(500, ""); _$jscoverage['middleware/staticCache.js'][187]++; res.end(); } }
defineGetter(req, 'fresh', function(){ var method = this.method; var statusCode = this.res.statusCode; if (method !== 'GET' && method !== 'HEAD') { return false; } if ((statusCode >= 200 && statusCode < 300) || statusCode === 304) { return fresh(this.headers, (this.res._headers || {})); } return false; });
var isFresh = function(req, etag){ if( req.headers['if-none-match'] || req.headers['if-modified-since'] ) { return fresh({ 'if-modified-since': req.headers['if-modified-since'], 'if-none-match': req.headers['if-none-match'], 'last-modified': req.headers['last-modified'], 'cache-control': req.headers['cache-control'] }, { 'etag': etag }); } return false; };
defineGetter(req, 'fresh', function(){ var method = this.method; var s = this.res.statusCode; // GET or HEAD for weak freshness validation only if ('GET' != method && 'HEAD' != method) return false; // 2xx or 304 as per rfc2616 14.26 if ((s >= 200 && s < 300) || 304 == s) { return fresh(this.headers, (this.res._headers || {})); } return false; });
/** * Set etag and last-modified from mongo id * * @param {Number} id * @param {Object} req * @param {Object} res * @return {Boolean} */ function setCacheFromIdAndVerifyFreshness(id, req, res) { var id = mongoose.Types.ObjectId(id); var headers = { 'etag': id.toString(), 'last-modified': id.getTimestamp().toUTCString() } for (var key in headers) { res.setHeader(key, headers[key]); } // Cache is stale if (!fresh(req.headers, headers)) { return false; } // Freshness! connect.utils.notModified(res); return true; }
defineGetter(req, 'fresh', function(){ var method = this.method; var res = this.res var status = res.statusCode // GET or HEAD for weak freshness validation only if ('GET' !== method && 'HEAD' !== method) return false; // 2xx or 304 as per rfc2616 14.26 if ((status >= 200 && status < 300) || 304 === status) { return fresh(this.headers, { 'etag': res.get('ETag'), 'last-modified': res.get('Last-Modified') }) } return false; });
getFromCache(headersCacheKey).then(function(headersData) { log('retrieved %s from cache', headersCacheKey); _.assign(req.cache, headersData); var cachedEtag = _.get(headersData, 'headers.etag'); var isFresh = fresh(req.headers, { 'etag': cachedEtag }); // TODO abstract `isFresh` as one of many validators // before returning `304` directly if (isFresh) { res.sendStatus(304); return reqComplete; } return headersData; }).then(function(headersData) {
function send(req, res, icon) { var headers = icon.headers; // Set headers var keys = Object.keys(headers); for (var i = 0; i < keys.length; i++) { var key = keys[i]; res.setHeader(key, headers[key]); } if (fresh(req.headers, res._headers)) { res.statusCode = 304; res.end(); return; } res.statusCode = 200; res.setHeader('Content-Length', icon.body.length); res.setHeader('Content-Type', 'image/x-icon'); res.end(icon.body); }
validatePublicCache: function(request, response, stat, callback) { var self = this; var headers; if (connect_utils.conditionalGET(request)) { if (stat) { headers = { "etag": connect_utils.etag(stat), "last-modified": stat.mtime }; } else { headers = { "etag": response.getHeader("etag"), "last-modified": response.getHeader("last-modified") }; } // check if the cache is still fresh if (fresh(request.headers, headers)) { return connect_utils.notModified(response); } else { return callback(); } } else { return callback(); } },
function respondFromCache(req, res, cacheEntry) { var status = cacheEntry[0] , headers = utils.merge({}, cacheEntry[1]) , content = cacheEntry.slice(2); headers.age = (new Date - new Date(headers.date)) / 1000 || 0; switch (req.method) { case 'HEAD': res.writeHead(status, headers); res.end(); break; case 'GET': if (fresh(req.headers, headers)) { headers['content-length'] = 0; res.writeHead(304, headers); res.end(); } else { res.writeHead(status, headers); function write() { while (content.length) { if (false === res.write(content.shift())) { res.once('drain', write); return; } } res.end(); } write(); } break; default: // This should never happen. res.writeHead(500, ''); res.end(); } }
module.exports = async function nuxtMiddleware(req, res, next) { // Get context const context = getContext(req, res) res.statusCode = 200 try { const result = await this.renderRoute(req.url, context) await this.nuxt.callHook('render:route', req.url, result) const { html, error, redirected, getPreloadFiles } = result if (redirected) { return html } if (error) { res.statusCode = context.nuxt.error.statusCode || 500 } // Add ETag header if (!error && this.options.render.etag) { const etag = generateETag(html, this.options.render.etag) if (fresh(req.headers, { etag })) { res.statusCode = 304 res.end() return } res.setHeader('ETag', etag) } // HTTP2 push headers for preload assets if (!error && this.options.render.http2.push) { // Parse resourceHints to extract HTTP.2 prefetch/push headers // https://w3c.github.io/preload/#server-push-http-2 const pushAssets = [] const preloadFiles = getPreloadFiles() const { shouldPush } = this.options.render.http2 const { publicPath } = this.resources.clientManifest preloadFiles.forEach(({ file, asType, fileWithoutQuery, extension }) => { // By default, we only preload scripts or css /* istanbul ignore if */ if (!shouldPush && asType !== 'script' && asType !== 'style') { return } // User wants to explicitly control what to preload if (shouldPush && !shouldPush(fileWithoutQuery, asType)) { return } pushAssets.push(`<${publicPath}${file}>; rel=preload; as=${asType}`) }) // Pass with single Link header // https://preloadFilesblog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header // https://www.w3.org/Protocols/9707-link-header.html res.setHeader('Link', pushAssets.join(',')) } // Send response res.setHeader('Content-Type', 'text/html; charset=utf-8') res.setHeader('Content-Length', Buffer.byteLength(html)) res.end(html, 'utf8') return html } catch (err) { /* istanbul ignore if */ if (context && context.redirected) { console.error(err) // eslint-disable-line no-console return err } next(err) } }
exports = module.exports = function(body){ var req = this.req , method = req.method , len; // allow status / body if (2 == arguments.length) { // res.send(body, status) backwards compat if ('number' != typeof body && 'number' == typeof arguments[1]) { this.statusCode = arguments[1]; } else { this.statusCode = body; body = arguments[1]; } } // null if (null == body) body = ''; // convert primitives body = body.valueOf(); switch (typeof body) { // response status case 'number': if (!this.getHeader('Content-Type')) this.setHeader('Content-Type', 'text/plain'); this.statusCode = body; body = http.STATUS_CODES[body]; break; // string defaulting to html case 'string': if (!this.getHeader('Content-Type')) { this.charset = this.charset || 'utf-8'; this.setHeader('Content-Type', 'text/html'); } break; case 'boolean': case 'object': if (null == body) { body = ''; } else if (Buffer.isBuffer(body)) { if (!this.getHeader('Content-Type')) this.setHeader('Content-Type', 'application/octet-stream'); } else { return this.json(body); } break; } // populate Content-Length if (undefined !== body && !this.getHeader('Content-Length')) { this.setHeader('Content-Length', len = Buffer.isBuffer(body) ? body.length : Buffer.byteLength(body)); } // ETag support // TODO: W/ support if (len > 1024) { var etag = String(Buffer.isBuffer(body) ? crc.buffer.crc32(body) : crc.crc32(body)); if (!this.getHeader('ETag')) this.setHeader('ETag', etag); } // determine if it's cacheable var cache = 'GET' == method || 'HEAD' == method; if (cache) cache = (this.statusCode >= 200 && this.statusCode < 300) || 304 == this.statusCode; // freshness if (fresh(req.headers, this._headers) && cache) { this.statusCode = 304; } // strip irrelevant headers if (204 == this.statusCode || 304 == this.statusCode) { this.removeHeader('Content-Type'); this.removeHeader('Content-Length'); body = ''; } // respond this.end('HEAD' == method ? null : body); return this; };
* still match. * * @return {Boolean} * @api public */ get fresh() { var method = this.method; var s = this.ctx.status; // GET or HEAD for weak freshness validation only if ('GET' != method && 'HEAD' != method) return false; // 2xx or 304 as per rfc2616 14.26 if ((s >= 200 && s < 300) || 304 == s) { return fresh(this.header, this.ctx.response.header); } return false; }, /** * Check if the request is stale, aka * "Last-Modified" and / or the "ETag" for the * resource has changed. * * @return {Boolean} * @api public */ get stale() {
"use strict";function defineGetter(e,r,t){Object.defineProperty(e,r,{configurable:!0,enumerable:!0,get:t})}var accepts=require("accepts"),deprecate=require("depd")("express"),isIP=require("net").isIP,typeis=require("type-is"),http=require("http"),fresh=require("fresh"),parseRange=require("range-parser"),parse=require("parseurl"),proxyaddr=require("proxy-addr"),req=exports=module.exports={__proto__:http.IncomingMessage.prototype};req.get=req.header=function(e){var r=e.toLowerCase();switch(r){case"referer":case"referrer":return this.headers.referrer||this.headers.referer;default:return this.headers[r]}},req.accepts=function(){var e=accepts(this);return e.types.apply(e,arguments)},req.acceptsEncodings=function(){var e=accepts(this);return e.encodings.apply(e,arguments)},req.acceptsEncoding=deprecate.function(req.acceptsEncodings,"req.acceptsEncoding: Use acceptsEncodings instead"),req.acceptsCharsets=function(){var e=accepts(this);return e.charsets.apply(e,arguments)},req.acceptsCharset=deprecate.function(req.acceptsCharsets,"req.acceptsCharset: Use acceptsCharsets instead"),req.acceptsLanguages=function(){var e=accepts(this);return e.languages.apply(e,arguments)},req.acceptsLanguage=deprecate.function(req.acceptsLanguages,"req.acceptsLanguage: Use acceptsLanguages instead"),req.range=function(e){var r=this.get("Range");if(r)return parseRange(e,r)},req.param=function(e,r){var t=this.params||{},s=this.body||{},n=this.query||{},a=1===arguments.length?"name":"name, default";return deprecate("req.param("+a+"): Use req.params, req.body, or req.query instead"),null!=t[e]&&t.hasOwnProperty(e)?t[e]:null!=s[e]?s[e]:null!=n[e]?n[e]:r},req.is=function(e){var r=e;if(!Array.isArray(e)){r=new Array(arguments.length);for(var t=0;t<r.length;t++)r[t]=arguments[t]}return typeis(this,r)},defineGetter(req,"protocol",function(){var e=this.connection.encrypted?"https":"http",r=this.app.get("trust proxy fn");return r(this.connection.remoteAddress,0)?(e=this.get("X-Forwarded-Proto")||e,e.split(/\s*,\s*/)[0]):e}),defineGetter(req,"secure",function(){return"https"===this.protocol}),defineGetter(req,"ip",function(){var e=this.app.get("trust proxy fn");return proxyaddr(this,e)}),defineGetter(req,"ips",function(){var e=this.app.get("trust proxy fn"),r=proxyaddr.all(this,e);return r.slice(1).reverse()}),defineGetter(req,"subdomains",function e(){var r=this.hostname;if(!r)return[];var t=this.app.get("subdomain offset"),e=isIP(r)?[r]:r.split(".").reverse();return e.slice(t)}),defineGetter(req,"path",function(){return parse(this).pathname}),defineGetter(req,"hostname",function(){var e=this.app.get("trust proxy fn"),r=this.get("X-Forwarded-Host");if(r&&e(this.connection.remoteAddress,0)||(r=this.get("Host")),r){var t="["===r[0]?r.indexOf("]")+1:0,s=r.indexOf(":",t);return-1!==s?r.substring(0,s):r}}),defineGetter(req,"host",deprecate.function(function(){return this.hostname},"req.host: Use req.hostname instead")),defineGetter(req,"fresh",function(){var e=this.method,r=this.res.statusCode;return"GET"!=e&&"HEAD"!=e?!1:r>=200&&300>r||304==r?fresh(this.headers,this.res._headers||{}):!1}),defineGetter(req,"stale",function(){return!this.fresh}),defineGetter(req,"xhr",function(){var e=this.get("X-Requested-With")||"";return"xmlhttprequest"===e.toLowerCase()});
return function(statusCode, body) { var call = { args: [].slice.call(arguments) }; // handle single argument replies if (typeof statusCode === 'string') { body = { message: statusCode }; statusCode = 200; } else if (statusCode instanceof Error) { body = statusCode; statusCode = 500; } // handle error objects in replies if (body instanceof Error) { if (typeof body.statusCode === 'number') { statusCode = body.statusCode; } if (body.expose) { body = body.toJSON ? body.toJSON() : { message: body.message }; } else { body = undefined; } } // default message if (body === undefined) { body = { message: http.STATUS_CODES[statusCode] || 'unknown response' }; } call.statusCode = statusCode; call.body = body; // allow user to override body before sending if (req.sf.responseMessage) { call = req.sf.responseMessage(call); if (!call) { debug('responseMessage handled reply'); return; } statusCode = call.statusCode; body = call.body; } var length = 0; var type; if (body !== undefined) { if (Buffer.isBuffer(body)) { length = body.length; } else if (res.sf && res.sf.produce) { type = res.sf.produce.mime; body = res.sf.produce.encoder(body); length = Buffer.byteLength(body, CHARSET); } else { type = 'application/json'; body = encoder[type](body); length = Buffer.byteLength(body, CHARSET); } } var noContent = statusCode === 204 || statusCode === 304; if (!noContent && !res.headersSent) { if (type && !res.getHeader('content-type')) { res.setHeader('content-type', type + '; charset=' + CHARSET); } if (!res.getHeader('content-length')) { res.setHeader('content-length', length); } // add etag header if not already set if (opts.etag && body && body.length && !res.getHeader('etag') && req.method === 'GET' && Math.floor(statusCode / 100) === 2) { var etag = crypto.createHash('md5').update(body).digest('hex'); res.setHeader('etag', etag); } // if cache is fresh set status code to 304 if (fresh(req.headers, res._headers)) { statusCode = 304; noContent = true; } if (noContent) { res.removeHeader('content-type'); res.removeHeader('content-length'); res.removeHeader('transfer-encoding'); } } if (noContent) body = ''; res.statusCode = statusCode; return res.end(req.method === 'HEAD' ? null : body); };
/** * @method isFresh * @private */ [isFresh]() { return fresh(this.request.headers, { etag: this.getHeader('ETag'), 'last-modified': this.getHeader('Last-Modified') }); }
SendStream.prototype.isFresh = function isFresh () { return fresh(this.req.headers, { 'etag': this.res.getHeader('ETag'), 'last-modified': this.res.getHeader('Last-Modified') }) }
function send(e,t,n){return new SendStream(e,t,n)}function SendStream(e,t,n){var r=this;n=n||{},this.req=e,this.path=t,this.options=n,this._etag=n.etag!==undefined?Boolean(n.etag):!0,this._dotfiles=n.dotfiles!==undefined?n.dotfiles:"ignore";if(["allow","deny","ignore"].indexOf(this._dotfiles)===-1)throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"');this._hidden=Boolean(n.hidden),"hidden"in n&&deprecate("hidden: use dotfiles: '"+(this._hidden?"allow":"ignore")+"' instead"),"dotfiles"in n||(this._dotfiles=undefined),this._extensions=n.extensions!==undefined?normalizeList(n.extensions):[],this._index=n.index!==undefined?normalizeList(n.index):["index.html"],this._lastModified=n.lastModified!==undefined?Boolean(n.lastModified):!0,this._maxage=n.maxAge||n.maxage,this._maxage=typeof this._maxage=="string"?ms(this._maxage):Number(this._maxage),this._maxage=isNaN(this._maxage)?0:Math.min(Math.max(0,this._maxage),maxMaxAge),this._root=n.root?resolve(n.root):null,!this._root&&n.from&&this.from(n.from)}function containsDotFile(e){for(var t=0;t<e.length;t++)if(e[t][0]===".")return!0;return!1}function decode(e){try{return decodeURIComponent(e)}catch(t){return-1}}function normalizeList(e){return[].concat(e||[])}var debug=require("debug")("send"),deprecate=require("depd")("send"),destroy=require("destroy"),escapeHtml=require("escape-html"),parseRange=require("range-parser"),Stream=require("stream"),mime=require("mime"),fresh=require("fresh"),path=require("path"),http=require("http"),fs=require("fs"),normalize=path.normalize,join=path.join,etag=require("etag"),EventEmitter=require("events").EventEmitter,ms=require("ms"),onFinished=require("on-finished"),extname=path.extname,maxMaxAge=31536e6,resolve=path.resolve,sep=path.sep,toString=Object.prototype.toString,upPathRegexp=/(?:^|[\\\/])\.\.(?:[\\\/]|$)/;exports=module.exports=send,exports.mime=mime;var listenerCount=EventEmitter.listenerCount||function(e,t){return e.listeners(t).length};SendStream.prototype.__proto__=Stream.prototype,SendStream.prototype.etag=deprecate.function(function(t){return t=Boolean(t),debug("etag %s",t),this._etag=t,this},"send.etag: pass etag as option"),SendStream.prototype.hidden=deprecate.function(function(t){return t=Boolean(t),debug("hidden %s",t),this._hidden=t,this._dotfiles=undefined,this},"send.hidden: use dotfiles option"),SendStream.prototype.index=deprecate.function(function e(t){var e=t?normalizeList(t):[];return debug("index %o",t),this._index=e,this},"send.index: pass index as option"),SendStream.prototype.root=function(e){return e=String(e),this._root=resolve(e),this},SendStream.prototype.from=deprecate.function(SendStream.prototype.root,"send.from: pass root as option"),SendStream.prototype.root=deprecate.function(SendStream.prototype.root,"send.root: pass root as option"),SendStream.prototype.maxage=deprecate.function(function(t){return t=typeof t=="string"?ms(t):Number(t),isNaN(t)&&(t=0),Infinity==t&&(t=31536e6),debug("max-age %d",t),this._maxage=t,this},"send.maxage: pass maxAge as option"),SendStream.prototype.error=function(e,t){var n=this.res,r=http.STATUS_CODES[e];t=t||new Error(r),t.status=e;if(listenerCount(this,"error")!==0)return this.emit("error",t);n._headers=undefined,n.statusCode=t.status,n.end(r)},SendStream.prototype.hasTrailingSlash=function(){return"/"==this.path[this.path.length-1]},SendStream.prototype.isConditionalGET=function(){return this.req.headers["if-none-match"]||this.req.headers["if-modified-since"]},SendStream.prototype.removeContentHeaderFields=function(){var e=this.res;Object.keys(e._headers).forEach(function(t){0==t.indexOf("content")&&e.removeHeader(t)})},SendStream.prototype.notModified=function(){var e=this.res;debug("not modified"),this.removeContentHeaderFields(),e.statusCode=304,e.end()},SendStream.prototype.headersAlreadySent=function(){var t=new Error("Can't set headers after they are sent.");debug("headers already sent"),this.error(500,t)},SendStream.prototype.isCachable=function(){var e=this.res;return e.statusCode>=200&&e.statusCode<300||304==e.statusCode},SendStream.prototype.onStatError=function(e){var t=["ENOENT","ENAMETOOLONG","ENOTDIR"];if(~t.indexOf(e.code))return this.error(404,e);this.error(500,e)},SendStream.prototype.isFresh=function(){return fresh(this.req.headers,this.res._headers)},SendStream.prototype.isRangeFresh=function(){var t=this.req.headers["if-range"];return t?~t.indexOf('"')?~t.indexOf(this.res._headers.etag):Date.parse(this.res._headers["last-modified"])<=Date.parse(t):!0},SendStream.prototype.redirect=function(e){if(listenerCount(this,"directory")!==0)return this.emit("directory");if(this.hasTrailingSlash())return this.error(403);var t=this.res;e+="/",t.statusCode=301,t.setHeader("Content-Type","text/html; charset=utf-8"),t.setHeader("Location",e),t.end('Redirecting to <a href="'+escapeHtml(e)+'">'+escapeHtml(e)+"</a>\n")},SendStream.prototype.pipe=function(e){var t=this,n=arguments,r=this._root;this.res=e;var i=decode(this.path);if(i===-1)return this.error(400);if(~i.indexOf("\0"))return this.error(400);var s;if(r!==null){i=normalize(join(r,i)),r=normalize(r+sep);if((i+sep).substr(0,r.length)!==r)return debug('malicious path "%s"',i),this.error(403);s=i.substr(r.length).split(sep)}else{if(upPathRegexp.test(i))return debug('malicious path "%s"',i),this.error(403);s=normalize(i).split(sep),i=resolve(i)}if(containsDotFile(s)){var o=this._dotfiles;o===undefined&&(o=s[s.length-1][0]==="."?this._hidden?"allow":"ignore":"allow"),debug('%s dotfile "%s"',o,i);switch(o){case"allow":break;case"deny":return this.error(403);case"ignore":default:return this.error(404)}}return this._index.length&&this.path[this.path.length-1]==="/"?(this.sendIndex(i),e):(this.sendFile(i),e)},SendStream.prototype.send=function(e,t){var n=this.options,r=t.size,i=this.res,s=this.req,o=s.headers.range,u=n.start||0;if(i._header)return this.headersAlreadySent();debug('pipe "%s"',e),this.setHeader(e,t),this.type(e);if(this.isConditionalGET()&&this.isCachable()&&this.isFresh())return this.notModified();r=Math.max(0,r-u);if(n.end!==undefined){var a=n.end-u+1;r>a&&(r=a)}if(o){o=parseRange(r,o),this.isRangeFresh()||(debug("range stale"),o=-2);if(-1==o)return debug("range unsatisfiable"),i.setHeader("Content-Range","bytes */"+t.size),this.error(416);-2!=o&&o.length===1&&(debug("range %j",o),n.start=u+o[0].start,n.end=u+o[0].end,i.statusCode=206,i.setHeader("Content-Range","bytes "+o[0].start+"-"+o[0].end+"/"+r),r=n.end-n.start+1)}i.setHeader("Content-Length",r);if("HEAD"==s.method)return i.end();this.stream(e,n)},SendStream.prototype.sendFile=function(t){function i(e){if(r._extensions.length<=n)return e?r.onStatError(e):r.error(404);var s=t+"."+r._extensions[n++];debug('stat "%s"',s),fs.stat(s,function(e,t){if(e)return i(e);if(t.isDirectory())return i();r.emit("file",s,t),r.send(s,t)})}var n=0,r=this;debug('stat "%s"',t),fs.stat(t,function(n,s){if(n&&n.code==="ENOENT"&&!extname(t)&&t[t.length-1]!==sep)return i(n);if(n)return r.onStatError(n);if(s.isDirectory())return r.redirect(r.path);r.emit("file",t,s),r.send(t,s)})},SendStream.prototype.sendIndex=function(t){function i(e){if(++n>=r._index.length)return e?r.onStatError(e):r.error(404);var s=join(t,r._index[n]);debug('stat "%s"',s),fs.stat(s,function(e,t){if(e)return i(e);if(t.isDirectory())return i();r.emit("file",s,t),r.send(s,t)})}var n=-1,r=this;i()},SendStream.prototype.stream=function(e,t){var n=!1,r=this,i=this.res,s=this.req,o=fs.createReadStream(e,t);this.emit("stream",o),o.pipe(i),onFinished(i,function(){n=!0,destroy(o)}),o.on("error",function(t){if(n)return;n=!0,destroy(o),r.onStatError(t)}),o.on("end",function(){r.emit("end")})},SendStream.prototype.type=function(e){var t=this.res;if(t.getHeader("Content-Type"))return;var n=mime.lookup(e),r=mime.charsets.lookup(n);debug("content-type %s",n),t.setHeader("Content-Type",n+(r?"; charset="+r:""))},SendStream.prototype.setHeader=function(t,n){var r=this.res;this.emit("headers",r,t,n),r.getHeader("Accept-Ranges")||r.setHeader("Accept-Ranges","bytes"),r.getHeader("Date")||r.setHeader("Date",(new Date).toUTCString()),r.getHeader("Cache-Control")||r.setHeader("Cache-Control","public, max-age="+Math.floor(this._maxage/1e3));if(this._lastModified&&!r.getHeader("Last-Modified")){var i=n.mtime.toUTCString();debug("modified %s",i),r.setHeader("Last-Modified",i)}if(this._etag&&!r.getHeader("ETag")){var s=etag(n);debug("etag %s",s),r.setHeader("ETag",s)}};
* still match. * * @return {Boolean} * @api public */ get fresh() { var method = this.method; var s = this.status; // GET or HEAD for weak freshness validation only if ('GET' != method && 'HEAD' != method) return false; // 2xx or 304 as per rfc2616 14.26 if ((s >= 200 && s < 300) || 304 == s) { return fresh(this.header, this.responseHeader); } return false; }, /** * Check if the request is stale, aka * "Last-Modified" and / or the "ETag" for the * resource has changed. * * @return {Boolean} * @api public */ get stale() {
SendStream.prototype.isFresh = function(){ return fresh(this.req.headers, this.res._headers); };
/*! * Connect - staticCache * Copyright(c) 2011 Sencha Inc. * MIT Licensed *//** * Module dependencies. */function respondFromCache(e,t,n){var r=n[0],i=utils.merge({},n[1]),s=n.slice(2);i.age=(new Date-new Date(i.date))/1e3||0;switch(e.method){case"HEAD":t.writeHead(r,i);t.end();break;case"GET":if(utils.conditionalGET(e)&&fresh(e.headers,i)){i["content-length"]=0;t.writeHead(304,i);t.end()}else{t.writeHead(r,i);function o(){while(s.length)if(!1===t.write(s.shift())){t.once("drain",o);return}t.end()}o()}break;default:t.writeHead(500,"");t.end()}}function mustRevalidate(e,t){var n=t[1],r=utils.parseCacheControl(e.headers["cache-control"]||""),i=utils.parseCacheControl(n["cache-control"]||""),s=(new Date-new Date(n.date))/1e3||0;return i["no-cache"]||i["must-revalidate"]||i["proxy-revalidate"]?!0:r["no-cache"]?!0:null!=r["max-age"]?r["max-age"]<s:null!=i["max-age"]?i["max-age"]<s:!1}function cacheKey(e){return utils.parseUrl(e).path}var utils=require("../utils"),Cache=require("../cache"),fresh=require("fresh");module.exports=function(t){var t=t||{},n=new Cache(t.maxObjects||128),r=t.maxLength||262144;if(process.env.NODE_ENV!=="test"){console.warn("connect.staticCache() is deprecated and will be removed in 3.0");console.warn("use varnish or similar reverse proxy caches.")}return function(t,i,s){var o=cacheKey(t),u=t.headers.range,a=t.headers.cookie,f=n.get(o);t.on("static",function(e){var t=i._headers,s=utils.parseCacheControl(t["cache-control"]||""),u=t["content-length"],f;if(t["set-cookie"])return a=!0;if(a)return;if(!u||u>r)return;if(t["content-range"])return;if(s["no-cache"]||s["no-store"]||s["private"]||s["must-revalidate"])return;if(f=n.get(o)){if(t.etag==f[0].etag){f[0].date=new Date;return}n.remove(o)}if(null==e)return;var l=[];e.on("data",function(e){l.push(e)});e.on("end",function(){var e=n.add(o);delete t["x-cache"];e.push(200);e.push(t);e.push.apply(e,l)})});if(t.method=="GET"||t.method=="HEAD")if(u)s();else if(!a&&f&&!mustRevalidate(t,f)){i.setHeader("X-Cache","HIT");respondFromCache(t,i,f)}else{i.setHeader("X-Cache","MISS");s()}else s()}};
"use strict";function favicon(e,r){var t,o,a,n=r||{},s=calcMaxAge(n.maxAge);if(!e)throw new TypeError("path to favicon.ico is required");if(Buffer.isBuffer(e))t=new Buffer(e.length),e.copy(t),o=createIcon(t,s);else{if("string"!=typeof e)throw new TypeError("path to favicon.ico must be string or buffer");if(e=resolve(e),a=fs.statSync(e),a.isDirectory())throw createIsDirError(e)}return function(r,t,a){return"/favicon.ico"!==parseUrl(r).pathname?void a():"GET"!==r.method&&"HEAD"!==r.method?(t.statusCode="OPTIONS"===r.method?200:405,t.setHeader("Allow","GET, HEAD, OPTIONS"),t.setHeader("Content-Length","0"),void t.end()):o?send(r,t,o):void fs.readFile(e,function(e,n){return e?a(e):(o=createIcon(n,s),void send(r,t,o))})}}function calcMaxAge(e){var r="string"==typeof e?ms(e):e;return null!=r?Math.min(Math.max(0,r),maxMaxAge):maxMaxAge}function createIcon(e,r){return{body:e,headers:{"Cache-Control":"public, max-age="+Math.floor(r/1e3),ETag:etag(e)}}}function createIsDirError(e){var r=new Error("EISDIR, illegal operation on directory '"+e+"'");return r.code="EISDIR",r.errno=28,r.path=e,r.syscall="open",r}function send(e,r,t){for(var o=t.headers,a=Object.keys(o),n=0;n<a.length;n++){var s=a[n];r.setHeader(s,o[s])}return fresh(e.headers,r._headers)?(r.statusCode=304,void r.end()):(r.statusCode=200,r.setHeader("Content-Length",t.body.length),r.setHeader("Content-Type","image/x-icon"),void r.end(t.body))}var etag=require("etag"),fresh=require("fresh"),fs=require("fs"),ms=require("ms"),parseUrl=require("parseurl"),path=require("path"),resolve=path.resolve;module.exports=favicon;var maxMaxAge=31536e6;
module.exports = function send (req, res, body, cb) { if (cb) { res.on('finish', cb) res.on('close', cb) } var head = req.method == 'HEAD' , type , stream , status = res.statusCode || 200 if (body == null) { type = 'text/html' body = '' } else if (typeof body == 'string') { type = 'text/html' body = new Buffer(body) } else if (body.pipe) { type = 'application/octet-stream' stream = body body = null } else { // buffer type = 'application/octet-stream' } if (!res.getHeader('Content-Type')) { res.setHeader('Content-Type', type) } if (!res.getHeader('Content-Length') && body != null) { res.setHeader('Content-Length', String(body.length)) } // ETag support if (body && body.length > 1024 && !res.getHeader('ETag')) { res.setHeader('ETag', '"' + crc(body) + '"') } res.statusCode = status >= 200 && status < 300 && fresh(req.headers, res._headers) ? 304 : status // strip irrelevant headers if (204 == res.statusCode || 304 == res.statusCode) { res.removeHeader('Content-Type') res.removeHeader('Content-Length') res.removeHeader('Transfer-Encoding') // https://github.com/visionmedia/express/pull/1451 body = stream = null } if (head) { res.end() } else if (stream) { res.on('close', function () { stream.unpipe(res) }) stream.pipe(res) } else { res.end(body) } }
function isFresh (req, res) { return fresh(req.headers, res._headers); }