Skip to content

Commit

Permalink
Add indexDocument option - which servers static web content
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBeale committed May 4, 2015
1 parent 75939b7 commit 4635b18
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 23 deletions.
4 changes: 2 additions & 2 deletions lib/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';
var app = function (hostname, port, directory, silent) {
var app = function (hostname, port, directory, silent, indexDocument, errorDocument) {
var express = require('express'),
app = express(),
logger = require('./logger')(silent),
Controllers = require('./controllers'),
controllers = new Controllers(directory, logger),
controllers = new Controllers(directory, logger, indexDocument, errorDocument),
concat = require('concat-stream');

/**
Expand Down
93 changes: 73 additions & 20 deletions lib/controllers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use strict';
module.exports = function (rootDirectory, logger) {
module.exports = function (rootDirectory, logger, indexDocument, errorDocument) {
var FileStore = require('./file-store'),
fileStore = new FileStore(rootDirectory),
templateBuilder = require('./xml-template-builder');
Expand All @@ -10,6 +10,23 @@ module.exports = function (rootDirectory, logger) {
return res.send(template);
};

var buildResponse = function (req, res, status, object, data) {
res.header('Etag', object.md5);
res.header('Last-Modified', new Date(object.modifiedDate).toUTCString());
res.header('Content-Type', object.contentType);
res.header('Content-Length', object.size);
if (object.customMetaData.length > 0) {
object.customMetaData.forEach(function (metaData) {
res.header(metaData.key, metaData.value);
});
}
res.status(200);
if (req.method === 'HEAD') {
return res.end();
}
return res.end(data);
};

/**
* The following methods correspond the S3 api. For more information visit:
* http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html
Expand Down Expand Up @@ -37,6 +54,7 @@ module.exports = function (rootDirectory, logger) {
return buildXmlResponse(res, 200, template);
},
getBucket: function (req, res) {

var options = {
marker: req.query.marker || null,
prefix: req.query.prefix || null,
Expand All @@ -46,8 +64,36 @@ module.exports = function (rootDirectory, logger) {
logger.info('Fetched bucket "%s" with options %s', req.bucket.name, options);
fileStore.getObjects(req.bucket, options, function (err, results) {
logger.info('Found %d objects for bucket "%s"', results.length, req.bucket.name);
var template = templateBuilder.buildBucketQuery(options, results);
return buildXmlResponse(res, 200, template);

var match = false;
if (indexDocument)
{
results.forEach(function(result){
if (result.key === indexDocument)
{
match = true;
fileStore.getObject(req.bucket, result.key, function (err, object, data) {
if (err)
{
var template = templateBuilder.buildKeyNotFound(keyName);
logger.error('Object "%s" in bucket "%s" does not exist', keyName, req.bucket.name);
return buildXmlResponse(res, 404, template);
}
else
{
logger.info('Serving Page: %s', object.key);
return buildResponse(req, res, 200, object, data);
}
});
}
});
}

if (!match)
{
var template = templateBuilder.buildBucketQuery(options, results);
return buildXmlResponse(res, 200, template);
}
});
},
putBucket: function (req, res) {
Expand Down Expand Up @@ -107,9 +153,28 @@ module.exports = function (rootDirectory, logger) {
}
fileStore.getObject(req.bucket, keyName, function (err, object, data) {
if (err) {
var template = templateBuilder.buildKeyNotFound(keyName);
logger.error('Object "%s" in bucket "%s" does not exist', keyName, req.bucket.name);
return buildXmlResponse(res, 404, template);

if (indexDocument)
{
return fileStore.getObject(req.bucket, keyName + indexDocument, function (err, object, data) {
if (err)
{
var template = templateBuilder.buildKeyNotFound(keyName);
logger.error('Object "%s" in bucket "%s" does not exist', keyName, req.bucket.name);
return buildXmlResponse(res, 404, template);
}
else
{
return buildResponse(req, res, 200, object, data);
}
});
}
else
{
var template = templateBuilder.buildKeyNotFound(keyName);
logger.error('Object "%s" in bucket "%s" does not exist', keyName, req.bucket.name);
return buildXmlResponse(res, 404, template);
}
}

var noneMatch = req.headers['if-none-match'];
Expand All @@ -124,20 +189,8 @@ module.exports = function (rootDirectory, logger) {
return res.status(304).end();
}
}
res.header('Etag', object.md5);
res.header('Last-Modified', new Date(object.modifiedDate).toUTCString());
res.header('Content-Type', object.contentType);
res.header('Content-Length', object.size);
if (object.customMetaData.length > 0) {
object.customMetaData.forEach(function (metaData) {
res.header(metaData.key, metaData.value);
});
}
res.status(200);
if (req.method === 'HEAD') {
return res.end();
}
return res.end(data);

return buildResponse(req, res, 200, object, data);
});
},
putObject: function (req, res) {
Expand Down
14 changes: 13 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ var S3rver = function () {
this.port = 4578;
this.hostname = 'localhost';
this.silent = false;
this.indexDocument = '';
this.errorDocument = '';
};

S3rver.prototype.setPort = function (port) {
Expand All @@ -26,8 +28,18 @@ S3rver.prototype.setSilent = function (silent) {
return this;
};

S3rver.prototype.setIndexDocument = function (indexDocument) {
this.indexDocument = indexDocument;
return this;
};

S3rver.prototype.setErrorDocument = function (errorDocument) {
this.errorDocument = errorDocument;
return this;
};

S3rver.prototype.run = function (done) {
var app = new App(this.hostname, this.port, this.directory, this.silent);
var app = new App(this.hostname, this.port, this.directory, this.silent, this.indexDocument, this.errorDocument);
return app.serve(done);

};
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"fs-extra": "^0.14.0",
"mocha": "^2.1.0",
"moment": "^2.8.4",
"request": "^2.55.0",
"should": "^4.4.2",
"xml2js": "^0.4.4"
},
Expand Down
124 changes: 124 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var path = require('path');
var md5 = require('MD5');
var S3rver = require('../lib');
var util = require('util');
var request = require('request');

describe('S3rver Tests', function () {
var s3Client;
Expand Down Expand Up @@ -550,3 +551,126 @@ describe('S3rver Tests', function () {
});
});
});


describe('S3rver Tests with Static Web Hosting', function () {
var s3Client;
before(function (done) {
/**
* Start the server
*/
var s3rver = new S3rver();
s3rver.setHostname('localhost')
.setPort(5694)
.setDirectory('/tmp/s3rver_test_directory')
.setSilent(true)
.setIndexDocument('index.html')
.run(function (err, hostname, port, directory) {
if (err) {
return done('Error starting server', err);
}
var config = {
accessKeyId: '123',
secretAccessKey: 'abc',
endpoint: util.format('%s:%d', hostname, port),
sslEnabled: false,
s3ForcePathStyle: true
};
AWS.config.update(config);
s3Client = new AWS.S3();
s3Client.endpoint = new AWS.Endpoint(config.endpoint);
/**
* Remove if exists and recreate the temporary directory
*/
fs.remove(directory, function (err) {
if (err) {
return done(err);
}
fs.mkdirs(directory, function (err) {
if (err) {
return done(err);
}
done();
});
});
});
});



it('should create a site bucket', function (done) {
s3Client.createBucket({Bucket: 'site'}, function (err) {
if (err) {
return done(err);
}
done();
});
});


it('should upload a html page to / path', function (done) {
var params = {Bucket: 'site', Key: 'index.html', Body: '<html><body>Hello</body></html>'};
s3Client.putObject(params, function (err, data) {
/[a-fA-F0-9]{32}/.test(data.ETag).should.equal(true);
if (err) {
return done(err);
}
done();
});
});


it('should upload a html page to a directory path', function (done) {
var params = {Bucket: 'site', Key: 'page/index.html', Body: '<html><body>Hello</body></html>'};
s3Client.putObject(params, function (err, data) {
/[a-fA-F0-9]{32}/.test(data.ETag).should.equal(true);
if (err) {
return done(err);
}
done();
});
});


it('should get an index page at / path', function (done) {
request('http://localhost:5694/site/', function (error, response, body) {
if (error)
{
return done(error);
}

if (response.statusCode !== 200) {
return done(new Error('Invalid status: ' + response.statusCode));
}

if (body !== '<html><body>Hello</body></html>')
{
return done(new Error('Invalid Content: ' + body));
}

done();
});
});


it('should get an index page at /page/ path', function (done) {
request('http://localhost:5694/site/page/', function (error, response, body) {
if (error)
{
return done(error);
}

if (response.statusCode !== 200) {
return done(new Error('Invalid status: ' + response.statusCode));
}

if (body !== '<html><body>Hello</body></html>')
{
return done(new Error('Invalid Content: ' + body));
}

done();
});
});

});

0 comments on commit 4635b18

Please sign in to comment.