urlencoded.js 2.7 KB

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