urlencoded.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. "use strict";
  2. const { utf8Encode, utf8DecodeWithoutBOM } = require("./encoding");
  3. const { percentDecodeBytes, utf8PercentEncodeString, isURLEncodedPercentEncode } = require("./percent-encoding");
  4. // https://url.spec.whatwg.org/#concept-urlencoded-parser
  5. function parseUrlencoded(input) {
  6. const sequences = strictlySplitByteSequence(input, 38);
  7. const output = [];
  8. for (const bytes of sequences) {
  9. if (bytes.length === 0) {
  10. continue;
  11. }
  12. let name;
  13. let value;
  14. const indexOfEqual = bytes.indexOf(61);
  15. if (indexOfEqual >= 0) {
  16. name = bytes.slice(0, indexOfEqual);
  17. value = bytes.slice(indexOfEqual + 1);
  18. } else {
  19. name = bytes;
  20. value = new Uint8Array(0);
  21. }
  22. name = replaceByteInByteSequence(name, 0x2B, 0x20);
  23. value = replaceByteInByteSequence(value, 0x2B, 0x20);
  24. const nameString = utf8DecodeWithoutBOM(percentDecodeBytes(name));
  25. const valueString = utf8DecodeWithoutBOM(percentDecodeBytes(value));
  26. output.push([nameString, valueString]);
  27. }
  28. return output;
  29. }
  30. // https://url.spec.whatwg.org/#concept-urlencoded-string-parser
  31. function parseUrlencodedString(input) {
  32. return parseUrlencoded(utf8Encode(input));
  33. }
  34. // https://url.spec.whatwg.org/#concept-urlencoded-serializer
  35. function serializeUrlencoded(tuples, encodingOverride = undefined) {
  36. let encoding = "utf-8";
  37. if (encodingOverride !== undefined) {
  38. // TODO "get the output encoding", i.e. handle encoding labels vs. names.
  39. encoding = encodingOverride;
  40. }
  41. let output = "";
  42. for (const [i, tuple] of tuples.entries()) {
  43. // TODO: handle encoding override
  44. const name = utf8PercentEncodeString(tuple[0], isURLEncodedPercentEncode, true);
  45. let value = tuple[1];
  46. if (tuple.length > 2 && tuple[2] !== undefined) {
  47. if (tuple[2] === "hidden" && name === "_charset_") {
  48. value = encoding;
  49. } else if (tuple[2] === "file") {
  50. // value is a File object
  51. value = value.name;
  52. }
  53. }
  54. value = utf8PercentEncodeString(value, isURLEncodedPercentEncode, true);
  55. if (i !== 0) {
  56. output += "&";
  57. }
  58. output += `${name}=${value}`;
  59. }
  60. return output;
  61. }
  62. function strictlySplitByteSequence(buf, cp) {
  63. const list = [];
  64. let last = 0;
  65. let i = buf.indexOf(cp);
  66. while (i >= 0) {
  67. list.push(buf.slice(last, i));
  68. last = i + 1;
  69. i = buf.indexOf(cp, last);
  70. }
  71. if (last !== buf.length) {
  72. list.push(buf.slice(last));
  73. }
  74. return list;
  75. }
  76. function replaceByteInByteSequence(buf, from, to) {
  77. let i = buf.indexOf(from);
  78. while (i >= 0) {
  79. buf[i] = to;
  80. i = buf.indexOf(from, i + 1);
  81. }
  82. return buf;
  83. }
  84. module.exports = {
  85. parseUrlencodedString,
  86. serializeUrlencoded
  87. };