diff --git a/chrome/content/zotero/xpcom/server/server.js b/chrome/content/zotero/xpcom/server/server.js index 23bfa30448..cee949fbec 100755 --- a/chrome/content/zotero/xpcom/server/server.js +++ b/chrome/content/zotero/xpcom/server/server.js @@ -109,7 +109,9 @@ Zotero.Server = new function () { var decodedData = {}; for (let variable of splitData) { var splitIndex = variable.indexOf("="); - decodedData[decodeURIComponent(variable.substr(0, splitIndex))] = decodeURIComponent(variable.substr(splitIndex+1)); + let key = variable.substr(0, splitIndex).replaceAll('+', ' '); + let value = variable.substr(splitIndex + 1).replaceAll('+', ' '); + decodedData[decodeURIComponent(key)] = decodeURIComponent(value); } return decodedData; } diff --git a/test/tests/serverTest.js b/test/tests/serverTest.js index 36fb6454b4..817820cd9d 100644 --- a/test/tests/serverTest.js +++ b/test/tests/serverTest.js @@ -141,6 +141,38 @@ describe("Zotero.Server", function () { }); }); + describe("application/x-www-form-urlencoded", function () { + it("should decode + as a space", async function () { + var data; + + var endpoint = "/test/" + Zotero.Utilities.randomString(); + var handler = function () {}; + handler.prototype = { + supportedMethods: ["POST"], + supportedDataTypes: ["application/x-www-form-urlencoded"], + + init: function (options) { + data = options.data; + return 204; + } + }; + Zotero.Server.Endpoints[endpoint] = handler; + + await Zotero.HTTP.request( + "POST", + serverPath + endpoint, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + body: "file+name=a+b%2Bc%20d.pdf" + } + ); + + assert.deepEqual(data, { "file name": "a b+c d.pdf" }); + }); + }); + describe("multipart/form-data", function () { it("should support text", async function () { var called = false;