tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Request.js (6243B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 var {
      8  findPlaceholders,
      9  getPath,
     10 } = require("resource://devtools/shared/protocol/utils.js");
     11 var {
     12  types,
     13  BULK_REQUEST,
     14 } = require("resource://devtools/shared/protocol/types.js");
     15 
     16 /**
     17 * Manages a request template.
     18 */
     19 class Request {
     20  /**
     21   * @param {string} type
     22   *    The type defined in the specification for this request.
     23   *    For methods, it will be the attribute name in "methods" dictionary.
     24   *    For events, it will be the attribute name in "events" dictionary.
     25   * @param {object} template
     26   *    The request template.
     27   */
     28  constructor(type, template = {}) {
     29    // The EventEmitter event name (this.type, attribute name in the event specification file) emitted on the Actor/Front,
     30    // may be different from the RDP JSON packet event name (ret[type], type attribute value in the event specification file)
     31    // In the specification:
     32    //   "my-event": { // <= EventEmitter name
     33    //     type: "myEvent", // <= RDP packet type attribute
     34    //     ...
     35    //   }
     36    this.type = template.type || type;
     37 
     38    this.template = template;
     39    this.args = findPlaceholders(template, Arg);
     40  }
     41 
     42  /**
     43   * Write a request.
     44   *
     45   * @param {Array} fnArgs
     46   *    The function arguments to place in the request.
     47   * @param {object} ctx
     48   *    The object making the request.
     49   * @returns a request packet.
     50   */
     51  write(fnArgs, ctx) {
     52    // Bulk request can't send custom attributes/custom JSON packet.
     53    // Only communicate "type" and "length" attributes to the transport layer,
     54    // which will emit a JSON RDP packet with an additional "actor attribute.
     55    if (this.template === BULK_REQUEST) {
     56      // The Front's method is expected to be called with a unique object argument
     57      // with a "length" attribute, which refers to the total size of bytes to be
     58      // sent via a the bulk StreamCopier.
     59      if (typeof fnArgs[0].length != "number") {
     60        throw new Error(
     61          "This front's method is expected to send a bulk request and should be called with an object argument with a length attribute."
     62        );
     63      }
     64      return { type: this.type, length: fnArgs[0].length };
     65    }
     66 
     67    const ret = {
     68      type: this.type,
     69    };
     70    for (const key in this.template) {
     71      const value = this.template[key];
     72      if (value instanceof Arg || value instanceof Option) {
     73        ret[key] = value.write(
     74          value.index in fnArgs ? fnArgs[value.index] : undefined,
     75          ctx,
     76          key
     77        );
     78      } else if (key == "type") {
     79        // Ignore the type attribute which have already been considered in the constructor.
     80        continue;
     81      } else {
     82        throw new Error(
     83          "Request can only an object with `Arg` or `Option` properties"
     84        );
     85      }
     86    }
     87    return ret;
     88  }
     89 
     90  /**
     91   * Read a request.
     92   *
     93   * @param {object} packet
     94   *    The request packet.
     95   * @param {object} ctx
     96   *    The object making the request.
     97   * @returns an arguments array
     98   */
     99  read(packet, ctx) {
    100    if (this.template === BULK_REQUEST) {
    101      // The transport layer will convey a custom packet object with length, copyTo and copyToBuffer,
    102      // which we transfer to the Actor's method via a unique object argument.
    103      // This help know about the incoming data size and read the binary buffer via `copyTo`
    104      // or `copyToBuffer`.
    105      return [
    106        {
    107          length: packet.length,
    108          copyTo: packet.copyTo,
    109          copyToBuffer: packet.copyToBuffer,
    110        },
    111      ];
    112    }
    113 
    114    const fnArgs = [];
    115    for (const templateArg of this.args) {
    116      const arg = templateArg.placeholder;
    117      const path = templateArg.path;
    118      const name = path[path.length - 1];
    119      arg.read(getPath(packet, path), ctx, fnArgs, name);
    120    }
    121    return fnArgs;
    122  }
    123 }
    124 
    125 exports.Request = Request;
    126 
    127 /**
    128 * Request/Response templates and generation
    129 *
    130 * Request packets are specified as json templates with
    131 * Arg and Option placeholders where arguments should be
    132 * placed.
    133 *
    134 * Reponse packets are also specified as json templates,
    135 * with a RetVal placeholder where the return value should be
    136 * placed.
    137 */
    138 
    139 /**
    140 * Placeholder for simple arguments.
    141 */
    142 class Arg {
    143  /**
    144   * @param {number} index
    145   *    The argument index to place at this position.
    146   * @param type type
    147   *    The argument should be marshalled as this type.
    148   */
    149  constructor(index, type) {
    150    this.index = index;
    151    // Prevent force loading all Arg types by accessing it only when needed
    152    loader.lazyGetter(this, "type", function () {
    153      return types.getType(type);
    154    });
    155  }
    156 
    157  write(arg, ctx) {
    158    return this.type.write(arg, ctx);
    159  }
    160 
    161  read(v, ctx, outArgs) {
    162    outArgs[this.index] = this.type.read(v, ctx);
    163  }
    164 }
    165 
    166 // Outside of protocol.js, Arg is called as factory method, without the new keyword.
    167 exports.Arg = function (index, type) {
    168  return new Arg(index, type);
    169 };
    170 
    171 /**
    172 * Placeholder for an options argument value that should be hoisted
    173 * into the packet.
    174 *
    175 * If provided in a method specification:
    176 *
    177 *   { optionArg: Option(1)}
    178 *
    179 * Then arguments[1].optionArg will be placed in the packet in this
    180 * value's place.
    181 */
    182 class Option extends Arg {
    183  /**
    184   * @param {number} index
    185   *    The argument index of the options value.
    186   * @param type type
    187   *    The argument should be marshalled as this type.
    188   */
    189  constructor(index, type) {
    190    super(index, type);
    191  }
    192 
    193  write(arg, ctx, name) {
    194    // Ignore if arg is undefined or null; allow other falsy values
    195    if (arg == undefined || arg[name] == undefined) {
    196      return undefined;
    197    }
    198    const v = arg[name];
    199    return this.type.write(v, ctx);
    200  }
    201 
    202  read(v, ctx, outArgs, name) {
    203    if (outArgs[this.index] === undefined) {
    204      outArgs[this.index] = {};
    205    }
    206    if (v === undefined) {
    207      return;
    208    }
    209    outArgs[this.index][name] = this.type.read(v, ctx);
    210  }
    211 }
    212 
    213 // Outside of protocol.js, Option is called as factory method, without the new keyword.
    214 exports.Option = function (index, type) {
    215  return new Option(index, type);
    216 };