Skip to content

Commit

Permalink
Standardize POST canonicalization query strings with pywb (#68)
Browse files Browse the repository at this point in the history
* Make non-GET query canonicalization consistent with new spec

This is part of bringing consistency between pywb and warcio.js
with regard to how query parameters are constructed from request
bodies for non-GET requests.

The more complicated test is duplicated in pywb to help ensure
that the results are consistent across our toolsets/packages.
  • Loading branch information
tw4l authored Aug 18, 2024
1 parent 7fa516a commit f151081
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 6 deletions.
40 changes: 34 additions & 6 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,41 @@ export function jsonToQueryParams(json: string | any, ignoreInvalid = true) {
return key + "." + ++dupes[key] + "_";
};

try {
JSON.stringify(json, (k, v) => {
if (!["object", "function"].includes(typeof v)) {
q.set(getKey(k), v);
const parser = (jsonObj: object, key="") => {
let queryValue = "";

// if object, attempt to recurse through key/value pairs
if (typeof(jsonObj) === "object" && !(jsonObj instanceof Array)) {
try {
for (const [key, value] of Object.entries(jsonObj)) {
parser(value, key);
}
} catch (e) {
// special case for null
if (jsonObj === null) {
queryValue = "null";
}
}
}

// if array, recurse through values, re-using key
else if (jsonObj instanceof Array) {
for (let i=0; i < jsonObj.length; i++) {
parser(jsonObj[i], key);
}
return v;
});
}

if (["string", "number", "boolean"].includes(typeof(jsonObj))) {
queryValue = jsonObj.toString();
}

if (queryValue) {
q.set(getKey(key), queryValue);
}
};

try {
parser(json);
} catch (e) {
if (!ignoreInvalid) {
throw e;
Expand Down
16 changes: 16 additions & 0 deletions test/testUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ describe("utils", () => {
).toBe("abc=def&data=bar&bar=2&a=3&a.2_=4&bar.2_=123&a.3_=5");
});

test("another json with more complicated data", () => {
expect(
toQuery({
"type": "event",
"id": 44.0,
"float": 35.7,
"values": [true, false, null],
"source": {
"type": "component",
"id": "a+b&c= d",
"values": [3, 4]
}
})
).toBe("type=event&id=44&float=35.7&values=true&values.2_=false&values.3_=null&type.2_=component&id.2_=a%2Bb%26c%3D+d&values.4_=3&values.5_=4");
});

test("post-to-get empty", () => {
const request = {
postData: "",
Expand Down

0 comments on commit f151081

Please sign in to comment.