From 7ef3ff33bc3293a460c61826212f141d417708d6 Mon Sep 17 00:00:00 2001 From: Philipp Naderer Date: Wed, 2 Mar 2016 16:03:50 +0100 Subject: [PATCH 1/2] Enables serving of precompressed static files --- lib/middleware/static.js | 24 ++++- test/middleware/fixtures/foo.html | 1 + test/middleware/fixtures/index.html | 1 + test/middleware/fixtures/index.html.gz | Bin 0 -> 46 bytes test/middleware/static_test.js | 126 +++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 test/middleware/fixtures/foo.html create mode 100644 test/middleware/fixtures/index.html create mode 100644 test/middleware/fixtures/index.html.gz create mode 100644 test/middleware/static_test.js diff --git a/lib/middleware/static.js b/lib/middleware/static.js index d54d7eb..dd573e0 100644 --- a/lib/middleware/static.js +++ b/lib/middleware/static.js @@ -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"); @@ -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); @@ -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 }); }; @@ -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"] || ""; + 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")); } } diff --git a/test/middleware/fixtures/foo.html b/test/middleware/fixtures/foo.html new file mode 100644 index 0000000..ac05ced --- /dev/null +++ b/test/middleware/fixtures/foo.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/middleware/fixtures/index.html b/test/middleware/fixtures/index.html new file mode 100644 index 0000000..763b073 --- /dev/null +++ b/test/middleware/fixtures/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/middleware/fixtures/index.html.gz b/test/middleware/fixtures/index.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..b73766c604f9e1d6b640e9e34421f77874f29251 GIT binary patch literal 46 zcmb2|=HO`jcrA>9IWsRMwL&kWBsYg)b70BGa=uQUg3uEhC(mwVX3*B;4diEFU;qG- C-Vd4p literal 0 HcmV?d00001 diff --git a/test/middleware/static_test.js b/test/middleware/static_test.js new file mode 100644 index 0000000..48de3c5 --- /dev/null +++ b/test/middleware/static_test.js @@ -0,0 +1,126 @@ +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"), ""); +}; + +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"); + + 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"), ""); +}; + +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"), ""); + + 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"), ""); +}; + +if (require.main === module) { + require("system").exit(require("test").run(module.id)); +} \ No newline at end of file From bd78beb0dbbd899d3ce3e9a393cfba9fd3bb4041 Mon Sep 17 00:00:00 2001 From: Philipp Naderer Date: Mon, 17 Oct 2016 17:23:35 +0200 Subject: [PATCH 2/2] Ignore case to detect encoding --- lib/middleware/static.js | 2 +- test/middleware/static_test.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/middleware/static.js b/lib/middleware/static.js index dd573e0..9b10f43 100644 --- a/lib/middleware/static.js +++ b/lib/middleware/static.js @@ -64,7 +64,7 @@ exports.middleware = function static(next, app) { 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"] || ""; + 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()) { diff --git a/test/middleware/static_test.js b/test/middleware/static_test.js index 48de3c5..969880d 100644 --- a/test/middleware/static_test.js +++ b/test/middleware/static_test.js @@ -79,6 +79,19 @@ exports.testPrecompressedStaticFile = function() { 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]);