diff options
| author | Aldo Cortesi <aldo@nullcube.com> | 2016-06-03 14:55:35 +1200 | 
|---|---|---|
| committer | Aldo Cortesi <aldo@nullcube.com> | 2016-06-03 14:55:35 +1200 | 
| commit | 08e4cd2a40778a1a10eef1bba0d2c167f6edeea9 (patch) | |
| tree | a36d0a637ae2a46c8425c56439927ff04392b5ef | |
| parent | 25671b91bd16d7c355742a6094addad00e331af5 (diff) | |
| parent | a2d13714a6d73595820e8ca7bdada3a4eabec36e (diff) | |
| download | mitmproxy-08e4cd2a40778a1a10eef1bba0d2c167f6edeea9.tar.gz mitmproxy-08e4cd2a40778a1a10eef1bba0d2c167f6edeea9.tar.bz2 mitmproxy-08e4cd2a40778a1a10eef1bba0d2c167f6edeea9.zip  | |
Merge remote-tracking branch 'upstream/master'
| -rw-r--r-- | .travis.yml | 4 | ||||
| -rw-r--r-- | README.rst | 45 | ||||
| -rwxr-xr-x | dev.sh | 5 | ||||
| -rw-r--r-- | mitmproxy/web/static/vendor.js | 897 | ||||
| -rw-r--r-- | netlib/tcp.py | 43 | ||||
| -rw-r--r-- | pathod/language/generators.py | 57 | ||||
| -rw-r--r-- | pathod/pathoc.py | 3 | ||||
| -rw-r--r-- | pathod/pathod.py | 18 | ||||
| -rw-r--r-- | pathod/protocols/websockets.py | 2 | ||||
| -rw-r--r-- | pathod/test.py | 49 | ||||
| -rw-r--r-- | setup.cfg | 3 | ||||
| -rw-r--r-- | test/pathod/test_app.py | 6 | ||||
| -rw-r--r-- | test/pathod/test_language_generators.py | 13 | ||||
| -rw-r--r-- | test/pathod/test_pathod.py | 22 | ||||
| -rw-r--r-- | test/pathod/test_test.py | 4 | ||||
| -rw-r--r-- | test/pathod/tutils.py | 48 | ||||
| -rw-r--r-- | web/.babelrc | 2 | ||||
| -rw-r--r-- | web/package.json | 1 | 
18 files changed, 604 insertions, 618 deletions
diff --git a/.travis.yml b/.travis.yml index 9d4dea88..5ec8b3bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,9 +22,9 @@ matrix:        git:          depth: 9999999      - python: 3.5 -      env: SCOPE="netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py" +      env: SCOPE="netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py ./test/pathod/test_language_generators.py"      - python: 3.5 -      env: SCOPE="netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py" NO_ALPN=1 +      env: SCOPE="netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py ./test/pathod/test_language_generators.py" NO_ALPN=1      - python: 2.7        env: DOCS=1        script: 'cd docs && make html' @@ -3,19 +3,24 @@ mitmproxy  |travis| |coveralls| |latest_release| |python_versions| -This repository contains the **mitmproxy** and **pathod** projects, as well as their shared networking library, **netlib**. +This repository contains the **mitmproxy** and **pathod** projects, as well as +their shared networking library, **netlib**. -``mitmproxy`` is an interactive, SSL-capable intercepting proxy with a console interface. +``mitmproxy`` is an interactive, SSL-capable intercepting proxy with a console +interface.  ``mitmdump`` is the command-line version of mitmproxy. Think tcpdump for HTTP. -``pathoc`` and ``pathod`` are perverse HTTP client and server applications designed to let you craft almost any conceivable HTTP request, including ones that creatively violate the standards. +``pathoc`` and ``pathod`` are perverse HTTP client and server applications +designed to let you craft almost any conceivable HTTP request, including ones +that creatively violate the standards.  Documentation & Help  -------------------- -Documentation, tutorials and precompiled binaries can be found on the mitmproxy and pathod websites. +Documentation, tutorials and precompiled binaries can be found on the mitmproxy +and pathod websites.  |mitmproxy_site| |pathod_site| @@ -28,12 +33,19 @@ You can join our developer chat on Slack.  |slack| +Installation +------------ + +The installation instructions are `here <http://docs.mitmproxy.org/en/stable/install.html>`_. +If you want to contribute changes, keep on reading. + +  Hacking  -------  To get started hacking on mitmproxy, make sure you have Python_ 2.7.x. with -virtualenv_ installed (you can find installation instructions for virtualenv here_). -Then do the following: +virtualenv_ installed (you can find installation instructions for virtualenv +here_). Then do the following:  .. code-block:: text @@ -42,10 +54,11 @@ Then do the following:      ./dev.sh -The *dev* script will create a virtualenv environment in a directory called "venv", -and install all mandatory and optional dependencies into it. -The primary mitmproxy components - mitmproxy, netlib and pathod - are installed as "editable", -so any changes to the source in the repository will be reflected live in the virtualenv. +The *dev* script will create a virtualenv environment in a directory called +"venv", and install all mandatory and optional dependencies into it. The +primary mitmproxy components - mitmproxy, netlib and pathod - are installed as +"editable", so any changes to the source in the repository will be reflected +live in the virtualenv.  To confirm that you're up and running, activate the virtualenv, and run the  mitmproxy test suite: @@ -56,9 +69,9 @@ mitmproxy test suite:      py.test  Note that the main executables for the project - ``mitmdump``, ``mitmproxy``, -``mitmweb``, ``pathod``, and ``pathoc`` - are all created within the virtualenv. After activating the -virtualenv, they will be on your $PATH, and you can run them like any other -command: +``mitmweb``, ``pathod``, and ``pathoc`` - are all created within the +virtualenv. After activating the virtualenv, they will be on your $PATH, and +you can run them like any other command:  .. code-block:: text @@ -85,9 +98,9 @@ suite. The project tries to maintain 100% test coverage.  Documentation  ------------- -The mitmproxy documentation is build using Sphinx_, which is installed automatically if you set up a development -environment as described above. -After installation, you can render the documentation like this: +The mitmproxy documentation is build using Sphinx_, which is installed +automatically if you set up a development environment as described above. After +installation, you can render the documentation like this:  .. code-block:: text @@ -1,5 +1,6 @@  #!/bin/sh  set -e +set -x  PYVERSION=$1  VENV="venv$1" @@ -8,8 +9,8 @@ echo "Creating dev environment in $VENV using Python $PYVERSION"  python$PYVERSION -m virtualenv "$VENV" --always-copy  . "$VENV/bin/activate" -pip$PYVERSION install -q -U pip setuptools -pip$PYVERSION install -q -r requirements.txt +pip$PYVERSION install -U pip setuptools +pip$PYVERSION install -r requirements.txt  echo ""  echo "* Virtualenv created in $VENV and all dependencies installed." diff --git a/mitmproxy/web/static/vendor.js b/mitmproxy/web/static/vendor.js index 4b2ff6bf..efe6f2b4 100644 --- a/mitmproxy/web/static/vendor.js +++ b/mitmproxy/web/static/vendor.js @@ -2116,15 +2116,13 @@ var KNOWN_STATICS = {  };  module.exports = function hoistNonReactStatics(targetComponent, sourceComponent) { -    if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components -        var keys = Object.getOwnPropertyNames(sourceComponent); -        for (var i=0; i<keys.length; ++i) { -            if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]]) { -                try { -                    targetComponent[keys[i]] = sourceComponent[keys[i]]; -                } catch (error) { - -                } +    var keys = Object.getOwnPropertyNames(sourceComponent); +    for (var i=0; i<keys.length; ++i) { +        if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]]) { +            try { +                targetComponent[keys[i]] = sourceComponent[keys[i]]; +            } catch (error) { +              }          }      } @@ -3087,9 +3085,6 @@ var currentQueue;  var queueIndex = -1;  function cleanUpNextTick() { -    if (!draining || !currentQueue) { -        return; -    }      draining = false;      if (currentQueue.length) {          queue = currentQueue.concat(queue); @@ -3894,7 +3889,6 @@ function compilePattern(pattern) {   * - **             Consumes (greedy) all characters up to the next character   *                  in the pattern, or to the end of the URL if there is none   * - *  The function calls callback(error, matched) when finished.   * The return value is an object with the following properties:   *   * - remainingPathname @@ -6094,17 +6088,13 @@ function matchRouteDeep(route, location, remainingPathname, paramNames, paramVal    // Only try to match the path if the route actually has a pattern, and if    // we're not just searching for potential nested absolute paths.    if (remainingPathname !== null && pattern) { -    try { -      var matched = (0, _PatternUtils.matchPattern)(pattern, remainingPathname); -      if (matched) { -        remainingPathname = matched.remainingPathname; -        paramNames = [].concat(paramNames, matched.paramNames); -        paramValues = [].concat(paramValues, matched.paramValues); -      } else { -        remainingPathname = null; -      } -    } catch (error) { -      callback(error); +    var matched = (0, _PatternUtils.matchPattern)(pattern, remainingPathname); +    if (matched) { +      remainingPathname = matched.remainingPathname; +      paramNames = [].concat(paramNames, matched.paramNames); +      paramValues = [].concat(paramValues, matched.paramValues); +    } else { +      remainingPathname = null;      }      // By assumption, pattern is non-empty here, which is the prerequisite for @@ -35727,7 +35717,8 @@ return jQuery;  (function (global){  /**   * @license - * lodash <https://lodash.com/> + * lodash 4.11.2 (Custom Build) <https://lodash.com/> + * Build: `lodash -d -o ./foo/lodash.js`   * Copyright jQuery Foundation and other contributors <https://jquery.org/>   * Released under MIT license <https://lodash.com/license>   * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> @@ -35739,7 +35730,7 @@ return jQuery;    var undefined;    /** Used as the semantic version number. */ -  var VERSION = '4.12.0'; +  var VERSION = '4.11.2';    /** Used as the size to enable large array optimizations. */    var LARGE_ARRAY_SIZE = 200; @@ -36184,6 +36175,30 @@ return jQuery;    }    /** +   * Creates a new array concatenating `array` with `other`. +   * +   * @private +   * @param {Array} array The first array to concatenate. +   * @param {Array} other The second array to concatenate. +   * @returns {Array} Returns the new concatenated array. +   */ +  function arrayConcat(array, other) { +    var index = -1, +        length = array.length, +        othIndex = -1, +        othLength = other.length, +        result = Array(length + othLength); + +    while (++index < length) { +      result[index] = array[index]; +    } +    while (++othIndex < othLength) { +      result[index++] = other[othIndex]; +    } +    return result; +  } + +  /**     * A specialized version of `_.forEach` for arrays without support for     * iteratee shorthands.     * @@ -36610,7 +36625,7 @@ return jQuery;     * @private     * @param {Object} object The object to query.     * @param {Array} props The property names to get values for. -   * @returns {Object} Returns the key-value pairs. +   * @returns {Object} Returns the new array of key-value pairs.     */    function baseToPairs(object, props) {      return arrayMap(props, function(key) { @@ -36623,7 +36638,7 @@ return jQuery;     *     * @private     * @param {Function} func The function to cap arguments for. -   * @returns {Function} Returns the new capped function. +   * @returns {Function} Returns the new function.     */    function baseUnary(func) {      return function(value) { @@ -36648,18 +36663,6 @@ return jQuery;    }    /** -   * Checks if a cache value for `key` exists. -   * -   * @private -   * @param {Object} cache The cache to query. -   * @param {string} key The key of the entry to check. -   * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. -   */ -  function cacheHas(cache, key) { -    return cache.has(key); -  } - -  /**     * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol     * that is not found in the character symbols.     * @@ -36815,11 +36818,11 @@ return jQuery;    }    /** -   * Converts `map` to its key-value pairs. +   * Converts `map` to an array.     *     * @private     * @param {Object} map The map to convert. -   * @returns {Array} Returns the key-value pairs. +   * @returns {Array} Returns the converted array.     */    function mapToArray(map) {      var index = -1, @@ -36857,11 +36860,11 @@ return jQuery;    }    /** -   * Converts `set` to an array of its values. +   * Converts `set` to an array.     *     * @private     * @param {Object} set The set to convert. -   * @returns {Array} Returns the values. +   * @returns {Array} Returns the converted array.     */    function setToArray(set) {      var index = -1, @@ -36874,23 +36877,6 @@ return jQuery;    }    /** -   * Converts `set` to its value-value pairs. -   * -   * @private -   * @param {Object} set The set to convert. -   * @returns {Array} Returns the value-value pairs. -   */ -  function setToPairs(set) { -    var index = -1, -        result = Array(set.size); - -    set.forEach(function(value) { -      result[++index] = [value, value]; -    }); -    return result; -  } - -  /**     * Gets the number of symbols in `string`.     *     * @private @@ -37143,10 +37129,10 @@ return jQuery;       * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,       * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,       * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`, -     * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`, -     * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`, -     * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`, -     * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`, +     * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`, `isBuffer`, +     * `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`, `isError`, +     * `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`, `isMatch`, +     * `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,       * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,       * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,       * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`, @@ -37155,9 +37141,9 @@ return jQuery;       * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,       * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,       * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`, -     * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`, -     * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`, -     * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`, +     * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toInteger`, +     * `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`, `toString`, +     * `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,       * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`       *       * @name _ @@ -37417,212 +37403,64 @@ return jQuery;       *       * @private       * @constructor -     * @param {Array} [entries] The key-value pairs to cache. -     */ -    function Hash(entries) { -      var index = -1, -          length = entries ? entries.length : 0; - -      this.clear(); -      while (++index < length) { -        var entry = entries[index]; -        this.set(entry[0], entry[1]); -      } -    } - -    /** -     * Removes all key-value entries from the hash. -     * -     * @private -     * @name clear -     * @memberOf Hash +     * @returns {Object} Returns the new hash object.       */ -    function hashClear() { -      this.__data__ = nativeCreate ? nativeCreate(null) : {}; -    } +    function Hash() {}      /**       * Removes `key` and its value from the hash.       *       * @private -     * @name delete -     * @memberOf Hash       * @param {Object} hash The hash to modify.       * @param {string} key The key of the value to remove.       * @returns {boolean} Returns `true` if the entry was removed, else `false`.       */ -    function hashDelete(key) { -      return this.has(key) && delete this.__data__[key]; +    function hashDelete(hash, key) { +      return hashHas(hash, key) && delete hash[key];      }      /**       * Gets the hash value for `key`.       *       * @private -     * @name get -     * @memberOf Hash +     * @param {Object} hash The hash to query.       * @param {string} key The key of the value to get.       * @returns {*} Returns the entry value.       */ -    function hashGet(key) { -      var data = this.__data__; +    function hashGet(hash, key) {        if (nativeCreate) { -        var result = data[key]; +        var result = hash[key];          return result === HASH_UNDEFINED ? undefined : result;        } -      return hasOwnProperty.call(data, key) ? data[key] : undefined; +      return hasOwnProperty.call(hash, key) ? hash[key] : undefined;      }      /**       * Checks if a hash value for `key` exists.       *       * @private -     * @name has -     * @memberOf Hash +     * @param {Object} hash The hash to query.       * @param {string} key The key of the entry to check.       * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.       */ -    function hashHas(key) { -      var data = this.__data__; -      return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); +    function hashHas(hash, key) { +      return nativeCreate ? hash[key] !== undefined : hasOwnProperty.call(hash, key);      }      /**       * Sets the hash `key` to `value`.       *       * @private -     * @name set -     * @memberOf Hash -     * @param {string} key The key of the value to set. -     * @param {*} value The value to set. -     * @returns {Object} Returns the hash instance. -     */ -    function hashSet(key, value) { -      var data = this.__data__; -      data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; -      return this; -    } - -    // Add methods to `Hash`. -    Hash.prototype.clear = hashClear; -    Hash.prototype['delete'] = hashDelete; -    Hash.prototype.get = hashGet; -    Hash.prototype.has = hashHas; -    Hash.prototype.set = hashSet; - -    /*------------------------------------------------------------------------*/ - -    /** -     * Creates an list cache object. -     * -     * @private -     * @constructor -     * @param {Array} [entries] The key-value pairs to cache. -     */ -    function ListCache(entries) { -      var index = -1, -          length = entries ? entries.length : 0; - -      this.clear(); -      while (++index < length) { -        var entry = entries[index]; -        this.set(entry[0], entry[1]); -      } -    } - -    /** -     * Removes all key-value entries from the list cache. -     * -     * @private -     * @name clear -     * @memberOf ListCache -     */ -    function listCacheClear() { -      this.__data__ = []; -    } - -    /** -     * Removes `key` and its value from the list cache. -     * -     * @private -     * @name delete -     * @memberOf ListCache -     * @param {string} key The key of the value to remove. -     * @returns {boolean} Returns `true` if the entry was removed, else `false`. -     */ -    function listCacheDelete(key) { -      var data = this.__data__, -          index = assocIndexOf(data, key); - -      if (index < 0) { -        return false; -      } -      var lastIndex = data.length - 1; -      if (index == lastIndex) { -        data.pop(); -      } else { -        splice.call(data, index, 1); -      } -      return true; -    } - -    /** -     * Gets the list cache value for `key`. -     * -     * @private -     * @name get -     * @memberOf ListCache -     * @param {string} key The key of the value to get. -     * @returns {*} Returns the entry value. -     */ -    function listCacheGet(key) { -      var data = this.__data__, -          index = assocIndexOf(data, key); - -      return index < 0 ? undefined : data[index][1]; -    } - -    /** -     * Checks if a list cache value for `key` exists. -     * -     * @private -     * @name has -     * @memberOf ListCache -     * @param {string} key The key of the entry to check. -     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. -     */ -    function listCacheHas(key) { -      return assocIndexOf(this.__data__, key) > -1; -    } - -    /** -     * Sets the list cache `key` to `value`. -     * -     * @private -     * @name set -     * @memberOf ListCache +     * @param {Object} hash The hash to modify.       * @param {string} key The key of the value to set.       * @param {*} value The value to set. -     * @returns {Object} Returns the list cache instance.       */ -    function listCacheSet(key, value) { -      var data = this.__data__, -          index = assocIndexOf(data, key); - -      if (index < 0) { -        data.push([key, value]); -      } else { -        data[index][1] = value; -      } -      return this; +    function hashSet(hash, key, value) { +      hash[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;      } -    // Add methods to `ListCache`. -    ListCache.prototype.clear = listCacheClear; -    ListCache.prototype['delete'] = listCacheDelete; -    ListCache.prototype.get = listCacheGet; -    ListCache.prototype.has = listCacheHas; -    ListCache.prototype.set = listCacheSet; +    // Avoid inheriting from `Object.prototype` when possible. +    Hash.prototype = nativeCreate ? nativeCreate(null) : objectProto;      /*------------------------------------------------------------------------*/ @@ -37631,15 +37469,15 @@ return jQuery;       *       * @private       * @constructor -     * @param {Array} [entries] The key-value pairs to cache. +     * @param {Array} [values] The values to cache.       */ -    function MapCache(entries) { +    function MapCache(values) {        var index = -1, -          length = entries ? entries.length : 0; +          length = values ? values.length : 0;        this.clear();        while (++index < length) { -        var entry = entries[index]; +        var entry = values[index];          this.set(entry[0], entry[1]);        }      } @@ -37651,10 +37489,10 @@ return jQuery;       * @name clear       * @memberOf MapCache       */ -    function mapCacheClear() { +    function mapClear() {        this.__data__ = {          'hash': new Hash, -        'map': new (Map || ListCache), +        'map': Map ? new Map : [],          'string': new Hash        };      } @@ -37668,8 +37506,12 @@ return jQuery;       * @param {string} key The key of the value to remove.       * @returns {boolean} Returns `true` if the entry was removed, else `false`.       */ -    function mapCacheDelete(key) { -      return getMapData(this, key)['delete'](key); +    function mapDelete(key) { +      var data = this.__data__; +      if (isKeyable(key)) { +        return hashDelete(typeof key == 'string' ? data.string : data.hash, key); +      } +      return Map ? data.map['delete'](key) : assocDelete(data.map, key);      }      /** @@ -37681,8 +37523,12 @@ return jQuery;       * @param {string} key The key of the value to get.       * @returns {*} Returns the entry value.       */ -    function mapCacheGet(key) { -      return getMapData(this, key).get(key); +    function mapGet(key) { +      var data = this.__data__; +      if (isKeyable(key)) { +        return hashGet(typeof key == 'string' ? data.string : data.hash, key); +      } +      return Map ? data.map.get(key) : assocGet(data.map, key);      }      /** @@ -37694,8 +37540,12 @@ return jQuery;       * @param {string} key The key of the entry to check.       * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.       */ -    function mapCacheHas(key) { -      return getMapData(this, key).has(key); +    function mapHas(key) { +      var data = this.__data__; +      if (isKeyable(key)) { +        return hashHas(typeof key == 'string' ? data.string : data.hash, key); +      } +      return Map ? data.map.has(key) : assocHas(data.map, key);      }      /** @@ -37708,23 +37558,30 @@ return jQuery;       * @param {*} value The value to set.       * @returns {Object} Returns the map cache instance.       */ -    function mapCacheSet(key, value) { -      getMapData(this, key).set(key, value); +    function mapSet(key, value) { +      var data = this.__data__; +      if (isKeyable(key)) { +        hashSet(typeof key == 'string' ? data.string : data.hash, key, value); +      } else if (Map) { +        data.map.set(key, value); +      } else { +        assocSet(data.map, key, value); +      }        return this;      }      // Add methods to `MapCache`. -    MapCache.prototype.clear = mapCacheClear; -    MapCache.prototype['delete'] = mapCacheDelete; -    MapCache.prototype.get = mapCacheGet; -    MapCache.prototype.has = mapCacheHas; -    MapCache.prototype.set = mapCacheSet; +    MapCache.prototype.clear = mapClear; +    MapCache.prototype['delete'] = mapDelete; +    MapCache.prototype.get = mapGet; +    MapCache.prototype.has = mapHas; +    MapCache.prototype.set = mapSet;      /*------------------------------------------------------------------------*/      /**       * -     * Creates an array cache object to store unique values. +     * Creates a set cache object to store unique values.       *       * @private       * @constructor @@ -37736,41 +37593,52 @@ return jQuery;        this.__data__ = new MapCache;        while (++index < length) { -        this.add(values[index]); +        this.push(values[index]);        }      }      /** -     * Adds `value` to the array cache. +     * Checks if `value` is in `cache`.       *       * @private -     * @name add -     * @memberOf SetCache -     * @alias push -     * @param {*} value The value to cache. -     * @returns {Object} Returns the cache instance. +     * @param {Object} cache The set cache to search. +     * @param {*} value The value to search for. +     * @returns {number} Returns `true` if `value` is found, else `false`.       */ -    function setCacheAdd(value) { -      this.__data__.set(value, HASH_UNDEFINED); -      return this; +    function cacheHas(cache, value) { +      var map = cache.__data__; +      if (isKeyable(value)) { +        var data = map.__data__, +            hash = typeof value == 'string' ? data.string : data.hash; + +        return hash[value] === HASH_UNDEFINED; +      } +      return map.has(value);      }      /** -     * Checks if `value` is in the array cache. +     * Adds `value` to the set cache.       *       * @private -     * @name has +     * @name push       * @memberOf SetCache -     * @param {*} value The value to search for. -     * @returns {number} Returns `true` if `value` is found, else `false`. +     * @param {*} value The value to cache.       */ -    function setCacheHas(value) { -      return this.__data__.has(value); +    function cachePush(value) { +      var map = this.__data__; +      if (isKeyable(value)) { +        var data = map.__data__, +            hash = typeof value == 'string' ? data.string : data.hash; + +        hash[value] = HASH_UNDEFINED; +      } +      else { +        map.set(value, HASH_UNDEFINED); +      }      }      // Add methods to `SetCache`. -    SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; -    SetCache.prototype.has = setCacheHas; +    SetCache.prototype.push = cachePush;      /*------------------------------------------------------------------------*/ @@ -37779,10 +37647,17 @@ return jQuery;       *       * @private       * @constructor -     * @param {Array} [entries] The key-value pairs to cache. +     * @param {Array} [values] The values to cache.       */ -    function Stack(entries) { -      this.__data__ = new ListCache(entries); +    function Stack(values) { +      var index = -1, +          length = values ? values.length : 0; + +      this.clear(); +      while (++index < length) { +        var entry = values[index]; +        this.set(entry[0], entry[1]); +      }      }      /** @@ -37793,7 +37668,7 @@ return jQuery;       * @memberOf Stack       */      function stackClear() { -      this.__data__ = new ListCache; +      this.__data__ = { 'array': [], 'map': null };      }      /** @@ -37806,7 +37681,10 @@ return jQuery;       * @returns {boolean} Returns `true` if the entry was removed, else `false`.       */      function stackDelete(key) { -      return this.__data__['delete'](key); +      var data = this.__data__, +          array = data.array; + +      return array ? assocDelete(array, key) : data.map['delete'](key);      }      /** @@ -37819,7 +37697,10 @@ return jQuery;       * @returns {*} Returns the entry value.       */      function stackGet(key) { -      return this.__data__.get(key); +      var data = this.__data__, +          array = data.array; + +      return array ? assocGet(array, key) : data.map.get(key);      }      /** @@ -37832,7 +37713,10 @@ return jQuery;       * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.       */      function stackHas(key) { -      return this.__data__.has(key); +      var data = this.__data__, +          array = data.array; + +      return array ? assocHas(array, key) : data.map.has(key);      }      /** @@ -37846,11 +37730,21 @@ return jQuery;       * @returns {Object} Returns the stack cache instance.       */      function stackSet(key, value) { -      var cache = this.__data__; -      if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) { -        cache = this.__data__ = new MapCache(cache.__data__); +      var data = this.__data__, +          array = data.array; + +      if (array) { +        if (array.length < (LARGE_ARRAY_SIZE - 1)) { +          assocSet(array, key, value); +        } else { +          data.array = null; +          data.map = new MapCache(array); +        } +      } +      var map = data.map; +      if (map) { +        map.set(key, value);        } -      cache.set(key, value);        return this;      } @@ -37864,6 +37758,90 @@ return jQuery;      /*------------------------------------------------------------------------*/      /** +     * Removes `key` and its value from the associative array. +     * +     * @private +     * @param {Array} array The array to modify. +     * @param {string} key The key of the value to remove. +     * @returns {boolean} Returns `true` if the entry was removed, else `false`. +     */ +    function assocDelete(array, key) { +      var index = assocIndexOf(array, key); +      if (index < 0) { +        return false; +      } +      var lastIndex = array.length - 1; +      if (index == lastIndex) { +        array.pop(); +      } else { +        splice.call(array, index, 1); +      } +      return true; +    } + +    /** +     * Gets the associative array value for `key`. +     * +     * @private +     * @param {Array} array The array to query. +     * @param {string} key The key of the value to get. +     * @returns {*} Returns the entry value. +     */ +    function assocGet(array, key) { +      var index = assocIndexOf(array, key); +      return index < 0 ? undefined : array[index][1]; +    } + +    /** +     * Checks if an associative array value for `key` exists. +     * +     * @private +     * @param {Array} array The array to query. +     * @param {string} key The key of the entry to check. +     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. +     */ +    function assocHas(array, key) { +      return assocIndexOf(array, key) > -1; +    } + +    /** +     * Gets the index at which the `key` is found in `array` of key-value pairs. +     * +     * @private +     * @param {Array} array The array to search. +     * @param {*} key The key to search for. +     * @returns {number} Returns the index of the matched value, else `-1`. +     */ +    function assocIndexOf(array, key) { +      var length = array.length; +      while (length--) { +        if (eq(array[length][0], key)) { +          return length; +        } +      } +      return -1; +    } + +    /** +     * Sets the associative array `key` to `value`. +     * +     * @private +     * @param {Array} array The array to modify. +     * @param {string} key The key of the value to set. +     * @param {*} value The value to set. +     */ +    function assocSet(array, key, value) { +      var index = assocIndexOf(array, key); +      if (index < 0) { +        array.push([key, value]); +      } else { +        array[index][1] = value; +      } +    } + +    /*------------------------------------------------------------------------*/ + +    /**       * Used by `_.defaults` to customize its `_.assignIn` use.       *       * @private @@ -37916,24 +37894,6 @@ return jQuery;      }      /** -     * Gets the index at which the `key` is found in `array` of key-value pairs. -     * -     * @private -     * @param {Array} array The array to search. -     * @param {*} key The key to search for. -     * @returns {number} Returns the index of the matched value, else `-1`. -     */ -    function assocIndexOf(array, key) { -      var length = array.length; -      while (length--) { -        if (eq(array[length][0], key)) { -          return length; -        } -      } -      return -1; -    } - -    /**       * Aggregates elements of `collection` on `accumulator` with keys transformed       * by `iteratee` and values set by `setter`.       * @@ -37970,7 +37930,7 @@ return jQuery;       * @private       * @param {Object} object The object to iterate over.       * @param {string[]} paths The property paths of elements to pick. -     * @returns {Array} Returns the picked elements. +     * @returns {Array} Returns the new array of picked elements.       */      function baseAt(object, paths) {        var index = -1, @@ -38085,7 +38045,7 @@ return jQuery;       *       * @private       * @param {Object} source The object of property predicates to conform to. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       */      function baseConforms(source) {        var props = keys(source), @@ -38398,7 +38358,7 @@ return jQuery;       * @private       * @param {Object} object The object to inspect.       * @param {Array} props The property names to filter. -     * @returns {Array} Returns the function names. +     * @returns {Array} Returns the new array of filtered property names.       */      function baseFunctions(object, props) {        return arrayFilter(props, function(key) { @@ -38439,7 +38399,9 @@ return jQuery;       */      function baseGetAllKeys(object, keysFunc, symbolsFunc) {        var result = keysFunc(object); -      return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); +      return isArray(object) +        ? result +        : arrayPush(result, symbolsFunc(object));      }      /** @@ -38831,7 +38793,7 @@ return jQuery;       *       * @private       * @param {Object} source The object of property values to match. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       */      function baseMatches(source) {        var matchData = getMatchData(source); @@ -38849,7 +38811,7 @@ return jQuery;       * @private       * @param {string} path The path of the property to get.       * @param {*} srcValue The value to match. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       */      function baseMatchesProperty(path, srcValue) {        if (isKey(path) && isStrictComparable(srcValue)) { @@ -39064,7 +39026,7 @@ return jQuery;       *       * @private       * @param {string} key The key of the property to get. -     * @returns {Function} Returns the new accessor function. +     * @returns {Function} Returns the new function.       */      function baseProperty(key) {        return function(object) { @@ -39077,7 +39039,7 @@ return jQuery;       *       * @private       * @param {Array|string} path The path of the property to get. -     * @returns {Function} Returns the new accessor function. +     * @returns {Function} Returns the new function.       */      function basePropertyDeep(path) {        return function(object) { @@ -39178,7 +39140,7 @@ return jQuery;       * @param {number} end The end of the range.       * @param {number} step The value to increment or decrement by.       * @param {boolean} [fromRight] Specify iterating from right to left. -     * @returns {Array} Returns the range of numbers. +     * @returns {Array} Returns the new array of numbers.       */      function baseRange(start, end, step, fromRight) {        var index = -1, @@ -39892,7 +39854,7 @@ return jQuery;       * placeholders, and provided arguments into a single array of arguments.       *       * @private -     * @param {Array} args The provided arguments. +     * @param {Array|Object} args The provided arguments.       * @param {Array} partials The arguments to prepend to those provided.       * @param {Array} holders The `partials` placeholder indexes.       * @params {boolean} [isCurried] Specify composing for a curried function. @@ -39927,7 +39889,7 @@ return jQuery;       * is tailored for `_.partialRight`.       *       * @private -     * @param {Array} args The provided arguments. +     * @param {Array|Object} args The provided arguments.       * @param {Array} partials The arguments to append to those provided.       * @param {Array} holders The `partials` placeholder indexes.       * @params {boolean} [isCurried] Specify composing for a curried function. @@ -40049,7 +40011,7 @@ return jQuery;              customizer = length > 1 ? sources[length - 1] : undefined,              guard = length > 2 ? sources[2] : undefined; -        customizer = (assigner.length > 3 && typeof customizer == 'function') +        customizer = typeof customizer == 'function'            ? (length--, customizer)            : undefined; @@ -40148,7 +40110,7 @@ return jQuery;       *       * @private       * @param {string} methodName The name of the `String` case method to use. -     * @returns {Function} Returns the new case function. +     * @returns {Function} Returns the new function.       */      function createCaseFirst(methodName) {        return function(string) { @@ -40233,7 +40195,7 @@ return jQuery;          var length = arguments.length,              args = Array(length),              index = length, -            placeholder = getHolder(wrapper); +            placeholder = getPlaceholder(wrapper);          while (index--) {            args[index] = arguments[index]; @@ -40348,14 +40310,14 @@ return jQuery;        function wrapper() {          var length = arguments.length, -            args = Array(length), -            index = length; +            index = length, +            args = Array(length);          while (index--) {            args[index] = arguments[index];          }          if (isCurried) { -          var placeholder = getHolder(wrapper), +          var placeholder = getPlaceholder(wrapper),                holdersCount = countHolders(args, placeholder);          }          if (partials) { @@ -40444,7 +40406,7 @@ return jQuery;       *       * @private       * @param {Function} arrayFunc The function to iterate over iteratees. -     * @returns {Function} Returns the new over function. +     * @returns {Function} Returns the new invoker function.       */      function createOver(arrayFunc) {        return rest(function(iteratees) { @@ -40643,26 +40605,6 @@ return jQuery;      };      /** -     * Creates a `_.toPairs` or `_.toPairsIn` function. -     * -     * @private -     * @param {Function} keysFunc The function to get the keys of a given object. -     * @returns {Function} Returns the new pairs function. -     */ -    function createToPairs(keysFunc) { -      return function(object) { -        var tag = getTag(object); -        if (tag == mapTag) { -          return mapToArray(object); -        } -        if (tag == setTag) { -          return setToPairs(object); -        } -        return baseToPairs(object, keysFunc(object)); -      }; -    } - -    /**       * Creates a function that either curries or invokes `func` with optional       * `this` binding and partially applied arguments.       * @@ -40679,7 +40621,6 @@ return jQuery;       *    64 - `_.partialRight`       *   128 - `_.rearg`       *   256 - `_.ary` -     *   512 - `_.flip`       * @param {*} [thisArg] The `this` binding of `func`.       * @param {Array} [partials] The arguments to be partially applied.       * @param {Array} [holders] The `partials` placeholder indexes. @@ -40758,7 +40699,9 @@ return jQuery;       * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.       */      function equalArrays(array, other, equalFunc, customizer, bitmask, stack) { -      var isPartial = bitmask & PARTIAL_COMPARE_FLAG, +      var index = -1, +          isPartial = bitmask & PARTIAL_COMPARE_FLAG, +          isUnordered = bitmask & UNORDERED_COMPARE_FLAG,            arrLength = array.length,            othLength = other.length; @@ -40770,10 +40713,7 @@ return jQuery;        if (stacked) {          return stacked == other;        } -      var index = -1, -          result = true, -          seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined; - +      var result = true;        stack.set(array, other);        // Ignore non-index properties. @@ -40794,12 +40734,10 @@ return jQuery;            break;          }          // Recursively compare arrays (susceptible to call stack limits). -        if (seen) { -          if (!arraySome(other, function(othValue, othIndex) { -                if (!seen.has(othIndex) && -                    (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) { -                  return seen.add(othIndex); -                } +        if (isUnordered) { +          if (!arraySome(other, function(othValue) { +                return arrValue === othValue || +                  equalFunc(arrValue, othValue, customizer, bitmask, stack);                })) {              result = false;              break; @@ -41034,18 +40972,6 @@ return jQuery;      }      /** -     * Gets the argument placeholder value for `func`. -     * -     * @private -     * @param {Function} func The function to inspect. -     * @returns {*} Returns the placeholder value. -     */ -    function getHolder(func) { -      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; -      return object.placeholder; -    } - -    /**       * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,       * this function returns the custom method, otherwise it returns `baseIteratee`.       * If arguments are provided, the chosen function is invoked with them and @@ -41076,21 +41002,6 @@ return jQuery;      var getLength = baseProperty('length');      /** -     * Gets the data for `map`. -     * -     * @private -     * @param {Object} map The map to query. -     * @param {string} key The reference key. -     * @returns {*} Returns the map data. -     */ -    function getMapData(map, key) { -      var data = map.__data__; -      return isKeyable(key) -        ? data[typeof key == 'string' ? 'string' : 'hash'] -        : data.map; -    } - -    /**       * Gets the property names, values, and compare flags of `object`.       *       * @private @@ -41121,6 +41032,18 @@ return jQuery;      }      /** +     * Gets the argument placeholder value for `func`. +     * +     * @private +     * @param {Function} func The function to inspect. +     * @returns {*} Returns the placeholder value. +     */ +    function getPlaceholder(func) { +      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; +      return object.placeholder; +    } + +    /**       * Gets the `[[Prototype]]` of `value`.       *       * @private @@ -41369,7 +41292,7 @@ return jQuery;       * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.       */      function isFlattenable(value) { -      return isArray(value) || isArguments(value); +      return isArrayLikeObject(value) && (isArray(value) || isArguments(value));      }      /** @@ -41513,7 +41436,7 @@ return jQuery;       * @private       * @param {string} key The key of the property to get.       * @param {*} srcValue The value to match. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       */      function matchesStrictComparable(key, srcValue) {        return function(object) { @@ -41765,7 +41688,7 @@ return jQuery;       * @param {Array} array The array to process.       * @param {number} [size=1] The length of each chunk       * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. -     * @returns {Array} Returns the new array of chunks. +     * @returns {Array} Returns the new array containing chunks.       * @example       *       * _.chunk(['a', 'b', 'c', 'd'], 2); @@ -41848,16 +41771,16 @@ return jQuery;       */      function concat() {        var length = arguments.length, -          args = Array(length ? length - 1 : 0), -          array = arguments[0], -          index = length; +          array = castArray(arguments[0]); -      while (index--) { -        args[index - 1] = arguments[index]; +      if (length < 2) { +        return length ? copyArray(array) : [];        } -      return length -        ? arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)) -        : []; +      var args = Array(length - 1); +      while (length--) { +        args[length - 1] = arguments[length]; +      } +      return arrayConcat(array, baseFlatten(args, 1));      }      /** @@ -42576,8 +42499,8 @@ return jQuery;      }      /** -     * Gets the element at `n` index of `array`. If `n` is negative, the nth -     * element from the end is returned. +     * Gets the nth element of `array`. If `n` is negative, the nth element +     * from the end is returned.       *       * @static       * @memberOf _ @@ -43457,7 +43380,7 @@ return jQuery;       * @memberOf _       * @since 0.1.0       * @category Array -     * @param {Array} array The array to inspect. +     * @param {Array} array The array to filter.       * @param {...*} [values] The values to exclude.       * @returns {Array} Returns the new array of filtered values.       * @see _.difference, _.xor @@ -43483,7 +43406,7 @@ return jQuery;       * @since 2.4.0       * @category Array       * @param {...Array} [arrays] The arrays to inspect. -     * @returns {Array} Returns the new array of filtered values. +     * @returns {Array} Returns the new array of values.       * @see _.difference, _.without       * @example       * @@ -43507,7 +43430,7 @@ return jQuery;       * @param {...Array} [arrays] The arrays to inspect.       * @param {Array|Function|Object|string} [iteratee=_.identity]       *  The iteratee invoked per element. -     * @returns {Array} Returns the new array of filtered values. +     * @returns {Array} Returns the new array of values.       * @example       *       * _.xorBy([2.1, 1.2], [4.3, 2.4], Math.floor); @@ -43536,7 +43459,7 @@ return jQuery;       * @category Array       * @param {...Array} [arrays] The arrays to inspect.       * @param {Function} [comparator] The comparator invoked per element. -     * @returns {Array} Returns the new array of filtered values. +     * @returns {Array} Returns the new array of values.       * @example       *       * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; @@ -44284,8 +44207,9 @@ return jQuery;       * // => Logs 'a' then 'b' (iteration order is not guaranteed).       */      function forEach(collection, iteratee) { -      var func = isArray(collection) ? arrayEach : baseEach; -      return func(collection, getIteratee(iteratee, 3)); +      return (typeof iteratee == 'function' && isArray(collection)) +        ? arrayEach(collection, iteratee) +        : baseEach(collection, getIteratee(iteratee));      }      /** @@ -44309,8 +44233,9 @@ return jQuery;       * // => Logs `2` then `1`.       */      function forEachRight(collection, iteratee) { -      var func = isArray(collection) ? arrayEachRight : baseEachRight; -      return func(collection, getIteratee(iteratee, 3)); +      return (typeof iteratee == 'function' && isArray(collection)) +        ? arrayEachRight(collection, iteratee) +        : baseEachRight(collection, getIteratee(iteratee));      }      /** @@ -44991,7 +44916,7 @@ return jQuery;       * @param {Function} func The function to cap arguments for.       * @param {number} [n=func.length] The arity cap.       * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. -     * @returns {Function} Returns the new capped function. +     * @returns {Function} Returns the new function.       * @example       *       * _.map(['6', '8', '10'], _.ary(parseInt, 1)); @@ -45075,7 +45000,7 @@ return jQuery;      var bind = rest(function(func, thisArg, partials) {        var bitmask = BIND_FLAG;        if (partials.length) { -        var holders = replaceHolders(partials, getHolder(bind)); +        var holders = replaceHolders(partials, getPlaceholder(bind));          bitmask |= PARTIAL_FLAG;        }        return createWrapper(func, bitmask, thisArg, partials, holders); @@ -45129,7 +45054,7 @@ return jQuery;      var bindKey = rest(function(object, key, partials) {        var bitmask = BIND_FLAG | BIND_KEY_FLAG;        if (partials.length) { -        var holders = replaceHolders(partials, getHolder(bindKey)); +        var holders = replaceHolders(partials, getPlaceholder(bindKey));          bitmask |= PARTIAL_FLAG;        }        return createWrapper(key, bitmask, object, partials, holders); @@ -45455,7 +45380,7 @@ return jQuery;       * @since 4.0.0       * @category Function       * @param {Function} func The function to flip arguments for. -     * @returns {Function} Returns the new flipped function. +     * @returns {Function} Returns the new function.       * @example       *       * var flipped = _.flip(function() { @@ -45488,7 +45413,7 @@ return jQuery;       * @category Function       * @param {Function} func The function to have its output memoized.       * @param {Function} [resolver] The function to resolve the cache key. -     * @returns {Function} Returns the new memoized function. +     * @returns {Function} Returns the new memoizing function.       * @example       *       * var object = { 'a': 1, 'b': 2 }; @@ -45546,7 +45471,7 @@ return jQuery;       * @since 3.0.0       * @category Function       * @param {Function} predicate The predicate to negate. -     * @returns {Function} Returns the new negated function. +     * @returns {Function} Returns the new function.       * @example       *       * function isEven(n) { @@ -45670,7 +45595,7 @@ return jQuery;       * // => 'hi fred'       */      var partial = rest(function(func, partials) { -      var holders = replaceHolders(partials, getHolder(partial)); +      var holders = replaceHolders(partials, getPlaceholder(partial));        return createWrapper(func, PARTIAL_FLAG, undefined, partials, holders);      }); @@ -45707,7 +45632,7 @@ return jQuery;       * // => 'hello fred'       */      var partialRight = rest(function(func, partials) { -      var holders = replaceHolders(partials, getHolder(partialRight)); +      var holders = replaceHolders(partials, getPlaceholder(partialRight));        return createWrapper(func, PARTIAL_RIGHT_FLAG, undefined, partials, holders);      }); @@ -45909,7 +45834,7 @@ return jQuery;       * @since 4.0.0       * @category Function       * @param {Function} func The function to cap arguments for. -     * @returns {Function} Returns the new capped function. +     * @returns {Function} Returns the new function.       * @example       *       * _.map(['6', '8', '10'], _.unary(parseInt)); @@ -46585,13 +46510,13 @@ return jQuery;       * _.isFinite(3);       * // => true       * -     * _.isFinite(Number.MIN_VALUE); +     * _.isFinite(Number.MAX_VALUE);       * // => true       * -     * _.isFinite(Infinity); -     * // => false +     * _.isFinite(3.14); +     * // => true       * -     * _.isFinite('3'); +     * _.isFinite(Infinity);       * // => false       */      function isFinite(value) { @@ -47314,41 +47239,6 @@ return jQuery;      }      /** -     * Converts `value` to a finite number. -     * -     * @static -     * @memberOf _ -     * @since 4.12.0 -     * @category Lang -     * @param {*} value The value to convert. -     * @returns {number} Returns the converted number. -     * @example -     * -     * _.toFinite(3.2); -     * // => 3.2 -     * -     * _.toFinite(Number.MIN_VALUE); -     * // => 5e-324 -     * -     * _.toFinite(Infinity); -     * // => 1.7976931348623157e+308 -     * -     * _.toFinite('3.2'); -     * // => 3.2 -     */ -    function toFinite(value) { -      if (!value) { -        return value === 0 ? value : 0; -      } -      value = toNumber(value); -      if (value === INFINITY || value === -INFINITY) { -        var sign = (value < 0 ? -1 : 1); -        return sign * MAX_INTEGER; -      } -      return value === value ? value : 0; -    } - -    /**       * Converts `value` to an integer.       *       * **Note:** This function is loosely based on @@ -47362,7 +47252,7 @@ return jQuery;       * @returns {number} Returns the converted integer.       * @example       * -     * _.toInteger(3.2); +     * _.toInteger(3);       * // => 3       *       * _.toInteger(Number.MIN_VALUE); @@ -47371,14 +47261,20 @@ return jQuery;       * _.toInteger(Infinity);       * // => 1.7976931348623157e+308       * -     * _.toInteger('3.2'); +     * _.toInteger('3');       * // => 3       */      function toInteger(value) { -      var result = toFinite(value), -          remainder = result % 1; - -      return result === result ? (remainder ? result - remainder : result) : 0; +      if (!value) { +        return value === 0 ? value : 0; +      } +      value = toNumber(value); +      if (value === INFINITY || value === -INFINITY) { +        var sign = (value < 0 ? -1 : 1); +        return sign * MAX_INTEGER; +      } +      var remainder = value % 1; +      return value === value ? (remainder ? value - remainder : value) : 0;      }      /** @@ -47396,7 +47292,7 @@ return jQuery;       * @returns {number} Returns the converted integer.       * @example       * -     * _.toLength(3.2); +     * _.toLength(3);       * // => 3       *       * _.toLength(Number.MIN_VALUE); @@ -47405,7 +47301,7 @@ return jQuery;       * _.toLength(Infinity);       * // => 4294967295       * -     * _.toLength('3.2'); +     * _.toLength('3');       * // => 3       */      function toLength(value) { @@ -47423,8 +47319,8 @@ return jQuery;       * @returns {number} Returns the number.       * @example       * -     * _.toNumber(3.2); -     * // => 3.2 +     * _.toNumber(3); +     * // => 3       *       * _.toNumber(Number.MIN_VALUE);       * // => 5e-324 @@ -47432,8 +47328,8 @@ return jQuery;       * _.toNumber(Infinity);       * // => Infinity       * -     * _.toNumber('3.2'); -     * // => 3.2 +     * _.toNumber('3'); +     * // => 3       */      function toNumber(value) {        if (typeof value == 'number') { @@ -47496,7 +47392,7 @@ return jQuery;       * @returns {number} Returns the converted integer.       * @example       * -     * _.toSafeInteger(3.2); +     * _.toSafeInteger(3);       * // => 3       *       * _.toSafeInteger(Number.MIN_VALUE); @@ -47505,7 +47401,7 @@ return jQuery;       * _.toSafeInteger(Infinity);       * // => 9007199254740991       * -     * _.toSafeInteger('3.2'); +     * _.toSafeInteger('3');       * // => 3       */      function toSafeInteger(value) { @@ -47698,7 +47594,7 @@ return jQuery;       * @category Object       * @param {Object} object The object to iterate over.       * @param {...(string|string[])} [paths] The property paths of elements to pick. -     * @returns {Array} Returns the picked values. +     * @returns {Array} Returns the new array of picked elements.       * @example       *       * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; @@ -47914,7 +47810,7 @@ return jQuery;      function forIn(object, iteratee) {        return object == null          ? object -        : baseFor(object, getIteratee(iteratee, 3), keysIn); +        : baseFor(object, getIteratee(iteratee), keysIn);      }      /** @@ -47946,7 +47842,7 @@ return jQuery;      function forInRight(object, iteratee) {        return object == null          ? object -        : baseForRight(object, getIteratee(iteratee, 3), keysIn); +        : baseForRight(object, getIteratee(iteratee), keysIn);      }      /** @@ -47978,7 +47874,7 @@ return jQuery;       * // => Logs 'a' then 'b' (iteration order is not guaranteed).       */      function forOwn(object, iteratee) { -      return object && baseForOwn(object, getIteratee(iteratee, 3)); +      return object && baseForOwn(object, getIteratee(iteratee));      }      /** @@ -48008,7 +47904,7 @@ return jQuery;       * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.       */      function forOwnRight(object, iteratee) { -      return object && baseForOwnRight(object, getIteratee(iteratee, 3)); +      return object && baseForOwnRight(object, getIteratee(iteratee));      }      /** @@ -48020,7 +47916,7 @@ return jQuery;       * @memberOf _       * @category Object       * @param {Object} object The object to inspect. -     * @returns {Array} Returns the function names. +     * @returns {Array} Returns the new array of property names.       * @see _.functionsIn       * @example       * @@ -48047,7 +47943,7 @@ return jQuery;       * @since 4.0.0       * @category Object       * @param {Object} object The object to inspect. -     * @returns {Array} Returns the function names. +     * @returns {Array} Returns the new array of property names.       * @see _.functions       * @example       * @@ -48400,7 +48296,7 @@ return jQuery;       * inherited enumerable string keyed properties of source objects into the       * destination object. Source properties that resolve to `undefined` are       * skipped if a destination value exists. Array and plain object properties -     * are merged recursively. Other objects and value types are overridden by +     * are merged recursively.Other objects and value types are overridden by       * assignment. Source objects are applied from left to right. Subsequent       * sources overwrite property assignments of previous sources.       * @@ -48685,8 +48581,7 @@ return jQuery;      /**       * Creates an array of own enumerable string keyed-value pairs for `object` -     * which can be consumed by `_.fromPairs`. If `object` is a map or set, its -     * entries are returned. +     * which can be consumed by `_.fromPairs`.       *       * @static       * @memberOf _ @@ -48694,7 +48589,7 @@ return jQuery;       * @alias entries       * @category Object       * @param {Object} object The object to query. -     * @returns {Array} Returns the key-value pairs. +     * @returns {Array} Returns the new array of key-value pairs.       * @example       *       * function Foo() { @@ -48707,12 +48602,13 @@ return jQuery;       * _.toPairs(new Foo);       * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)       */ -    var toPairs = createToPairs(keys); +    function toPairs(object) { +      return baseToPairs(object, keys(object)); +    }      /**       * Creates an array of own and inherited enumerable string keyed-value pairs -     * for `object` which can be consumed by `_.fromPairs`. If `object` is a map -     * or set, its entries are returned. +     * for `object` which can be consumed by `_.fromPairs`.       *       * @static       * @memberOf _ @@ -48720,7 +48616,7 @@ return jQuery;       * @alias entriesIn       * @category Object       * @param {Object} object The object to query. -     * @returns {Array} Returns the key-value pairs. +     * @returns {Array} Returns the new array of key-value pairs.       * @example       *       * function Foo() { @@ -48731,9 +48627,11 @@ return jQuery;       * Foo.prototype.c = 3;       *       * _.toPairsIn(new Foo); -     * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) +     * // => [['a', 1], ['b', 2], ['c', 1]] (iteration order is not guaranteed)       */ -    var toPairsIn = createToPairs(keysIn); +    function toPairsIn(object) { +      return baseToPairs(object, keysIn(object)); +    }      /**       * An alternative to `_.reduce`; this method transforms `object` to a new @@ -49563,7 +49461,7 @@ return jQuery;       * @param {string} [string=''] The string to split.       * @param {RegExp|string} separator The separator pattern to split by.       * @param {number} [limit] The length to truncate results to. -     * @returns {Array} Returns the string segments. +     * @returns {Array} Returns the new array of string segments.       * @example       *       * _.split('a-b-c', '-', 2); @@ -49708,6 +49606,12 @@ return jQuery;       * compiled({ 'user': 'pebbles' });       * // => 'hello pebbles!'       * +     * // Use custom template delimiters. +     * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; +     * var compiled = _.template('hello {{ user }}!'); +     * compiled({ 'user': 'mustache' }); +     * // => 'hello mustache!' +     *       * // Use backslashes to treat delimiters as plain text.       * var compiled = _.template('<%= "\\<%- value %\\>" %>');       * compiled({ 'value': 'ignored' }); @@ -49733,15 +49637,9 @@ return jQuery;       * //   return __p;       * // }       * -     * // Use custom template delimiters. -     * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; -     * var compiled = _.template('hello {{ user }}!'); -     * compiled({ 'user': 'mustache' }); -     * // => 'hello mustache!' -     *       * // Use the `source` property to inline compiled templates for meaningful       * // line numbers in error messages and stack traces. -     * fs.writeFileSync(path.join(process.cwd(), 'jst.js'), '\ +     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\       *   var JST = {\       *     "main": ' + _.template(mainText).source + '\       *   };\ @@ -50277,7 +50175,7 @@ return jQuery;       * @since 4.0.0       * @category Util       * @param {Array} pairs The predicate-function pairs. -     * @returns {Function} Returns the new composite function. +     * @returns {Function} Returns the new function.       * @example       *       * var func = _.cond([ @@ -50327,7 +50225,7 @@ return jQuery;       * @since 4.0.0       * @category Util       * @param {Object} source The object of property predicates to conform to. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       * @example       *       * var users = [ @@ -50350,7 +50248,7 @@ return jQuery;       * @since 2.4.0       * @category Util       * @param {*} value The value to return from the new function. -     * @returns {Function} Returns the new constant function. +     * @returns {Function} Returns the new function.       * @example       *       * var object = { 'user': 'fred' }; @@ -50375,7 +50273,7 @@ return jQuery;       * @since 3.0.0       * @category Util       * @param {...(Function|Function[])} [funcs] Functions to invoke. -     * @returns {Function} Returns the new composite function. +     * @returns {Function} Returns the new function.       * @see _.flowRight       * @example       * @@ -50398,7 +50296,7 @@ return jQuery;       * @memberOf _       * @category Util       * @param {...(Function|Function[])} [funcs] Functions to invoke. -     * @returns {Function} Returns the new composite function. +     * @returns {Function} Returns the new function.       * @see _.flow       * @example       * @@ -50491,7 +50389,7 @@ return jQuery;       * @since 3.0.0       * @category Util       * @param {Object} source The object of property values to match. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       * @example       *       * var users = [ @@ -50519,7 +50417,7 @@ return jQuery;       * @category Util       * @param {Array|string} path The path of the property to get.       * @param {*} srcValue The value to match. -     * @returns {Function} Returns the new spec function. +     * @returns {Function} Returns the new function.       * @example       *       * var users = [ @@ -50544,7 +50442,7 @@ return jQuery;       * @category Util       * @param {Array|string} path The path of the method to invoke.       * @param {...*} [args] The arguments to invoke the method with. -     * @returns {Function} Returns the new invoker function. +     * @returns {Function} Returns the new function.       * @example       *       * var objects = [ @@ -50575,7 +50473,7 @@ return jQuery;       * @category Util       * @param {Object} object The object to query.       * @param {...*} [args] The arguments to invoke the method with. -     * @returns {Function} Returns the new invoker function. +     * @returns {Function} Returns the new function.       * @example       *       * var array = _.times(3, _.constant), @@ -50705,7 +50603,7 @@ return jQuery;      }      /** -     * Creates a function that gets the argument at `n` index. If `n` is negative, +     * Creates a function that returns its nth argument. If `n` is negative,       * the nth argument from the end is returned.       *       * @static @@ -50713,7 +50611,7 @@ return jQuery;       * @since 4.0.0       * @category Util       * @param {number} [n=0] The index of the argument to return. -     * @returns {Function} Returns the new pass-thru function. +     * @returns {Function} Returns the new function.       * @example       *       * var func = _.nthArg(1); @@ -50811,7 +50709,7 @@ return jQuery;       * @since 2.4.0       * @category Util       * @param {Array|string} path The path of the property to get. -     * @returns {Function} Returns the new accessor function. +     * @returns {Function} Returns the new function.       * @example       *       * var objects = [ @@ -50838,7 +50736,7 @@ return jQuery;       * @since 3.0.0       * @category Util       * @param {Object} object The object to query. -     * @returns {Function} Returns the new accessor function. +     * @returns {Function} Returns the new function.       * @example       *       * var array = [0, 1, 2], @@ -50872,7 +50770,7 @@ return jQuery;       * @param {number} [start=0] The start of the range.       * @param {number} end The end of the range.       * @param {number} [step=1] The value to increment or decrement by. -     * @returns {Array} Returns the range of numbers. +     * @returns {Array} Returns the new array of numbers.       * @see _.inRange, _.rangeRight       * @example       * @@ -50910,7 +50808,7 @@ return jQuery;       * @param {number} [start=0] The start of the range.       * @param {number} end The end of the range.       * @param {number} [step=1] The value to increment or decrement by. -     * @returns {Array} Returns the range of numbers. +     * @returns {Array} Returns the new array of numbers.       * @see _.inRange, _.range       * @example       * @@ -51671,7 +51569,6 @@ return jQuery;      lodash.sumBy = sumBy;      lodash.template = template;      lodash.times = times; -    lodash.toFinite = toFinite;      lodash.toInteger = toInteger;      lodash.toLength = toLength;      lodash.toLower = toLower; diff --git a/netlib/tcp.py b/netlib/tcp.py index 914aa701..de12102e 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -6,6 +6,7 @@ import sys  import threading  import time  import traceback +import contextlib  import binascii  from six.moves import range @@ -577,6 +578,12 @@ class _Connection(object):          return context +@contextlib.contextmanager +def _closer(client): +    yield +    client.close() + +  class TCPClient(_Connection):      def __init__(self, address, source_address=None): @@ -708,6 +715,7 @@ class TCPClient(_Connection):          self.connection = connection          self.ip_address = Address(connection.getpeername())          self._makefile() +        return _closer(self)      def settimeout(self, n):          self.connection.settimeout(n) @@ -833,6 +841,25 @@ class BaseHandler(_Connection):              return b"" +class Counter: +    def __init__(self): +        self._count = 0 +        self._lock = threading.Lock() + +    @property +    def count(self): +        with self._lock: +            return self._count + +    def __enter__(self): +        with self._lock: +            self._count += 1 + +    def __exit__(self, *args): +        with self._lock: +            self._count -= 1 + +  class TCPServer(object):      request_queue_size = 20 @@ -845,15 +872,17 @@ class TCPServer(object):          self.socket.bind(self.address())          self.address = Address.wrap(self.socket.getsockname())          self.socket.listen(self.request_queue_size) +        self.handler_counter = Counter()      def connection_thread(self, connection, client_address): -        client_address = Address(client_address) -        try: -            self.handle_client_connection(connection, client_address) -        except: -            self.handle_error(connection, client_address) -        finally: -            close_socket(connection) +        with self.handler_counter: +            client_address = Address(client_address) +            try: +                self.handle_client_connection(connection, client_address) +            except: +                self.handle_error(connection, client_address) +            finally: +                close_socket(connection)      def serve_forever(self, poll_interval=0.1):          self.__is_shut_down.clear() diff --git a/pathod/language/generators.py b/pathod/language/generators.py index a17e7052..9fff3082 100644 --- a/pathod/language/generators.py +++ b/pathod/language/generators.py @@ -2,17 +2,19 @@ import string  import random  import mmap +import six +  DATATYPES = dict( -    ascii_letters=string.ascii_letters, -    ascii_lowercase=string.ascii_lowercase, -    ascii_uppercase=string.ascii_uppercase, -    digits=string.digits, -    hexdigits=string.hexdigits, -    octdigits=string.octdigits, -    punctuation=string.punctuation, -    whitespace=string.whitespace, -    ascii=string.printable, -    bytes="".join(chr(i) for i in range(256)) +    ascii_letters=string.ascii_letters.encode(), +    ascii_lowercase=string.ascii_lowercase.encode(), +    ascii_uppercase=string.ascii_uppercase.encode(), +    digits=string.digits.encode(), +    hexdigits=string.hexdigits.encode(), +    octdigits=string.octdigits.encode(), +    punctuation=string.punctuation.encode(), +    whitespace=string.whitespace.encode(), +    ascii=string.printable.encode(), +    bytes=bytes(bytearray(range(256)))  ) @@ -35,16 +37,25 @@ class TransformGenerator(object):      def __getitem__(self, x):          d = self.gen.__getitem__(x) +        if isinstance(x, slice): +            return self.transform(x.start, d)          return self.transform(x, d) -    def __getslice__(self, a, b): -        d = self.gen.__getslice__(a, b) -        return self.transform(a, d) -      def __repr__(self):          return "'transform(%s)'" % self.gen +def rand_byte(chars): +    """ +        Return a random character as byte from a charset. +    """ +    # bytearray has consistent behaviour on both Python 2 and 3 +    # while bytes does not +    if six.PY2: +        return random.choice(chars) +    return bytes([random.choice(chars)]) + +  class RandomGenerator(object):      def __init__(self, dtype, length): @@ -55,12 +66,10 @@ class RandomGenerator(object):          return self.length      def __getitem__(self, x): -        return random.choice(DATATYPES[self.dtype]) - -    def __getslice__(self, a, b): -        b = min(b, self.length)          chars = DATATYPES[self.dtype] -        return "".join(random.choice(chars) for x in range(a, b)) +        if isinstance(x, slice): +            return b"".join(rand_byte(chars) for _ in range(*x.indices(self.length))) +        return rand_byte(chars)      def __repr__(self):          return "%s random from %s" % (self.length, self.dtype) @@ -70,17 +79,17 @@ class FileGenerator(object):      def __init__(self, path):          self.path = path -        self.fp = file(path, "rb") +        self.fp = open(path, "rb")          self.map = mmap.mmap(self.fp.fileno(), 0, access=mmap.ACCESS_READ)      def __len__(self):          return len(self.map)      def __getitem__(self, x): -        return self.map.__getitem__(x) - -    def __getslice__(self, a, b): -        return self.map.__getslice__(a, b) +        if isinstance(x, slice): +            return self.map.__getitem__(x) +        # A slice of length 1 returns a byte object (not an integer) +        return self.map.__getitem__(slice(x, x + 1 or self.map.size()))      def __repr__(self):          return "<%s" % self.path diff --git a/pathod/pathoc.py b/pathod/pathoc.py index 2b7d053c..5cfb4591 100644 --- a/pathod/pathoc.py +++ b/pathod/pathoc.py @@ -286,7 +286,7 @@ class Pathoc(tcp.TCPClient):          if self.use_http2 and not self.ssl:              raise NotImplementedError("HTTP2 without SSL is not supported.") -        tcp.TCPClient.connect(self) +        ret = tcp.TCPClient.connect(self)          if connect_to:              self.http_connect(connect_to) @@ -324,6 +324,7 @@ class Pathoc(tcp.TCPClient):          if self.timeout:              self.settimeout(self.timeout) +        return ret      def stop(self):          if self.ws_framereader: diff --git a/pathod/pathod.py b/pathod/pathod.py index 7795df0e..0449c0c1 100644 --- a/pathod/pathod.py +++ b/pathod/pathod.py @@ -353,6 +353,8 @@ class Pathod(tcp.TCPServer):              staticdir=self.staticdir          ) +        self.loglock = threading.Lock() +      def check_policy(self, req, settings):          """              A policy check that verifies the request size is within limits. @@ -403,8 +405,7 @@ class Pathod(tcp.TCPServer):      def add_log(self, d):          if not self.noapi: -            lock = threading.Lock() -            with lock: +            with self.loglock:                  d["id"] = self.logid                  self.log.insert(0, d)                  if len(self.log) > self.LOGBUF: @@ -413,17 +414,18 @@ class Pathod(tcp.TCPServer):              return d["id"]      def clear_log(self): -        lock = threading.Lock() -        with lock: +        with self.loglock:              self.log = []      def log_by_id(self, identifier): -        for i in self.log: -            if i["id"] == identifier: -                return i +        with self.loglock: +            for i in self.log: +                if i["id"] == identifier: +                    return i      def get_log(self): -        return self.log +        with self.loglock: +            return self.log  def main(args):  # pragma: no cover diff --git a/pathod/protocols/websockets.py b/pathod/protocols/websockets.py index 134d27bc..2b60e618 100644 --- a/pathod/protocols/websockets.py +++ b/pathod/protocols/websockets.py @@ -18,7 +18,7 @@ class WebsocketsProtocol:                      frm = websockets.Frame.from_file(self.pathod_handler.rfile)                  except NetlibException as e:                      lg("Error reading websocket frame: %s" % e) -                    break +                    return None, None                  ended = time.time()                  lg(frm.human_readable())              retlog = dict( diff --git a/pathod/test.py b/pathod/test.py index 23b7a5b6..11462729 100644 --- a/pathod/test.py +++ b/pathod/test.py @@ -1,12 +1,14 @@  from six.moves import cStringIO as StringIO  import threading +import time +  from six.moves import queue -import requests -import requests.packages.urllib3  from . import pathod -requests.packages.urllib3.disable_warnings() + +class TimeoutError(Exception): +    pass  class Daemon: @@ -39,39 +41,51 @@ class Daemon:          """          return "%s/p/%s" % (self.urlbase, spec) -    def info(self): -        """ -            Return some basic info about the remote daemon. -        """ -        resp = requests.get("%s/api/info" % self.urlbase, verify=False) -        return resp.json() -      def text_log(self):          return self.logfp.getvalue() +    def wait_for_silence(self, timeout=5): +        start = time.time() +        while 1: +            if time.time() - start >= timeout: +                raise TimeoutError( +                    "%s service threads still alive" % +                    self.thread.server.handler_counter.count +                ) +            if self.thread.server.handler_counter.count == 0: +                return + +    def expect_log(self, n, timeout=5): +        l = [] +        start = time.time() +        while True: +            l = self.log() +            if time.time() - start >= timeout: +                return None +            if len(l) >= n: +                break +        return l +      def last_log(self):          """              Returns the last logged request, or None.          """ -        l = self.log() +        l = self.expect_log(1)          if not l:              return None -        return l[0] +        return l[-1]      def log(self):          """              Return the log buffer as a list of dictionaries.          """ -        resp = requests.get("%s/api/log" % self.urlbase, verify=False) -        return resp.json()["log"] +        return self.thread.server.get_log()      def clear_log(self):          """              Clear the log.          """ -        self.logfp.truncate(0) -        resp = requests.get("%s/api/clear_log" % self.urlbase, verify=False) -        return resp.ok +        return self.thread.server.clear_log()      def shutdown(self):          """ @@ -88,6 +102,7 @@ class _PaThread(threading.Thread):          self.name = "PathodThread"          self.iface, self.q, self.ssl = iface, q, ssl          self.daemonargs = daemonargs +        self.server = None      def run(self):          self.server = pathod.Pathod( @@ -11,8 +11,7 @@ addopts = --capture=no  [coverage:run]  branch = True -include = *mitmproxy*, *netlib*, *pathod* -omit = *contrib*, *tnetstring*, *platform*, *console*, *main.py +omit = *contrib*, *tnetstring*, *platform*, *main.py  [coverage:report]  show_missing = True diff --git a/test/pathod/test_app.py b/test/pathod/test_app.py index ac89c44c..fbaa773c 100644 --- a/test/pathod/test_app.py +++ b/test/pathod/test_app.py @@ -11,11 +11,11 @@ class TestApp(tutils.DaemonTests):      def test_about(self):          r = self.getpath("/about") -        assert r.ok +        assert r.status_code == 200      def test_download(self):          r = self.getpath("/download") -        assert r.ok +        assert r.status_code == 200      def test_docs(self):          assert self.getpath("/docs/pathod").status_code == 200 @@ -27,7 +27,7 @@ class TestApp(tutils.DaemonTests):      def test_log(self):          assert self.getpath("/log").status_code == 200          assert self.get("200:da").status_code == 200 -        id = self.d.log()[0]["id"] +        id = self.d.expect_log(1)[0]["id"]          assert self.getpath("/log").status_code == 200          assert self.getpath("/log/%s" % id).status_code == 200          assert self.getpath("/log/9999999").status_code == 404 diff --git a/test/pathod/test_language_generators.py b/test/pathod/test_language_generators.py index 0fceae85..51f55991 100644 --- a/test/pathod/test_language_generators.py +++ b/test/pathod/test_language_generators.py @@ -7,24 +7,27 @@ import tutils  def test_randomgenerator():      g = generators.RandomGenerator("bytes", 100)      assert repr(g) +    assert g[0] +    assert len(g[0]) == 1      assert len(g[:10]) == 10      assert len(g[1:10]) == 9      assert len(g[:1000]) == 100      assert len(g[1000:1001]) == 0 -    assert g[0]  def test_filegenerator():      with tutils.tmpdir() as t:          path = os.path.join(t, "foo")          f = open(path, "wb") -        f.write("x" * 10000) +        f.write(b"x" * 10000)          f.close()          g = generators.FileGenerator(path)          assert len(g) == 10000 -        assert g[0] == "x" -        assert g[-1] == "x" -        assert g[0:5] == "xxxxx" +        assert g[0] == b"x" +        assert g[-1] == b"x" +        assert g[0:5] == b"xxxxx" +        assert len(g[1:10]) == 9 +        assert len(g[10000:10001]) == 0          assert repr(g)          # remove all references to FileGenerator instance to close the file          # handle. diff --git a/test/pathod/test_pathod.py b/test/pathod/test_pathod.py index 4d969158..ec9c169f 100644 --- a/test/pathod/test_pathod.py +++ b/test/pathod/test_pathod.py @@ -1,7 +1,6 @@  from six.moves import cStringIO as StringIO -import pytest -from pathod import pathod, version +from pathod import pathod  from netlib import tcp  from netlib.exceptions import HttpException, TlsException  import tutils @@ -129,7 +128,6 @@ class CommonTests(tutils.DaemonTests):          assert self.d.last_log()          # FIXME: Other binary data elements -    @pytest.mark.skip(reason="race condition")      def test_sizelimit(self):          r = self.get("200:b@1g")          assert r.status_code == 800 @@ -140,21 +138,15 @@ class CommonTests(tutils.DaemonTests):          r, _ = self.pathoc([r"get:'/p/200':i0,'\r\n'"])          assert r[0].status_code == 200 -    def test_info(self): -        assert tuple(self.d.info()["version"]) == version.IVERSION - -    @pytest.mark.skip(reason="race condition")      def test_logs(self): -        assert self.d.clear_log() -        assert not self.d.last_log() +        self.d.clear_log()          assert self.get("202:da") -        assert len(self.d.log()) == 1 -        assert self.d.clear_log() +        assert self.d.expect_log(1) +        self.d.clear_log()          assert len(self.d.log()) == 0      def test_disconnect(self): -        rsp = self.get("202:b@100k:d200") -        assert len(rsp.content) < 200 +        tutils.raises("unexpected eof", self.get, "202:b@100k:d200")      def test_parserr(self):          rsp = self.get("400:msg,b:") @@ -166,7 +158,7 @@ class CommonTests(tutils.DaemonTests):          assert rsp.content.strip() == "testfile"      def test_anchor(self): -        rsp = self.getpath("anchor/foo") +        rsp = self.getpath("/anchor/foo")          assert rsp.status_code == 202      def test_invalid_first_line(self): @@ -223,7 +215,6 @@ class CommonTests(tutils.DaemonTests):          )          assert r[1].payload == "test" -    @pytest.mark.skip(reason="race condition")      def test_websocket_frame_reflect_error(self):          r, _ = self.pathoc(              ["ws:/p/", "wf:-mask:knone:f'wf:b@10':i13,'a'"], @@ -233,7 +224,6 @@ class CommonTests(tutils.DaemonTests):          # FIXME: Race Condition?          assert "Parse error" in self.d.text_log() -    @pytest.mark.skip(reason="race condition")      def test_websocket_frame_disconnect_error(self):          self.pathoc(["ws:/p/", "wf:b@10:d3"], ws_read_limit=0)          assert self.d.last_log() diff --git a/test/pathod/test_test.py b/test/pathod/test_test.py index cee286a4..6399894e 100644 --- a/test/pathod/test_test.py +++ b/test/pathod/test_test.py @@ -2,6 +2,10 @@ import logging  import requests  from pathod import test  import tutils + +import requests.packages.urllib3 + +requests.packages.urllib3.disable_warnings()  logging.disable(logging.CRITICAL) diff --git a/test/pathod/tutils.py b/test/pathod/tutils.py index f7bb22e5..b9f38d86 100644 --- a/test/pathod/tutils.py +++ b/test/pathod/tutils.py @@ -3,6 +3,7 @@ import re  import shutil  import requests  from six.moves import cStringIO as StringIO +import urllib  from netlib import tcp  from netlib import utils @@ -63,10 +64,11 @@ class DaemonTests(object):          shutil.rmtree(cls.confdir)      def teardown(self): +        self.d.wait_for_silence()          if not (self.noweb or self.noapi):              self.d.clear_log() -    def getpath(self, path, params=None): +    def _getpath(self, path, params=None):          scheme = "https" if self.ssl else "http"          resp = requests.get(              "%s://localhost:%s/%s" % ( @@ -79,9 +81,29 @@ class DaemonTests(object):          )          return resp +    def getpath(self, path, params=None): +        logfp = StringIO() +        c = pathoc.Pathoc( +            ("localhost", self.d.port), +            ssl=self.ssl, +            fp=logfp, +        ) +        with c.connect(): +            if params: +                path = path + "?" + urllib.urlencode(params) +            resp = c.request("get:%s" % path) +            return resp +      def get(self, spec): -        resp = requests.get(self.d.p(spec), verify=False) -        return resp +        logfp = StringIO() +        c = pathoc.Pathoc( +            ("localhost", self.d.port), +            ssl=self.ssl, +            fp=logfp, +        ) +        with c.connect(): +            resp = c.request("get:/p/%s" % urllib.quote(spec).encode("string_escape")) +            return resp      def pathoc(          self, @@ -106,16 +128,16 @@ class DaemonTests(object):              fp=logfp,              use_http2=use_http2,          ) -        c.connect(connect_to) -        ret = [] -        for i in specs: -            resp = c.request(i) -            if resp: -                ret.append(resp) -        for frm in c.wait(): -            ret.append(frm) -        c.stop() -        return ret, logfp.getvalue() +        with c.connect(connect_to): +            ret = [] +            for i in specs: +                resp = c.request(i) +                if resp: +                    ret.append(resp) +            for frm in c.wait(): +                ret.append(frm) +            c.stop() +            return ret, logfp.getvalue()  tmpdir = tutils.tmpdir diff --git a/web/.babelrc b/web/.babelrc index 5dd7708c..e2d67e33 100644 --- a/web/.babelrc +++ b/web/.babelrc @@ -1,4 +1,4 @@  {    "presets": ["es2015", "react"], -  "plugins": ["transform-class-properties"] +  "plugins": ["transform-class-properties", "transform-object-rest-spread"]  }
\ No newline at end of file diff --git a/web/package.json b/web/package.json index 2eaac445..88e976e1 100644 --- a/web/package.json +++ b/web/package.json @@ -30,6 +30,7 @@      "babel-core": "^6.7.7",      "babel-jest": "^12.0.2",      "babel-plugin-transform-class-properties": "^6.6.0", +    "babel-plugin-transform-object-rest-spread": "^6.8.0",      "babel-preset-es2015": "^6.6.0",      "babel-preset-react": "^6.5.0",      "babelify": "^7.3.0",  | 
