@zkochan/pnpm
Version:
A fast implementation of npm install
155 lines (133 loc) • 4.32 kB
JavaScript
/**
* Copyright 2015 Google Inc. All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file or at
* https://github.com/firebase/superstatic/blob/master/LICENSE
*/
'use strict';
var RSVP = require('rsvp');
var _ = require('lodash');
var mime = require('mime-types');
var path = require('path');
var awaitFinished = RSVP.denodeify(require('on-finished'));
var destroy = require('destroy');
var compress = require('compression')();
var Responder = function(req, res, options) {
this.req = req;
this.res = res;
this.provider = options.provider;
this.config = options.config || {};
this.gzip = options.gzip;
};
Responder.prototype.isNotModified = function(stats) {
if (stats.etag && stats.etag === this.req.headers['if-none-match']) {
return true;
}
var reqModified = this.req.headers['if-modified-since'];
if (reqModified) {
reqModified = new Date(reqModified).getTime();
}
if (stats.modified && reqModified && stats.modified < reqModified) {
return true;
}
return false;
};
Responder.prototype.handle = function(item, next) {
var self = this;
return this._handle(item).then(function(responded) {
if (!responded && next) {
next();
}
return responded;
}).catch(function(err) {
return self.handleError(err);
});
};
Responder.prototype._handle = function(item) {
if (_.isArray(item)) {
return this.handleStack(item);
} else if (_.isString(item)) {
return this.handleFile({file: item});
} else if (_.isObject(item)) {
if (item.file) {
return this.handleFile(item);
} else if (item.redirect) {
return this.handleRedirect(item);
} else if (item.data) {
return this.handleData(item);
}
}
return RSVP.reject(new Error(JSON.stringify(item) + ' is not a recognized responder directive'));
};
Responder.prototype.handleError = function(err) {
this.res.statusCode = 500;
console.log(err.stack);
this.res.end('Unexpected error occurred.');
};
Responder.prototype.handleStack = function(stack) {
var self = this;
if (stack.length) {
return this._handle(stack.shift()).then(function(responded) {
return responded ? true : self.handleStack(stack);
});
}
return RSVP.resolve(false);
};
Responder.prototype.handleFile = function(file) {
var self = this;
return this.provider(this.req, file.file).then(function(result) {
if (!result) {
return false;
}
if (self.isNotModified(result)) {
return self.handleNotModified(result);
}
return self.handleFileStream(file, result);
});
};
Responder.prototype.handleFileStream = function(file, result) {
var self = this;
this.streamedFile = file;
this.res.statusCode = file.status || 200;
if (this.res.statusCode === 200 && file.file === this.config.errorPage) {
this.res.statusCode = 404;
}
this.res.setHeader('Content-Type', result.contentType || mime.contentType(path.extname(file.file)));
if (result.size) { this.res.setHeader('Content-Length', result.size); }
if (result.etag) { this.res.setHeader('ETag', result.etag); }
if (result.modified) {
this.res.setHeader('Last-Modified', new Date(result.modified).toUTCString());
}
if (this.gzip) {
compress(this.req, this.res, function() {
result.stream.pipe(self.res);
});
} else {
result.stream.pipe(self.res);
}
return awaitFinished(this.res).then(function() {
destroy(result.stream);
return true;
});
};
Responder.prototype.handleNotModified = function() {
this.res.statusCode = 304;
this.res.removeHeader('Content-Type');
this.res.removeHeader('Content-Length');
this.res.removeHeader('Transfer-Encoding');
this.res.end();
};
Responder.prototype.handleRedirect = function(redirect) {
this.res.statusCode = redirect.status || 301;
this.res.setHeader('Location', redirect.redirect);
this.res.setHeader('Content-Type', 'text/html; charset=utf-8');
this.res.end('Redirecting to ' + redirect.redirect);
return RSVP.resolve(true);
};
Responder.prototype.handleData = function(data) {
this.res.statusCode = data.status || 200;
this.res.setHeader('Content-Type', data.contentType || 'text/html; charset=utf-8');
this.res.end(data.data);
return RSVP.resolve(true);
};
module.exports = Responder;