JavaScript 65.5%
Python 17.8%
CSS 13%
HTML 3.7%
1'use strict';23const { tokenChars } = require('./validation');45/**6 * Adds an offer to the map of extension offers or a parameter to the map of7 * parameters.8 *9 * @param {Object} dest The map of extension offers or parameters10 * @param {String} name The extension or parameter name11 * @param {(Object|Boolean|String)} elem The extension parameters or the12 * parameter value13 * @private14 */15function push(dest, name, elem) {16 if (dest[name] === undefined) dest[name] = [elem];17 else dest[name].push(elem);18}1920/**21 * Parses the `Sec-WebSocket-Extensions` header into an object.22 *23 * @param {String} header The field value of the header24 * @return {Object} The parsed object25 * @public26 */27function parse(header) {28 const offers = Object.create(null);29 let params = Object.create(null);30 let mustUnescape = false;31 let isEscaping = false;32 let inQuotes = false;33 let extensionName;34 let paramName;35 let start = -1;36 let code = -1;37 let end = -1;38 let i = 0;3940 for (; i < header.length; i++) {41 code = header.charCodeAt(i);4243 if (extensionName === undefined) {44 if (end === -1 && tokenChars[code] === 1) {45 if (start === -1) start = i;46 } else if (47 i !== 0 &&48 (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */49 ) {50 if (end === -1 && start !== -1) end = i;51 } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {52 if (start === -1) {53 throw new SyntaxError(`Unexpected character at index ${i}`);54 }5556 if (end === -1) end = i;57 const name = header.slice(start, end);58 if (code === 0x2c) {59 push(offers, name, params);60 params = Object.create(null);61 } else {62 extensionName = name;63 }6465 start = end = -1;66 } else {67 throw new SyntaxError(`Unexpected character at index ${i}`);68 }69 } else if (paramName === undefined) {70 if (end === -1 && tokenChars[code] === 1) {71 if (start === -1) start = i;72 } else if (code === 0x20 || code === 0x09) {73 if (end === -1 && start !== -1) end = i;74 } else if (code === 0x3b || code === 0x2c) {75 if (start === -1) {76 throw new SyntaxError(`Unexpected character at index ${i}`);77 }7879 if (end === -1) end = i;80 push(params, header.slice(start, end), true);81 if (code === 0x2c) {82 push(offers, extensionName, params);83 params = Object.create(null);84 extensionName = undefined;85 }8687 start = end = -1;88 } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {89 paramName = header.slice(start, i);90 start = end = -1;91 } else {92 throw new SyntaxError(`Unexpected character at index ${i}`);93 }94 } else {95 //96 // The value of a quoted-string after unescaping must conform to the97 // token ABNF, so only token characters are valid.98 // Ref: https://tools.ietf.org/html/rfc6455#section-9.199 //100 if (isEscaping) {101 if (tokenChars[code] !== 1) {102 throw new SyntaxError(`Unexpected character at index ${i}`);103 }104 if (start === -1) start = i;105 else if (!mustUnescape) mustUnescape = true;106 isEscaping = false;107 } else if (inQuotes) {108 if (tokenChars[code] === 1) {109 if (start === -1) start = i;110 } else if (code === 0x22 /* '"' */ && start !== -1) {111 inQuotes = false;112 end = i;113 } else if (code === 0x5c /* '\' */) {114 isEscaping = true;115 } else {116 throw new SyntaxError(`Unexpected character at index ${i}`);117 }118 } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {119 inQuotes = true;120 } else if (end === -1 && tokenChars[code] === 1) {121 if (start === -1) start = i;122 } else if (start !== -1 && (code === 0x20 || code === 0x09)) {123 if (end === -1) end = i;124 } else if (code === 0x3b || code === 0x2c) {125 if (start === -1) {126 throw new SyntaxError(`Unexpected character at index ${i}`);127 }128129 if (end === -1) end = i;130 let value = header.slice(start, end);131 if (mustUnescape) {132 value = value.replace(/\\/g, '');133 mustUnescape = false;134 }135 push(params, paramName, value);136 if (code === 0x2c) {137 push(offers, extensionName, params);138 params = Object.create(null);139 extensionName = undefined;140 }141142 paramName = undefined;143 start = end = -1;144 } else {145 throw new SyntaxError(`Unexpected character at index ${i}`);146 }147 }148 }149150 if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {151 throw new SyntaxError('Unexpected end of input');152 }153154 if (end === -1) end = i;155 const token = header.slice(start, end);156 if (extensionName === undefined) {157 push(offers, token, params);158 } else {159 if (paramName === undefined) {160 push(params, token, true);161 } else if (mustUnescape) {162 push(params, paramName, token.replace(/\\/g, ''));163 } else {164 push(params, paramName, token);165 }166 push(offers, extensionName, params);167 }168169 return offers;170}171172/**173 * Builds the `Sec-WebSocket-Extensions` header field value.174 *175 * @param {Object} extensions The map of extensions and parameters to format176 * @return {String} A string representing the given object177 * @public178 */179function format(extensions) {180 return Object.keys(extensions)181 .map((extension) => {182 let configurations = extensions[extension];183 if (!Array.isArray(configurations)) configurations = [configurations];184 return configurations185 .map((params) => {186 return [extension]187 .concat(188 Object.keys(params).map((k) => {189 let values = params[k];190 if (!Array.isArray(values)) values = [values];191 return values192 .map((v) => (v === true ? k : `${k}=${v}`))193 .join('; ');194 })195 )196 .join('; ');197 })198 .join(', ');199 })200 .join(', ');201}202203module.exports = { format, parse };204