diff --git a/lib/urllib.js b/lib/urllib.js index baf0fdc0..899da2dd 100644 --- a/lib/urllib.js +++ b/lib/urllib.js @@ -666,12 +666,26 @@ exports.requestWithCallback = function (url, args, callback) { }; function parseJSON(data) { + var escMap = { + '"': '\\"', // \u0022 + '\\': '\\\\', // \u005c + '\b': '\\b', // \u0008 + '\f': '\\f', // \u000c + '\n': '\\n', // \u000a + '\r': '\\r', // \u000d + '\t': '\\t' // \u0009 + }; + var escFunc = function (m) { + return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); + }; + var escRE = /[\u0000-\u001F\u005C]/g; + var result = { error: null, data: null }; try { - result.data = JSON.parse(data); + result.data = JSON.parse(data.replace(escRE, escFunc)); } catch (err) { if (err.name === 'SyntaxError') { err.name = 'JSONResponseFormatError'; diff --git a/test/fixtures/server.js b/test/fixtures/server.js index 904e6e29..910b3b86 100644 --- a/test/fixtures/server.js +++ b/test/fixtures/server.js @@ -159,6 +159,9 @@ var server = http.createServer(function (req, res) { } else if (req.url === '/errorcharset') { res.setHeader('Content-Type', 'text/plain;charset=notfound'); return res.end('你好'); + } else if (req.url === '/json_with_controls_unicode') { + res.writeHeader(200); + return res.end(new Buffer('{"foo":"\u000e"}')); } var url = req.url.split('?'); diff --git a/test/urllib.test.js b/test/urllib.test.js index f4a235f4..135dd307 100644 --- a/test/urllib.test.js +++ b/test/urllib.test.js @@ -1161,4 +1161,16 @@ describe('urllib.test.js', function () { }); }); }); + + describe('json string with controls unicode', function() { + it('should handle GET /json_with_controls_unicode with dataType=json', function (done) { + urllib.request(host + '/json_with_controls_unicode', { + dataType: 'json' + }, function (err, data, res) { + res.should.status(200); + data.should.eql({foo:"\u000e"}); + done(); + }); + }); + }); });