diff --git a/lib/_debugger.js b/lib/_debugger.js index a9c98b271fb..6622e6cde5b 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -99,31 +99,42 @@ Protocol.prototype.execute = function(d) { if (endHeaderIndex < 0) break; - var lines = res.raw.slice(0, endHeaderIndex).split('\r\n'); + var rawHeader = res.raw.slice(0, endHeaderIndex); + var endHeaderByteIndex = Buffer.byteLength(rawHeader, 'utf8'); + var lines = rawHeader.split('\r\n'); for (var i = 0; i < lines.length; i++) { var kv = lines[i].split(/: +/); res.headers[kv[0]] = kv[1]; } this.contentLength = +res.headers['Content-Length']; - this.bodyStartIndex = endHeaderIndex + 4; + this.bodyStartByteIndex = endHeaderByteIndex + 4; this.state = 'body'; - if (res.raw.length - this.bodyStartIndex < this.contentLength) break; - // pass thru + if (Buffer.byteLength(res.raw, 'utf8') - this.bodyStartByteIndex + < this.contentLength) { + break; + } + // pass thru case 'body': - if (res.raw.length - this.bodyStartIndex >= this.contentLength) { + var resRawByteLength = Buffer.byteLength(res.raw, 'utf8'); + + if (resRawByteLength - this.bodyStartByteIndex >= this.contentLength) { + var buf = new Buffer(resRawByteLength); + buf.write(res.raw, 0, resRawByteLength, 'utf8'); res.body = - res.raw.slice(this.bodyStartIndex, - this.bodyStartIndex + this.contentLength); + buf.slice(this.bodyStartByteIndex, + this.bodyStartByteIndex + + this.contentLength).toString('utf8'); // JSON parse body? res.body = res.body.length ? JSON.parse(res.body) : {}; // Done! this.onResponse(res); - this._newRes(res.raw.slice(this.bodyStartIndex + this.contentLength)); + this._newRes(buf.slice(this.bodyStartByteIndex + + this.contentLength).toString('utf8')); } break; @@ -138,7 +149,8 @@ Protocol.prototype.serialize = function(req) { req.type = 'request'; req.seq = this.reqSeq++; var json = JSON.stringify(req); - return 'Content-Length: ' + json.length + '\r\n\r\n' + json; + return 'Content-Length: ' + Buffer.byteLength(json,'utf8') + '\r\n\r\n' + + json; }; diff --git a/test/fixtures/breakpoints_utf8.js b/test/fixtures/breakpoints_utf8.js new file mode 100644 index 00000000000..cd05c760234 --- /dev/null +++ b/test/fixtures/breakpoints_utf8.js @@ -0,0 +1,19 @@ +debugger; +function a(x) { + var i = 10; + while (--i != 0); + debugger; + return i; +} +function b() { + return ['こんにち', 'わ'].join(' '); +} +a(); +a(1); +b(); +b(); + + + +setInterval(function() { +}, 5000); diff --git a/test/simple/test-debugger-repl-utf8.js b/test/simple/test-debugger-repl-utf8.js new file mode 100644 index 00000000000..820a5391432 --- /dev/null +++ b/test/simple/test-debugger-repl-utf8.js @@ -0,0 +1,172 @@ +// 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. + + +var common = require('../common'); +var assert = require('assert'); +var spawn = require('child_process').spawn; +var debug = require('_debugger'); + +var script = common.fixturesDir + '/breakpoints_utf8.js'; + +var child = spawn(process.execPath, ['debug', script]); + +var buffer = ''; +child.stdout.setEncoding('utf-8'); +child.stdout.on('data', function(data) { + data = (buffer + data.toString()).split(/\n/g); + buffer = data.pop(); + data.forEach(function(line) { + child.emit('line', line); + }); +}); +child.stderr.pipe(process.stdout); + +var expected = []; + +child.on('line', function(line) { + assert.ok(expected.length > 0, 'Got unexpected line: ' + line); + + var expectedLine = expected[0].lines.shift(); + assert.ok(line.match(expectedLine) !== null, line + ' != ' + expectedLine); + + if (expected[0].lines.length === 0) { + var callback = expected[0].callback; + expected.shift(); + callback && callback(); + } +}); + +function addTest(input, output) { + function next() { + if (expected.length > 0) { + child.stdin.write(expected[0].input + '\n'); + + if (!expected[0].lines) { + setTimeout(function() { + var callback = expected[0].callback; + expected.shift(); + + callback && callback(); + }, 50); + } + } else { + finish(); + } + }; + expected.push({input: input, lines: output, callback: next}); +} + +// Initial lines +addTest(null, [ + /listening on port 5858/, + /connecting... ok/, + /break in .*:1/, + /1/, /2/, /3/ +]); + +// Next +addTest('n', [ + /break in .*:11/, + /9/, /10/, /11/, /12/, /13/ +]); + +// Watch +addTest('watch("\'x\'")'); + +// Continue +addTest('c', [ + /break in .*:5/, + /Watchers/, + /0:\s+'x' = "x"/, + /()/, + /3/, /4/, /5/, /6/, /7/ +]); + +// Show watchers +addTest('watchers', [ + /0:\s+'x' = "x"/ +]); + +// Unwatch +addTest('unwatch("\'x\'")'); + +// Step out +addTest('o', [ + /break in .*:12/, + /10/, /11/, /12/, /13/, /14/ +]); + +// Continue +addTest('c', [ + /break in .*:5/, + /3/, /4/, /5/, /6/, /7/ +]); + +// Set breakpoint by function name +addTest('sb("setInterval()", "!(setInterval.flag++)")', [ + /1/, /2/, /3/, /4/, /5/, /6/, /7/, /8/, /9/, /10/ +]); + +// Continue +addTest('c', [ + /break in node.js:\d+/, + /\d/, /\d/, /\d/, /\d/, /\d/ +]); + +// Continue +addTest('c, bt', [ + /Can't request backtrace now/ +]); + + +function finish() { + process.exit(0); +} + +function quit() { + if (quit.called) return; + quit.called = true; + child.stdin.write('quit'); +} + +setTimeout(function() { + var err = 'Timeout'; + if (expected.length > 0 && expected[0].lines) { + err = err + '. Expected: ' + expected[0].lines.shift(); + } + + throw new Error(err); +}, 5000); + +process.once('uncaughtException', function(e) { + quit(); + console.error(e.toString()); + child.kill('SIGKILL'); + process.exit(1); +}); + +process.on('exit', function(code) { + quit(); + if (code === 0) { + assert.equal(expected.length, 0); + } +});