"use strict"; const { utf8Encode, utf8DecodeWithoutBOM } = require("./encoding"); const { percentDecodeBytes, utf8PercentEncodeString, isURLEncodedPercentEncode } = require("./percent-encoding"); function p(char) { return char.codePointAt(0); } // https://url.spec.whatwg.org/#concept-urlencoded-parser function parseUrlencoded(input) { const sequences = strictlySplitByteSequence(input, p("&")); const output = []; for (const bytes of sequences) { if (bytes.length === 0) { continue; } let name, value; const indexOfEqual = bytes.indexOf(p("=")); if (indexOfEqual >= 0) { name = bytes.slice(0, indexOfEqual); value = bytes.slice(indexOfEqual + 1); } else { name = bytes; value = new Uint8Array(0); } name = replaceByteInByteSequence(name, 0x2B, 0x20); value = replaceByteInByteSequence(value, 0x2B, 0x20); const nameString = utf8DecodeWithoutBOM(percentDecodeBytes(name)); const valueString = utf8DecodeWithoutBOM(percentDecodeBytes(value)); output.push([nameString, valueString]); } return output; } // https://url.spec.whatwg.org/#concept-urlencoded-string-parser function parseUrlencodedString(input) { return parseUrlencoded(utf8Encode(input)); } // https://url.spec.whatwg.org/#concept-urlencoded-serializer function serializeUrlencoded(tuples, encodingOverride = undefined) { let encoding = "utf-8"; if (encodingOverride !== undefined) { // TODO "get the output encoding", i.e. handle encoding labels vs. names. encoding = encodingOverride; } let output = ""; for (const [i, tuple] of tuples.entries()) { // TODO: handle encoding override const name = utf8PercentEncodeString(tuple[0], isURLEncodedPercentEncode, true); let value = tuple[1]; if (tuple.length > 2 && tuple[2] !== undefined) { if (tuple[2] === "hidden" && name === "_charset_") { value = encoding; } else if (tuple[2] === "file") { // value is a File object value = value.name; } } value = utf8PercentEncodeString(value, isURLEncodedPercentEncode, true); if (i !== 0) { output += "&"; } output += `${name}=${value}`; } return output; } function strictlySplitByteSequence(buf, cp) { const list = []; let last = 0; let i = buf.indexOf(cp); while (i >= 0) { list.push(buf.slice(last, i)); last = i + 1; i = buf.indexOf(cp, last); } if (last !== buf.length) { list.push(buf.slice(last)); } return list; } function replaceByteInByteSequence(buf, from, to) { let i = buf.indexOf(from); while (i >= 0) { buf[i] = to; i = buf.indexOf(from, i + 1); } return buf; } module.exports = { parseUrlencodedString, serializeUrlencoded };