From e657523a4514b5a2b37ba5c0e11fdfc69e8dee5b Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Thu, 29 Dec 2016 20:06:48 -0500 Subject: [PATCH] Calculate buffer length of utf-8 data for json correctly --- lib/bhttp.coffee | 9 ++++++--- lib/bhttp.js | 6 ++++-- test-utf8-json.coffee | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test-utf8-json.coffee diff --git a/lib/bhttp.coffee b/lib/bhttp.coffee index 77331e2..f423ef6 100644 --- a/lib/bhttp.coffee +++ b/lib/bhttp.coffee @@ -226,16 +226,19 @@ preparePayload = (request, response, requestState) -> if request.options.encodeJSON debugRequest "... but encodeJSON was set, so we will send JSON instead" - request.options.headers["content-type"] = "application/json" + request.options.headers["content-type"] = "application/json; charset=utf-8" request.payload = JSON.stringify request.options.formFields ? null + # strings are utf-16, so string.length can be wrong + request.options.headers["content-length"] = Buffer.byteLength(request.payload, "utf8") else if not _.isEmpty request.options.formFields # The `querystring` module copies the key name verbatim, even if the value is actually an array. Things like PHP don't understand this, and expect every array-containing key to be suffixed with []. We'll just append that ourselves, then. request.options.headers["content-type"] = "application/x-www-form-urlencoded" request.payload = querystring.stringify formFixArray(request.options.formFields) + # URI encoded string is assumed to be ascii + request.options.headers["content-length"] = request.payload.length else request.payload = "" - - request.options.headers["content-length"] = request.payload.length + request.options.headers["content-length"] = 0 return Promise.resolve() else if request.options.formFields? and multipart diff --git a/lib/bhttp.js b/lib/bhttp.js index 6f90a5f..3b3c6e2 100644 --- a/lib/bhttp.js +++ b/lib/bhttp.js @@ -257,15 +257,17 @@ preparePayload = function(request, response, requestState) { debugRequest("got url-encodable form-data"); if (request.options.encodeJSON) { debugRequest("... but encodeJSON was set, so we will send JSON instead"); - request.options.headers["content-type"] = "application/json"; + request.options.headers["content-type"] = "application/json; charset=utf-8"; request.payload = JSON.stringify((ref1 = request.options.formFields) != null ? ref1 : null); + request.options.headers["content-length"] = Buffer.byteLength(request.payload, "utf8"); } else if (!_.isEmpty(request.options.formFields)) { request.options.headers["content-type"] = "application/x-www-form-urlencoded"; request.payload = querystring.stringify(formFixArray(request.options.formFields)); + request.options.headers["content-length"] = request.payload.length; } else { request.payload = ""; + request.options.headers["content-length"] = 0; } - request.options.headers["content-length"] = request.payload.length; return Promise.resolve(); } else if ((request.options.formFields != null) && multipart) { debugRequest("got multipart form-data"); diff --git a/test-utf8-json.coffee b/test-utf8-json.coffee new file mode 100644 index 0000000..39e1b08 --- /dev/null +++ b/test-utf8-json.coffee @@ -0,0 +1,22 @@ +Promise = require "bluebird" +bhttp = require "./" + +formatLine = (line) -> line.toString().replace(/\n/g, "\\n").replace(/\r/g, "\\r") + +# this just tests we can parse the data we sent. if ever we send +# more data than the content-length it will probably be broken +Promise.try -> + bhttp.post "http://posttestserver.com/post.php", + value: "hello \ud801\udc37", + , + encodeJSON: true, + headers: {"user-agent": "bhttp/test POST UTF-8 JSON"} +.then (response) -> + responseUrl = response.body.toString().split("\n")[1].replace(/^View it at /, "") + console.log responseUrl + bhttp.get responseUrl +.then (response) -> + lines = response.body.toString().split("\n") + JSON.parse lines[lines.length-1] +.then (obj) -> + console.log "POST UTF-8 string", obj