SPB Git forge

spb/admin-ka

Public
41commits 1branches 0releases
172.9 MBsize
maindefault branch
19 days agolast push
JavaScript 65.5% Python 17.8% CSS 13% HTML 3.7%
6.0 KB · 204 lines javascript
Raw Blame History
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