nyc-bookstores/node_modules/filed/main.js
2013-05-27 13:45:59 -07:00

203 lines
6.2 KiB
JavaScript

var fs = require('fs')
, util = require('util')
, crypto = require('crypto')
, stream = require('stream')
, path = require('path')
, mimetypes = require('./mimetypes')
, rfc822 = require('./rfc822')
;
function File (options) {
stream.Stream.call(this)
this.writable = true
this.readable = true
this.buffers = []
var self = this
if (typeof options === 'string') options = {path:options}
if (!options.index) options.index = 'index.html'
self.writable = (typeof options.writable === "undefined") ? true : options.writable
self.readable = (typeof options.readable === "undefined") ? true : options.readable
self.path = options.path
self.index = options.index
self.on('pipe', function (src) {
this.src = src
})
this.buffering = true
this.mimetype = options.mimetype || mimetypes.lookup(this.path.slice(this.path.lastIndexOf('.')+1))
var stopBuffering = function () {
self.buffering = false
while (self.buffers.length) {
self.emit('data', self.buffers.shift())
}
if (self.ended) self.emit('end')
}
fs.stat(options.path, function (err, stats) {
var finish = function (err, stats) {
self.stat = stats
if (err && err.code === 'ENOENT' && !self.dest && !self.src) self.src = self.path
if (err && !self.dest && !self.src) return self.emit('error', err)
if (err && self.dest && !self.dest.writeHead) return self.emit('error', err)
// See if writes are disabled
if (self.src && self.src.method &&
!self.writable && self.dest.writeHead &&
(self.src.method === 'PUT' || self.src.method === 'POST')) {
self.dest.writeHead(405, {'content-type':'text/plain'})
self.dest.end(self.src.method+' Not Allowed')
return
}
if (!err) {
self.etag = crypto.createHash('md5').update(stats.ino+'/'+stats.mtime+'/'+stats.size).digest("hex")
self.lastmodified = rfc822.getRFC822Date(stats.mtime)
}
process.nextTick(function () {
stopBuffering()
})
// 404 and 500
if ( err && self.dest && self.dest.writeHead && // We have an error object and dest is an HTTP response
( // Either we have a source and it's a GET/HEAD or we don't have a src
(self.src && (self.src.method == 'GET' || self.src.method === 'HEAD')) || (!self.src)
)
) {
if (err.code === 'ENOENT') {
self.dest.statusCode = 404
self.dest.end('Not Found')
} else {
self.dest.statusCode = 500
self.dest.end(err.message)
}
return
}
// Source is an HTTP Server Request
if (self.src && (self.src.method === 'GET' || self.src.method === 'HEAD') && self.dest) {
if (self.dest.setHeader) {
self.dest.setHeader('content-type', self.mimetype)
self.dest.setHeader('etag', self.etag)
self.dest.setHeader('last-modified', self.lastmodified)
}
if (self.dest.writeHead) {
if (self.src && self.src.headers) {
if (self.src.headers['if-none-match'] === self.etag ||
// Lazy last-modifed matching but it's faster than parsing Datetime
self.src.headers['if-modified-since'] === self.lastmodified) {
self.dest.statusCode = 304
self.dest.end()
return
}
}
// We're going to return the whole file
self.dest.statusCode = 200
self.dest.setHeader('content-length', stats.size)
} else {
// Destination is not an HTTP response, GET and HEAD method are not allowed
return
}
if (self.src.method !== 'HEAD') {
fs.createReadStream(self.path).pipe(self.dest)
}
return
}
if (self.src && (self.src.method === 'PUT' || self.src.method === 'POST')) {
if (!err) {
// TODO handle overwrite case
return
}
stream.Stream.prototype.pipe.call(self, fs.createWriteStream(self.path))
if (self.dest && self.dest.writeHead) {
self.on('end', function () {
self.dest.statusCode = 201
self.dest.setHeader('content-length', 0)
self.dest.end()
})
}
return
}
// Desination is an HTTP response, we already handled 404 and 500
if (self.dest && self.dest.writeHead) {
self.dest.statusCode = 200
self.dest.setHeader('content-type', self.mimetype)
self.dest.setHeader('etag', self.etag)
self.dest.setHeader('last-modified', self.lastmodified)
self.dest.setHeader('content-length', stats.size)
fs.createReadStream(self.path).pipe(self.dest)
return
}
// Destination is not an HTTP request
if (self.src && !self.dest) {
stream.Stream.prototype.pipe.call(self, fs.createWriteStream(self.path))
} else if (self.dest && !self.src) {
fs.createReadStream(self.path).pipe(self.dest)
}
}
if (!err && stats.isDirectory()) {
self.path = path.join(self.path, self.index)
self.mimetype = mimetypes.lookup(self.path.slice(self.path.lastIndexOf('.')+1))
fs.stat(self.path, finish)
return
} else {
finish(err, stats)
}
if (!self.src && !self.dest) {
if (self.buffers.length > 0) {
stream.Stream.prototype.pipe.call(self, fs.createWriteStream(self.path))
} else if (self.listeners('data').length > 0) {
fs.createReadStream(self.path).pipe(self.dest)
} else {
fs.createReadStream(self.path).pipe(self)
}
}
})
}
util.inherits(File, stream.Stream)
File.prototype.pipe = function (dest, options) {
this.dest = dest
this.destOptions = options
dest.emit('pipe', this)
// stream.Stream.prototype.pipe.call(this, dest, options)
}
File.prototype.write = function (chunk, encoding) {
if (encoding) chunk = chunk.toString(encoding)
if (this.buffering) {
this.buffers.push(chunk)
} else {
this.emit('data', chunk)
}
}
File.prototype.end = function (chunk) {
if (chunk) this.write(chunk)
if (this.buffering) {
this.ended = true
} else {
this.emit('end')
}
}
module.exports = function (options) {
return new File(options)
}
module.exports.File = File