From 48e51ba19b83225a8916560b8653cdad9b8073d9 Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Thu, 11 Oct 2012 19:48:53 +0200 Subject: [PATCH 1/8] Add middleware for catching large posts This will return an error response that isn't currently picked up by the JS Bin front end, but it does stop MySQL throwing exceptions. It can be turned off by setting the "bin max-size" to 0 or null. --- lib/middleware.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/middleware.js b/lib/middleware.js index 66b7ce88..c712773a 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -1,6 +1,7 @@ var utils = require('./utils'), helpers = require('./helpers'), custom = require('./custom'), + errors = require('./errors'), connect = require('express/node_modules/connect'); // Custom middleware used by the application. @@ -118,5 +119,20 @@ module.exports = { } next(); }; + }, + + // Limit the file size that can be uploaded. + limitContentLength: function (options) { + return function (req, res, next) { + if (options && options.limit) { + var contentLength = parseInt(req.header('Content-Length', 0), 10), + message = 'Sorry, the content you have uploaded is larger than JS Bin can handle. Max size is ' + options.limit + ' bytes'; + + if (contentLength > options.limit) { + return next(new errors.RequestEntityTooLarge(message)); + } + } + next(); + }; } }; From 5603844722fe4e478c2cdd37c6a5aefbc4ff3afa Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Thu, 11 Oct 2012 19:49:13 +0200 Subject: [PATCH 2/8] Add "bin max-size" option This is set in bytes and limits the maximum size of a bin panel. The default is currently 1.5Mb. --- config.default.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.default.json b/config.default.json index 761bfe95..73a6be43 100644 --- a/config.default.json +++ b/config.default.json @@ -7,6 +7,9 @@ "ssl": false, "static": false }, + "bin": { + "max-size": 1572864 + }, "store": { "adapter": "sqlite", "sqlite": { From 2fc79eff0fcebbe3bc44e0ccf9ed7d0e73246d63 Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Thu, 11 Oct 2012 19:49:59 +0200 Subject: [PATCH 3/8] Add a 413 Request Entity Too Large error object --- lib/errors.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/errors.js b/lib/errors.js index 8c385a9e..48e7ac42 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -76,6 +76,12 @@ exports.BadRequest = HTTPError.extend({ } }); +exports.RequestEntityTooLarge = HTTPError.extend({ + constructor: function RequestEntityTooLarge(message) { + HTTPError.call(this, 413, message); + } +}); + exports.MailerError = HTTPError.extend({ constructor: function MailerError(message) { HTTPError.call(this, 500, message); From fd3493da4d5b180a5b186a0caedba68715efa87a Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Thu, 11 Oct 2012 19:50:19 +0200 Subject: [PATCH 4/8] Include the limitContentLength() middleware in the app --- lib/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/app.js b/lib/app.js index 4514ec3c..b65c4ea6 100644 --- a/lib/app.js +++ b/lib/app.js @@ -128,6 +128,7 @@ app.connect = function (callback) { } app.use(mount, express.static(path.join(app.set('root'), 'public'))); + app.use(middleware.limitContentLength({limit: app.set('bin max-size')})); app.use(express.cookieParser(app.set('session secret'))); app.use(express.cookieSession({key: 'jsbin'})); app.use(express.urlencoded()); From 2caed53c1ceb669c46ef8ec0b5e299764f28ce91 Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Sat, 27 Oct 2012 18:11:52 +0100 Subject: [PATCH 5/8] Improve API for setting max request limit Renamed the key to "max-request-size" and allow units to be provided rather than just a number. The default is now 1MB. --- config.default.json | 4 +--- lib/app.js | 2 +- lib/middleware.js | 31 ++++++++++++++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/config.default.json b/config.default.json index 73a6be43..314f7c9a 100644 --- a/config.default.json +++ b/config.default.json @@ -7,9 +7,7 @@ "ssl": false, "static": false }, - "bin": { - "max-size": 1572864 - }, + "max-request-size": "1MB", "store": { "adapter": "sqlite", "sqlite": { diff --git a/lib/app.js b/lib/app.js index b65c4ea6..38f99299 100644 --- a/lib/app.js +++ b/lib/app.js @@ -128,7 +128,7 @@ app.connect = function (callback) { } app.use(mount, express.static(path.join(app.set('root'), 'public'))); - app.use(middleware.limitContentLength({limit: app.set('bin max-size')})); + app.use(middleware.limitContentLength({limit: app.set('max-request-size')})); app.use(express.cookieParser(app.set('session secret'))); app.use(express.cookieSession({key: 'jsbin'})); app.use(express.urlencoded()); diff --git a/lib/middleware.js b/lib/middleware.js index c712773a..0d98b305 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -123,12 +123,37 @@ module.exports = { // Limit the file size that can be uploaded. limitContentLength: function (options) { + var powers = { k: 1, m: 2, g: 3, t: 4 }, + regexp = /^(\d+(?:.\d+)?)\s*([kmgt]?)b?$/; + + // Parse a string representing a file size and convert it into bytes. + // A number on it's own will be assumed to be bytes. A multiple such as + // "k" or "m" can be appended to the string to handle larger numbers. This + // is case insensitive and uses powers of 1024 rather than (1000). + // So both 1kB and 1kb == 1024. + function parseLimit(string) { + var matches = ('' + string).toLowerCase().match(regexp), + bytes = null, power; + + if (matches) { + bytes = parseFloat(matches[1]); + power = powers[matches[2]]; + + if (bytes && power) { + bytes = Math.pow(bytes * 1024, power); + } + } + + return bytes || null; + } + return function (req, res, next) { if (options && options.limit) { - var contentLength = parseInt(req.header('Content-Length', 0), 10), - message = 'Sorry, the content you have uploaded is larger than JS Bin can handle. Max size is ' + options.limit + ' bytes'; + var limit = options.limit && parseLimit(options.limit), + contentLength = parseInt(req.header('Content-Length', 0), 10), + message = 'Sorry, the content you have uploaded is larger than JS Bin can handle. Max size is ' + options.limit; - if (contentLength > options.limit) { + if (limit && contentLength > limit) { return next(new errors.RequestEntityTooLarge(message)); } } From 7bd1992a5c01a03fa56b45fe9836dd9d61e689e6 Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Sat, 27 Oct 2012 18:24:43 +0100 Subject: [PATCH 6/8] Add very basic client error handling for large bins This basically hijacks the tips notification and displays a message informing the user the bin is too big. --- public/js/chrome/save.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/public/js/chrome/save.js b/public/js/chrome/save.js index 964f71c7..e946bcb8 100644 --- a/public/js/chrome/save.js +++ b/public/js/chrome/save.js @@ -14,6 +14,16 @@ $document.one('saved', function () { $shareLinks.removeClass('disabled').unbind('click mousedown mouseup'); }); +function onSaveError(jqXHR) { + if (jqXHR.status === 413) { + // Hijack the tip label to show an error message. + $('#tip p').html('Sorry this bin is too large for us to save'); + $(document.documentElement).addClass('showtip'); + } else { + console && console.log('update error'); + } +} + function updateSavedState() { $shareLinks.each(function () { var url = jsbin.getURL() + this.getAttribute('data-path'), @@ -149,9 +159,7 @@ if (!jsbin.saveDisabled) { }); } }, - error: function () { - console && console.log('update error'); - } + error: onSaveError }); } }, 250)); @@ -249,9 +257,7 @@ function saveCode(method, ajax, ajaxCallback) { window.location.hash = data.edit; } }, - error: function () { - - } + error: onSaveError }); } else { $form.submit(); From 7274bab3dc03cd6693d1bd340ffcd2c230ae838c Mon Sep 17 00:00:00 2001 From: Aron Carroll Date: Sat, 27 Oct 2012 18:40:00 +0100 Subject: [PATCH 7/8] Only parse the upload limit option once --- lib/middleware.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/middleware.js b/lib/middleware.js index 0d98b305..d91cd3b6 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -123,9 +123,6 @@ module.exports = { // Limit the file size that can be uploaded. limitContentLength: function (options) { - var powers = { k: 1, m: 2, g: 3, t: 4 }, - regexp = /^(\d+(?:.\d+)?)\s*([kmgt]?)b?$/; - // Parse a string representing a file size and convert it into bytes. // A number on it's own will be assumed to be bytes. A multiple such as // "k" or "m" can be appended to the string to handle larger numbers. This @@ -147,10 +144,13 @@ module.exports = { return bytes || null; } + var powers = { k: 1, m: 2, g: 3, t: 4 }, + regexp = /^(\d+(?:.\d+)?)\s*([kmgt]?)b?$/, + limit = options && parseLimit(options.limit); + return function (req, res, next) { - if (options && options.limit) { - var limit = options.limit && parseLimit(options.limit), - contentLength = parseInt(req.header('Content-Length', 0), 10), + if (limit) { + var contentLength = parseInt(req.header('Content-Length', 0), 10), message = 'Sorry, the content you have uploaded is larger than JS Bin can handle. Max size is ' + options.limit; if (limit && contentLength > limit) { From 3fa5f203477c3eed57d6a80174783bc6ecdbb4ee Mon Sep 17 00:00:00 2001 From: Tom Ashworth Date: Thu, 10 Jan 2013 11:19:22 +0000 Subject: [PATCH 8/8] Fix the layout on the home page to show scrollbars. Fix #291. --- public/css/style.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index f77a49d2..86a70b3d 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -2020,12 +2020,13 @@ a.active:hover { text-shadow: none; /*-webkit-transition-delay: 100ms;*/ font-size: 13px; - padding: 40px 0 10px 0; + padding: 0 0 10px 0; position: absolute; /*height: 100%;*/ - top: 0; + top: 36px; left: 0; right: 0; + right: 30%; bottom: 0; overflow: auto; background: #fff; @@ -2064,7 +2065,7 @@ a.active:hover { #history table { border-collapse: collapse; table-layout: fixed; - width: 70%; + width: 100%; position: relative; }