Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

changed node querystring to handle empty elements differently

  • Loading branch information...
commit efb7e0872fe0dbe63ecf57f5f0a7c3d646bdaed6 1 parent 4912e1c
@dscape authored
View
188 lib/nuvem/querystring.js
@@ -0,0 +1,188 @@
+// Fork of the official node.js query string module to ensure
+// empty strings inside arrays are not serialized
+//
+// e.g. {a: ["", "c"]}
+// nodejs: a=&a=c
+// this file: a=c
+
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// Query String Utilities
+
+var QueryString = exports;
+var urlDecode = process.binding('http_parser').urlDecode;
+
+
+function charCode(c) {
+ return c.charCodeAt(0);
+}
+
+
+// a safe fast alternative to decodeURIComponent
+QueryString.unescapeBuffer = function(s, decodeSpaces) {
+ var out = new Buffer(s.length);
+ var state = 'CHAR'; // states: CHAR, HEX0, HEX1
+ var n, m, hexchar;
+
+ for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
+ var c = s.charCodeAt(inIndex);
+ switch (state) {
+ case 'CHAR':
+ switch (c) {
+ case charCode('%'):
+ n = 0;
+ m = 0;
+ state = 'HEX0';
+ break;
+ case charCode('+'):
+ if (decodeSpaces) c = charCode(' ');
+ // pass thru
+ default:
+ out[outIndex++] = c;
+ break;
+ }
+ break;
+
+ case 'HEX0':
+ state = 'HEX1';
+ hexchar = c;
+ if (charCode('0') <= c && c <= charCode('9')) {
+ n = c - charCode('0');
+ } else if (charCode('a') <= c && c <= charCode('f')) {
+ n = c - charCode('a') + 10;
+ } else if (charCode('A') <= c && c <= charCode('F')) {
+ n = c - charCode('A') + 10;
+ } else {
+ out[outIndex++] = charCode('%');
+ out[outIndex++] = c;
+ state = 'CHAR';
+ break;
+ }
+ break;
+
+ case 'HEX1':
+ state = 'CHAR';
+ if (charCode('0') <= c && c <= charCode('9')) {
+ m = c - charCode('0');
+ } else if (charCode('a') <= c && c <= charCode('f')) {
+ m = c - charCode('a') + 10;
+ } else if (charCode('A') <= c && c <= charCode('F')) {
+ m = c - charCode('A') + 10;
+ } else {
+ out[outIndex++] = charCode('%');
+ out[outIndex++] = hexchar;
+ out[outIndex++] = c;
+ break;
+ }
+ out[outIndex++] = 16 * n + m;
+ break;
+ }
+ }
+
+ // TODO support returning arbitrary buffers.
+
+ return out.slice(0, outIndex - 1);
+};
+
+
+QueryString.unescape = function(s, decodeSpaces) {
+ return QueryString.unescapeBuffer(s, decodeSpaces).toString();
+};
+
+
+QueryString.escape = function(str) {
+ return encodeURIComponent(str);
+};
+
+var stringifyPrimitive = function(v) {
+ switch (typeof v) {
+ case 'string':
+ return v;
+
+ case 'boolean':
+ return v ? 'true' : 'false';
+
+ case 'number':
+ return isFinite(v) ? v : '';
+
+ default:
+ return '';
+ }
+};
+
+
+QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) {
+ sep = sep || '&';
+ eq = eq || '=';
+ obj = (obj === null) ? undefined : obj;
+
+ switch (typeof obj) {
+ case 'object':
+ return Object.keys(obj).map(function(k) {
+ if (Array.isArray(obj[k])) {
+ return obj[k].filter(function(v){
+ return (stringifyPrimitive(v) !== "")
+ }).map(function(v) {
+ return QueryString.escape(stringifyPrimitive(k)) +
+ eq +
+ QueryString.escape(stringifyPrimitive(v));
+ }).join(sep);
+ } else {
+ return QueryString.escape(stringifyPrimitive(k)) +
+ eq +
+ QueryString.escape(stringifyPrimitive(obj[k]));
+ }
+ }).join(sep);
+
+ default:
+ if (!name) return '';
+ return QueryString.escape(stringifyPrimitive(name)) + eq +
+ QueryString.escape(stringifyPrimitive(obj));
+ }
+};
+
+// Parse a key=val string.
+QueryString.parse = QueryString.decode = function(qs, sep, eq) {
+ sep = sep || '&';
+ eq = eq || '=';
+ var obj = {};
+
+ if (typeof qs !== 'string' || qs.length === 0) {
+ return obj;
+ }
+
+ qs.split(sep).forEach(function(kvp) {
+ var x = kvp.split(eq);
+ var k = QueryString.unescape(x[0], true);
+ var v = QueryString.unescape(x.slice(1).join(eq), true);
+
+ if (!(k in obj)) {
+ obj[k] = v;
+ } else if (!Array.isArray(obj[k])) {
+ obj[k] = [obj[k], v];
+ } else {
+ obj[k].push(v);
+ }
+ });
+
+ return obj;
+};
View
2  lib/nuvem/util.js
@@ -1,4 +1,4 @@
-var qs = require('querystring')
+var qs = require('./querystring')
_ = require('underscore');
/*
View
2  test/common/util.js
@@ -8,5 +8,7 @@ vows.describe('qs_stringify').addBatch(
, "one opt": function() { assert.equal(u.qs_stringify({f: "b"}), "?f=b"); }
, "multiple opts": function() {
assert.equal(u.qs_stringify({f: "b", a: "c"}), "?f=b&a=c"); }
+ , "empty collection": function() {
+ assert.equal(u.qs_stringify({collection: ["","c"]}), "?collection=c"); }
}
).run();
Please sign in to comment.
Something went wrong with that request. Please try again.