Skip to content

Commit

Permalink
Merge pull request #50 from ringo/local-gzip
Browse files Browse the repository at this point in the history
Enables serving of precompressed static files
  • Loading branch information
botic authored Oct 18, 2016
2 parents 5b5dff8 + bd78beb commit a72d7b6
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 2 deletions.
24 changes: 22 additions & 2 deletions lib/middleware/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@
*
* * `baseURI`: a common prefix for a resource URI (e.g. "/static")
*
* * `options`: an object with fine-grained configuration options
* ** `servePrecompressed`: if true (default), static resources with a pre-compressed gzip equivalent will be
* served instead of the original file.
*
* You can call `static()` multiple times to register multiple resources to be served.
*/

var objects = require("ringo/utils/objects");
var response = require("ringo/jsgi/response");
var {mimeType} = require("ringo/mime");

Expand All @@ -27,7 +32,10 @@ exports.middleware = function static(next, app) {

var resourceConfigs = [];

app.static = function(base, index, baseURI) {
app.static = function(base, index, baseURI, options) {
var opts = objects.merge(options || {}, {
"servePrecompressed": true
});
var baseRepository;
if (typeof base === "string") {
baseRepository = getRepository(base);
Expand All @@ -40,7 +48,8 @@ exports.middleware = function static(next, app) {
resourceConfigs.push({
repository: baseRepository,
index: index,
prefix: (typeof baseURI === "string" ? baseURI : "")
prefix: (typeof baseURI === "string" ? baseURI : ""),
options: opts
});
};

Expand All @@ -54,6 +63,17 @@ exports.middleware = function static(next, app) {
if (path.length > 1) {
var resource = config.repository.getResource(path);
if (resource && resource.exists()) {
// check if precompressed gzip resource is available and it's serving is enabled
let acceptEncoding = (request.headers["accept-encoding"] || "").toLowerCase();
if (acceptEncoding.indexOf("gzip") > -1 && config.options.servePrecompressed === true) {
let gzippedResource = config.repository.getResource(path + ".gz");
if (gzippedResource && gzippedResource.exists()) {
let jsgiResponse = response.static(gzippedResource, mimeType(path, "text/plain"));
jsgiResponse.headers["Content-Encoding"] = "gzip";
return jsgiResponse;
}
}

return response.static(resource, mimeType(path, "text/plain"));
}
}
Expand Down
1 change: 1 addition & 0 deletions test/middleware/fixtures/foo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!DOCTYPE html><foo></foo>
1 change: 1 addition & 0 deletions test/middleware/fixtures/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!DOCTYPE html>
Binary file added test/middleware/fixtures/index.html.gz
Binary file not shown.
139 changes: 139 additions & 0 deletions test/middleware/static_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
const system = require("system");
const assert = require("assert");

var {Application} = require("../../lib/stick");
var {static} = require("../../lib/middleware");

const bodyAsString = function(body, charset) {
if (body && typeof body.forEach == "function") {
var output = new java.io.ByteArrayOutputStream();
var writer = function(part) {
if (!(part instanceof Binary)) {
part = part.toByteString(charset);
}
output.write(part);
};
body.forEach(writer);
if (typeof body.close == "function") {
body.close(writer);
}

return output.toString(charset);
} else {
throw new Error("Response body doesn't implement forEach: " + body);
}
};

const bodyAsByteArray = function(body) {
if (body && typeof body.forEach == "function") {
var output = new java.io.ByteArrayOutputStream();
var writer = function(part) {
if (!(part instanceof Binary)) {
part = part.toByteString(charset);
}
output.write(part);
};
body.forEach(writer);
if (typeof body.close == "function") {
body.close(writer);
}

return ByteArray.wrap(output.toByteArray());
} else {
throw new Error("Response body doesn't implement forEach: " + body);
}
};

exports.testStaticFile = function() {
var app = new Application();

app.configure("static");
app.static(module.resolve("./fixtures"), "index.html", "/customStatic");

let response = app({
method: 'GET',
headers: {},
env: {},
pathInfo: '/customStatic/'
});
assert.equal(response.headers["Content-Type"], "text/html");
assert.equal(bodyAsString(response.body, "utf-8"), "<!DOCTYPE html>");
};

exports.testPrecompressedStaticFile = function() {
var app = new Application();

app.configure("static");
app.static(module.resolve("./fixtures"), "index.html", "/customStatic", {});

let response = app({
method: 'GET',
headers: {
"accept-encoding": "gzip"
},
env: {},
pathInfo: '/customStatic/'
});

assert.equal(response.status, 200);
assert.equal(response.headers["Content-Type"], "text/html");
assert.equal(response.headers["Content-Encoding"], "gzip");

response = app({
method: 'GET',
headers: {
"accept-encoding": "GzIp"
},
env: {},
pathInfo: '/customStatic/'
});

assert.equal(response.status, 200);
assert.equal(response.headers["Content-Type"], "text/html");
assert.equal(response.headers["Content-Encoding"], "gzip");

const ba = new ByteArray([0x1F, 0x8B, 0x08, 0x08, 0x81, 0xF1, 0xD6, 0x56, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78,
0x2E, 0x68, 0x74, 0x6D, 0x6C, 0x00, 0xB3, 0x51, 0x74, 0xF1, 0x77, 0x0E, 0x89, 0x0C, 0x70,
0x55, 0xC8, 0x28, 0xC9, 0xCD, 0xB1, 0x03, 0x00, 0x2B, 0x29, 0x0D, 0x51, 0x0F, 0x00, 0x00, 0x00]);
assert.isTrue(java.util.Arrays.equals(bodyAsByteArray(response.body).unwrap(), ba.unwrap()), "gzipped content not equal!")

response = app({
method: 'GET',
headers: {},
env: {},
pathInfo: '/customStatic/foo.html'
});
assert.equal(response.headers["Content-Type"], "text/html");
assert.equal(bodyAsString(response.body, "utf-8"), "<!DOCTYPE html><foo></foo>");
};

exports.testDeactivatedPrecompression = function() {
var app = new Application();

app.configure("static");
app.static(module.resolve("./fixtures"), "index.html", "/customStatic", {
servePrecompressed: false
});

let response = app({
method: 'GET',
headers: {},
env: {},
pathInfo: '/customStatic/'
});
assert.equal(response.headers["Content-Type"], "text/html");
assert.equal(bodyAsString(response.body, "utf-8"), "<!DOCTYPE html>");

response = app({
method: 'GET',
headers: {},
env: {},
pathInfo: '/customStatic/foo.html'
});
assert.equal(response.headers["Content-Type"], "text/html");
assert.equal(bodyAsString(response.body, "utf-8"), "<!DOCTYPE html><foo></foo>");
};

if (require.main === module) {
require("system").exit(require("test").run(module.id));
}

0 comments on commit a72d7b6

Please sign in to comment.