diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..497e913 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +node_modules +dist +web +web2 +resource +*.sh +docs \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..1b7b3f1 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,75 @@ +{ + "extends": "airbnb", + "parser": "babel-eslint", + "env": { + "browser": true, + "node": true, + "es6": true, + "jasmine": true + }, + "globals": { + "React": true, + "ReactDOM": true, + "Zepto": true, + "JsBridgeUtil": true + }, + "rules": { + "semi": [0], + "comma-dangle": [0], + "global-require": [0], + "no-alert": [0], + "no-console": [0], + "no-param-reassign": [0], + "max-len": [0], + "func-names": [0], + "no-underscore-dangle": [0], + "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": false }], + "object-shorthand": [0], + "arrow-body-style": [0], + "no-new": [0], + "strict": [0], + "no-script-url": [0], + "spaced-comment": [0], + "no-empty": [0], + "no-constant-condition": [0], + "no-else-return": [0], + "no-use-before-define": [0], + "no-unused-expressions": [0], + "no-class-assign": [0], + "new-cap": [0], + "array-callback-return": [0], + "prefer-template": [0], + "no-restricted-syntax": [0], + "no-trailing-spaces": [0], + "import/no-unresolved": [0], + "jsx-a11y/img-has-alt": [0], + "camelcase": [0], + "consistent-return": [0], + "guard-for-in": [0], + "one-var": [0], + "react/wrap-multilines": [0], + "react/no-multi-comp": [0], + "react/jsx-no-bind": [0], + "react/prop-types": [0], + "react/prefer-stateless-function": [0], + "react/jsx-first-prop-new-line": [0], + "react/sort-comp": [0], + "import/no-extraneous-dependencies": [0], + "import/extensions": [0], + "react/forbid-prop-types": [0], + "react/require-default-props": [0], + "class-methods-use-this": [0], + "jsx-a11y/no-static-element-interactions": [0], + "react/no-did-mount-set-state": [0], + "jsx-a11y/alt-text": [0], + "import/no-dynamic-require": [0], + "no-extra-boolean-cast": [0], + "no-lonely-if": [0], + "no-plusplus": [0], + "generator-star-spacing": ["error", {"before": true, "after": false}], + "require-yield": [0], + "arrow-parens": [0], + "no-template-curly-in-string": [0], + "no-mixed-operators": [0] + } +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 5d96e44..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,245 +0,0 @@ -module.exports = { - // http://eslint.org/docs/rules/ - - "ecmaFeatures": { - "arrowFunctions": false, // enable arrow functions - "binaryLiterals": false, // enable binary literals - "blockBindings": false, // enable let and const (aka block bindings) - "classes": false, // enable classes - "defaultParams": false, // enable default function parameters - "destructuring": false, // enable destructuring - "forOf": false, // enable for-of loops - "generators": false, // enable generators - "modules": false, // enable modules and global strict mode - "objectLiteralComputedProperties": false, // enable computed object literal property names - "objectLiteralDuplicateProperties": false, // enable duplicate object literal properties in strict mode - "objectLiteralShorthandMethods": false, // enable object literal shorthand methods - "objectLiteralShorthandProperties": false, // enable object literal shorthand properties - "octalLiterals": false, // enable octal literals - "regexUFlag": false, // enable the regular expression u flag - "regexYFlag": false, // enable the regular expression y flag - "restParams": false, // enable the rest parameters - "spread": false, // enable the spread operator - "superInFunctions": false, // enable super references inside of functions - "templateStrings": false, // enable template strings - "unicodeCodePointEscapes": false, // enable code point escapes - "globalReturn": false, // allow return statements in the global scope - "jsx": false // enable JSX - }, - "parser": "babel-eslint", - - "env": { - "browser": true, // browser global variables. - "node": false, // Node.js global variables and Node.js-specific rules. - "worker": false, // web workers global variables. - "amd": true, // defines require() and define() as global variables as per the amd spec. - "mocha": false, // adds all of the Mocha testing global variables. - "jasmine": true, // adds all of the Jasmine testing global variables for version 1.3 and 2.0. - "phantomjs": false, // phantomjs global variables. - "jquery": false, // jquery global variables. - "prototypejs": false, // prototypejs global variables. - "shelljs": false, // shelljs global variables. - "meteor": false, // meteor global variables. - "mongo": false, // mongo global variables. - "applescript": false, // applescript global variables. - "es6": true, // enable all ECMAScript 6 features except for modules. - }, - - "globals": { - "goog": true, - "module": true, - "exports": true, - "__dirname": true, - "process": true - }, - - "plugins": [ - // e.g. "react" (must run `npm install eslint-plugin-react` first) - ], - - "rules": { - - // Possible Errors - "comma-dangle": 0, // disallow trailing commas in object literals - "no-cond-assign": 0, // disallow assignment in conditional expressions - "no-console": 0, // disallow use of console (off by default in the node environment) - "no-constant-condition": 0, // disallow use of constant expressions in conditions - "no-control-regex": 0, // disallow control characters in regular expressions - "no-debugger": 0, // disallow use of debugger - "no-dupe-args": 0, // disallow duplicate arguments in functions - "no-dupe-keys": 0, // disallow duplicate keys when creating object literals - "no-duplicate-case": 0, // disallow a duplicate case label - "no-empty-character-class": 0, // disallow the use of empty character classes in regular expressions - "no-empty": 0, // disallow empty statements - "no-ex-assign": 0, // disallow assigning to the exception in a catch block - "no-extra-boolean-cast": 0, // disallow double-negation boolean casts in a boolean context - "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) - "no-extra-semi": 1, // disallow unnecessary semicolons - "no-func-assign": 0, // disallow overwriting functions written as function declarations - "no-inner-declarations": 2, // disallow function or variable declarations in nested blocks - "no-invalid-regexp": 0, // disallow invalid regular expression strings in the RegExp constructor - "no-irregular-whitespace": 0, // disallow irregular whitespace outside of strings and comments - "no-negated-in-lhs": 0, // disallow negation of the left operand of an in expression - "no-obj-calls": 0, // disallow the use of object properties of the global object (Math and JSON) as functions - "no-regex-spaces": 0, // disallow multiple spaces in a regular expression literal - "no-reserved-keys": 0, // disallow reserved words being used as object literal keys (off by default) - "no-sparse-arrays": 0, // disallow sparse arrays - "no-unreachable": 0, // disallow unreachable statements after a return, throw, continue, or break statement - "use-isnan": 0, // disallow comparisons with the value NaN - "valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default) - "valid-typeof": 0, // Ensure that the results of typeof are compared against a valid string - "no-unexpected-multiline": 0, // Avoid code that looks like two expressions but is actually one (off by default) - - - // Best Practices - "accessor-pairs": 0, // enforces getter/setter pairs in objects (off by default) - "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default) - "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) - "consistent-return": 0, // require return statements to either always or never specify values - "curly": 2, // specify curly brace conventions for all control statements - "default-case": 0, // require default case in switch statements (off by default) - "dot-notation": 0, // encourages use of dot notation whenever possible - "dot-location": 0, // enforces consistent newlines before or after dots (off by default) - "eqeqeq": 0, // require the use of === and !== - "guard-for-in": 0, // make sure for-in loops have an if statement (off by default) - "no-alert": 0, // disallow the use of alert, confirm, and prompt - "no-caller": 0, // disallow use of arguments.caller or arguments.callee - "no-div-regex": 0, // disallow division operators explicitly at beginning of regular expression (off by default) - "no-else-return": 0, // disallow else after a return in an if (off by default) - "no-empty-label": 0, // disallow use of labels for anything other then loops and switches - "no-eq-null": 0, // disallow comparisons to null without a type-checking operator (off by default) - "no-eval": 2, // disallow use of eval() - "no-extend-native": 2, // disallow adding to native types - "no-extra-bind": 0, // disallow unnecessary function binding - "no-fallthrough": 0, // disallow fallthrough of case statements - "no-floating-decimal": 0, // disallow the use of leading or trailing decimal points in numeric literals (off by default) - "no-implied-eval": 0, // disallow use of eval()-like methods - "no-iterator": 0, // disallow usage of __iterator__ property - "no-labels": 0, // disallow use of labeled statements - "no-lone-blocks": 0, // disallow unnecessary nested blocks - "no-loop-func": 0, // disallow creation of functions within loops - "no-multi-spaces": 0, // disallow use of multiple spaces - "no-multi-str": 0, // disallow use of multiline strings - "no-native-reassign": 0, // disallow reassignments of native objects - "no-new-func": 0, // disallow use of new operator for Function object - "no-new-wrappers": 2, // disallows creating new instances of String, Number, and Boolean - "no-new": 0, // disallow use of new operator when not part of the assignment or comparison - "no-octal-escape": 0, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; - "no-octal": 0, // disallow use of octal literals - "no-param-reassign": 0, // disallow reassignment of function parameters (off by default) - "no-process-env": 0, // disallow use of process.env (off by default) - "no-proto": 0, // disallow usage of __proto__ property - "no-redeclare": 0, // disallow declaring the same variable more then once - "no-return-assign": 0, // disallow use of assignment in return statement - "no-script-url": 0, // disallow use of javascript: urls - "no-self-compare": 0, // disallow comparisons where both sides are exactly the same (off by default) - "no-sequences": 0, // disallow use of comma operator - "no-throw-literal": 0, // restrict what can be thrown as an exception (off by default) - "no-unused-expressions": 0, // disallow usage of expressions in statement position - "no-void": 0, // disallow use of void operator (off by default) - "no-warning-comments": 0, // disallow usage of configurable warning terms in comments, e.g. TODO or FIXME (off by default) - "no-with": 2, // disallow use of the with statement - "radix": 0, // require use of the second argument for parseInt() (off by default) - "vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default) - "wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default) - "yoda": 0, // require or disallow Yoda conditions - - - // Strict Mode - "strict": 0, // controls location of Use Strict Directives - - - // Variables - "no-catch-shadow": 0, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) - "no-delete-var": 0, // disallow deletion of variables - "no-label-var": 0, // disallow labels that share a name with a variable - "no-shadow": 0, // disallow declaration of variables already declared in the outer scope - "no-shadow-restricted-names": 0, // disallow shadowing of names such as arguments - "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block - "no-undef-init": 0, // disallow use of undefined when initializing variables - "no-undefined": 0, // disallow use of undefined variable (off by default) - "no-unused-vars": 0, // disallow declaration of variables that are not used in the code - "no-use-before-define": 0, // disallow use of variables before they are defined - - - // Node.js - "handle-callback-err": 0, // enforces error handling in callbacks (off by default) (on by default in the node environment) - "no-mixed-requires": 0, // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment) - "no-new-require": 0, // disallow use of new operator with the require function (off by default) (on by default in the node environment) - "no-path-concat": 0, // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment) - "no-process-exit": 0, // disallow process.exit() (on by default in the node environment) - "no-restricted-modules": 0, // restrict usage of specified node modules (off by default) - "no-sync": 0, // disallow use of synchronous methods (off by default) - - - // Stylistic Issues - "array-bracket-spacing": [2, "never"], // enforce spacing inside array brackets (off by default) - "brace-style": 0, // enforce one true brace style (off by default) - "camelcase": 0, // require camel case names - "comma-spacing": 0, // enforce spacing before and after comma - "comma-style": 0, // enforce one true comma style (off by default) - "computed-property-spacing": 0, // require or disallow padding inside computed properties (off by default) - "consistent-this": 0, // enforces consistent naming when capturing the current execution context (off by default) - "eol-last": 0, // enforce newline at the end of file, with no multiple empty lines - "func-names": 0, // require function expressions to have a name (off by default) - "func-style": 0, // enforces use of function declarations or expressions (off by default) - "indent": [2, 4], // this option sets a specific tab width for your code (off by default) - "key-spacing": 0, // enforces spacing between keys and values in object literal properties - "lines-around-comment": 0, // enforces empty lines around comments (off by default) - "linebreak-style": 0, // disallow mixed 'LF' and 'CRLF' as linebreaks (off by default) - "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default) - "new-cap": 0, // require a capital letter for constructors - "new-parens": 0, // disallow the omission of parentheses when invoking a constructor with no arguments - "new-parens": 0, // disallow the omission of parentheses when invoking a constructor with no arguments - "newline-after-var": 0, // allow/disallow an empty newline after var statement (off by default) - "no-array-constructor": 2, // disallow use of the Array constructor - "no-continue": 0, // disallow use of the continue statement (off by default) - "no-inline-comments": 0, // disallow comments inline after code (off by default) - "no-lonely-if": 0, // disallow if as the only statement in an else block (off by default) - "no-mixed-spaces-and-tabs": 2, // disallow mixed spaces and tabs for indentation - "no-multiple-empty-lines": 0, // disallow multiple empty lines (off by default) - "no-nested-ternary": 0, // disallow nested ternary expressions (off by default) - "no-new-object": 2, // disallow use of the Object constructor - "no-spaced-func": 0, // disallow space between function identifier and application - "no-ternary": 0, // disallow the use of ternary operators (off by default) - "no-trailing-spaces": 0, // disallow trailing whitespace at the end of lines - "no-underscore-dangle": 0, // disallow dangling underscores in identifiers - "object-curly-spacing": [2, "always"], // require or disallow padding inside curly braces (off by default) - "one-var": 0, // allow just one var statement per function (off by default) - "operator-assignment": 0, // require assignment operator shorthand where possible or prohibit it entirely (off by default) - "operator-linebreak": 0, // enforce operators to be placed before or after line breaks (off by default) - "padded-blocks": 0, // enforce padding within blocks (off by default) - "quote-props": 0, // require quotes around object literal property names (off by default) - "quotes": 0, // specify whether double or single quotes should be used - "semi-spacing": 0, // enforce spacing before and after semicolons - "semi": [2, "always"], // require or disallow use of semicolons instead of ASI - "sort-vars": 0, // sort variables within the same declaration block (off by default) - "space-after-keywords": 0, // require a space after certain keywords (off by default) - "space-before-blocks": 0, // require or disallow space before blocks (off by default) - "space-before-function-paren": 0, // require or disallow space before function opening parenthesis (off by default) - "space-in-parens": 0, // require or disallow spaces inside parentheses (off by default) - "space-infix-ops": 0, // require spaces around operators - "space-return-throw-case": 0, // require a space after return, throw, and case - "space-unary-ops": 0, // require or disallow spaces before/after unary operators (words on by default, nonwords off by default) - "spaced-comment": 0, // require or disallow a space immediately following the // or /* in a comment (off by default) - "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) - - - // ECMAScript 6 - "constructor-super": 0, // verify super() callings in constructors (off by default) - "generator-star-spacing": 0, // enforce the spacing around the * in generator functions (off by default) - "no-this-before-super": 0, // disallow to use this/super before super() calling in constructors (off by default) - "no-var": 0, // require let or const instead of var (off by default) - "object-shorthand": 0, // require method and property shorthand syntax for object literals (off by default) - "prefer-const": 0, // suggest using of const declaration for variables that are never modified after declared (off by default) - - - // Legacy - "max-depth": 0, // specify the maximum depth that blocks can be nested (off by default) - "max-len": [1, 120, 2], // specify the maximum length of a line in your program (off by default) - "max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default) - "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) - "no-bitwise": 0, // disallow use of bitwise operators (off by default) - "no-plusplus": 0 // disallow use of unary operators, ++ and -- (off by default) - } -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2fa5ecd..f751563 100644 --- a/.gitignore +++ b/.gitignore @@ -3,15 +3,18 @@ cert/**/*.key cert/**/*.crt cert/**/*.csr web/build/.module-cache/ +test/report/*.txt tmp.txt .*.swp ._* .DS_Store +.vscode .git .hg .lock-wscript .svn .wafpickle-* +.vscode CVS npm-debug.log logs @@ -25,4 +28,10 @@ coverage build/Release node_modules .lock-wscript -temp \ No newline at end of file +temp +dist +test/report +*.tgz +doc_compiled.md +docs-md/cn/doc.md +docs-md/en/doc.md \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..ee4cfdf --- /dev/null +++ b/.npmignore @@ -0,0 +1,31 @@ +cert/**/*.srl +cert/**/*.key +cert/**/*.crt +cert/**/*.csr +web/build/.module-cache/ +tmp.txt +.*.swp +._* +.DS_Store +.git +.hg +.lock-wscript +.svn +.wafpickle-* +CVS +npm-debug.log +logs +*.log +pids +*.pid +*.seed +lib-cov +coverage +.grunt +node_modules +.lock-wscript +temp +releases +rule_sample +test +*.tgz \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index b0561c6..e33d013 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +22 Dec 2016: AnyProxy 4.0.0-beta: + + * to AnyProxy rules: all the rule interfaces are asynchronous now, you can write them in a Promise way + * to the UI, rewrite the code and enhance the user experience + 26 Feb 2016: AnyProxy 3.10.4: * let users assign the port for web socket in AnyProxy cli diff --git a/README.md b/README.md index 3c57c93..5d98594 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ AnyProxy -========== - -> AnyProxy 4.x is in beta release now. Rule interface is simplified and Promise is supported. Welcome to have a try! http://anyproxy.io/4.x/ - -> AnyProxy 4.x 正在beta中,rule接口大幅简化,同时支持了Promise。文档及试用:http://anyproxy.io/4.x/ +---------------- [![NPM version][npm-image]][npm-url] [![node version][node-image]][node-url] @@ -11,134 +7,23 @@ AnyProxy [npm-image]: https://img.shields.io/npm/v/anyproxy.svg?style=flat-square [npm-url]: https://npmjs.org/package/anyproxy -[node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square +[node-image]: https://img.shields.io/badge/node.js-%3E=_5.0.0-green.svg?style=flat-square [node-url]: http://nodejs.org/download/ [download-image]: https://img.shields.io/npm/dm/anyproxy.svg?style=flat-square [download-url]: https://npmjs.org/package/anyproxy -A fully configurable HTTP/HTTPS proxy in NodeJS. +AnyProxy is A fully configurable HTTP/HTTPS proxy in NodeJS. Home page : [AnyProxy.io](http://anyproxy.io) -(Chinese in this doc is nothing but translation of some key points. Be relax if you dont understand.) +Issue: https://github.com/alibaba/anyproxy/issues -anyproxy logo +AnyProxy是一个基于NodeJS的,可供插件配置的HTTP/HTTPS代理服务器。 -特性 ------------- -* 支持https明文代理 -* 支持低网速模拟 -* 支持二次开发,可以用javascript控制代理的全部流程,搭建前端个性化调试环境 -* 提供web版界面,观测请求情况 -* 中文文档: - * [代理服务器的新轮子](https://github.com/alibaba/anyproxy/wiki/%E4%BB%A3%E7%90%86%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E6%96%B0%E8%BD%AE%E5%AD%90%EF%BC%9Aanyproxy) ,介绍AnyProxy的设计初衷,以及它的开放式设计可以解决什么问题。 - * [HTTPS相关教程](https://github.com/alibaba/anyproxy/wiki/HTTPS%E7%9B%B8%E5%85%B3%E6%95%99%E7%A8%8B),讲解如何配置AnyProxy的https特性 -* 中文主页:[http://anyproxy.io](http://anyproxy.io/cn) +主页:[AnyProxy.io](http://anyproxy.io) -Feature ------------- -* work as http or https proxy -* fully configurable, you could modify a request at any stage with your customized javascript code -* when working as https proxy, AnyProxy could intercept https requests for any domain without complaint by browser (after you trust its root CA) -* a web interface for you to watch realtime request details, where html with (almost) any charset could be shown correctly +![](https://gw.alipayobjects.com/zos/rmsportal/JoxHUbVhXNedsPUUilnj.gif) -Quick Start --------------- - -### step 1 - install - -* install [NodeJS](http://nodejs.org/) -* ``npm install -g anyproxy`` , may require ``sudo`` -* python is optional, it will be OK if you get some error about it during installing. - -### step 2 - start proxy server - -* start with default settings : ``anyproxy`` -* ...or start with a specific port: ``anyproxy --port 8001`` -* ...start with a rule file: ``anyproxy --rule ./rule_sample/rule_allow_CORS.js`` - -### step 3 - set up proxy settings in your browser , or system ,or mobile devices ,etc - -* default proxy should be 127.0.0.1:8001 , or you_ip_address:8001 on remote devices - -### step 4 - launch web interface - -* visit [http://127.0.0.1:8002](http://127.0.0.1:8002) ,you will see realtime requests in your browser -* be sure to use web interface with modern browsers - -![screenshot](http://gtms01.alicdn.com/tps/i1/TB1IdgqGXXXXXa9apXXLExM2pXX-854-480.gif) - - - -Rule module -------------------- -* Rule module is the specialty for AnyProxy. Think it as a middleware, you could write your own code to hack requests at any stage, no matter it is about to response or the proxy just gets the request. In this way, AnyProxy would be flexible to meet your own demands. - -* It's highly recommended to read this guide before using: [What is rule file and how to write one ?](https://github.com/alibaba/anyproxy/wiki/What-is-rule-file-and-how-to-write-one) - -* An entire scheme of rule file could be found at [./rule_sample/rule__blank.js](https://github.com/alibaba/anyproxy/blob/master/rule_sample/rule__blank.js). Besides, there are some samples at [./rule_sample](https://github.com/alibaba/anyproxy/tree/master/rule_sample). That may help you a lot when writing your own rule files. - - -Https features ---------------- -After configuring rootCA, AnyProxy could help to decrypt https requests, whose approach is also called Man-In-The-Middle(MITM). -A guide about configuring https features is here : [https://github.com/alibaba/anyproxy/wiki/How-to-config-https-proxy](https://github.com/alibaba/anyproxy/wiki/How-to-config-https-proxy) - -HTTPS配置中文教程 : [https://github.com/alibaba/anyproxy/wiki/HTTPS%E7%9B%B8%E5%85%B3%E6%95%99%E7%A8%8B](https://github.com/alibaba/anyproxy/wiki/HTTPS%E7%9B%B8%E5%85%B3%E6%95%99%E7%A8%8B) - - -Others ------------------ - -#### to save request data -* to save request data to local file, use ``` anyproxy --file /path/to/file ``` -* anyproxy uses [nedb](https://github.com/louischatriot/nedb) to save request data. Since NeDB's persistence uses an append-only format, you may get some redundant record in local file. For those dupplicated ones with the same id, just use the lastest line of record. -* [TrafficeRecorder](https://github.com/ottomao/TrafficRecorder) is another tool based on anyproxy to help recording all request data, including header and body. You may have a try. - -#### throttling -* for instance, ``` anyproxy --throttle 10 ``` sets the speed limit to 10kb/s (kbyte/sec) -* this is just a rough throttling for downstream, not for network simulation - -#### work as a module for nodejs -* use it as a module and develop your own proxy. - -``` -npm install anyproxy --save -``` - -```javascript -var proxy = require("anyproxy"); - -//create cert when you want to use https features -//please manually trust this rootCA when it is the first time you run it -!proxy.isRootCAFileExists() && proxy.generateRootCA(); - -var options = { - type : "http", - port : 8001, - hostname : "localhost", - rule : require("path/to/my/ruleModule.js"), - dbFile : null, // optional, save request data to a specified file, will use in-memory db if not specified - webPort : 8002, // optional, port for web interface - socketPort : 8003, // optional, internal port for web socket, replace this when it is conflict with your own service - throttle : 10, // optional, speed limit in kb/s - disableWebInterface : false, //optional, set it when you don't want to use the web interface - setAsGlobalProxy : false, //set anyproxy as your system proxy - silent : false //optional, do not print anything into terminal. do not set it when you are still debugging. -}; -new proxy.proxyServer(options); - -``` - -Contact ------------------ - -* Please feel free to [raise issue](https://github.com/alibaba/anyproxy/issues), or give us some advice. :) -* AnyProxy用户旺旺群已经停止维护,任何疑问可以直接[提issue](https://github.com/alibaba/anyproxy/issues) :) - - -License ------------------ - -* Apache License, Version 2.0 +Legacy doc of version 3.x : https://github.com/alibaba/anyproxy/wiki/3.x-docs \ No newline at end of file diff --git a/bin.js b/bin.js deleted file mode 100755 index 19e561a..0000000 --- a/bin.js +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env node - -var program = require('commander'), - color = require('colorful'), - fs = require("fs"), - path = require("path"), - npm = require("npm"), - packageInfo = require("./package.json"), - util = require("./lib/util"), - logUtil = require("./lib/log"); - -program - .version(packageInfo.version) - .option('-u, --host [value]', 'hostname for https proxy, localhost for default') - .option('-t, --type [value]', 'http|https, http for default') - .option('-p, --port [value]', 'proxy port, 8001 for default') - .option('-w, --web [value]' , 'web GUI port, 8002 for default') - .option('-e, --ws [value]' , 'web socket port, 8003 for default') - .option('-f, --file [value]', 'save request data to a specified file, will use in-memory db if not specified') - .option('-r, --rule [value]', 'path for rule file,') - .option('-g, --root [value]', 'generate root CA') - .option('-l, --throttle [value]', 'throttle speed in kb/s (kbyte / sec)') - .option('-i, --intercept', 'intercept(decrypt) https requests when root CA exists') - .option('-s, --silent', 'do not print anything into terminal') - .option('-c, --clear', 'clear all the tmp certificates') - .option('-o, --global', 'set as global proxy for system') - .option('install', '[alpha] install node modules') - .parse(process.argv); - -if(program.clear){ - require("./lib/certMgr").clearCerts(function(){ - console.log( color.green("all certs cleared") ); - process.exit(0); - }); - -}else if(program.root){ - require("./lib/certMgr").generateRootCA(function(){ - process.exit(0); - }); - -}else if(program.install){ - npm.load({ - "prefix": process.env.NODE_PATH + '/anyproxy/' - }, function (er) { - npm.commands.install(program.args || [], function (er, data) { - if(er)throw er; - }); - npm.registry.log.on("log", function (message) {}); - }); -}else{ - var proxy = require("./proxy.js"); - var ruleModule; - - if(program.silent){ - logUtil.setPrintStatus(false); - } - - if(program.rule){ - var ruleFilePath = path.resolve(process.cwd(),program.rule); - try{ - if(fs.existsSync(ruleFilePath)){ - ruleModule = require(ruleFilePath); - logUtil.printLog("rule file loaded :" + ruleFilePath); - }else{ - var logText = color.red("can not find rule file at " + ruleFilePath); - logUtil.printLog(logText, logUtil.T_ERR); - } - }catch(e){ - logUtil.printLog("failed to load rule file :" + e.toString(), logUtil.T_ERR); - } - }else{ - //a feature for donghua.yan - //read rule file from a specific position - (function(){ - try{ - var anyproxyHome = path.join(util.getAnyProxyHome()); - if(fs.existsSync(path.join(anyproxyHome,"rule_default.js"))){ - ruleModule = require(path.join(anyproxyHome,"rule_default")); - } - if(fs.existsSync(path.join(process.cwd(),'rule.js'))){ - ruleModule = require(path.join(process.cwd(),'rule')); - } - }catch(e){} - })(); - } - - new proxy.proxyServer({ - type : program.type, - port : program.port, - hostname : program.host, - dbFile : program.file, - throttle : program.throttle, - webPort : program.web, - socketPort : program.ws, - rule : ruleModule, - disableWebInterface : false, - setAsGlobalProxy : program.global, - interceptHttps : program.intercept, - silent : program.silent - }); -} diff --git a/bin/anyproxy b/bin/anyproxy new file mode 100755 index 0000000..330e083 --- /dev/null +++ b/bin/anyproxy @@ -0,0 +1,114 @@ +#!/usr/bin/env node + +'use strict' +const program = require('commander'), + color = require('colorful'), + packageInfo = require('../package.json'), + ruleLoader = require('../lib/ruleLoader'), + util = require('../lib/util'), + logUtil = require('../lib/log'); + +program + .version(packageInfo.version) + .option('-p, --port [value]', 'proxy port, 8001 for default') + .option('-w, --web [value]', 'web GUI port, 8002 for default') + .option('-r, --rule [value]', 'path for rule file,') + .option('-l, --throttle [value]', 'throttle speed in kb/s (kbyte / sec)') + .option('-i, --intercept', 'intercept(decrypt) https requests when root CA exists') + .option('-s, --silent', 'do not print anything into terminal') + .option('-c, --clear', 'clear all the certificates and temp files') + .option('--ignore-unauthorized-ssl', 'ignore all ssl error') + .parse(process.argv); + +if (program.clear) { + require('../lib/certMgr').clearCerts(() => { + util.deleteFolderContentsRecursive(util.getAnyProxyPath('cache')); + console.log(color.green('done !')); + process.exit(0); + }); +} else if (program.root) { + require('../lib/certMgr').generateRootCA(() => { + process.exit(0); + }); +} else { + const AnyProxy = require('../proxy.js'); + let proxyServer; + + if (program.silent) { + logUtil.setPrintStatus(false); + } + + // load rule module + new Promise((resolve, reject) => { + if (program.rule) { + resolve(ruleLoader.requireModule(program.rule)); + } else { + resolve(null); + } + }) + .catch(e => { + logUtil.printLog('Failed to load rule file', logUtil.T_ERR); + logUtil.printLog(e, logUtil.T_ERR); + process.exit(); + }) + + //start proxy + .then(ruleModule => { + proxyServer = new AnyProxy.ProxyServer({ + type: 'http', + port: program.port || 8001, + throttle: program.throttle, + rule: ruleModule, + webInterface: { + enable: true, + webPort: program.web, + }, + forceProxyHttps: program.intercept, + dangerouslyIgnoreUnauthorized: !!program.ignoreUnauthorizedSsl, + silent: program.silent + }); + // proxyServer.on('ready', () => {}); + proxyServer.start(); + }) + .catch(e => { + logUtil.printLog(e, logUtil.T_ERR); + if (e && e.code) { + logUtil.printLog('code ' + e.code, logUtil.T_ERR); + } + logUtil.printLog(e.stack, logUtil.T_ERR); + }); + + process.on('exit', (code) => { + if (code > 0) { + logUtil.printLog('AnyProxy is about to exit with code: ' + code, logUtil.T_ERR); + } + + process.exit(); + }); + + //exit cause ctrl+c + process.on('SIGINT', () => { + try { + proxyServer && proxyServer.close(); + } catch (e) { + console.error(e); + } + process.exit(); + }); + + process.on('uncaughtException', (err) => { + let errorTipText = 'got an uncaught exception, is there anything goes wrong in your rule file ?\n'; + try { + if (err && err.stack) { + errorTipText += err.stack; + } else { + errorTipText += err; + } + } catch (e) {} + logUtil.printLog(errorTipText, logUtil.T_ERR); + try { + proxyServer && proxyServer.close(); + } catch (e) {} + process.exit(); + }); +} diff --git a/bin/anyproxy-ca b/bin/anyproxy-ca new file mode 100755 index 0000000..f03a60f --- /dev/null +++ b/bin/anyproxy-ca @@ -0,0 +1,102 @@ +#!/usr/bin/env node + +'use strict' + +// exist-false, trusted-false : create CA +// exist-true, trusted-false : trust CA +// exist-true, trusted-true : all things done +const program = require('commander'); +const color = require('colorful'); +const certMgr = require('../lib/certMgr'); +const AnyProxy = require('../proxy'); +const exec = require('child_process').exec; +const co = require('co'); +const path = require('path'); +const inquirer = require('inquirer'); + +program + .option('-c, --clear', 'clear all the tmp certificates and root CA') + .option('-g, --generate', 'generate a new rootCA') + .parse(process.argv); + +function openFolderOfFile(filePath) { + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: path.dirname(filePath) }); + } else { + exec(`open -R ${filePath}`); + } +} + +function guideToGenrateCA() { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath, crtPath) => { + if (!error) { + const certDir = path.dirname(keyPath); + console.log(`The cert is generated at ${certDir}. Please trust the ${color.bold('rootCA.crt')}.`); + // TODO: console.log('guide to install'); + openFolderOfFile(crtPath); + } else { + console.error('failed to generate rootCA', error); + } + }); +} + +function guideToTrustCA() { + const certPath = AnyProxy.utils.certMgr.getRootCAFilePath(); + if (certPath) { + // TODO: console.log('guide to install'); + openFolderOfFile(certPath); + } else { + console.error('failed to get cert path'); + } +} + +if (program.clear) { + AnyProxy.utils.certMgr.clearCerts(() => { + console.log(color.green('done !')); + }); +} else if (program.generate) { + guideToGenrateCA(); +} else { + console.log('detecting CA status...'); + co(certMgr.getCAStatus) + .then(status => { + if (!status.exist) { + console.log('AnyProxy CA does not exist.'); + const questions = [{ + type: 'confirm', + name: 'ifCreate', + message: 'Would you like to generate one ?', + default: true + }]; + inquirer.prompt(questions).then(answers => { + if (answers.ifCreate) { + guideToGenrateCA(); + } + }); + } else if (!status.trusted) { + if (/^win/.test(process.platform)) { + console.log('AnyProxy CA exists, make sure it has been trusted'); + } else { + console.log('AnyProxy CA exists, but not be trusted'); + const questions = [{ + type: 'confirm', + name: 'ifGotoTrust', + message: 'Would you like to open the folder and trust it ?', + default: true + }]; + inquirer.prompt(questions).then(answers => { + if (answers.ifGotoTrust) { + guideToTrustCA(); + } + }); + } + // AnyProxy.utils.certMgr.clearCerts() + } else { + console.log(color.green('AnyProxy CA has already been trusted')); + } + }) + .catch(e => { + console.log(e); + }) +} diff --git a/build_scripts/prebuild-doc.js b/build_scripts/prebuild-doc.js new file mode 100644 index 0000000..58645ff --- /dev/null +++ b/build_scripts/prebuild-doc.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); + +const textTpl = [ + '```bash', + 'anyproxy --rule {{url}}', + '```', + '```js', + '{{content}}', + '```' +].join('\n'); + +/** + * + * @param {*} config + * @param {string} config.input input markdown path + * @param {string} config.ouput output markdown path + */ +function mergeMdWithRuleFile(config) { + const doc = fs.readFileSync(config.input, { encoding: 'utf8' }); + const rules = doc.match(/\{\{sample-rule:([\S]+)\}\}/g).map((rawToReplace) => ({ + raw: rawToReplace, + url: rawToReplace.replace(/\{\{sample-rule:([\S]+)\}\}/g, ($0, $1) => { + return $1; + }) + })); + + const tasks = rules.map((item) => ( + new Promise((resolve, reject) => { + fs.readFile(item.url, 'utf8', (err, data) => { + if (!err) { + const result = Object.assign({}, item); + result.content = data; + resolve(result); + } else { + reject(err); + } + }); + }) + )); + + // fetch all samples + return Promise.all(tasks) + .then((results) => { + // merge to doc + let resultDoc = doc; + results.forEach((item) => { + const contentToInsert = textTpl.replace('{{url}}', item.url).replace('{{content}}', item.content); + resultDoc = resultDoc.replace(item.raw, contentToInsert); + }); + fs.writeFileSync(config.output, resultDoc); + }, (fail) => { + console.log('failed to load resource'); + console.log(fail); + process.exit(); + }) + .catch(e => { + console.log(e); + process.exit(); + }); +} + +Promise.all([ + { + input: path.join(__dirname, '../docs-src/cn/src_doc.md'), + output: path.join(__dirname, '../docs-src/cn/README.md'), + }, + { + input: path.join(__dirname, '../docs-src/en/src_doc.md'), + output: path.join(__dirname, '../docs-src/en/README.md'), + } +].map(mergeMdWithRuleFile)).then(result => { + console.log('done'); +}).catch(e => { + console.log('err'); + console.log(e); +}); diff --git a/docs-src/CNAME b/docs-src/CNAME new file mode 100644 index 0000000..edd7608 --- /dev/null +++ b/docs-src/CNAME @@ -0,0 +1 @@ +anyproxy.io \ No newline at end of file diff --git a/docs-src/LANGS.md b/docs-src/LANGS.md new file mode 100644 index 0000000..b2beea8 --- /dev/null +++ b/docs-src/LANGS.md @@ -0,0 +1,4 @@ +# Languages + +* [中文](cn/) +* [English](en/) diff --git a/docs-src/README.md b/docs-src/README.md new file mode 100644 index 0000000..c55bde3 --- /dev/null +++ b/docs-src/README.md @@ -0,0 +1,2 @@ +# THIS IS AUTO GENERATED FILE, DO NOT EDIT THE HTML DIRECTLY. +# YOU CAN EDIT THE SOURCE IN docs-md FOLDER diff --git a/docs-src/_layouts/layout.html b/docs-src/_layouts/layout.html new file mode 100644 index 0000000..0c1a708 --- /dev/null +++ b/docs-src/_layouts/layout.html @@ -0,0 +1,29 @@ + + + + + + {% block title %}{{ config.title|d("GitBook", true) }}{% endblock %} + + + + {% if config.author %}{% endif %} + {% if config.isbn %}{% endif %} + {% block style %} + {% for resource in plugins.resources.css %} + {% if resource.url %} + + {% else %} + + {% endif %} + {% endfor %} + {% endblock %} + {% block head %}{% endblock %} + + + + + {% block body %}{% endblock %} + {% block javascript %}{% endblock %} + + diff --git a/docs-src/assets/auto-lang.js b/docs-src/assets/auto-lang.js new file mode 100644 index 0000000..ae1bf34 --- /dev/null +++ b/docs-src/assets/auto-lang.js @@ -0,0 +1,23 @@ +/* eslint no-var: off */ +/** +* detect if the browser is in UTF-8 zone +* @return boolean +*/ +function isUTF8Zone() { + return new Date().getTimezoneOffset() === -480; +} + +/** +* detect if the browser is already in a locale view +*/ +function isInLocaleView() { + return /(cn|en)/i.test(location.href); +} + +function initDefaultLocaleAndStatic() { + if (!isInLocaleView()) { + location.href = isUTF8Zone() ? '/cn' : 'en'; + } +} + +initDefaultLocaleAndStatic(); diff --git a/docs-src/assets/favicon.png b/docs-src/assets/favicon.png new file mode 100644 index 0000000..0969aee Binary files /dev/null and b/docs-src/assets/favicon.png differ diff --git a/docs-src/assets/main.js b/docs-src/assets/main.js new file mode 100644 index 0000000..feb1d8e --- /dev/null +++ b/docs-src/assets/main.js @@ -0,0 +1,12 @@ +/* eslint no-var: off */ +function injectBaiduStatic() { + var _hmt = _hmt || []; + var hm = document.createElement('script'); + var s = document.getElementsByTagName('script')[0]; + + hm.src = '//hm.baidu.com/hm.js?4e51565b7d471fd6623c163a8fd79e07'; + s.parentNode.insertBefore(hm, s); +} + + +injectBaiduStatic(); diff --git a/docs-src/assets/website.css b/docs-src/assets/website.css new file mode 100644 index 0000000..ee2f8a2 --- /dev/null +++ b/docs-src/assets/website.css @@ -0,0 +1,51 @@ +.book .book-summary ul.summary li.active>a, .book .book-summary ul.summary li a:hover { + color: #008cff; + background: transparent; + text-decoration: none !important; +} + +h1 { + color: #2674BA; +} +h2 { + color: #0099CC; +} +h3 { + color: #108ee9; +} +h4 { + color: #662D91; +} +h5 { + color: #444444; +} + +.gitbook-link { + display: none !important; +} + +.summary-title-span { + position: relative !important; + padding: 0 !important; +} +.rule-title:after, +.sample-title:after { + font-size: 12px; + padding: 0 3px; + border-radius: 3px; + color: #fff; +} + +.rule-title:after{ + content: 'rule'; + background-color: #108ee9; +} + +.sample-title:after { + content: 'sample'; + background-color: #00a854; +} + +.page-inner { + max-width: 1000px !important; +} diff --git a/docs-src/book.json b/docs-src/book.json new file mode 100644 index 0000000..3b839fd --- /dev/null +++ b/docs-src/book.json @@ -0,0 +1,11 @@ +{ + "title": "AnyProxy", + "author": "AnyProxy", + "description": "A fully configurable http/https proxy in NodeJS", + "plugins": [ + + ], + "pluginsConfig": { + + } +} diff --git a/docs-src/cn/README.md b/docs-src/cn/README.md new file mode 100644 index 0000000..88cacab --- /dev/null +++ b/docs-src/cn/README.md @@ -0,0 +1,885 @@ +AnyProxy +=================== + +> 本文档的适用范围是AnyProxy 4.0,欢迎提供反馈 + +Ref: [English Doc](../en) + +AnyProxy是一个开放式的HTTP代理服务器。 + +Github主页:https://github.com/alibaba/anyproxy/tree/4.x + +主要特性包括: + +* 基于Node.js,开放二次开发能力,允许自定义请求处理逻辑 +* 支持Https的解析 +* 提供GUI界面,用以观察请求 +相比3.x版本,AnyProxy 4.0的主要变化: + +* 规则文件(Rule)全面支持Promise和Generator +* 简化了规则文件内的接口 +* Web版界面重构 + + + +# 快速开始 +## 作为全局模块 +### 安装 + +对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 `nodejs-legacy` + +```bash +sudo apg-get install nodejs-legacy +``` + +然后,安装AnyProxy + +```bash +npm install -g anyproxy +``` + +### 启动 + +* 命令行启动AnyProxy,默认端口号8001 + +```bash +anyproxy +``` + +* 启动后将终端http代理服务器配置为127.0.0.1:8001即可 +* 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息 + +### 其他命令 + +* 配置启动端口,如1080端口启动 + +```bash +anyproxy --port 1080 +``` + +## 作为npm模块使用 + +AnyProxy可以作为一个npm模块使用,整合进其他工具。 + +> 如要启用https解析,请在代理服务器启动前自行调用`AnyProxy.utils.certMgr`相关方法生成证书,并引导用户信任安装。或引导用户使用`anyproxy-ca`方法。 + +* 引入 + +```bash +npm i anyproxy --save +``` + +* 使用举例 + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * 创建代理服务器 + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} 必选,代理服务器端口 + * `rule` {object} 自定义规则模块 + * `throttle` {number} 限速值,单位kb/s,默认不限速 + * `forceProxyHttps` {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认`false` + * `silent` {boolean} 是否屏蔽所有console输出,默认`false` + * `dangerouslyIgnoreUnauthorized` {boolean} 是否忽略请求中的证书错误,默认`false` + * `webInterface` {object} web版界面配置 + * `enable` {boolean} 是否启用web版界面,默认`false` + * `webPort` {number} web版界面端口号,默认`8002` + * Event: `ready` + * 代理服务器启动完成 + * 示例 + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * 代理服务器发生错误 + * 示例 + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * 启动代理服务器 + * 示例 + + ```js + proxy.start(); + ``` + * Method: `close` + * 关闭代理服务器 + * 示例 + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * 管理系统的全局代理配置,方法调用时可能会弹出密码框 + * 使用示例 + + ```js + // 配置127.0.0.1:8001为全局http代理服务器 + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // 关闭全局代理服务器 + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * 管理AnyProxy的证书 + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * 校验系统内是否存在AnyProxy的根证书 + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件 + * 样例 + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# 代理HTTPS +* AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书 + +> 解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程 + +* 生成证书并解析所有https请求 + +```bash +anyproxy-ca #生成rootCA证书,生成后需要手动信任 +anyproxy --intercept #启动AnyProxy,并解析所有https请求 +``` + +* [附录:如何信任CA证书](#证书配置) + +# rule模块 + +AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。 + +>注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题 + +规则模块的能力范围包括: + +* 拦截并修改正在发送的请求 + * 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等 +* 拦截并修改服务端响应 + * 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等 +* 拦截https请求,对内容做修改 + * 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA + +### 开发示例 + +* 举例 + * 需要编写一个规则模块,在 GET http://httpbin.org/user-agent 的返回值里加上测试信息,并延迟5秒返回 + +* Step 1,编写规则 + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '
-- AnyProxy Hacked! --'; + + newResponse.body += ``; + newResponse.header['Content-Type'] = 'text/html'; + + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, 启动AnyProxy,加载规则 + * 运行 `anyproxy --rule sample.js` + +* Step 3, 测试规则 + + * 用curl测试 + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent + + * 经过代理服务器后,期望的返回如下 + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, 查看请求信息 + + * 浏览器访问http://127.0.0.1:8002 ,界面上能看到刚才的请求信息 + +### 处理流程 + +* 处理流程图如下 + + + +* 当http请求经过代理服务器时,具体处理过程是: + * 收集请求所有请求参数,包括method, header, body等 + * AnyProxy调用规则模块`beforeSendRequest`方法,由模块做处理,返回新的请求参数,或返回响应内容 + * 如果`beforeSendRequest`返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。 + * 根据请求参数,向服务端发出请求,接收服务端响应。 + * 调用规则模块`beforeSendResponse`方法,由模块对响应内容进行处理 + * 把响应信息返回给客户端 + +* 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。 + * 调用规则模块`beforeDealHttpsRequest`方法,如果返回`true`,会明文解析这个请求,其他请求不处理 + * 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。 + + +### 如何引用 + +如下几种方案都可以用来引用规则模块: + +* 使用本地路径 +```bash +anyproxy --rule ./rule.js +``` +* 使用在线地址 +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* 使用npm包 + * AnyProxy使用`require()`加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包 + + ```bash +anyproxy --rule ./myRulePkg/ #本地包 +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包 + ``` + + +# rule接口文档 + +规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。 + +```js +module.exports = { + // 模块介绍 + summary: 'my customized rule for AnyProxy', + // 发送请求前拦截处理 + *beforeSendRequest(requestDetail) { /* ... */ }, + // 发送响应前处理 + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // 是否处理https请求 + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // 请求出错的事件 + *onError(requestDetail, error) { /* ... */ }, + // https连接服务器出错 + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> 规则文件中,除了summary,都是由 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。 + +### summary + +#### summary(): string | summary:string + +* 规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串 + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* AnyProxy向服务端发送请求前,会调用`beforeSendRequest`,并带上参数`requestDetail` +* `requestDetail` + * `protocol` {string} 请求使用的协议,http或者https + * `requestOptions` {object} 即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} 请求Body + * `url` {string} 请求url + * `_req` {object} 请求的原始request +* 举例:请求 *anyproxy.io* 时,`requestDetail`参数内容大致如下 + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改请求协议,如强制改用https发起请求 + + ```js + return { + protocol: 'https' + }; + ``` + + * 修改请求参数 + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * 修改请求body + + ```js + return { + requestData: 'my new request data' + //这里也可以同时加上requestOptions + }; + ``` + * 直接返回客户端,不再发起请求,其中`statusCode` `header` 是必选字段 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* AnyProxy向客户端发送请求前,会调用`beforeSendResponse`,并带上参数`requestDetail` `responseDetail` +* `requestDetail` 同`beforeSendRequest`中的参数 +* `responseDetail` + * `response` {object} 服务端的返回信息,包括`statusCode` `header` `body`三个字段 + * `_res` {object} 原始的服务端返回对象 +* 举例,请求 *anyproxy.io* 时,`responseDetail`参数内容大致如下 + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改返回的状态码 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * 修改返回的内容 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* AnyProxy收到https请求时,会调用`beforeDealHttpsRequest`,并带上参数`requestDetail` +* 如果配置了全局解析https的参数,则AnyProxy会略过这个调用 +* 只有返回`true`时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。 +* 注意:https over http的代理模式中,这里的request是CONNECT请求 +* `requestDetail` + * `host` {string} 请求目标的Host,受制于协议,这里无法获取完整url + * `_req` {object} 请求的原始request +* 返回值 + * `true`或者`false`,表示是否需要AnyProxy替换证书并解析https + +### onError + +#### onError(requestDetail, error) + +* 在请求处理过程中发生错误时,AnyProxy会调用`onError`方法,并提供对应的错误信息 +* 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等 +* `requestDetail` 同`beforeSendRequest`中的参数 +* 以下几种返回都是合法的 + * 不做任何处理。此时AnyProxy会返回一个默认的错误页。 + + ```js + return null; + ``` + + * 返回自定义错误页 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法 +* `requestDetail` 同`beforeDealHttpsRequest`中的参数 +* 此处无法控制向客户端的返回信息,无需返回值。 + +# rule样例 + +* 这里提供一些样例,来讲解规则模块的常见用法 +* 你可以通过 `anyproxy --rule http://....js` 来加载模块并体验 +* 用curl发请求测试的方法如下 + * 直接请求服务器:`curl http://httpbin.org/` + * 通过代理服务器请求:`curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### 使用本地数据 + * 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回 + +```bash +anyproxy --rule rule_sample/sample_use_local_response.js +``` +```js +/* + sample: + intercept all requests toward httpbin.org, use a local response + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + const localResponse = { + statusCode: 200, + header: { 'Content-Type': 'application/json' }, + body: '{"hello": "this is local response"}' + }; + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + return { + response: localResponse + }; + } + }, +}; + +``` + +### 修改请求头 + * 修改发送到 httpbin.org 的user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_header.js +``` +```js +/* + sample: + modify the user-agent in requests toward httpbin.org + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### 修改请求数据 + * 修改发送到 http://httpbin.org/post 的post数据 + +```bash +anyproxy --rule rule_sample/sample_modify_request_data.js +``` +```js +/* + sample: + modify the post data towards http://httpbin.org/post + test: + curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001 + expected response: + { "data": "i-am-anyproxy-modified-post-data" } +*/ +module.exports = { + summary: 'Rule to modify request data', + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) { + return { + requestData: 'i-am-anyproxy-modified-post-data' + }; + } + }, +}; + +``` + +### 修改请求的目标地址 + * 把所有发送到 http://httpbin.org/ 的请求全部改到 http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_path.js +``` +```js +/* + sample: + redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html + test: + curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + expected response: + 'hello world' from 127.0.0.1:8001/index.html +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) { + const newRequestOptions = requestDetail.requestOptions; + requestDetail.protocol = 'http'; + newRequestOptions.hostname = '127.0.0.1' + newRequestOptions.port = '8008'; + newRequestOptions.path = '/index.html'; + newRequestOptions.method = 'GET'; + return requestDetail; + } + }, + *beforeDealHttpsRequest(requestDetail) { + return true; + } +}; + +``` + +### 修改请求协议 + * 把用http协议请求的 http://httpbin.org 改成https并发送 + +```bash +anyproxy --rule rule_sample/sample_modify_request_protocol.js +``` +```js +/* + sample: + redirect all http requests of httpbin.org to https + test: + curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001 + expected response: + { "X-Forwarded-Protocol": "https" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newOption = requestDetail.requestOptions; + newOption.port = 443; + return { + protocol: 'https', + requestOptions: newOption + }; + } + } +}; + +``` + +### 修改返回状态码 + * 把 所有http://httpbin.org 的返回状态码都改成404 + +```bash +anyproxy --rule rule_sample/sample_modify_response_statuscode.js +``` +```js +/* + sample: + modify all status code of http://httpbin.org/ to 404 + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + HTTP/1.1 404 Not Found +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 404; + return { + response: newResponse + }; + } + } +}; + +``` + +### 修改返回头 + * 在 http://httpbin.org/user-agent 的返回头里加上 X-Proxy-By:AnyProxy + +```bash +anyproxy --rule rule_sample/sample_modify_response_header.js +``` +```js +/* + sample: + modify response header of http://httpbin.org/user-agent + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + X-Proxy-By: AnyProxy +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) { + const newResponse = responseDetail.response; + newResponse.header['X-Proxy-By'] = 'AnyProxy'; + return { + response: newResponse + }; + } + } +}; + +``` + +### 修改返回内容并延迟 + * 在 http://httpbin.org/user-agent 的返回最后追加AnyProxy的签名,并延迟5秒 + +```bash +anyproxy --rule rule_sample/sample_modify_response_data.js +``` +```js +/* + sample: + modify response data of http://httpbin.org/user-agent + test: + curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! -- +*/ + +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay the response for 5s + resolve({ response: newResponse }); + }, 5000); + }); + } + }, +}; + +``` + +# 证书配置 + +### OSX系统信任CA证书 + +* 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的 + + + +> 警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管 + +安装CA: + +* 双击打开*rootCA.crt* + +* 确认将证书添加到login或system + + + +* 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust) + + + +### Windows系统信任CA证书 + + + + +### 配置OSX系统代理 + +* 在wifi高级设置中,配置http代理即可 + + + +### 配置浏览器HTTP代理 + +* 以Chrome的[SwitchyOmega插件](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### iOS系统信任CA证书 + +* 点击web ui中的 *Root CA*,按提示扫描二维码即可安装 + + + +### iOS >= 10.3信任CA证书 + +* 除了上述证书安装过程,还需要在 *设置->通用->关于本机->证书信任设置* 中把AnyProxy证书的开关打开,否则safari将报错。 + + + +### 配置iOS/Android系统代理 + +* 代理服务器都在wifi设置中配置 + +* iOS HTTP代理配置 + + + +* Android HTTP代理配置 + + + +# FAQ + +#### Q: 为什么https请求不能进入处理函数? + A: 以下任意一项都能用来改变https的处理特性: + 1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析 + 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析 + +#### Q: 提示 *function is not yieldable* + * A: 规则模块是用 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。 + +#### Q: The connection is not private +当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问: +- 命令行直接启动 + + 通过启动参数 `--ignore-unauthorized-ssl` 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。 + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` + +- 在Nodejs代码中启动 + + 在构造AnyProxy实例的时候,传入参数`dangerouslyIgnoreUnauthorized:true`, 如下: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略* + +- 通过自定义的Rule来修改 + + 我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。 + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // 设置属性 rejectUnauthorized 为 false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` diff --git a/docs-src/cn/SUMMARY.md b/docs-src/cn/SUMMARY.md new file mode 100644 index 0000000..e9fe3aa --- /dev/null +++ b/docs-src/cn/SUMMARY.md @@ -0,0 +1,39 @@ +# Summary + + +* [简介](README.md) +* [快速开始](README.md#快速开始) + * [安装](README.md#安装) + * [启动](README.md#启动) + * [其他命令](README.md#其他命令) + * [作为npm模块启动](README.md#作为npm模块使用) +* [代理HTTPS](README.md#代理https) +* [rule模块](README.md#rule模块) + * [开发示例](README.md#开发示例) + * [处理流程](README.md#处理流程) + * [如何引用](README.md#如何引用) +* [rule接口文档](README.md#rule接口文档) + * [summary_class=rule-title](README.md#summary) + * [beforeSendRequest_class=rule-title](README.md#beforesendrequest) + * [beforeSendResponse_class=rule-title](README.md#beforesendresponse) + * [beforeDealHttpsRequest_class=rule-title](README.md#beforedealhttpsrequest) + * [onError_class=rule-title](README.md#onerror) + * [onConnectError_class=rule-title](README.md#onconnecterror) +* [rule样例](README.md#rule样例) + * [使用本地数据_class=sample-title](README.md#使用本地数据) + * [修改请求头_class=sample-title](README.md#修改请求头) + * [修改请求数据_class=sample-title](README.md#修改请求数据) + * [修改请求的目标地址_class=sample-title](README.md#修改请求的目标地址) + * [修改请求协议_class=sample-title](README.md#修改请求协议) + * [修改返回状态码_class=sample-title](README.md#修改返回状态码) + * [修改返回头_class=sample-title](README.md#修改返回头) + * [修改返回内容并延迟_class=sample-title](README.md#修改返回内容并延迟) +* [证书配置](README.md#证书配置) + * [OSX系统信任CA证书](README.md#osx系统信任ca证书) + * [Windows系统信任CA证书](README.md#windows系统信任ca证书) + * [配置OSX系统代理](README.md#配置osx系统代理) + * [配置浏览器HTTP代理](README.md#配置浏览器http代理) + * [iOS系统信任CA证书](README.md#ios系统信任ca证书) + * [iOS >= 10.3信任CA证书](README.md#ios--103信任ca证书) + * [配置iOS/Android系统代理](README.md#配置iosandroid系统代理) +* [FAQ](README.md#faq) diff --git a/docs-src/cn/_layouts/layout.html b/docs-src/cn/_layouts/layout.html new file mode 100644 index 0000000..e380a2a --- /dev/null +++ b/docs-src/cn/_layouts/layout.html @@ -0,0 +1,33 @@ + + + + + + {% block title %}{{ config.title|d("GitBook", true) }}{% endblock %} + + + + {% if config.author %}{% endif %} + {% if config.isbn %}{% endif %} + {% block style %} + {% for resource in plugins.resources.css %} + {% if resource.url %} + + {% else %} + + {% endif %} + {% endfor %} + {% endblock %} + {% block head %}{% endblock %} + + + + + +
+ {% set regExp = r/^foo.*/g %} +
+ {% block body %}{% endblock %} + {% block javascript %}{% endblock %} + + diff --git a/docs-src/cn/_layouts/website/summary.html b/docs-src/cn/_layouts/website/summary.html new file mode 100644 index 0000000..b6e041d --- /dev/null +++ b/docs-src/cn/_layouts/website/summary.html @@ -0,0 +1,64 @@ +{% macro articles(_articles) %} + {% for article in _articles %} +
  • + {% if article.path and getPageByPath(article.path) %} + + {% elif article.url %} + + {% else %} + + {% endif %} + {% if article.level != "0" and config.pluginsConfig['theme-default'].showLevel %} + {{ article.level }}. + {% endif %} +
    + {{ article.title | replace(r/_class=.+/, '') }} +
    + + {% if article.path or article.url %} +
    + {% else %} + + {% endif %} + + {% if article.articles.length > 0 %} +
      + {{ articles(article.articles, file, config) }} +
    + {% endif %} +
  • + {% endfor %} +{% endmacro %} + +
      + {% set _divider = false %} + {% if config.links.sidebar %} + {% for linkTitle, link in config.links.sidebar %} + {% set _divider = true %} +
    • + {{ linkTitle }} +
    • + {% endfor %} + {% endif %} + + {% if _divider %} +
    • + {% endif %} + + {% for part in summary.parts %} + {% if part.title %} +
    • {{ part.title }}
    • + {% elif not loop.first %} +
    • + {% endif %} + {{ articles(part.articles, file, config) }} + {% endfor %} + +
    • + +
    • + + {{ "GITBOOK_LINK"|t }} + +
    • +
    diff --git a/docs-src/cn/doc.md b/docs-src/cn/doc.md new file mode 100644 index 0000000..8faeb35 --- /dev/null +++ b/docs-src/cn/doc.md @@ -0,0 +1,848 @@ +# 快速开始 +## 作为全局模块 +### 安装 + +对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 `nodejs-legacy` + +```bash +sudo apg-get install nodejs-legacy +``` + +然后,安装AnyProxy + +```bash +npm install -g anyproxy@beta #本文档对应的AnyProxy为4.0Beta版 +``` + +### 启动 + +* 命令行启动AnyProxy,默认端口号8001 + +```bash +anyproxy +``` + +* 启动后将终端http代理服务器配置为127.0.0.1:8001即可 +* 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息 + +### 其他命令 + +* 配置启动端口,如1080端口启动 + +```bash +anyproxy --port 1080 +``` + +## 作为npm模块使用 + +AnyProxy可以作为一个npm模块使用,整合进其他工具。 + +> 如要启用https解析,请在代理服务器启动前自行调用`AnyProxy.utils.certMgr`相关方法生成证书,并引导用户信任安装。或引导用户使用`anyproxy-ca`方法。 + +* 引入 + +```bash +npm i anyproxy@beta --save # 4.0版正在beta中 +``` + +* 使用举例 + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * 创建代理服务器 + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} 必选,代理服务器端口 + * `rule` {object} 自定义规则模块 + * `throttle` {number} 限速值,单位kb/s,默认不限速 + * `forceProxyHttps` {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认`false` + * `silent` {boolean} 是否屏蔽所有console输出,默认`false` + * `dangerouslyIgnoreUnauthorized` {boolean} 是否忽略请求中的证书错误,默认`false` + * `webInterface` {object} web版界面配置 + * `enable` {boolean} 是否启用web版界面,默认`false` + * `webPort` {number} web版界面端口号,默认`8002` + * Event: `ready` + * 代理服务器启动完成 + * 示例 + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * 代理服务器发生错误 + * 示例 + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * 启动代理服务器 + * 示例 + + ```js + proxy.start(); + ``` + * Method: `close` + * 关闭代理服务器 + * 示例 + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * 管理系统的全局代理配置,方法调用时可能会弹出密码框 + * 使用示例 + + ```js + // 配置127.0.0.1:8001为全局http代理服务器 + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // 关闭全局代理服务器 + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * 管理AnyProxy的证书 + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * 校验系统内是否存在AnyProxy的根证书 + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件 + * 样例 + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# 代理HTTPS +* AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书 + +> 解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程 + +* 生成证书并解析所有https请求 + +```bash +anyproxy-ca #生成rootCA证书,生成后需要手动信任 +anyproxy --intercept #启动AnyProxy,并解析所有https请求 +``` + +* [附录:如何信任CA证书](#证书配置) + +# rule模块 + +AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。 + +>注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题 + +规则模块的能力范围包括: + +* 拦截并修改正在发送的请求 + * 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等 +* 拦截并修改服务端响应 + * 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等 +* 拦截https请求,对内容做修改 + * 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA + +### 开发示例 + +* 举例 + * 需要编写一个规则模块,在 GET http://httpbin.org/user-agent 的返回值里加上测试信息,并延迟5秒返回 + +* Step 1,编写规则 + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, 启动AnyProxy,加载规则 + * 运行 `anyproxy --rule sample.js` + +* Step 3, 测试规则 + + * 用curl测试 + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent + + * 经过代理服务器后,期望的返回如下 + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, 查看请求信息 + + * 浏览器访问http://127.0.0.1:8002 ,界面上能看到刚才的请求信息 + +### 处理流程 + +* 处理流程图如下 + + + +* 当http请求经过代理服务器时,具体处理过程是: + * 收集请求所有请求参数,包括method, header, body等 + * AnyProxy调用规则模块`beforeSendRequest`方法,由模块做处理,返回新的请求参数,或返回响应内容 + * 如果`beforeSendRequest`返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。 + * 根据请求参数,向服务端发出请求,接收服务端响应。 + * 调用规则模块`beforeSendResponse`方法,由模块对响应内容进行处理 + * 把响应信息返回给客户端 + +* 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。 + * 调用规则模块`beforeDealHttpsRequest`方法,如果返回`true`,会明文解析这个请求,其他请求不处理 + * 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。 + + +### 如何引用 + +如下几种方案都可以用来引用规则模块: + +* 使用本地路径 +```bash +anyproxy --rule ./rule.js +``` +* 使用在线地址 +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* 使用npm包 + * AnyProxy使用`require()`加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包 + + ```bash +anyproxy --rule ./myRulePkg/ #本地包 +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包 + ``` + + +# rule接口文档 + +规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。 + +```js +module.exports = { + // 模块介绍 + summary: 'my customized rule for AnyProxy', + // 发送请求前拦截处理 + *beforeSendRequest(requestDetail) { /* ... */ }, + // 发送响应前处理 + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // 是否处理https请求 + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // 请求出错的事件 + *onError(requestDetail, error) { /* ... */ }, + // https连接服务器出错 + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> 规则文件中,除了summary,都是由 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。 + +### summary + +#### summary(): string | summary:string + +* 规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串 + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* AnyProxy向服务端发送请求前,会调用`beforeSendRequest`,并带上参数`requestDetail` +* `requestDetail` + * `protocol` {string} 请求使用的协议,http或者https + * `requestOptions` {object} 即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} 请求Body + * `url` {string} 请求url + * `_req` {object} 请求的原始request +* 举例:请求 *anyproxy.io* 时,`requestDetail`参数内容大致如下 + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改请求协议,如强制改用https发起请求 + + ```js + return { + protocol: 'https' + }; + ``` + + * 修改请求参数 + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * 修改请求body + + ```js + return { + requestData: 'my new request data' + //这里也可以同时加上requestOptions + }; + ``` + * 直接返回客户端,不再发起请求,其中`statusCode` `header` 是必选字段 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* AnyProxy向客户端发送请求前,会调用`beforeSendResponse`,并带上参数`requestDetail` `responseDetail` +* `requestDetail` 同`beforeSendRequest`中的参数 +* `responseDetail` + * `response` {object} 服务端的返回信息,包括`statusCode` `header` `body`三个字段 + * `_res` {object} 原始的服务端返回对象 +* 举例,请求 *anyproxy.io* 时,`responseDetail`参数内容大致如下 + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改返回的状态码 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * 修改返回的内容 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* AnyProxy收到https请求时,会调用`beforeDealHttpsRequest`,并带上参数`requestDetail` +* 如果配置了全局解析https的参数,则AnyProxy会略过这个调用 +* 只有返回`true`时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。 +* 注意:https over http的代理模式中,这里的request是CONNECT请求 +* `requestDetail` + * `host` {string} 请求目标的Host,受制于协议,这里无法获取完整url + * `_req` {object} 请求的原始request +* 返回值 + * `true`或者`false`,表示是否需要AnyProxy替换证书并解析https + +### onError + +#### onError(requestDetail, error) + +* 在请求处理过程中发生错误时,AnyProxy会调用`onError`方法,并提供对应的错误信息 +* 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等 +* `requestDetail` 同`beforeSendRequest`中的参数 +* 以下几种返回都是合法的 + * 不做任何处理。此时AnyProxy会返回一个默认的错误页。 + + ```js + return null; + ``` + + * 返回自定义错误页 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法 +* `requestDetail` 同`beforeDealHttpsRequest`中的参数 +* 此处无法控制向客户端的返回信息,无需返回值。 + +# rule样例 + +* 这里提供一些样例,来讲解规则模块的常见用法 +* 你可以通过 `anyproxy --rule http://....js` 来加载模块并体验 +* 用curl发请求测试的方法如下 + * 直接请求服务器:`curl http://httpbin.org/` + * 通过代理服务器请求:`curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### 使用本地数据 + * 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回 + +```bash +anyproxy --rule rule_sample/sample_use_local_response.js +``` +```js +/* + sample: + intercept all requests toward httpbin.org, use a local response + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + const localResponse = { + statusCode: 200, + header: { 'Content-Type': 'application/json' }, + body: '{"hello": "this is local response"}' + }; + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + return { + response: localResponse + }; + } + }, +}; + +``` + +### 修改请求头 + * 修改发送到 httpbin.org 的user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_header.js +``` +```js +/* + sample: + modify the user-agent in requests toward httpbin.org + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### 修改请求数据 + * 修改发送到 http://httpbin.org/post 的post数据 + +```bash +anyproxy --rule rule_sample/sample_modify_request_data.js +``` +```js +/* + sample: + modify the post data towards http://httpbin.org/post + test: + curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001 + expected response: + { "data": "i-am-anyproxy-modified-post-data" } +*/ +module.exports = { + summary: 'Rule to modify request data', + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) { + return { + requestData: 'i-am-anyproxy-modified-post-data' + }; + } + }, +}; + +``` + +### 修改请求的目标地址 + * 把所有发送到 http://httpbin.org/ 的请求全部改到 http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_path.js +``` +```js +/* + sample: + redirect all httpbin.org requests to http://httpbin.org/user-agent + test: + curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.path = '/user-agent'; + newRequestOptions.method = 'GET'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### 修改请求协议 + * 把用http协议请求的 http://httpbin.org 改成https并发送 + +```bash +anyproxy --rule rule_sample/sample_modify_request_protocol.js +``` +```js +/* + sample: + redirect all http requests of httpbin.org to https + test: + curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001 + expected response: + { "X-Forwarded-Protocol": "https" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newOption = requestDetail.requestOptions; + newOption.port = 443; + return { + protocol: 'https', + requestOptions: newOption + }; + } + } +}; + +``` + +### 修改返回状态码 + * 把 所有http://httpbin.org 的返回状态码都改成404 + +```bash +anyproxy --rule rule_sample/sample_modify_response_statuscode.js +``` +```js +/* + sample: + modify all status code of http://httpbin.org/ to 404 + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + HTTP/1.1 404 Not Found +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 404; + return { + response: newResponse + }; + } + } +}; + +``` + +### 修改返回头 + * 在 http://httpbin.org/user-agent 的返回头里加上 X-Proxy-By:AnyProxy + +```bash +anyproxy --rule rule_sample/sample_modify_response_header.js +``` +```js +/* + sample: + modify response header of http://httpbin.org/user-agent + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + X-Proxy-By: AnyProxy +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) { + const newResponse = responseDetail.response; + newResponse.header['X-Proxy-By'] = 'AnyProxy'; + return { + response: newResponse + }; + } + } +}; + +``` + +### 修改返回内容并延迟 + * 在 http://httpbin.org/user-agent 的返回最后追加AnyProxy的签名,并延迟5秒 + +```bash +anyproxy --rule rule_sample/sample_modify_response_data.js +``` +```js +/* + sample: + modify response data of http://httpbin.org/user-agent + test: + curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! -- +*/ + +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay the response for 5s + resolve({ response: newResponse }); + }, 5000); + }); + } + }, +}; + +``` + +# 证书配置 + +### OSX系统信任CA证书 + +* 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的 + + + +> 警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管 + +安装CA: + +* 双击打开*rootCA.crt* + +* 确认将证书添加到login或system + + + +* 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust) + + + +### Windows系统信任CA证书 + + + + +### 配置OSX系统代理 + +* 在wifi高级设置中,配置http代理即可 + + + +### 配置浏览器HTTP代理 + +* 以Chrome的[SwitchyOmega插件](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### iOS系统信任CA证书 + +* 点击web ui中的 *Root CA*,按提示扫描二维码即可安装 + + + +### iOS >= 10.3信任CA证书 + +* 除了上述证书安装过程,还需要在 *设置->通用->关于本机->证书信任设置* 中把AnyProxy证书的开关打开,否则safari将报错。 + + + +### 配置iOS/Android系统代理 + +* 代理服务器都在wifi设置中配置 + +* iOS HTTP代理配置 + + + +* Android HTTP代理配置 + + + +# FAQ + +#### Q: 为什么https请求不能进入处理函数? + A: 以下任意一项都能用来改变https的处理特性: + 1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析 + 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析 + +#### Q: 提示 *function is not yieldable* + * A: 规则模块是用 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。 + +#### Q: The connection is not private +当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问: +- 命令行直接启动 + + 通过启动参数 `--ignore-unauthorized-ssl` 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。 + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` + +- 在Nodejs代码中启动 + + 在构造AnyProxy实例的时候,传入参数`dangerouslyIgnoreUnauthorized:true`, 如下: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略* + +- 通过自定义的Rule来修改 + + 我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。 + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // 设置属性 rejectUnauthorized 为 false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` \ No newline at end of file diff --git a/docs-src/cn/src_doc.md b/docs-src/cn/src_doc.md new file mode 100644 index 0000000..432ea9c --- /dev/null +++ b/docs-src/cn/src_doc.md @@ -0,0 +1,686 @@ +AnyProxy +=================== + +> 本文档的适用范围是AnyProxy 4.0,欢迎提供反馈 + +Ref: [English Doc](../en) + +AnyProxy是一个开放式的HTTP代理服务器。 + +Github主页:https://github.com/alibaba/anyproxy/tree/4.x + +主要特性包括: + +* 基于Node.js,开放二次开发能力,允许自定义请求处理逻辑 +* 支持Https的解析 +* 提供GUI界面,用以观察请求 +相比3.x版本,AnyProxy 4.0的主要变化: + +* 规则文件(Rule)全面支持Promise和Generator +* 简化了规则文件内的接口 +* Web版界面重构 + + + +# 快速开始 +## 作为全局模块 +### 安装 + +对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 `nodejs-legacy` + +```bash +sudo apg-get install nodejs-legacy +``` + +然后,安装AnyProxy + +```bash +npm install -g anyproxy +``` + +### 启动 + +* 命令行启动AnyProxy,默认端口号8001 + +```bash +anyproxy +``` + +* 启动后将终端http代理服务器配置为127.0.0.1:8001即可 +* 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息 + +### 其他命令 + +* 配置启动端口,如1080端口启动 + +```bash +anyproxy --port 1080 +``` + +## 作为npm模块使用 + +AnyProxy可以作为一个npm模块使用,整合进其他工具。 + +> 如要启用https解析,请在代理服务器启动前自行调用`AnyProxy.utils.certMgr`相关方法生成证书,并引导用户信任安装。或引导用户使用`anyproxy-ca`方法。 + +* 引入 + +```bash +npm i anyproxy --save +``` + +* 使用举例 + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * 创建代理服务器 + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} 必选,代理服务器端口 + * `rule` {object} 自定义规则模块 + * `throttle` {number} 限速值,单位kb/s,默认不限速 + * `forceProxyHttps` {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认`false` + * `silent` {boolean} 是否屏蔽所有console输出,默认`false` + * `dangerouslyIgnoreUnauthorized` {boolean} 是否忽略请求中的证书错误,默认`false` + * `webInterface` {object} web版界面配置 + * `enable` {boolean} 是否启用web版界面,默认`false` + * `webPort` {number} web版界面端口号,默认`8002` + * Event: `ready` + * 代理服务器启动完成 + * 示例 + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * 代理服务器发生错误 + * 示例 + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * 启动代理服务器 + * 示例 + + ```js + proxy.start(); + ``` + * Method: `close` + * 关闭代理服务器 + * 示例 + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * 管理系统的全局代理配置,方法调用时可能会弹出密码框 + * 使用示例 + + ```js + // 配置127.0.0.1:8001为全局http代理服务器 + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // 关闭全局代理服务器 + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * 管理AnyProxy的证书 + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * 校验系统内是否存在AnyProxy的根证书 + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件 + * 样例 + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# 代理HTTPS +* AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书 + +> 解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程 + +* 生成证书并解析所有https请求 + +```bash +anyproxy-ca #生成rootCA证书,生成后需要手动信任 +anyproxy --intercept #启动AnyProxy,并解析所有https请求 +``` + +* [附录:如何信任CA证书](#证书配置) + +# rule模块 + +AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。 + +>注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题 + +规则模块的能力范围包括: + +* 拦截并修改正在发送的请求 + * 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等 +* 拦截并修改服务端响应 + * 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等 +* 拦截https请求,对内容做修改 + * 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA + +### 开发示例 + +* 举例 + * 需要编写一个规则模块,在 GET http://httpbin.org/user-agent 的返回值里加上测试信息,并延迟5秒返回 + +* Step 1,编写规则 + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '
    -- AnyProxy Hacked! --'; + + newResponse.body += ``; + newResponse.header['Content-Type'] = 'text/html'; + + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, 启动AnyProxy,加载规则 + * 运行 `anyproxy --rule sample.js` + +* Step 3, 测试规则 + + * 用curl测试 + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent + + * 经过代理服务器后,期望的返回如下 + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, 查看请求信息 + + * 浏览器访问http://127.0.0.1:8002 ,界面上能看到刚才的请求信息 + +### 处理流程 + +* 处理流程图如下 + + + +* 当http请求经过代理服务器时,具体处理过程是: + * 收集请求所有请求参数,包括method, header, body等 + * AnyProxy调用规则模块`beforeSendRequest`方法,由模块做处理,返回新的请求参数,或返回响应内容 + * 如果`beforeSendRequest`返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。 + * 根据请求参数,向服务端发出请求,接收服务端响应。 + * 调用规则模块`beforeSendResponse`方法,由模块对响应内容进行处理 + * 把响应信息返回给客户端 + +* 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。 + * 调用规则模块`beforeDealHttpsRequest`方法,如果返回`true`,会明文解析这个请求,其他请求不处理 + * 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。 + + +### 如何引用 + +如下几种方案都可以用来引用规则模块: + +* 使用本地路径 +```bash +anyproxy --rule ./rule.js +``` +* 使用在线地址 +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* 使用npm包 + * AnyProxy使用`require()`加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包 + + ```bash +anyproxy --rule ./myRulePkg/ #本地包 +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包 + ``` + + +# rule接口文档 + +规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。 + +```js +module.exports = { + // 模块介绍 + summary: 'my customized rule for AnyProxy', + // 发送请求前拦截处理 + *beforeSendRequest(requestDetail) { /* ... */ }, + // 发送响应前处理 + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // 是否处理https请求 + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // 请求出错的事件 + *onError(requestDetail, error) { /* ... */ }, + // https连接服务器出错 + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> 规则文件中,除了summary,都是由 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。 + +### summary + +#### summary(): string | summary:string + +* 规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串 + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* AnyProxy向服务端发送请求前,会调用`beforeSendRequest`,并带上参数`requestDetail` +* `requestDetail` + * `protocol` {string} 请求使用的协议,http或者https + * `requestOptions` {object} 即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} 请求Body + * `url` {string} 请求url + * `_req` {object} 请求的原始request +* 举例:请求 *anyproxy.io* 时,`requestDetail`参数内容大致如下 + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改请求协议,如强制改用https发起请求 + + ```js + return { + protocol: 'https' + }; + ``` + + * 修改请求参数 + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * 修改请求body + + ```js + return { + requestData: 'my new request data' + //这里也可以同时加上requestOptions + }; + ``` + * 直接返回客户端,不再发起请求,其中`statusCode` `header` 是必选字段 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* AnyProxy向客户端发送请求前,会调用`beforeSendResponse`,并带上参数`requestDetail` `responseDetail` +* `requestDetail` 同`beforeSendRequest`中的参数 +* `responseDetail` + * `response` {object} 服务端的返回信息,包括`statusCode` `header` `body`三个字段 + * `_res` {object} 原始的服务端返回对象 +* 举例,请求 *anyproxy.io* 时,`responseDetail`参数内容大致如下 + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改返回的状态码 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * 修改返回的内容 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* AnyProxy收到https请求时,会调用`beforeDealHttpsRequest`,并带上参数`requestDetail` +* 如果配置了全局解析https的参数,则AnyProxy会略过这个调用 +* 只有返回`true`时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。 +* 注意:https over http的代理模式中,这里的request是CONNECT请求 +* `requestDetail` + * `host` {string} 请求目标的Host,受制于协议,这里无法获取完整url + * `_req` {object} 请求的原始request +* 返回值 + * `true`或者`false`,表示是否需要AnyProxy替换证书并解析https + +### onError + +#### onError(requestDetail, error) + +* 在请求处理过程中发生错误时,AnyProxy会调用`onError`方法,并提供对应的错误信息 +* 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等 +* `requestDetail` 同`beforeSendRequest`中的参数 +* 以下几种返回都是合法的 + * 不做任何处理。此时AnyProxy会返回一个默认的错误页。 + + ```js + return null; + ``` + + * 返回自定义错误页 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法 +* `requestDetail` 同`beforeDealHttpsRequest`中的参数 +* 此处无法控制向客户端的返回信息,无需返回值。 + +# rule样例 + +* 这里提供一些样例,来讲解规则模块的常见用法 +* 你可以通过 `anyproxy --rule http://....js` 来加载模块并体验 +* 用curl发请求测试的方法如下 + * 直接请求服务器:`curl http://httpbin.org/` + * 通过代理服务器请求:`curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### 使用本地数据 + * 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回 + +{{sample-rule:rule_sample/sample_use_local_response.js}} + +### 修改请求头 + * 修改发送到 httpbin.org 的user-agent + +{{sample-rule:rule_sample/sample_modify_request_header.js}} + +### 修改请求数据 + * 修改发送到 http://httpbin.org/post 的post数据 + +{{sample-rule:rule_sample/sample_modify_request_data.js}} + +### 修改请求的目标地址 + * 把所有发送到 http://httpbin.org/ 的请求全部改到 http://httpbin.org/user-agent + +{{sample-rule:rule_sample/sample_modify_request_path.js}} + +### 修改请求协议 + * 把用http协议请求的 http://httpbin.org 改成https并发送 + +{{sample-rule:rule_sample/sample_modify_request_protocol.js}} + +### 修改返回状态码 + * 把 所有http://httpbin.org 的返回状态码都改成404 + +{{sample-rule:rule_sample/sample_modify_response_statuscode.js}} + +### 修改返回头 + * 在 http://httpbin.org/user-agent 的返回头里加上 X-Proxy-By:AnyProxy + +{{sample-rule:rule_sample/sample_modify_response_header.js}} + +### 修改返回内容并延迟 + * 在 http://httpbin.org/user-agent 的返回最后追加AnyProxy的签名,并延迟5秒 + +{{sample-rule:rule_sample/sample_modify_response_data.js}} + +# 证书配置 + +### OSX系统信任CA证书 + +* 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的 + + + +> 警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管 + +安装CA: + +* 双击打开*rootCA.crt* + +* 确认将证书添加到login或system + + + +* 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust) + + + +### Windows系统信任CA证书 + + + + +### 配置OSX系统代理 + +* 在wifi高级设置中,配置http代理即可 + + + +### 配置浏览器HTTP代理 + +* 以Chrome的[SwitchyOmega插件](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### iOS系统信任CA证书 + +* 点击web ui中的 *Root CA*,按提示扫描二维码即可安装 + + + +### iOS >= 10.3信任CA证书 + +* 除了上述证书安装过程,还需要在 *设置->通用->关于本机->证书信任设置* 中把AnyProxy证书的开关打开,否则safari将报错。 + + + +### 配置iOS/Android系统代理 + +* 代理服务器都在wifi设置中配置 + +* iOS HTTP代理配置 + + + +* Android HTTP代理配置 + + + +# FAQ + +#### Q: 为什么https请求不能进入处理函数? + A: 以下任意一项都能用来改变https的处理特性: + 1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析 + 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析 + +#### Q: 提示 *function is not yieldable* + * A: 规则模块是用 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。 + +#### Q: The connection is not private +当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问: +- 命令行直接启动 + + 通过启动参数 `--ignore-unauthorized-ssl` 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。 + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` + +- 在Nodejs代码中启动 + + 在构造AnyProxy实例的时候,传入参数`dangerouslyIgnoreUnauthorized:true`, 如下: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略* + +- 通过自定义的Rule来修改 + + 我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。 + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // 设置属性 rejectUnauthorized 为 false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` diff --git a/docs-src/en/README.md b/docs-src/en/README.md new file mode 100644 index 0000000..6561db0 --- /dev/null +++ b/docs-src/en/README.md @@ -0,0 +1,881 @@ +AnyProxy +=================== + +AnyProxy is a fully configurable http/https proxy in NodeJS. + +Ref: [中文文档](../cn) + +Github: + +* https://github.com/alibaba/anyproxy/tree/4.x + +Features: + +* Offer you the ablity to handle http traffic by invoking a js module +* Intercept https +* GUI webinterface + +Change Logs since 3.x: + +* Support Promise and Generator in rule module +* Simplified interface in rule module +* A newly designed web interface + + + +# Getting Start +### install + +To Debian and Ubuntu users, you may need to install `nodejs-legacy` at the same time + +```bash +sudo apg-get install nodejs-legacy +``` + +Then install the AnyProxy + +```bash +npm install -g anyproxy +``` + +### launch + +* start AnyProxy in command line, with default port 8001 + +```bash +anyproxy +``` + +* now you can use http proxy server by 127.0.0.1:8001 +* visit http://127.0.0.1:8002 to see the http requests + +### options + +* specify the port of http proxy + +```bash +anyproxy --port 1080 +``` + +### Use AnyProxy as an npm module + +AnyProxy can be used as an npm module + +> To enable https feature, please guide users to use `anyproxy-ca` in cli. Or use methods under `AnyProxy.utils.certMgr` to generate certificates. + +* install + +```bash +npm i anyproxy --save +``` + +* sample + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * create a proxy server + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} required, port number of proxy server + * `rule` {object} your rule module + * `throttle` {number} throttle in kb/s, unlimited for default + * `forceProxyHttps` {boolean} in force intercept all https request, false for default + * `silent` {boolean} if keep silent in console, false for default`false` + * `dangerouslyIgnoreUnauthorized` {boolean} if ignore certificate error in request, false for default + * `webInterface` {object} config for web interface + * `enable` {boolean} if enable web interface, false for default + * `webPort` {number} port number for web interface + * Event: `ready` + * emit when proxy server is ready + * sample + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * emit when error happened inside proxy server + * sample + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * start proxy server + * sample + + ```js + proxy.start(); + ``` + * Method: `close` + * close proxy server + * sample + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * manage the system proxy config. sudo password may be required + * sample + + ```js + // set 127.0.0.1:8001 as system http server + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // disable global proxy server + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * Manage certificates of AnyProxy + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * detect if AnyProx rootCA exists + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * generate a rootCA + * Sample + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# Proxy Https + +* AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate. + +> Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network. + +* generate certifycates and intercept + +```bash +anyproxy-ca #generate root CA. manually trust it after that. +anyproxy --intercept #launch anyproxy and intercept all https traffic +``` + +* [Appendix:how to trust CA](#config-certification) + +# Rule Introduction + +AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests. + +> Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk. + +Rule module could do the following stuff: +* intercept and modify the request which is being sent + * editable fields include request header, body, target address +* intercept and modify the response from server + * editable fields include response status code, header, body +* intercept https requests, modify request and response + +### sample + +* Target + * write a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds + +* Step 1,Write the rule file, save as sample.js + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + + newResponse.body += '
    -- AnyProxy Hacked! --'; + + newResponse.body += ``; + newResponse.header['Content-Type'] = 'text/html'; + + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, start AnyProxy and load the rule file + * run `anyproxy --rule sample.js` + +* Step 3, test + + * use curl + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent + + * the expected response from proxy is + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, view the request log + + * visit http://127.0.0.1:8002, the request just sent should be listed here + +### how does it work + +* The flow chart is as follows + + + +* When got an http request, the entire process of proxy server is + * AnyProxy collects all the quest info, include method, header, body + * AnyProxy calls `beforeSendRequest` of the rule module. Rule module deal the request, return new request param or response content + * If `beforeSendRequest` returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here. + * Send request to target server, collect response + * Call `beforeSendResponse` of the rule module. Rule module deal the response data + * Send response to client + +* When AnyProxy get https request, it could replace the certificate and decrypt the request data + * AnyProxy calls `beforeDealHttpsRequest` of the rule module + * If the function returns `true`, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed. + +### how to load rule module + +* use local file +```bash +anyproxy --rule ./rule.js +``` + +* use an online rule file +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* use an npm module + * AnyProxy uses `require()` to load rule module. You could either load a local npm module or a global-installed one. + + ```bash +anyproxy --rule ./myRulePkg/ #local module +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module + ``` + +# Rule module interface + +A typical rule module is as follows. All the functions are optional, just write the part you are interested in. + +```js +module.exports = { + // introduction + summary: 'my customized rule for AnyProxy', + // intercept before send request to server + *beforeSendRequest(requestDetail) { /* ... */ }, + // deal response before send to client + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // if deal https request + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // error happened when dealing requests + *onError(requestDetail, error) { /* ... */ }, + // error happened when connect to https server + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> All functions in your rule file, except summary, are all driven by [co](https://www.npmjs.com/package/co) . They should be yieldable, i.e. return a promise or be a generator function. + +### summary + +#### summary + +* Introduction of this rule file. AnyProxy will read this field and give some tip to user. + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* Before sending request to server, AnyProxy will call `beforeSendRequest` with param `requestDetail` +* `requestDetail` + * `protocol` {string} the protocol to use, http or https + * `requestOptions` {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} request body + * `url` {string} request url + * `_req` {object} the native node.js request object +* e.g. When requesting *anyproxy.io*, `requestDetail` is something like the following + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the request protocol,i.e. force use https + + ```js + return { + protocol: 'https' + }; + ``` + + * modify request param + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * modify request body + + ```js + return { + requestData: 'my new request data' + // requestOptions can also be used here + }; + ``` + * give response to the client, not sending request any longer. `statusCode` `headers`are required is this situation. + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* Before sending response to client, AnyProxy will call `beforeSendResponse` with param `requestDetail` `responseDetail` +* `requestDetail` is the same param as in `beforeSendRequest` +* `responseDetail` + * `response` {object} the response from server, includes `statusCode` `header` `body` + * `_res` {object} the native node.js response object +* e.g. When requesting *anyproxy.io*, `responseDetail` is something like the following + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the response status code + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * modify the response content + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* When receiving https request, AnyProxy will call `beforeDealHttpsRequest` with param `requestDetail` +* If configed with `forceProxyHttps` in launching, AnyProxy will skip calling this method +* Only by returning true, AnyProxy will try to replace the certificate and intercept the https request. +* `requestDetail` + * `host` {string} the target host to request. Due to the request protocol, full url couldn't be got here + * `_req` {object} the native node.js request object. The `_req` here refers to the CONNECT request. +* return value + * `true` or `false`, whether AnyProxy should intercept the https request + +### onError + +#### onError(requestDetail, error) + +* AnyProxy will call this method when an error happened in request handling. +* Errors usually are issued during requesting, e.g. DNS failure, request timeout +* `requestDetail` is the same one as in `beforeSendRequest` +* Any of these return values are valid + * do nothing, and AnyProxy will response a default error page + + ```js + return null; + ``` + + * return a customized error page + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy will call this method when failed to connect target server in https request +* `requestDetail` is the same one as in `beforeDealHttpsRequest` +* no return value is required + +# Rule Samples + +* here are some samples about frequently used rule file +* try these samples by `anyproxy --rule http://....js` +* how to test with curl: + * request the server directly `curl http://httpbin.org/` + * request the server via proxy `curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### use local response + * intercept the request towards http://httpbin.org , return the local-defined response + +```bash +anyproxy --rule rule_sample/sample_use_local_response.js +``` +```js +/* + sample: + intercept all requests toward httpbin.org, use a local response + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + const localResponse = { + statusCode: 200, + header: { 'Content-Type': 'application/json' }, + body: '{"hello": "this is local response"}' + }; + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + return { + response: localResponse + }; + } + }, +}; + +``` + +### modify request header + * modify the user-agent sent to httpbin.org + +```bash +anyproxy --rule rule_sample/sample_modify_request_header.js +``` +```js +/* + sample: + modify the user-agent in requests toward httpbin.org + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### modify request body + * modify the post body of http://httpbin.org/post + +```bash +anyproxy --rule rule_sample/sample_modify_request_data.js +``` +```js +/* + sample: + modify the post data towards http://httpbin.org/post + test: + curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001 + expected response: + { "data": "i-am-anyproxy-modified-post-data" } +*/ +module.exports = { + summary: 'Rule to modify request data', + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) { + return { + requestData: 'i-am-anyproxy-modified-post-data' + }; + } + }, +}; + +``` + +### modify the request target + * send all the request towards http://httpbin.org/ to http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_path.js +``` +```js +/* + sample: + redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html + test: + curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + expected response: + 'hello world' from 127.0.0.1:8001/index.html +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) { + const newRequestOptions = requestDetail.requestOptions; + requestDetail.protocol = 'http'; + newRequestOptions.hostname = '127.0.0.1' + newRequestOptions.port = '8008'; + newRequestOptions.path = '/index.html'; + newRequestOptions.method = 'GET'; + return requestDetail; + } + }, + *beforeDealHttpsRequest(requestDetail) { + return true; + } +}; + +``` + +### modify request protocol + * modify the http request towards http://httpbin.org to https + +```bash +anyproxy --rule rule_sample/sample_modify_request_protocol.js +``` +```js +/* + sample: + redirect all http requests of httpbin.org to https + test: + curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001 + expected response: + { "X-Forwarded-Protocol": "https" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newOption = requestDetail.requestOptions; + newOption.port = 443; + return { + protocol: 'https', + requestOptions: newOption + }; + } + } +}; + +``` + +### modify response status code + * modify all status code from http://httpbin.org to 404 + +```bash +anyproxy --rule rule_sample/sample_modify_response_statuscode.js +``` +```js +/* + sample: + modify all status code of http://httpbin.org/ to 404 + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + HTTP/1.1 404 Not Found +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 404; + return { + response: newResponse + }; + } + } +}; + +``` + +### modify the response header + * add X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_response_header.js +``` +```js +/* + sample: + modify response header of http://httpbin.org/user-agent + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + X-Proxy-By: AnyProxy +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) { + const newResponse = responseDetail.response; + newResponse.header['X-Proxy-By'] = 'AnyProxy'; + return { + response: newResponse + }; + } + } +}; + +``` + +### modify response data and delay + * append some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds. + +```bash +anyproxy --rule rule_sample/sample_modify_response_data.js +``` +```js +/* + sample: + modify response data of http://httpbin.org/user-agent + test: + curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! -- +*/ + +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay the response for 5s + resolve({ response: newResponse }); + }, 5000); + }); + } + }, +}; + +``` + +# Config Certification + +### Config root CA in OSX + +* this kind of errors is usually caused by untrusted root CA + + + +> Warning: please keep your root CA safe since it may influence your system security. + +install : + +* double click *rootCA.crt* + +* add cert into login or system + + + +* find the newly imported AnyProxy certificates, configured as **Always Trust** + + + +### Config root CA in windows + + + + +### Config OSX system proxy + +* the config is in wifi - advanced + + + +### config http proxy server + +* take Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### trust root CA in iOS + +* Click *Root CA* in web ui, and follow the instruction to install + + + +### trust root CA in iOS after 10.3 + +* Besides installing root CA, you have to "turn on" the certificate for web manually in *settings - general - about - Certificate Trust Settings*. Otherwire, safari will not trust the root CA generated by AnyProxy. + + + +### config iOS/Android proxy server + +* proxy settings are placed in wifi setting + +* iOS + + + +* Android + + + + +# FAQ + + +### Q: can not deal https request in rule module. + * A: Any of these options could be used to change the way AnyProxy deall https requests + 1. config `--intercept` when luanching AnyProxy via cli, or use `forceProxyHttps` when using as an npm module + 2. place a `beforeDealHttpsRequest` function in your rule file and determine which request to intercept by your own. + +### Q: get an error says *function is not yieldable* + * A: Rule module is driven by [co](https://www.npmjs.com/package/co). The functions inside should be yieldable, i.e. return a promise or be a generator function. + +### Q: The connection is not private +AnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below. + +- If you run AnyProxy by command line + Pass in the option `--ignore-unauthorized-ssl` to ignore the certification errors, please mind that the option will be active for all connections. + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` +- If you run AnyProxy by Nodejs + Pass in the option `dangerouslyIgnoreUnauthorized:true`, like this: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *This is also a global option, all certification errors will be ignored* + +- With the help of AnyProxy Rule + You can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs `Http.rquest`, as we do in AnyProxy. A simple demo below: + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // set rejectUnauthorized as false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` + + And we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to! diff --git a/docs-src/en/SUMMARY.md b/docs-src/en/SUMMARY.md new file mode 100644 index 0000000..af787b5 --- /dev/null +++ b/docs-src/en/SUMMARY.md @@ -0,0 +1,38 @@ +# Summary + +* [Introduction](README.md) +* [Getting-Start](doc.md#getting-start) + * [Install](doc.md#install) + * [Launch](doc.md#launch) + * [Options](doc.md#options) + * [As Node Module](doc.md#use-anyproxy-as-an-npm-module) +* [Proxy HTTPS](doc.md#proxy-https) +* [Rule Introduction](doc.md#rule-introduction) + * [Sample](doc.md#sample) + * [How Does It Work](doc.md#how-does-it-work) + * [Load A Rule](doc.md#how-to-load-rule-module) +* [Rule Module Interfaces](doc.md#rule-module-interface) + * [summary_class=rule-title](doc.md#summary) + * [beforeSendRequest_class=rule-title](doc.md#beforesendrequest) + * [beforeSendResponse_class=rule-title](doc.md#beforesendresponse) + * [beforeDealHttpsRequest_class=rule-title](doc.md#beforedealhttpsrequest) + * [onError_class=rule-title](doc.md#onerror) + * [onConnectError_class=rule-title](doc.md#onconnecterror) +* [Rule Samples](doc.md#rule-samples) + * [Use local response_class=sample-title](doc.md#use-local-response) + * [Modify Request Header_class=sample-title](doc.md#modify-request-header) + * [Modify Request Body_class=sample-title](doc.md#modify-request-body) + * [Modify The Request Target_class=sample-title](doc.md#modify-the-request-target) + * [Modify Request Protocol_class=sample-title](doc.md#modify-request-protocol) + * [Modify Response Status Code_class=sample-title](doc.md#modify-response-status-code) + * [Modify The Response Header_class=sample-title](doc.md#modify-the-response-header) + * [Modify Response Data And Delay_class=sample-title](doc.md#modify-response-data-and-delay) +* [Config Certification](doc.md#config-certification) + * [Config Root CA In OSX](doc.md#config-root-ca-in-osx) + * [Configure Root CA In windows](doc.md#config-root-ca-in-windows) + * [Config OSX System Proxy](doc.md#config-osx-system-proxy) + * [Config As Http Proxy Server](doc.md#config-http-proxy-server) + * [Trust Root CA In IOS](doc.md#trust-root-ca-in-ios) + * [Trust Root CA In iOS after 10.3](doc.md#trust-root-ca-in-ios-after-103) + * [Config IOS/Android Proxy Server](doc.md#config-iosandroid-proxy-server) +* [FAQ](doc.md) diff --git a/docs-src/en/_layouts/layout.html b/docs-src/en/_layouts/layout.html new file mode 100644 index 0000000..42b150c --- /dev/null +++ b/docs-src/en/_layouts/layout.html @@ -0,0 +1,30 @@ + + + + + + {% block title %}{{ config.title|d("GitBook", true) }}{% endblock %} + + + + {% if config.author %}{% endif %} + {% if config.isbn %}{% endif %} + {% block style %} + {% for resource in plugins.resources.css %} + {% if resource.url %} + + {% else %} + + {% endif %} + {% endfor %} + {% endblock %} + {% block head %}{% endblock %} + + + + + + {% block body %}{% endblock %} + {% block javascript %}{% endblock %} + + diff --git a/docs-src/en/_layouts/website/summary.html b/docs-src/en/_layouts/website/summary.html new file mode 100644 index 0000000..b6e041d --- /dev/null +++ b/docs-src/en/_layouts/website/summary.html @@ -0,0 +1,64 @@ +{% macro articles(_articles) %} + {% for article in _articles %} +
  • + {% if article.path and getPageByPath(article.path) %} + + {% elif article.url %} + + {% else %} + + {% endif %} + {% if article.level != "0" and config.pluginsConfig['theme-default'].showLevel %} + {{ article.level }}. + {% endif %} +
    + {{ article.title | replace(r/_class=.+/, '') }} +
    + + {% if article.path or article.url %} +
    + {% else %} + + {% endif %} + + {% if article.articles.length > 0 %} +
      + {{ articles(article.articles, file, config) }} +
    + {% endif %} +
  • + {% endfor %} +{% endmacro %} + +
      + {% set _divider = false %} + {% if config.links.sidebar %} + {% for linkTitle, link in config.links.sidebar %} + {% set _divider = true %} +
    • + {{ linkTitle }} +
    • + {% endfor %} + {% endif %} + + {% if _divider %} +
    • + {% endif %} + + {% for part in summary.parts %} + {% if part.title %} +
    • {{ part.title }}
    • + {% elif not loop.first %} +
    • + {% endif %} + {{ articles(part.articles, file, config) }} + {% endfor %} + +
    • + +
    • + + {{ "GITBOOK_LINK"|t }} + +
    • +
    diff --git a/docs-src/en/book.json b/docs-src/en/book.json new file mode 100644 index 0000000..a57cda5 --- /dev/null +++ b/docs-src/en/book.json @@ -0,0 +1,5 @@ +{ + "styles": { + "website": "styles/website.css" + } +} \ No newline at end of file diff --git a/docs-src/en/doc.md b/docs-src/en/doc.md new file mode 100644 index 0000000..eb0fb76 --- /dev/null +++ b/docs-src/en/doc.md @@ -0,0 +1,842 @@ +# Getting Start +### install + +To Debian and Ubuntu users, you may need to install `nodejs-legacy` at the same time + +```bash +sudo apg-get install nodejs-legacy +``` + +Then install the AnyProxy + +```bash +npm install -g anyproxy@beta # 4.x is in beta now +``` + +### launch + +* start AnyProxy in command line, with default port 8001 + +```bash +anyproxy +``` + +* now you can use http proxy server by 127.0.0.1:8001 +* visit http://127.0.0.1:8002 to see the http requests + +### options + +* specify the port of http proxy + +```bash +anyproxy --port 1080 +``` + +### Use AnyProxy as an npm module + +AnyProxy can be used as an npm module + +> To enable https feature, please guide users to use `anyproxy-ca` in cli. Or use methods under `AnyProxy.utils.certMgr` to generate certificates. + +* install + +```bash +npm i anyproxy@beta --save # 4.0 is in beta now +``` + +* sample + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * create a proxy server + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} required, port number of proxy server + * `rule` {object} your rule module + * `throttle` {number} throttle in kb/s, unlimited for default + * `forceProxyHttps` {boolean} in force intercept all https request, false for default + * `silent` {boolean} if keep silent in console, false for default`false` + * `dangerouslyIgnoreUnauthorized` {boolean} if ignore certificate error in request, false for default + * `webInterface` {object} config for web interface + * `enable` {boolean} if enable web interface, false for default + * `webPort` {number} port number for web interface + * Event: `ready` + * emit when proxy server is ready + * sample + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * emit when error happened inside proxy server + * sample + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * start proxy server + * sample + + ```js + proxy.start(); + ``` + * Method: `close` + * close proxy server + * sample + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * manage the system proxy config. sudo password may be required + * sample + + ```js + // set 127.0.0.1:8001 as system http server + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // disable global proxy server + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * Manage certificates of AnyProxy + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * detect if AnyProx rootCA exists + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * generate a rootCA + * Sample + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# Proxy Https + +* AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate. + +> Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network. + +* generate certifycates and intercept + +```bash +anyproxy-ca #generate root CA. manually trust it after that. +anyproxy --intercept #launch anyproxy and intercept all https traffic +``` + +* [Appendix:how to trust CA](#config-certification) + +# Rule Introduction + +AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests. + +> Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk. + +Rule module could do the following stuff: +* intercept and modify the request which is being sent + * editable fields include request header, body, target address +* intercept and modify the response from server + * editable fields include response status code, header, body +* intercept https requests, modify request and response + +### sample + +* Target + * write a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds + +* Step 1,Write the rule file, save as sample.js + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, start AnyProxy and load the rule file + * run `anyproxy --rule sample.js` + +* Step 3, test + + * use curl + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent + + * the expected response from proxy is + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, view the request log + + * visit http://127.0.0.1:8002, the request just sent should be listed here + +### how does it work + +* The flow chart is as follows + + + +* When got an http request, the entire process of proxy server is + * AnyProxy collects all the quest info, include method, header, body + * AnyProxy calls `beforeSendRequest` of the rule module. Rule module deal the request, return new request param or response content + * If `beforeSendRequest` returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here. + * Send request to target server, collect response + * Call `beforeSendResponse` of the rule module. Rule module deal the response data + * Send response to client + +* When AnyProxy get https request, it could replace the certificate and decrypt the request data + * AnyProxy calls `beforeDealHttpsRequest` of the rule module + * If the function returns `true`, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed. + +### how to load rule module + +* use local file +```bash +anyproxy --rule ./rule.js +``` + +* use an online rule file +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* use an npm module + * AnyProxy uses `require()` to load rule module. You could either load a local npm module or a global-installed one. + + ```bash +anyproxy --rule ./myRulePkg/ #local module +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module + ``` + +# Rule module interface + +A typical rule module is as follows. All the functions are optional, just write the part you are interested in. + +```js +module.exports = { + // introduction + summary: 'my customized rule for AnyProxy', + // intercept before send request to server + *beforeSendRequest(requestDetail) { /* ... */ }, + // deal response before send to client + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // if deal https request + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // error happened when dealing requests + *onError(requestDetail, error) { /* ... */ }, + // error happened when connect to https server + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> All functions in your rule file, except summary, are all driven by [co](https://www.npmjs.com/package/co) . They should be yieldable, i.e. return a promise or be a generator function. + +### summary + +#### summary + +* Introduction of this rule file. AnyProxy will read this field and give some tip to user. + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* Before sending request to server, AnyProxy will call `beforeSendRequest` with param `requestDetail` +* `requestDetail` + * `protocol` {string} the protocol to use, http or https + * `requestOptions` {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} request body + * `url` {string} request url + * `_req` {object} the native node.js request object +* e.g. When requesting *anyproxy.io*, `requestDetail` is something like the following + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the request protocol,i.e. force use https + + ```js + return { + protocol: 'https' + }; + ``` + + * modify request param + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * modify request body + + ```js + return { + requestData: 'my new request data' + // requestOptions can also be used here + }; + ``` + * give response to the client, not sending request any longer. `statusCode` `headers`are required is this situation. + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* Before sending response to client, AnyProxy will call `beforeSendResponse` with param `requestDetail` `responseDetail` +* `requestDetail` is the same param as in `beforeSendRequest` +* `responseDetail` + * `response` {object} the response from server, includes `statusCode` `header` `body` + * `_res` {object} the native node.js response object +* e.g. When requesting *anyproxy.io*, `responseDetail` is something like the following + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the response status code + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * modify the response content + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* When receiving https request, AnyProxy will call `beforeDealHttpsRequest` with param `requestDetail` +* If configed with `forceProxyHttps` in launching, AnyProxy will skip calling this method +* Only by returning true, AnyProxy will try to replace the certificate and intercept the https request. +* `requestDetail` + * `host` {string} the target host to request. Due to the request protocol, full url couldn't be got here + * `_req` {object} the native node.js request object. The `_req` here refers to the CONNECT request. +* return value + * `true` or `false`, whether AnyProxy should intercept the https request + +### onError + +#### onError(requestDetail, error) + +* AnyProxy will call this method when an error happened in request handling. +* Errors usually are issued during requesting, e.g. DNS failure, request timeout +* `requestDetail` is the same one as in `beforeSendRequest` +* Any of these return values are valid + * do nothing, and AnyProxy will response a default error page + + ```js + return null; + ``` + + * return a customized error page + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy will call this method when failed to connect target server in https request +* `requestDetail` is the same one as in `beforeDealHttpsRequest` +* no return value is required + +# Rule Samples + +* here are some samples about frequently used rule file +* try these samples by `anyproxy --rule http://....js` +* how to test with curl: + * request the server directly `curl http://httpbin.org/` + * request the server via proxy `curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### use local response + * intercept the request towards http://httpbin.org , return the local-defined response + +```bash +anyproxy --rule rule_sample/sample_use_local_response.js +``` +```js +/* + sample: + intercept all requests toward httpbin.org, use a local response + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + const localResponse = { + statusCode: 200, + header: { 'Content-Type': 'application/json' }, + body: '{"hello": "this is local response"}' + }; + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + return { + response: localResponse + }; + } + }, +}; + +``` + +### modify request header + * modify the user-agent sent to httpbin.org + +```bash +anyproxy --rule rule_sample/sample_modify_request_header.js +``` +```js +/* + sample: + modify the user-agent in requests toward httpbin.org + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### modify request body + * modify the post body of http://httpbin.org/post + +```bash +anyproxy --rule rule_sample/sample_modify_request_data.js +``` +```js +/* + sample: + modify the post data towards http://httpbin.org/post + test: + curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001 + expected response: + { "data": "i-am-anyproxy-modified-post-data" } +*/ +module.exports = { + summary: 'Rule to modify request data', + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) { + return { + requestData: 'i-am-anyproxy-modified-post-data' + }; + } + }, +}; + +``` + +### modify the request target + * send all the request towards http://httpbin.org/ to http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_path.js +``` +```js +/* + sample: + redirect all httpbin.org requests to http://httpbin.org/user-agent + test: + curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.path = '/user-agent'; + newRequestOptions.method = 'GET'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### modify request protocol + * modify the http request towards http://httpbin.org to https + +```bash +anyproxy --rule rule_sample/sample_modify_request_protocol.js +``` +```js +/* + sample: + redirect all http requests of httpbin.org to https + test: + curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001 + expected response: + { "X-Forwarded-Protocol": "https" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newOption = requestDetail.requestOptions; + newOption.port = 443; + return { + protocol: 'https', + requestOptions: newOption + }; + } + } +}; + +``` + +### modify response status code + * modify all status code from http://httpbin.org to 404 + +```bash +anyproxy --rule rule_sample/sample_modify_response_statuscode.js +``` +```js +/* + sample: + modify all status code of http://httpbin.org/ to 404 + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + HTTP/1.1 404 Not Found +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 404; + return { + response: newResponse + }; + } + } +}; + +``` + +### modify the response header + * add X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_response_header.js +``` +```js +/* + sample: + modify response header of http://httpbin.org/user-agent + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + X-Proxy-By: AnyProxy +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) { + const newResponse = responseDetail.response; + newResponse.header['X-Proxy-By'] = 'AnyProxy'; + return { + response: newResponse + }; + } + } +}; + +``` + +### modify response data and delay + * append some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds. + +```bash +anyproxy --rule rule_sample/sample_modify_response_data.js +``` +```js +/* + sample: + modify response data of http://httpbin.org/user-agent + test: + curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! -- +*/ + +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay the response for 5s + resolve({ response: newResponse }); + }, 5000); + }); + } + }, +}; + +``` + +# Config Certification + +### Config root CA in OSX + +* this kind of errors is usually caused by untrusted root CA + + + +> Warning: please keep your root CA safe since it may influence your system security. + +install : + +* double click *rootCA.crt* + +* add cert into login or system + + + +* find the newly imported AnyProxy certificates, configured as **Always Trust** + + + +### Config root CA in windows + + + + +### Config OSX system proxy + +* the config is in wifi - advanced + + + +### config http proxy server + +* take Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### trust root CA in iOS + +* Click *Root CA* in web ui, and follow the instruction to install + + + +### trust root CA in iOS after 10.3 + +* Besides installing root CA, you have to "turn on" the certificate for web manually in *settings - general - about - Certificate Trust Settings*. Otherwire, safari will not trust the root CA generated by AnyProxy. + + + +### config iOS/Android proxy server + +* proxy settings are placed in wifi setting + +* iOS + + + +* Android + + + + +# FAQ + + +### Q: can not deal https request in rule module. + * A: Any of these options could be used to change the way AnyProxy deall https requests + 1. config `--intercept` when luanching AnyProxy via cli, or use `forceProxyHttps` when using as an npm module + 2. place a `beforeDealHttpsRequest` function in your rule file and determine which request to intercept by your own. + +### Q: get an error says *function is not yieldable* + * A: Rule module is driven by [co](https://www.npmjs.com/package/co). The functions inside should be yieldable, i.e. return a promise or be a generator function. + +### Q: The connection is not private +AnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below. + +- If you run AnyProxy by command line + Pass in the option `--ignore-unauthorized-ssl` to ignore the certification errors, please mind that the option will be active for all connections. + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` +- If you run AnyProxy by Nodejs + Pass in the option `dangerouslyIgnoreUnauthorized:true`, like this: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *This is also a global option, all certification errors will be ignored* + +- With the help of AnyProxy Rule + You can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs `Http.rquest`, as we do in AnyProxy. A simple demo below: + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // set rejectUnauthorized as false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` + + And we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to! diff --git a/docs-src/en/src_doc.md b/docs-src/en/src_doc.md new file mode 100644 index 0000000..43572bb --- /dev/null +++ b/docs-src/en/src_doc.md @@ -0,0 +1,682 @@ +AnyProxy +=================== + +AnyProxy is a fully configurable http/https proxy in NodeJS. + +Ref: [中文文档](../cn) + +Github: + +* https://github.com/alibaba/anyproxy/tree/4.x + +Features: + +* Offer you the ablity to handle http traffic by invoking a js module +* Intercept https +* GUI webinterface + +Change Logs since 3.x: + +* Support Promise and Generator in rule module +* Simplified interface in rule module +* A newly designed web interface + + + +# Getting Start +### install + +To Debian and Ubuntu users, you may need to install `nodejs-legacy` at the same time + +```bash +sudo apg-get install nodejs-legacy +``` + +Then install the AnyProxy + +```bash +npm install -g anyproxy +``` + +### launch + +* start AnyProxy in command line, with default port 8001 + +```bash +anyproxy +``` + +* now you can use http proxy server by 127.0.0.1:8001 +* visit http://127.0.0.1:8002 to see the http requests + +### options + +* specify the port of http proxy + +```bash +anyproxy --port 1080 +``` + +### Use AnyProxy as an npm module + +AnyProxy can be used as an npm module + +> To enable https feature, please guide users to use `anyproxy-ca` in cli. Or use methods under `AnyProxy.utils.certMgr` to generate certificates. + +* install + +```bash +npm i anyproxy --save +``` + +* sample + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * create a proxy server + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} required, port number of proxy server + * `rule` {object} your rule module + * `throttle` {number} throttle in kb/s, unlimited for default + * `forceProxyHttps` {boolean} in force intercept all https request, false for default + * `silent` {boolean} if keep silent in console, false for default`false` + * `dangerouslyIgnoreUnauthorized` {boolean} if ignore certificate error in request, false for default + * `webInterface` {object} config for web interface + * `enable` {boolean} if enable web interface, false for default + * `webPort` {number} port number for web interface + * Event: `ready` + * emit when proxy server is ready + * sample + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * emit when error happened inside proxy server + * sample + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * start proxy server + * sample + + ```js + proxy.start(); + ``` + * Method: `close` + * close proxy server + * sample + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * manage the system proxy config. sudo password may be required + * sample + + ```js + // set 127.0.0.1:8001 as system http server + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // disable global proxy server + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * Manage certificates of AnyProxy + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * detect if AnyProx rootCA exists + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * generate a rootCA + * Sample + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# Proxy Https + +* AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate. + +> Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network. + +* generate certifycates and intercept + +```bash +anyproxy-ca #generate root CA. manually trust it after that. +anyproxy --intercept #launch anyproxy and intercept all https traffic +``` + +* [Appendix:how to trust CA](#config-certification) + +# Rule Introduction + +AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests. + +> Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk. + +Rule module could do the following stuff: +* intercept and modify the request which is being sent + * editable fields include request header, body, target address +* intercept and modify the response from server + * editable fields include response status code, header, body +* intercept https requests, modify request and response + +### sample + +* Target + * write a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds + +* Step 1,Write the rule file, save as sample.js + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + + newResponse.body += '
    -- AnyProxy Hacked! --'; + + newResponse.body += ``; + newResponse.header['Content-Type'] = 'text/html'; + + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, start AnyProxy and load the rule file + * run `anyproxy --rule sample.js` + +* Step 3, test + + * use curl + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent + + * the expected response from proxy is + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, view the request log + + * visit http://127.0.0.1:8002, the request just sent should be listed here + +### how does it work + +* The flow chart is as follows + + + +* When got an http request, the entire process of proxy server is + * AnyProxy collects all the quest info, include method, header, body + * AnyProxy calls `beforeSendRequest` of the rule module. Rule module deal the request, return new request param or response content + * If `beforeSendRequest` returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here. + * Send request to target server, collect response + * Call `beforeSendResponse` of the rule module. Rule module deal the response data + * Send response to client + +* When AnyProxy get https request, it could replace the certificate and decrypt the request data + * AnyProxy calls `beforeDealHttpsRequest` of the rule module + * If the function returns `true`, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed. + +### how to load rule module + +* use local file +```bash +anyproxy --rule ./rule.js +``` + +* use an online rule file +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* use an npm module + * AnyProxy uses `require()` to load rule module. You could either load a local npm module or a global-installed one. + + ```bash +anyproxy --rule ./myRulePkg/ #local module +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module + ``` + +# Rule module interface + +A typical rule module is as follows. All the functions are optional, just write the part you are interested in. + +```js +module.exports = { + // introduction + summary: 'my customized rule for AnyProxy', + // intercept before send request to server + *beforeSendRequest(requestDetail) { /* ... */ }, + // deal response before send to client + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // if deal https request + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // error happened when dealing requests + *onError(requestDetail, error) { /* ... */ }, + // error happened when connect to https server + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> All functions in your rule file, except summary, are all driven by [co](https://www.npmjs.com/package/co) . They should be yieldable, i.e. return a promise or be a generator function. + +### summary + +#### summary + +* Introduction of this rule file. AnyProxy will read this field and give some tip to user. + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* Before sending request to server, AnyProxy will call `beforeSendRequest` with param `requestDetail` +* `requestDetail` + * `protocol` {string} the protocol to use, http or https + * `requestOptions` {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} request body + * `url` {string} request url + * `_req` {object} the native node.js request object +* e.g. When requesting *anyproxy.io*, `requestDetail` is something like the following + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the request protocol,i.e. force use https + + ```js + return { + protocol: 'https' + }; + ``` + + * modify request param + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * modify request body + + ```js + return { + requestData: 'my new request data' + // requestOptions can also be used here + }; + ``` + * give response to the client, not sending request any longer. `statusCode` `headers`are required is this situation. + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* Before sending response to client, AnyProxy will call `beforeSendResponse` with param `requestDetail` `responseDetail` +* `requestDetail` is the same param as in `beforeSendRequest` +* `responseDetail` + * `response` {object} the response from server, includes `statusCode` `header` `body` + * `_res` {object} the native node.js response object +* e.g. When requesting *anyproxy.io*, `responseDetail` is something like the following + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the response status code + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * modify the response content + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* When receiving https request, AnyProxy will call `beforeDealHttpsRequest` with param `requestDetail` +* If configed with `forceProxyHttps` in launching, AnyProxy will skip calling this method +* Only by returning true, AnyProxy will try to replace the certificate and intercept the https request. +* `requestDetail` + * `host` {string} the target host to request. Due to the request protocol, full url couldn't be got here + * `_req` {object} the native node.js request object. The `_req` here refers to the CONNECT request. +* return value + * `true` or `false`, whether AnyProxy should intercept the https request + +### onError + +#### onError(requestDetail, error) + +* AnyProxy will call this method when an error happened in request handling. +* Errors usually are issued during requesting, e.g. DNS failure, request timeout +* `requestDetail` is the same one as in `beforeSendRequest` +* Any of these return values are valid + * do nothing, and AnyProxy will response a default error page + + ```js + return null; + ``` + + * return a customized error page + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy will call this method when failed to connect target server in https request +* `requestDetail` is the same one as in `beforeDealHttpsRequest` +* no return value is required + +# Rule Samples + +* here are some samples about frequently used rule file +* try these samples by `anyproxy --rule http://....js` +* how to test with curl: + * request the server directly `curl http://httpbin.org/` + * request the server via proxy `curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### use local response + * intercept the request towards http://httpbin.org , return the local-defined response + +{{sample-rule:rule_sample/sample_use_local_response.js}} + +### modify request header + * modify the user-agent sent to httpbin.org + +{{sample-rule:rule_sample/sample_modify_request_header.js}} + +### modify request body + * modify the post body of http://httpbin.org/post + +{{sample-rule:rule_sample/sample_modify_request_data.js}} + +### modify the request target + * send all the request towards http://httpbin.org/ to http://httpbin.org/user-agent + +{{sample-rule:rule_sample/sample_modify_request_path.js}} + +### modify request protocol + * modify the http request towards http://httpbin.org to https + +{{sample-rule:rule_sample/sample_modify_request_protocol.js}} + +### modify response status code + * modify all status code from http://httpbin.org to 404 + +{{sample-rule:rule_sample/sample_modify_response_statuscode.js}} + +### modify the response header + * add X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent + +{{sample-rule:rule_sample/sample_modify_response_header.js}} + +### modify response data and delay + * append some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds. + +{{sample-rule:rule_sample/sample_modify_response_data.js}} + +# Config Certification + +### Config root CA in OSX + +* this kind of errors is usually caused by untrusted root CA + + + +> Warning: please keep your root CA safe since it may influence your system security. + +install : + +* double click *rootCA.crt* + +* add cert into login or system + + + +* find the newly imported AnyProxy certificates, configured as **Always Trust** + + + +### Config root CA in windows + + + + +### Config OSX system proxy + +* the config is in wifi - advanced + + + +### config http proxy server + +* take Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### trust root CA in iOS + +* Click *Root CA* in web ui, and follow the instruction to install + + + +### trust root CA in iOS after 10.3 + +* Besides installing root CA, you have to "turn on" the certificate for web manually in *settings - general - about - Certificate Trust Settings*. Otherwire, safari will not trust the root CA generated by AnyProxy. + + + +### config iOS/Android proxy server + +* proxy settings are placed in wifi setting + +* iOS + + + +* Android + + + + +# FAQ + + +### Q: can not deal https request in rule module. + * A: Any of these options could be used to change the way AnyProxy deall https requests + 1. config `--intercept` when luanching AnyProxy via cli, or use `forceProxyHttps` when using as an npm module + 2. place a `beforeDealHttpsRequest` function in your rule file and determine which request to intercept by your own. + +### Q: get an error says *function is not yieldable* + * A: Rule module is driven by [co](https://www.npmjs.com/package/co). The functions inside should be yieldable, i.e. return a promise or be a generator function. + +### Q: The connection is not private +AnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below. + +- If you run AnyProxy by command line + Pass in the option `--ignore-unauthorized-ssl` to ignore the certification errors, please mind that the option will be active for all connections. + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` +- If you run AnyProxy by Nodejs + Pass in the option `dangerouslyIgnoreUnauthorized:true`, like this: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *This is also a global option, all certification errors will be ignored* + +- With the help of AnyProxy Rule + You can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs `Http.rquest`, as we do in AnyProxy. A simple demo below: + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // set rejectUnauthorized as false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` + + And we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to! diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..edd7608 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +anyproxy.io \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c55bde3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,2 @@ +# THIS IS AUTO GENERATED FILE, DO NOT EDIT THE HTML DIRECTLY. +# YOU CAN EDIT THE SOURCE IN docs-md FOLDER diff --git a/docs/assets/auto-lang.js b/docs/assets/auto-lang.js new file mode 100644 index 0000000..ae1bf34 --- /dev/null +++ b/docs/assets/auto-lang.js @@ -0,0 +1,23 @@ +/* eslint no-var: off */ +/** +* detect if the browser is in UTF-8 zone +* @return boolean +*/ +function isUTF8Zone() { + return new Date().getTimezoneOffset() === -480; +} + +/** +* detect if the browser is already in a locale view +*/ +function isInLocaleView() { + return /(cn|en)/i.test(location.href); +} + +function initDefaultLocaleAndStatic() { + if (!isInLocaleView()) { + location.href = isUTF8Zone() ? '/cn' : 'en'; + } +} + +initDefaultLocaleAndStatic(); diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png new file mode 100644 index 0000000..0969aee Binary files /dev/null and b/docs/assets/favicon.png differ diff --git a/docs/assets/main.js b/docs/assets/main.js new file mode 100644 index 0000000..feb1d8e --- /dev/null +++ b/docs/assets/main.js @@ -0,0 +1,12 @@ +/* eslint no-var: off */ +function injectBaiduStatic() { + var _hmt = _hmt || []; + var hm = document.createElement('script'); + var s = document.getElementsByTagName('script')[0]; + + hm.src = '//hm.baidu.com/hm.js?4e51565b7d471fd6623c163a8fd79e07'; + s.parentNode.insertBefore(hm, s); +} + + +injectBaiduStatic(); diff --git a/docs/assets/website.css b/docs/assets/website.css new file mode 100644 index 0000000..ee2f8a2 --- /dev/null +++ b/docs/assets/website.css @@ -0,0 +1,51 @@ +.book .book-summary ul.summary li.active>a, .book .book-summary ul.summary li a:hover { + color: #008cff; + background: transparent; + text-decoration: none !important; +} + +h1 { + color: #2674BA; +} +h2 { + color: #0099CC; +} +h3 { + color: #108ee9; +} +h4 { + color: #662D91; +} +h5 { + color: #444444; +} + +.gitbook-link { + display: none !important; +} + +.summary-title-span { + position: relative !important; + padding: 0 !important; +} +.rule-title:after, +.sample-title:after { + font-size: 12px; + padding: 0 3px; + border-radius: 3px; + color: #fff; +} + +.rule-title:after{ + content: 'rule'; + background-color: #108ee9; +} + +.sample-title:after { + content: 'sample'; + background-color: #00a854; +} + +.page-inner { + max-width: 1000px !important; +} diff --git a/docs/cn/doc.md b/docs/cn/doc.md new file mode 100644 index 0000000..8faeb35 --- /dev/null +++ b/docs/cn/doc.md @@ -0,0 +1,848 @@ +# 快速开始 +## 作为全局模块 +### 安装 + +对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 `nodejs-legacy` + +```bash +sudo apg-get install nodejs-legacy +``` + +然后,安装AnyProxy + +```bash +npm install -g anyproxy@beta #本文档对应的AnyProxy为4.0Beta版 +``` + +### 启动 + +* 命令行启动AnyProxy,默认端口号8001 + +```bash +anyproxy +``` + +* 启动后将终端http代理服务器配置为127.0.0.1:8001即可 +* 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息 + +### 其他命令 + +* 配置启动端口,如1080端口启动 + +```bash +anyproxy --port 1080 +``` + +## 作为npm模块使用 + +AnyProxy可以作为一个npm模块使用,整合进其他工具。 + +> 如要启用https解析,请在代理服务器启动前自行调用`AnyProxy.utils.certMgr`相关方法生成证书,并引导用户信任安装。或引导用户使用`anyproxy-ca`方法。 + +* 引入 + +```bash +npm i anyproxy@beta --save # 4.0版正在beta中 +``` + +* 使用举例 + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * 创建代理服务器 + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} 必选,代理服务器端口 + * `rule` {object} 自定义规则模块 + * `throttle` {number} 限速值,单位kb/s,默认不限速 + * `forceProxyHttps` {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认`false` + * `silent` {boolean} 是否屏蔽所有console输出,默认`false` + * `dangerouslyIgnoreUnauthorized` {boolean} 是否忽略请求中的证书错误,默认`false` + * `webInterface` {object} web版界面配置 + * `enable` {boolean} 是否启用web版界面,默认`false` + * `webPort` {number} web版界面端口号,默认`8002` + * Event: `ready` + * 代理服务器启动完成 + * 示例 + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * 代理服务器发生错误 + * 示例 + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * 启动代理服务器 + * 示例 + + ```js + proxy.start(); + ``` + * Method: `close` + * 关闭代理服务器 + * 示例 + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * 管理系统的全局代理配置,方法调用时可能会弹出密码框 + * 使用示例 + + ```js + // 配置127.0.0.1:8001为全局http代理服务器 + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // 关闭全局代理服务器 + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * 管理AnyProxy的证书 + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * 校验系统内是否存在AnyProxy的根证书 + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件 + * 样例 + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# 代理HTTPS +* AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书 + +> 解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程 + +* 生成证书并解析所有https请求 + +```bash +anyproxy-ca #生成rootCA证书,生成后需要手动信任 +anyproxy --intercept #启动AnyProxy,并解析所有https请求 +``` + +* [附录:如何信任CA证书](#证书配置) + +# rule模块 + +AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。 + +>注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题 + +规则模块的能力范围包括: + +* 拦截并修改正在发送的请求 + * 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等 +* 拦截并修改服务端响应 + * 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等 +* 拦截https请求,对内容做修改 + * 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA + +### 开发示例 + +* 举例 + * 需要编写一个规则模块,在 GET http://httpbin.org/user-agent 的返回值里加上测试信息,并延迟5秒返回 + +* Step 1,编写规则 + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, 启动AnyProxy,加载规则 + * 运行 `anyproxy --rule sample.js` + +* Step 3, 测试规则 + + * 用curl测试 + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent + + * 经过代理服务器后,期望的返回如下 + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, 查看请求信息 + + * 浏览器访问http://127.0.0.1:8002 ,界面上能看到刚才的请求信息 + +### 处理流程 + +* 处理流程图如下 + + + +* 当http请求经过代理服务器时,具体处理过程是: + * 收集请求所有请求参数,包括method, header, body等 + * AnyProxy调用规则模块`beforeSendRequest`方法,由模块做处理,返回新的请求参数,或返回响应内容 + * 如果`beforeSendRequest`返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。 + * 根据请求参数,向服务端发出请求,接收服务端响应。 + * 调用规则模块`beforeSendResponse`方法,由模块对响应内容进行处理 + * 把响应信息返回给客户端 + +* 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。 + * 调用规则模块`beforeDealHttpsRequest`方法,如果返回`true`,会明文解析这个请求,其他请求不处理 + * 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。 + + +### 如何引用 + +如下几种方案都可以用来引用规则模块: + +* 使用本地路径 +```bash +anyproxy --rule ./rule.js +``` +* 使用在线地址 +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* 使用npm包 + * AnyProxy使用`require()`加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包 + + ```bash +anyproxy --rule ./myRulePkg/ #本地包 +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包 + ``` + + +# rule接口文档 + +规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。 + +```js +module.exports = { + // 模块介绍 + summary: 'my customized rule for AnyProxy', + // 发送请求前拦截处理 + *beforeSendRequest(requestDetail) { /* ... */ }, + // 发送响应前处理 + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // 是否处理https请求 + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // 请求出错的事件 + *onError(requestDetail, error) { /* ... */ }, + // https连接服务器出错 + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> 规则文件中,除了summary,都是由 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。 + +### summary + +#### summary(): string | summary:string + +* 规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串 + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* AnyProxy向服务端发送请求前,会调用`beforeSendRequest`,并带上参数`requestDetail` +* `requestDetail` + * `protocol` {string} 请求使用的协议,http或者https + * `requestOptions` {object} 即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} 请求Body + * `url` {string} 请求url + * `_req` {object} 请求的原始request +* 举例:请求 *anyproxy.io* 时,`requestDetail`参数内容大致如下 + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改请求协议,如强制改用https发起请求 + + ```js + return { + protocol: 'https' + }; + ``` + + * 修改请求参数 + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * 修改请求body + + ```js + return { + requestData: 'my new request data' + //这里也可以同时加上requestOptions + }; + ``` + * 直接返回客户端,不再发起请求,其中`statusCode` `header` 是必选字段 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* AnyProxy向客户端发送请求前,会调用`beforeSendResponse`,并带上参数`requestDetail` `responseDetail` +* `requestDetail` 同`beforeSendRequest`中的参数 +* `responseDetail` + * `response` {object} 服务端的返回信息,包括`statusCode` `header` `body`三个字段 + * `_res` {object} 原始的服务端返回对象 +* 举例,请求 *anyproxy.io* 时,`responseDetail`参数内容大致如下 + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改返回的状态码 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * 修改返回的内容 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* AnyProxy收到https请求时,会调用`beforeDealHttpsRequest`,并带上参数`requestDetail` +* 如果配置了全局解析https的参数,则AnyProxy会略过这个调用 +* 只有返回`true`时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。 +* 注意:https over http的代理模式中,这里的request是CONNECT请求 +* `requestDetail` + * `host` {string} 请求目标的Host,受制于协议,这里无法获取完整url + * `_req` {object} 请求的原始request +* 返回值 + * `true`或者`false`,表示是否需要AnyProxy替换证书并解析https + +### onError + +#### onError(requestDetail, error) + +* 在请求处理过程中发生错误时,AnyProxy会调用`onError`方法,并提供对应的错误信息 +* 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等 +* `requestDetail` 同`beforeSendRequest`中的参数 +* 以下几种返回都是合法的 + * 不做任何处理。此时AnyProxy会返回一个默认的错误页。 + + ```js + return null; + ``` + + * 返回自定义错误页 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法 +* `requestDetail` 同`beforeDealHttpsRequest`中的参数 +* 此处无法控制向客户端的返回信息,无需返回值。 + +# rule样例 + +* 这里提供一些样例,来讲解规则模块的常见用法 +* 你可以通过 `anyproxy --rule http://....js` 来加载模块并体验 +* 用curl发请求测试的方法如下 + * 直接请求服务器:`curl http://httpbin.org/` + * 通过代理服务器请求:`curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### 使用本地数据 + * 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回 + +```bash +anyproxy --rule rule_sample/sample_use_local_response.js +``` +```js +/* + sample: + intercept all requests toward httpbin.org, use a local response + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + const localResponse = { + statusCode: 200, + header: { 'Content-Type': 'application/json' }, + body: '{"hello": "this is local response"}' + }; + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + return { + response: localResponse + }; + } + }, +}; + +``` + +### 修改请求头 + * 修改发送到 httpbin.org 的user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_header.js +``` +```js +/* + sample: + modify the user-agent in requests toward httpbin.org + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### 修改请求数据 + * 修改发送到 http://httpbin.org/post 的post数据 + +```bash +anyproxy --rule rule_sample/sample_modify_request_data.js +``` +```js +/* + sample: + modify the post data towards http://httpbin.org/post + test: + curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001 + expected response: + { "data": "i-am-anyproxy-modified-post-data" } +*/ +module.exports = { + summary: 'Rule to modify request data', + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) { + return { + requestData: 'i-am-anyproxy-modified-post-data' + }; + } + }, +}; + +``` + +### 修改请求的目标地址 + * 把所有发送到 http://httpbin.org/ 的请求全部改到 http://httpbin.org/user-agent + +```bash +anyproxy --rule rule_sample/sample_modify_request_path.js +``` +```js +/* + sample: + redirect all httpbin.org requests to http://httpbin.org/user-agent + test: + curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.path = '/user-agent'; + newRequestOptions.method = 'GET'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; + +``` + +### 修改请求协议 + * 把用http协议请求的 http://httpbin.org 改成https并发送 + +```bash +anyproxy --rule rule_sample/sample_modify_request_protocol.js +``` +```js +/* + sample: + redirect all http requests of httpbin.org to https + test: + curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001 + expected response: + { "X-Forwarded-Protocol": "https" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newOption = requestDetail.requestOptions; + newOption.port = 443; + return { + protocol: 'https', + requestOptions: newOption + }; + } + } +}; + +``` + +### 修改返回状态码 + * 把 所有http://httpbin.org 的返回状态码都改成404 + +```bash +anyproxy --rule rule_sample/sample_modify_response_statuscode.js +``` +```js +/* + sample: + modify all status code of http://httpbin.org/ to 404 + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + HTTP/1.1 404 Not Found +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 404; + return { + response: newResponse + }; + } + } +}; + +``` + +### 修改返回头 + * 在 http://httpbin.org/user-agent 的返回头里加上 X-Proxy-By:AnyProxy + +```bash +anyproxy --rule rule_sample/sample_modify_response_header.js +``` +```js +/* + sample: + modify response header of http://httpbin.org/user-agent + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + X-Proxy-By: AnyProxy +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) { + const newResponse = responseDetail.response; + newResponse.header['X-Proxy-By'] = 'AnyProxy'; + return { + response: newResponse + }; + } + } +}; + +``` + +### 修改返回内容并延迟 + * 在 http://httpbin.org/user-agent 的返回最后追加AnyProxy的签名,并延迟5秒 + +```bash +anyproxy --rule rule_sample/sample_modify_response_data.js +``` +```js +/* + sample: + modify response data of http://httpbin.org/user-agent + test: + curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! -- +*/ + +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay the response for 5s + resolve({ response: newResponse }); + }, 5000); + }); + } + }, +}; + +``` + +# 证书配置 + +### OSX系统信任CA证书 + +* 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的 + + + +> 警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管 + +安装CA: + +* 双击打开*rootCA.crt* + +* 确认将证书添加到login或system + + + +* 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust) + + + +### Windows系统信任CA证书 + + + + +### 配置OSX系统代理 + +* 在wifi高级设置中,配置http代理即可 + + + +### 配置浏览器HTTP代理 + +* 以Chrome的[SwitchyOmega插件](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### iOS系统信任CA证书 + +* 点击web ui中的 *Root CA*,按提示扫描二维码即可安装 + + + +### iOS >= 10.3信任CA证书 + +* 除了上述证书安装过程,还需要在 *设置->通用->关于本机->证书信任设置* 中把AnyProxy证书的开关打开,否则safari将报错。 + + + +### 配置iOS/Android系统代理 + +* 代理服务器都在wifi设置中配置 + +* iOS HTTP代理配置 + + + +* Android HTTP代理配置 + + + +# FAQ + +#### Q: 为什么https请求不能进入处理函数? + A: 以下任意一项都能用来改变https的处理特性: + 1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析 + 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析 + +#### Q: 提示 *function is not yieldable* + * A: 规则模块是用 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。 + +#### Q: The connection is not private +当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问: +- 命令行直接启动 + + 通过启动参数 `--ignore-unauthorized-ssl` 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。 + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` + +- 在Nodejs代码中启动 + + 在构造AnyProxy实例的时候,传入参数`dangerouslyIgnoreUnauthorized:true`, 如下: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略* + +- 通过自定义的Rule来修改 + + 我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。 + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // 设置属性 rejectUnauthorized 为 false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` \ No newline at end of file diff --git a/docs/cn/index.html b/docs/cn/index.html new file mode 100644 index 0000000..8a71c2a --- /dev/null +++ b/docs/cn/index.html @@ -0,0 +1,1675 @@ + + + + + + + 简介 · AnyProxy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    +
    + + + + + + + + +
    + +
    + +
    + + + + + + + + +
    +
    + +
    +
    + +
    + +

    AnyProxy

    +
    +

    本文档的适用范围是AnyProxy 4.0,欢迎提供反馈

    +
    +

    Ref: English Doc

    +

    AnyProxy是一个开放式的HTTP代理服务器。

    +

    Github主页:https://github.com/alibaba/anyproxy/tree/4.x

    +

    主要特性包括:

    +
      +
    • 基于Node.js,开放二次开发能力,允许自定义请求处理逻辑
    • +
    • 支持Https的解析
    • +
    • 提供GUI界面,用以观察请求 +相比3.x版本,AnyProxy 4.0的主要变化:

      +
    • +
    • 规则文件(Rule)全面支持Promise和Generator

      +
    • +
    • 简化了规则文件内的接口
    • +
    • Web版界面重构
    • +
    +

    +

    快速开始

    +

    作为全局模块

    +

    安装

    +

    对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 nodejs-legacy

    +
    sudo apg-get install nodejs-legacy
    +
    +

    然后,安装AnyProxy

    +
    npm install -g anyproxy
    +
    +

    启动

    +
      +
    • 命令行启动AnyProxy,默认端口号8001
    • +
    +
    anyproxy
    +
    +
      +
    • 启动后将终端http代理服务器配置为127.0.0.1:8001即可
    • +
    • 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息
    • +
    +

    其他命令

    +
      +
    • 配置启动端口,如1080端口启动
    • +
    +
    anyproxy --port 1080
    +
    +

    作为npm模块使用

    +

    AnyProxy可以作为一个npm模块使用,整合进其他工具。

    +
    +

    如要启用https解析,请在代理服务器启动前自行调用AnyProxy.utils.certMgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxy-ca方法。

    +
    +
      +
    • 引入
    • +
    +
    npm i anyproxy --save
    +
    +
      +
    • 使用举例
    • +
    +
    const AnyProxy = require('anyproxy');
    +const options = {
    +  port: 8001,
    +  rule: require('myRuleModule'),
    +  webInterface: {
    +    enable: true,
    +    webPort: 8002,
    +    wsPort: 8003,
    +  },
    +  throttle: 10000,
    +  forceProxyHttps: false,
    +  silent: false
    +};
    +const proxyServer = new AnyProxy.ProxyServer(options);
    +
    +proxyServer.on('ready', () => { /* */ });
    +proxyServer.on('error', (e) => { /* */ });
    +proxyServer.start();
    +
    +//when finished
    +proxyServer.close();
    +
    +
      +
    • Class: AnyProxy.proxyServer

      +
        +
      • 创建代理服务器

        +
        const proxy = new AnyProxy.proxyServer(options)
        +
        +
      • +
      • options

        +
          +
        • port {number} 必选,代理服务器端口
        • +
        • rule {object} 自定义规则模块
        • +
        • throttle {number} 限速值,单位kb/s,默认不限速
        • +
        • forceProxyHttps {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认false
        • +
        • silent {boolean} 是否屏蔽所有console输出,默认false
        • +
        • dangerouslyIgnoreUnauthorized {boolean} 是否忽略请求中的证书错误,默认false
        • +
        • webInterface {object} web版界面配置
            +
          • enable {boolean} 是否启用web版界面,默认false
          • +
          • webPort {number} web版界面端口号,默认8002
          • +
          +
        • +
        +
      • +
      • Event: ready

        +
          +
        • 代理服务器启动完成
        • +
        • 示例
        • +
        +
        proxy.on('ready', function() { })
        +
        +
      • +
      • Event: error

        +
          +
        • 代理服务器发生错误
        • +
        • 示例
        • +
        +
        proxy.on('error', function() { })
        +
        +
      • +
      • Method: start

        +
          +
        • 启动代理服务器
        • +
        • 示例
        • +
        +
        proxy.start();
        +
        +
      • +
      • Method: close

        +
          +
        • 关闭代理服务器
        • +
        • 示例
        • +
        +
        proxy.close();
        +
        +
      • +
      +
    • +
    • AnyProxy.utils.systemProxyMgr

      +
        +
      • 管理系统的全局代理配置,方法调用时可能会弹出密码框
      • +
      • 使用示例
      • +
      +
      // 配置127.0.0.1:8001为全局http代理服务器
      +AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');
      +
      +// 关闭全局代理服务器
      +AnyProxy.utils.systemProxyMgr.disableGlobalProxy();
      +
      +
    • +
    • AnyProxy.utils.certMgr

      +
        +
      • 管理AnyProxy的证书
      • +
      • AnyProxy.utils.certMgr.ifRootCAFileExists()
          +
        • 校验系统内是否存在AnyProxy的根证书
        • +
        +
      • +
      • AnyProxy.utils.certMgr.generateRootCA(callback)
          +
        • 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件
        • +
        +
      • +
      • 样例
      • +
      +
        const AnyProxy = require('AnyProxy');
      +  const exec = require('child_process').exec;
      +
      +  if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {
      +    AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {
      +      // let users to trust this CA before using proxy
      +      if (!error) {
      +        const certDir = require('path').dirname(keyPath);
      +        console.log('The cert is generated at', certDir);
      +        const isWin = /^win/.test(process.platform);
      +        if (isWin) {
      +          exec('start .', { cwd: certDir });
      +        } else {
      +          exec('open .', { cwd: certDir });
      +        }
      +      } else {
      +        console.error('error when generating rootCA', error);
      +      }
      +    });
      +  }
      +
      +
    • +
    +

    代理HTTPS

    +
      +
    • AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书
    • +
    +
    +

    解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程

    +
    +
      +
    • 生成证书并解析所有https请求
    • +
    +
    anyproxy-ca #生成rootCA证书,生成后需要手动信任
    +anyproxy --intercept #启动AnyProxy,并解析所有https请求
    +
    + +

    rule模块

    +

    AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。

    +
    +

    注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题

    +
    +

    规则模块的能力范围包括:

    +
      +
    • 拦截并修改正在发送的请求
        +
      • 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等
      • +
      +
    • +
    • 拦截并修改服务端响应
        +
      • 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等
      • +
      +
    • +
    • 拦截https请求,对内容做修改
        +
      • 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA
      • +
      +
    • +
    +

    开发示例

    +
      +
    • 举例

      + +
    • +
    • Step 1,编写规则

      +
      // file: sample.js
      +module.exports = {
      +  summary: 'a rule to modify response',
      +  *beforeSendResponse(requestDetail, responseDetail) {
      +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
      +      const newResponse = responseDetail.response;
      +      newResponse.body += '<br/><span style="color:blue">-- AnyProxy Hacked! --</span>';
      +
      +      newResponse.body += `<script>
      +        setTimeout(
      +          function (){
      +            window.alert("Sorry, You Are Hacked...")
      +          }, 300);
      +        </script>`;
      +      newResponse.header['Content-Type'] = 'text/html';
      +
      +      return new Promise((resolve, reject) => {
      +        setTimeout(() => { // delay
      +          resolve({ response: newResponse });
      +        }, 5000);
      +      });
      +    }
      +  },
      +};
      +
      +
    • +
    • Step 2, 启动AnyProxy,加载规则

      +
        +
      • 运行 anyproxy --rule sample.js
      • +
      +
    • +
    • Step 3, 测试规则

      +
        +
      • 用curl测试

        +
        curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
        +
        +
      • +
      • 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent

        +
      • +
      • 经过代理服务器后,期望的返回如下

        +
      • +
      +
      {
      +  "user-agent": "curl/7.43.0"
      +}
      +- AnyProxy Hacked!
      +
    • +
    • Step 4, 查看请求信息

      + +
    • +
    +

    处理流程

    +
      +
    • 处理流程图如下
    • +
    +

    +
      +
    • 当http请求经过代理服务器时,具体处理过程是:

      +
        +
      • 收集请求所有请求参数,包括method, header, body等
      • +
      • AnyProxy调用规则模块beforeSendRequest方法,由模块做处理,返回新的请求参数,或返回响应内容
      • +
      • 如果beforeSendRequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。
      • +
      • 根据请求参数,向服务端发出请求,接收服务端响应。
      • +
      • 调用规则模块beforeSendResponse方法,由模块对响应内容进行处理
      • +
      • 把响应信息返回给客户端
      • +
      +
    • +
    • 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。

      +
        +
      • 调用规则模块beforeDealHttpsRequest方法,如果返回true,会明文解析这个请求,其他请求不处理
      • +
      • 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。
      • +
      +
    • +
    +

    如何引用

    +

    如下几种方案都可以用来引用规则模块:

    +
      +
    • 使用本地路径
      anyproxy --rule ./rule.js
      +
      +
    • +
    • 使用在线地址

      +
      anyproxy --rule https://sample.com/rule.js
      +
      +
    • +
    • 使用npm包

      +
        +
      • AnyProxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包
      • +
      +
      anyproxy --rule ./myRulePkg/ #本地包
      +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包
      +
      +
    • +
    +

    rule接口文档

    +

    规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。

    +
    module.exports = {
    +  // 模块介绍
    +  summary: 'my customized rule for AnyProxy',
    +  // 发送请求前拦截处理
    +  *beforeSendRequest(requestDetail) { /* ... */ },
    +  // 发送响应前处理
    +  *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },
    +  // 是否处理https请求
    +  *beforeDealHttpsRequest(requestDetail) { /* ... */ },
    +  // 请求出错的事件
    +  *onError(requestDetail, error) { /* ... */ },
    +  // https连接服务器出错
    +  *onConnectError(requestDetail, error) { /* ... */ }
    +};
    +
    +
    +

    规则文件中,除了summary,都是由 co 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。

    +
    +

    summary

    +

    summary(): string | summary:string

    +
      +
    • 规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串
    • +
    +

    beforeSendRequest

    +

    beforeSendRequest(requestDetail)

    +
      +
    • AnyProxy向服务端发送请求前,会调用beforeSendRequest,并带上参数requestDetail
    • +
    • requestDetail +
    • +
    • 举例:请求 anyproxy.io 时,requestDetail参数内容大致如下

      +
      {
      +  protocol: 'http',
      +  url: 'http://anyproxy.io/',
      +  requestOptions: {
      +    hostname: 'anyproxy.io',
      +    port: 80,
      +    path: '/',
      +    method: 'GET',
      +    headers: {
      +      Host: 'anyproxy.io',
      +      'Proxy-Connection': 'keep-alive',
      +      'User-Agent': '...'
      +    }
      +  },
      +  requestData: '...',
      +  _req: { /* ... */}
      +}
      +
      +
    • +
    • 以下几种返回都是合法的

      +
        +
      • 不做任何处理,返回null
      • +
      +
      return null;
      +
      +
        +
      • 修改请求协议,如强制改用https发起请求
      • +
      +
      return {
      +  protocol: 'https'
      +};
      +
      +
        +
      • 修改请求参数
      • +
      +
      var newOption = Object.assign({}, requestDetail.requestOptions);
      +newOption.path = '/redirect/to/another/path';
      +return {
      +  requestOptions: newOption
      +};
      +
      +
        +
      • 修改请求body
      • +
      +
      return {
      +  requestData: 'my new request data'
      +  //这里也可以同时加上requestOptions
      +};
      +
      +
        +
      • 直接返回客户端,不再发起请求,其中statusCode header 是必选字段
      • +
      +
      return {
      +  response: {
      +    statusCode: 200,
      +    header: { 'content-type': 'text/html' },
      +    body: 'this could be a <string> or <buffer>'
      +  }
      +};
      +
      +
    • +
    +

    beforeSendResponse

    +

    beforeSendResponse(requestDetail, responseDetail)

    +
      +
    • AnyProxy向客户端发送请求前,会调用beforeSendResponse,并带上参数requestDetail responseDetail
    • +
    • requestDetailbeforeSendRequest中的参数
    • +
    • responseDetail
        +
      • response {object} 服务端的返回信息,包括statusCode header body三个字段
      • +
      • _res {object} 原始的服务端返回对象
      • +
      +
    • +
    • 举例,请求 anyproxy.io 时,responseDetail参数内容大致如下

      +
      {
      +  response: {
      +    statusCode: 200,
      +    header: {
      +      'Content-Type': 'image/gif',
      +      Connection: 'close',
      +      'Cache-Control': '...'
      +    },
      +    body: '...'
      +  },
      +  _res: { /* ... */ }
      +}
      +
      +
    • +
    • 以下几种返回都是合法的

      +
        +
      • 不做任何处理,返回null
      • +
      +
      return null;
      +
      +
        +
      • 修改返回的状态码
      • +
      +
      var newResponse = Object.assign({}, responseDetail.response);
      +newResponse.statusCode = 404;
      +return {
      +  response: newResponse
      +};
      +
      +
        +
      • 修改返回的内容
      • +
      +
      var newResponse = Object.assign({}, responseDetail.response);
      +newResponse.body += '--from anyproxy--';
      +return {
      +  response: newResponse
      +};
      +
      +
    • +
    +

    beforeDealHttpsRequest

    +

    beforeDealHttpsRequest(requestDetail)

    +
      +
    • AnyProxy收到https请求时,会调用beforeDealHttpsRequest,并带上参数requestDetail
    • +
    • 如果配置了全局解析https的参数,则AnyProxy会略过这个调用
    • +
    • 只有返回true时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。
    • +
    • 注意:https over http的代理模式中,这里的request是CONNECT请求
    • +
    • requestDetail
        +
      • host {string} 请求目标的Host,受制于协议,这里无法获取完整url
      • +
      • _req {object} 请求的原始request
      • +
      +
    • +
    • 返回值
        +
      • true或者false,表示是否需要AnyProxy替换证书并解析https
      • +
      +
    • +
    +

    onError

    +

    onError(requestDetail, error)

    +
      +
    • 在请求处理过程中发生错误时,AnyProxy会调用onError方法,并提供对应的错误信息
    • +
    • 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等
    • +
    • requestDetailbeforeSendRequest中的参数
    • +
    • 以下几种返回都是合法的

      +
        +
      • 不做任何处理。此时AnyProxy会返回一个默认的错误页。
      • +
      +
      return null;
      +
      +
        +
      • 返回自定义错误页
      • +
      +
      return {
      +  response: {
      +    statusCode: 200,
      +    header: { 'content-type': 'text/html' },
      +    body: 'this could be a <string> or <buffer>'
      +  }
      +};
      +
      +
    • +
    +

    onConnectError

    +

    onConnectError(requestDetail, error)

    +
      +
    • AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法
    • +
    • requestDetailbeforeDealHttpsRequest中的参数
    • +
    • 此处无法控制向客户端的返回信息,无需返回值。
    • +
    +

    rule样例

    +
      +
    • 这里提供一些样例,来讲解规则模块的常见用法
    • +
    • 你可以通过 anyproxy --rule http://....js 来加载模块并体验
    • +
    • 用curl发请求测试的方法如下
        +
      • 直接请求服务器:curl http://httpbin.org/
      • +
      • 通过代理服务器请求:curl http://httpbin.org/ --proxy http://127.0.0.1:8001
      • +
      +
    • +
    +

    使用本地数据

    +
      +
    • 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回
    • +
    +
    anyproxy --rule rule_sample/sample_use_local_response.js
    +
    +
    /* 
    +  sample: 
    +    intercept all requests toward httpbin.org, use a local response
    +  test:
    +    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
    +*/
    +module.exports = {
    +  *beforeSendRequest(requestDetail) {
    +    const localResponse = {
    +      statusCode: 200,
    +      header: { 'Content-Type': 'application/json' },
    +      body: '{"hello": "this is local response"}'
    +    };
    +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
    +      return {
    +        response: localResponse
    +      };
    +    }
    +  },
    +};
    +
    +

    修改请求头

    +
      +
    • 修改发送到 httpbin.org 的user-agent
    • +
    +
    anyproxy --rule rule_sample/sample_modify_request_header.js
    +
    +
    /* 
    +  sample: 
    +    modify the user-agent in requests toward httpbin.org
    +  test:
    +    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
    +*/
    +module.exports = {
    +  *beforeSendRequest(requestDetail) {
    +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
    +      const newRequestOptions = requestDetail.requestOptions;
    +      newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';
    +      return {
    +        requestOptions: newRequestOptions
    +      };
    +    }
    +  },
    +};
    +
    +

    修改请求数据

    + +
    anyproxy --rule rule_sample/sample_modify_request_data.js
    +
    +
    /*
    +  sample:
    +    modify the post data towards http://httpbin.org/post
    +  test:
    +    curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001
    +  expected response:
    +    { "data": "i-am-anyproxy-modified-post-data" }
    +*/
    +module.exports = {
    +  summary: 'Rule to modify request data',
    +  *beforeSendRequest(requestDetail) {
    +    if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {
    +      return {
    +        requestData: 'i-am-anyproxy-modified-post-data'
    +      };
    +    }
    +  },
    +};
    +
    +

    修改请求的目标地址

    + +
    anyproxy --rule rule_sample/sample_modify_request_path.js
    +
    +
    /*
    +  sample:
    +    redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html
    +  test:
    +    curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001
    +  expected response:
    +    'hello world' from 127.0.0.1:8001/index.html
    +*/
    +module.exports = {
    +  *beforeSendRequest(requestDetail) {
    +    if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) {
    +      const newRequestOptions = requestDetail.requestOptions;
    +      requestDetail.protocol = 'http';
    +      newRequestOptions.hostname = '127.0.0.1'
    +      newRequestOptions.port = '8008';
    +      newRequestOptions.path = '/index.html';
    +      newRequestOptions.method = 'GET';
    +      return requestDetail;
    +    }
    +  },
    +  *beforeDealHttpsRequest(requestDetail) {
    +    return true;
    +  }
    +};
    +
    +

    修改请求协议

    + +
    anyproxy --rule rule_sample/sample_modify_request_protocol.js
    +
    +
    /* 
    +  sample: 
    +    redirect all http requests of httpbin.org to https
    +  test:
    +    curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001
    +  expected response:
    +    { "X-Forwarded-Protocol": "https" }
    +*/
    +module.exports = {
    +  *beforeSendRequest(requestDetail) {
    +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
    +      const newOption = requestDetail.requestOptions;
    +      newOption.port = 443;
    +      return {
    +        protocol: 'https',
    +        requestOptions: newOption
    +      };
    +    }
    +  }
    +};
    +
    +

    修改返回状态码

    + +
    anyproxy --rule rule_sample/sample_modify_response_statuscode.js
    +
    +
    /* 
    +  sample: 
    +    modify all status code of http://httpbin.org/ to 404
    +  test:
    +    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
    +  expected response:
    +    HTTP/1.1 404 Not Found
    +*/
    +module.exports = {
    +  *beforeSendResponse(requestDetail, responseDetail) {
    +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
    +      const newResponse = responseDetail.response;
    +      newResponse.statusCode = 404;
    +      return {
    +        response: newResponse
    +      };
    +    }
    +  }
    +};
    +
    +

    修改返回头

    + +
    anyproxy --rule rule_sample/sample_modify_response_header.js
    +
    +
    /* 
    +  sample: 
    +    modify response header of http://httpbin.org/user-agent
    +  test:
    +    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
    +  expected response:
    +    X-Proxy-By: AnyProxy
    +*/
    +module.exports = {
    +  *beforeSendResponse(requestDetail, responseDetail) {
    +    if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {
    +      const newResponse = responseDetail.response;
    +      newResponse.header['X-Proxy-By'] = 'AnyProxy';
    +      return {
    +        response: newResponse
    +      };
    +    }
    +  }
    +};
    +
    +

    修改返回内容并延迟

    + +
    anyproxy --rule rule_sample/sample_modify_response_data.js
    +
    +
    /* 
    +  sample: 
    +    modify response data of http://httpbin.org/user-agent
    +  test:
    +    curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
    +  expected response:
    +    { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! --
    +*/
    +
    +module.exports = {
    +  *beforeSendResponse(requestDetail, responseDetail) {
    +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
    +      const newResponse = responseDetail.response;
    +      newResponse.body += '-- AnyProxy Hacked! --';
    +      return new Promise((resolve, reject) => {
    +        setTimeout(() => { // delay the response for 5s
    +          resolve({ response: newResponse });
    +        }, 5000);
    +      });
    +    }
    +  },
    +};
    +
    +

    证书配置

    +

    OSX系统信任CA证书

    +
      +
    • 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的
    • +
    +

    +
    +

    警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管

    +
    +

    安装CA:

    +
      +
    • 双击打开rootCA.crt

      +
    • +
    • 确认将证书添加到login或system

      +
    • +
    +

    +
      +
    • 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust)
    • +
    +

    +

    Windows系统信任CA证书

    +

    +

    配置OSX系统代理

    +
      +
    • 在wifi高级设置中,配置http代理即可
    • +
    +

    +

    配置浏览器HTTP代理

    + +

    +

    iOS系统信任CA证书

    +
      +
    • 点击web ui中的 Root CA,按提示扫描二维码即可安装
    • +
    +

    +

    iOS >= 10.3信任CA证书

    +
      +
    • 除了上述证书安装过程,还需要在 设置->通用->关于本机->证书信任设置 中把AnyProxy证书的开关打开,否则safari将报错。
    • +
    +

    +

    配置iOS/Android系统代理

    +
      +
    • 代理服务器都在wifi设置中配置

      +
    • +
    • iOS HTTP代理配置

      +
    • +
    +

    +
      +
    • Android HTTP代理配置
    • +
    +

    +

    FAQ

    +

    Q: 为什么https请求不能进入处理函数?

    +

    A: 以下任意一项都能用来改变https的处理特性:

    +
     1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析
    + 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析
    +

    Q: 提示 function is not yieldable

    +
      +
    • A: 规则模块是用 co 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。
    • +
    +

    Q: The connection is not private

    +

    当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问:

    +
      +
    • 命令行直接启动

      +

      通过启动参数 --ignore-unauthorized-ssl 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。

      +
      anyproxy -i --ignore-unauthorized-ssl
      +
      +
    • +
    • 在Nodejs代码中启动

      +

      在构造AnyProxy实例的时候,传入参数dangerouslyIgnoreUnauthorized:true, 如下:

      +
       const options = {
      +  ...,
      +  dangerouslyIgnoreUnauthorized: true
      + };
      +
      + const anyproxyIns = new AnyProxy.ProxyCore(options);
      + anyproxyIns.start();
      +
      +

      通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略

      +
    • +
    • 通过自定义的Rule来修改

      +

      我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。

      +
      module.exports = {
      +  *beforeSendRequest(requestDetail) {
      +    if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {
      +      const newRequestOptions = requestDetail.requestOptions;
      +      // 设置属性 rejectUnauthorized 为 false
      +      newRequestOptions.rejectUnauthorized = false;
      +      return {
      +        requestOptions: newRequestOptions
      +      };
      +    }
      +  },
      +};
      +
      +
    • +
    + + +
    + +
    +
    +
    + +

    results matching ""

    +
      + +
      +
      + +

      No results matching ""

      + +
      +
      +
      + +
      +
      + +
      + + + + + + + + + + +
      + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/cn/search_index.json b/docs/cn/search_index.json new file mode 100644 index 0000000..053f3e9 --- /dev/null +++ b/docs/cn/search_index.json @@ -0,0 +1 @@ +{"index":{"version":"0.5.12","fields":[{"name":"title","boost":10},{"name":"keywords","boost":15},{"name":"body","boost":1}],"ref":"url","documentStore":{"store":{"./":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"user","\"x","#全局包","#启动anyproxy,并解析所有https请求","#本地包","#生成rootca证书,生成后需要手动信任","&&","'","'...'","'...',","'/',","'/index.html';","'/redirect/to/another/path';","'127.0.0.1'","'8001');","'8008';","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'hello","'http',","'http';","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'text/html';","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(){","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","*true*","+=",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","//这里也可以同时加上requestopt","/^win/.test(process.platform);","0)","1.","10.3信任ca证书","10000,","1080","127.0.0.1:8001/index.html","127.0.0.1:8001,访问","1,编写规则","2,","2.","200,","3,","300);","4,","4.0的主要变化:","4.0,欢迎提供反馈","404","404;","443;","5000);","5s","80,","8001,","8002,","8003,","=","===","=>",">=",">关于本机",">证书信任设置",">通用","_re","_req","_req:","_res:","`","`;","a:","agent","agent\":","agent'","agent')","agent':","agent']","alive',","android","anyproxi","anyproxy',","anyproxy.io","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxyin","anyproxyins.start();","anyproxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包","anyproxy可以作为一个npm模块使用,整合进其他工具。","anyproxy向客户端发送请求前,会调用beforesendresponse,并带上参数requestdetail","anyproxy向服务端发送请求前,会调用beforesendrequest,并带上参数requestdetail","anyproxy在与目标https服务器建立连接的过程中,如果发生错误,anyproxy会调用这个方法","anyproxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。","anyproxy收到https请求时,会调用beforedealhttpsrequest,并带上参数requestdetail","anyproxy是一个开放式的http代理服务器。","anyproxy调用规则模块beforesendrequest方法,由模块做处理,返回新的请求参数,或返回响应内容","anyproxy默认不对https请求做处理,如需看到明文信息,需要配置ca证书","apg","at',","attack),需要客户端提前信任anyproxy生成的ca","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","body:","body三个字段","body等","body),甚至是请求的目标地址等","by']","by:","by:anyproxi","ca","ca方法。","ca,按提示扫描二维码即可安装","cert","certdir","certdir);","class:","close","co","code","code)、响应头(respons","connect","connection':","connection:","console.error('error","console.log('th","const","control':","curl","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","data","data\"","data'","data',","delay","doc","enabl","enable:","english","error","error)","error);","event:","exec","exec('open","exec('start","expect","fals","false,","false;","faq","file:","finish","forceproxyhttp","forceproxyhttps:","forward","found","function","function()","g","gener","github主页:https://github.com/alibaba/anyproxy/tree/4.x","h","hacked!","hacked...\")","header","header),请求体(request","header,","header:","headers:","header)、响应内容等","host","host:","hostname:","http","http/1.1","http://....j","http://127.0.0.1:8001","http://httpbin.org","http://httpbin.org/","http://httpbin.org/post","http://httpbin.org/us","http://localhost:8008/index.html","httpbin.org","httpbin.org,","https://httpbin.org/us","https://sample.com/rule.j","https连接服务器出错","http代理配置","http的代理模式中,这里的request是connect请求","ignor","instal","intercept","intercept`参数,按npm模块启动时配置`forceproxyhttps`参数,所有https请求都会被替换证书并解析","io","ios系统信任ca证书","iswin","keypath)","know.com')","legaci","local","localrespons","method:","middl","middle),用户必须信任anyproxy生成的ca证书,才能进行后续流程","modifi","module.export","myrulepkg","new","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.hostnam","newrequestoptions.method","newrequestoptions.path","newrequestoptions.port","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['cont","newresponse.header['x","newresponse.statuscod","nodej","npm","null;","object.assign({},","onconnecterror","onconnecterror(requestdetail,","onerror","onerror(requestdetail,","option","osx系统信任ca证书","over","path:","port","port:","post","privat","promise((resolve,","protocol","protocol\":","protocol:","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","readi","redirect","ref:","reject)","rejectunauthor","request","requestdata","requestdata:","requestdetail","requestdetail.protocol","requestdetail.requestoptions);","requestdetail.requestoptions;","requestdetail;","requestopt","requestoptions:","require('anyproxy');","require('child_process').exec;","require('myrulemodule'),","require('path').dirname(keypath);","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","root","rootca',","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","rule接口文档","rule样例","rule模块","sample.j","sample:","save","settimeout(","settimeout(()","silent","silent:","site","ssl","start","statu","statuscode:","step","string","sudo","summari","summary():","summary:","summary:str","test:","text/plain\"","throttl","throttle:","toward","true","true,","true;","true或者false,表示是否需要anyproxy替换证书并解析http","trust","trust)","type':","type']","type:","ui中的","unauthor","url","url:","us","user","var","webinterfac","webinterface:","webport","webport:","web版界面端口号,默认8002","web版界面配置","web版界面重构","window.alert(\"sorry,","windows系统信任ca证书","world'","wsport:","x","yieldabl","{","{boolean}","{number}","{object}","{string}","|","}","})","});","},","};","不做任何处理。此时anyproxy会返回一个默认的错误页。","不做任何处理,返回null","中把anyproxy证书的开关打开,否则safari将报错。","为","为什么https请求不能进入处理函数?","主要特性包括:","举例","举例,请求","举例:请求","代理http","代理服务器发生错误","代理服务器启动完成","代理服务器都在wifi设置中配置","以chrome的switchyomega插件为例","以下任意一项都能用来改变https的处理特性:","以下几种返回都是合法的","作为npm模块使用","作为全局模块","你可以通过","使用npm包","使用举例","使用在线地址","使用本地数据","使用本地路径anyproxi","使用示例","修改发送到","修改请求bodi","修改请求协议","修改请求协议,如强制改用https发起请求","修改请求参数","修改请求头","修改请求数据","修改请求的目标地址","修改返回内容并延迟","修改返回头","修改返回状态码","修改返回的内容","修改返回的状态码","关闭代理服务器","关闭全局代理服务器","其他命令","创建代理服务器","即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback","原始的服务端返回对象","双击打开rootca.crt","发送响应前处理","发送请求前拦截处理","只有返回true时,anyproxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。","可以是一个函数,也可以是一个普通的字符串","可修改内容包括请求头(request","可修改的内容包括http状态码(statu","同beforedealhttpsrequest中的参数","同beforesendrequest中的参数","启动","启动anyproxy,加载规则","启动代理服务器","启动后将终端http代理服务器配置为127.0.0.1:8001即可","命令行启动anyproxy时配置`","命令行启动anyproxy,默认端口号8001","命令行直接启动","在","在nodejs代码中启动","在wifi高级设置中,配置http代理即可","在构造anyproxy实例的时候,传入参数dangerouslyignoreunauthorized:true,","在请求处理过程中发生错误时,anyproxy会调用onerror方法,并提供对应的错误信息","基于node.js,开放二次开发能力,允许自定义请求处理逻辑","处理流程","处理流程图如下","多数场景下,错误会在请求目标服务器的时候发生,比如dns解析失败、请求超时等","如下几种方案都可以用来引用规则模块:","如下:","如何引用","如果beforesendrequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。","如果配置了全局解析https的参数,则anyproxy会略过这个调用","如要启用https解析,请在代理服务器启动前自行调用anyproxy.utils.certmgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxi","安装","安装ca:","对于debian或者ubuntu系统,在安装anyproxy之前,可能还需要安装","开发示例","引入","当http请求经过代理服务器时,具体处理过程是:","当代理服务器收到https请求时,anyproxy可以替换证书,对请求做明文解析。","当访问特定的https站点,anyproxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问:","必选,代理服务器端口","快速开始","我们自然也可以借助自定义的rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。","所有http://httpbin.org","找到刚刚导入的anyproxy证书,配置为信任(alway","把","把响应信息返回给客户端","把所有发送到","把用http协议请求的","拦截https请求,对内容做修改","拦截发送到","拦截并修改服务端响应","拦截并修改正在发送的请求","提供gui界面,用以观察请求","提示","支持https的解析","收集请求所有请求参数,包括method,","改成https并发送","时,requestdetail参数内容大致如下","时,responsedetail参数内容大致如下","是否启用web版界面,默认fals","是否处理https请求","是否屏蔽所有console输出,默认fals","是否强制拦截所有的https,忽略规则模块的返回,默认fals","是否忽略请求中的证书错误,默认fals","是必选字段","服务端的返回信息,包括statuscod","本文档的适用范围是anyproxi","本质是中间人攻击(man","来加载模块并体验","来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。","查看请求信息","校验系统内是否存在anyproxy的根证书","样例","根据请求参数,向服务端发出请求,接收服务端响应。","模块介绍","此处无法控制向客户端的返回信息,无需返回值。","注意:http","注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题","测试规则","浏览器访问http://127.0.0.1:8002","点击web","然后,安装anyproxi","生成anyproxy的rootca,完成后请引导用户信任.crt文件","生成证书并解析所有https请求","用curl发请求测试的方法如下","用curl测试","用浏览器测试:配置浏览器http代理为","的https请求会被解析","的post数据","的user","的请求全部改到","的请求,使用本地数据代替服务端返回","的返回值里加上测试信息,并延迟5秒返回","的返回头里加上","的返回最后追加anyproxy的签名,并延迟5秒","的返回状态码都改成404","直接请求服务器:curl","直接返回客户端,不再发起请求,其中statuscod","相比3.x版本,anyproxi","确认将证书添加到login或system","示例","简介","简化了规则文件内的接口","管理anyproxy的证书","管理系统的全局代理配置,方法调用时可能会弹出密码框","类似这种报错都是因为系统没有信任anyproxy生成的ca所造成的","经过代理服务器后,期望的返回如下","自定义规则模块","被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。","规则文件中,除了summary,都是由","规则文件内提供`beforedealhttpsrequest`方法,返回","规则文件(rule)全面支持promise和gener","规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。","规则模块是用","规则模块的介绍文案,用于anyproxy提示用户,","规则模块的能力范围包括:","解析https请求的原理是中间人攻击(man","警告:ca证书和系统安全息息相关,建议亲自生成,并妥善保管","设置","设置属性","访问http://127.0.0.1:8002","证书配置","请求bodi","请求url","请求使用的协议,http或者http","请求出错的事件","请求的原始request","请求目标的host,受制于协议,这里无法获取完整url","调用规则模块beforedealhttpsrequest方法,如果返回true,会明文解析这个请求,其他请求不处理","调用规则模块beforesendresponse方法,由模块对响应内容进行处理","运行","返回值","返回自定义错误页","这里提供一些样例,来讲解规则模块的常见用法","通过代理服务器请求:curl","通过启动参数","通过自定义的rule来修改","通过这种方式初始化的anyproxy,其配置也是全局性的,所有网站的证书问题都会被忽略","配置127.0.0.1:8001为全局http代理服务器","配置ios/android系统代理","配置osx系统代理","配置启动端口,如1080端口启动","配置浏览器http代理","附录:如何信任ca证书","限速值,单位kb/s,默认不限速","除了上述证书安装过程,还需要在","需要编写一个规则模块,在","驱动的,函数需要满足yieldable。可以使用generator方法或是返回promise。","驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。",",web界面上能看到所有的请求信息",",界面上能看到刚才的请求信息"],"doc.html":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#","#gener","#global","#launch","#local","&&","'","'...'","'...',","'/',","'/redirect/to/another/path';","'/user","'8001');","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'http',","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","/^win/.test(process.platform);","0)","10.3","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","1,write","2,","200,","3,","4,","4.0","4.x","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>","[switchyomega]","_re","_req","_req:","_res:","a:","abil","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent';","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxy@beta","anyproxyin","anyproxyins.start();","apg","append","appendix:how","at',","attack","attack.","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","beta","bodi","body,","body:","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","detect","determin","directli","disabl","dn","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","function","function()","function.","g","gener","get","give","global","go,","guid","h","hacked!","handl","handling.","happen","header","header,","header:","headers:","headersar","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/ani","http://httpbin.org/post","http://httpbin.org/us","httpbin.org","httpbin.org,","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","interest","interfac","interface,","introduct","io","ios/android","issu","iswin","it,","it.","javascript.","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.method","newrequestoptions.path","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['x","newresponse.statuscod","node.j","nodej","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","otherwire,","otherwise,","own.","page","param","part","pass","password","path","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.requestoptions);","requestdetail.requestoptions;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(()","settings.","side","signed.","silent","silent:","simpl","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","stuff:","sudo","summari","summary,","summary:","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","trust","type':","type:","typic","ubuntu","ui,","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","whether","wifi","window","without","work","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","}","})","});","},","};",":"]},"length":2},"tokenStore":{"root":{"0":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.005983545250560957},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}},"1":{"0":{"0":{"0":{"0":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"8":{"0":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"docs":{}},"docs":{},".":{"3":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"docs":{}}},"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"/":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},",":{"docs":{},"访":{"docs":{},"问":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},",":{"docs":{},"编":{"docs":{},"写":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"w":{"docs":{},"r":{"docs":{},"i":{"docs":{},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"2":{"0":{"0":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"3":{"0":{"0":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{},"x":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"4":{"0":{"4":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},";":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"docs":{}},"4":{"3":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"0":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"的":{"docs":{},"主":{"docs":{},"要":{"docs":{},"变":{"docs":{},"化":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},",":{"docs":{},"欢":{"docs":{},"迎":{"docs":{},"提":{"docs":{},"供":{"docs":{},"反":{"docs":{},"馈":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"docs":{},"x":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"5":{"0":{"0":{"0":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"docs":{}},"docs":{}},"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"2":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"3":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},"\"":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{},"/":{"7":{"docs":{},".":{"4":{"3":{"docs":{},".":{"0":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"docs":{}}},"docs":{}},"docs":{}}},"docs":{}}}}}},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"t":{"docs":{},"h":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"r":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"#":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"全":{"docs":{},"局":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"启":{"docs":{},"动":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"本":{"docs":{},"地":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"生":{"docs":{},"成":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"生":{"docs":{},"成":{"docs":{},"后":{"docs":{},"需":{"docs":{},"要":{"docs":{},"手":{"docs":{},"动":{"docs":{},"信":{"docs":{},"任":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"l":{"docs":{},"a":{"docs":{},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"&":{"docs":{},"&":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"'":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"8":{"0":{"0":{"1":{"docs":{},"'":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"8":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},".":{"docs":{},".":{"docs":{},".":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"/":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"/":{"docs":{},"t":{"docs":{},"o":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"o":{"docs":{},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{},"/":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},";":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"a":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{},"i":{"docs":{},"o":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"/":{"0":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"docs":{}}},"docs":{}}},"docs":{}}}}}}}}},"p":{"docs":{},"p":{"docs":{},"l":{"docs":{},"i":{"docs":{},"c":{"docs":{},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"/":{"docs":{},"j":{"docs":{},"s":{"docs":{},"o":{"docs":{},"n":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"a":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"h":{"docs":{},"e":{"docs":{},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"i":{"docs":{},"o":{"docs":{},"/":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{},"?":{"docs":{},"s":{"docs":{},"h":{"docs":{},"o":{"docs":{},"w":{"docs":{},"_":{"docs":{},"e":{"docs":{},"n":{"docs":{},"v":{"docs":{},"=":{"1":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"m":{"docs":{},"a":{"docs":{},"g":{"docs":{},"e":{"docs":{},"/":{"docs":{},"g":{"docs":{},"i":{"docs":{},"f":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"k":{"docs":{},"e":{"docs":{},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"m":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"o":{"docs":{},"r":{"docs":{},"i":{"docs":{},"g":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{},"e":{"docs":{},"x":{"docs":{},"t":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"h":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"{":{"docs":{},"\"":{"docs":{},"h":{"docs":{},"e":{"docs":{},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"(":{"docs":{},"!":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{},".":{"docs":{},"i":{"docs":{},"f":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"f":{"docs":{},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"e":{"docs":{},"x":{"docs":{},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{},"s":{"docs":{},"(":{"docs":{},")":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"{":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"e":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},".":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},"o":{"docs":{},"f":{"docs":{},"(":{"docs":{},"'":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}},"/":{"docs":{},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"*":{"docs":{},"/":{"docs":{"./":{"ref":"./","tf":0.011967090501121914},"doc.html":{"ref":"doc.html","tf":0.007797270955165692}},"}":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.005235602094240838},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},"*":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"+":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},".":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.005235602094240838},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"/":{"docs":{},"m":{"docs":{},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"p":{"docs":{},"k":{"docs":{},"g":{"docs":{},"/":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"/":{"docs":{},"*":{"docs":{"./":{"ref":"./","tf":0.012715033657442034},"doc.html":{"ref":"doc.html","tf":0.008284600389863547}}},"/":{"docs":{"./":{"ref":"./","tf":0.009723261032161555},"doc.html":{"ref":"doc.html","tf":0.00682261208576998}},"w":{"docs":{},"h":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"这":{"docs":{},"里":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"同":{"docs":{},"时":{"docs":{},"加":{"docs":{},"上":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"^":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{},"/":{"docs":{},".":{"docs":{},"t":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{},".":{"docs":{},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"t":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"m":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"=":{"docs":{"./":{"ref":"./","tf":0.0344053851907255},"doc.html":{"ref":"doc.html","tf":0.02046783625730994}},"=":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0074794315632011965},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}}},">":{"docs":{"./":{"ref":"./","tf":0.005235602094240838},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},">":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"关":{"docs":{},"于":{"docs":{},"本":{"docs":{},"机":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"证":{"docs":{},"书":{"docs":{},"信":{"docs":{},"任":{"docs":{},"设":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"通":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"q":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"`":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"a":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.010471204188481676},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"'":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"]":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"l":{"docs":{},"i":{"docs":{},"v":{"docs":{},"e":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"w":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.02318623784592371},"doc.html":{"ref":"doc.html","tf":0.03167641325536062}}},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"c":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},"r":{"docs":{},"(":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"(":{"docs":{},"(":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"f":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"f":{"docs":{},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"e":{"docs":{},"x":{"docs":{},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{},"s":{"docs":{},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"d":{"docs":{},"i":{"docs":{},"s":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"(":{"docs":{},"'":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"s":{"docs":{},".":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"使":{"docs":{},"用":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},"加":{"docs":{},"载":{"docs":{},"本":{"docs":{},"地":{"docs":{},"规":{"docs":{},"则":{"docs":{},",":{"docs":{},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"在":{"docs":{},"参":{"docs":{},"数":{"docs":{},"里":{"docs":{},"传":{"docs":{},"入":{"docs":{},"一":{"docs":{},"个":{"docs":{},"本":{"docs":{},"地":{"docs":{},"的":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{},"路":{"docs":{},"径":{"docs":{},",":{"docs":{},"或":{"docs":{},"是":{"docs":{},"某":{"docs":{},"个":{"docs":{},"全":{"docs":{},"局":{"docs":{},"安":{"docs":{},"装":{"docs":{},"的":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"可":{"docs":{},"以":{"docs":{},"作":{"docs":{},"为":{"docs":{},"一":{"docs":{},"个":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"使":{"docs":{},"用":{"docs":{},",":{"docs":{},"整":{"docs":{},"合":{"docs":{},"进":{"docs":{},"其":{"docs":{},"他":{"docs":{},"工":{"docs":{},"具":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"向":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"发":{"docs":{},"送":{"docs":{},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"发":{"docs":{},"送":{"docs":{},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"在":{"docs":{},"与":{"docs":{},"目":{"docs":{},"标":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"建":{"docs":{},"立":{"docs":{},"连":{"docs":{},"接":{"docs":{},"的":{"docs":{},"过":{"docs":{},"程":{"docs":{},"中":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"这":{"docs":{},"个":{"docs":{},"方":{"docs":{},"法":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"提":{"docs":{},"供":{"docs":{},"了":{"docs":{},"二":{"docs":{},"次":{"docs":{},"开":{"docs":{},"发":{"docs":{},"的":{"docs":{},"能":{"docs":{},"力":{"docs":{},",":{"docs":{},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"j":{"docs":{},"s":{"docs":{},"编":{"docs":{},"写":{"docs":{},"自":{"docs":{},"己":{"docs":{},"的":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"(":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},")":{"docs":{},",":{"docs":{},"来":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"网":{"docs":{},"络":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"处":{"docs":{},"理":{"docs":{},"逻":{"docs":{},"辑":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"收":{"docs":{},"到":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"时":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"开":{"docs":{},"放":{"docs":{},"式":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}},"调":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"由":{"docs":{},"模":{"docs":{},"块":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},",":{"docs":{},"返":{"docs":{},"回":{"docs":{},"新":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"或":{"docs":{},"返":{"docs":{},"回":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"默":{"docs":{},"认":{"docs":{},"不":{"docs":{},"对":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},",":{"docs":{},"如":{"docs":{},"需":{"docs":{},"看":{"docs":{},"到":{"docs":{},"明":{"docs":{},"文":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"需":{"docs":{},"要":{"docs":{},"配":{"docs":{},"置":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"@":{"docs":{},"b":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}},"p":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"x":{"docs":{},":":{"docs":{},"h":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"t":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"t":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},")":{"docs":{},",":{"docs":{},"需":{"docs":{},"要":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"提":{"docs":{},"前":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"b":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"l":{"docs":{},"i":{"docs":{},"t":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"c":{"docs":{},"t":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"d":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"v":{"docs":{},"a":{"docs":{},"n":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"b":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"t":{"docs":{},"a":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"o":{"docs":{},"d":{"docs":{},"y":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"三":{"docs":{},"个":{"docs":{},"字":{"docs":{},"段":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},")":{"docs":{},",":{"docs":{},"甚":{"docs":{},"至":{"docs":{},"是":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"目":{"docs":{},"标":{"docs":{},"地":{"docs":{},"址":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},"n":{"docs":{},"o":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"y":{"docs":{},"'":{"docs":{},"]":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"r":{"docs":{},"o":{"docs":{},"w":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.00682261208576998}},"方":{"docs":{},"法":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},",":{"docs":{},"按":{"docs":{},"提":{"docs":{},"示":{"docs":{},"扫":{"docs":{},"描":{"docs":{},"二":{"docs":{},"维":{"docs":{},"码":{"docs":{},"即":{"docs":{},"可":{"docs":{},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"f":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"i":{"docs":{},"c":{"docs":{},"a":{"docs":{},"t":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"y":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"l":{"docs":{},"a":{"docs":{},"s":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"i":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"d":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},")":{"docs":{},"、":{"docs":{},"响":{"docs":{},"应":{"docs":{},"头":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"g":{"docs":{},"(":{"docs":{},"'":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{"./":{"ref":"./","tf":0.014210919970082274},"doc.html":{"ref":"doc.html","tf":0.009259259259259259}}}},"t":{"docs":{},"r":{"docs":{},"o":{"docs":{},"l":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"f":{"docs":{},"i":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"l":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"l":{"docs":{},"d":{"docs":{},"n":{"docs":{},"'":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"s":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.006731488406881077},"doc.html":{"ref":"doc.html","tf":0.005847953216374269}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"w":{"docs":{},"d":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"h":{"docs":{},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"u":{"docs":{},"s":{"docs":{},"l":{"docs":{},"y":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"z":{"docs":{},"e":{"docs":{},"d":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"\"":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"'":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"e":{"docs":{},"l":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"e":{"docs":{},"d":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"b":{"docs":{},"i":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"c":{"docs":{},"r":{"docs":{},"y":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"f":{"docs":{},"a":{"docs":{},"u":{"docs":{},"l":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"m":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"m":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"u":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"l":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"s":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"r":{"docs":{},"i":{"docs":{},"v":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"u":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"g":{"docs":{},"l":{"docs":{},"i":{"docs":{},"s":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"t":{"docs":{},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.007309941520467836}},")":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"v":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"x":{"docs":{},"e":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"'":{"docs":{},"o":{"docs":{},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"p":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.004487658937920718},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"(":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"c":{"docs":{},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},".":{"docs":{},"g":{"docs":{},"o":{"docs":{},"o":{"docs":{},"g":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"/":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"/":{"docs":{},"p":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"k":{"docs":{},"g":{"docs":{},"c":{"docs":{},"e":{"docs":{},"m":{"docs":{},"l":{"docs":{},"o":{"docs":{},"k":{"docs":{},"b":{"docs":{},"a":{"docs":{},"d":{"docs":{},"o":{"docs":{},"h":{"docs":{},"g":{"docs":{},"k":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"j":{"docs":{},"o":{"docs":{},"m":{"docs":{},"c":{"docs":{},"l":{"docs":{},"g":{"docs":{},"j":{"docs":{},"g":{"docs":{},"i":{"docs":{},"f":{"docs":{},")":{"docs":{},"为":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},".":{"docs":{},"g":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},"d":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"m":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"q":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"u":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"i":{"docs":{},"s":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"o":{"docs":{},"r":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}},"w":{"docs":{},"a":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"e":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"g":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}}},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":5.000487329434698}}}},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},"主":{"docs":{},"页":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"g":{"docs":{},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"a":{"docs":{},"l":{"docs":{},"i":{"docs":{},"b":{"docs":{},"a":{"docs":{},"b":{"docs":{},"a":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"/":{"docs":{},"t":{"docs":{},"r":{"docs":{},"e":{"docs":{},"e":{"docs":{},"/":{"4":{"docs":{},".":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"v":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"o":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{},"e":{"docs":{},"d":{"docs":{},"!":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},".":{"docs":{},".":{"docs":{},".":{"docs":{},"\"":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"n":{"docs":{},"d":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"p":{"docs":{},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},")":{"docs":{},",":{"docs":{},"请":{"docs":{},"求":{"docs":{},"体":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},":":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"a":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},")":{"docs":{},"、":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"l":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.014619883040935672}},"/":{"1":{"docs":{},".":{"1":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"docs":{}}},"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0074794315632011965},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}},"2":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{},".":{"docs":{},".":{"docs":{},".":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"/":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0074794315632011965},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}},"a":{"docs":{},"n":{"docs":{},"i":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},":":{"8":{"0":{"0":{"8":{"docs":{},"/":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}}},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"a":{"docs":{},"l":{"docs":{},"i":{"docs":{},"b":{"docs":{},"a":{"docs":{},"b":{"docs":{},"a":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"/":{"docs":{},"t":{"docs":{},"r":{"docs":{},"e":{"docs":{},"e":{"docs":{},"/":{"4":{"docs":{},".":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"a":{"docs":{},"p":{"docs":{},"i":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"#":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"_":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"连":{"docs":{},"接":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"出":{"docs":{},"错":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}},"的":{"docs":{},"代":{"docs":{},"理":{"docs":{},"模":{"docs":{},"式":{"docs":{},"中":{"docs":{},",":{"docs":{},"这":{"docs":{},"里":{"docs":{},"的":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"是":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}},".":{"docs":{},"r":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}},"n":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}},"r":{"docs":{},"u":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.007309941520467836}},"`":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"按":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"启":{"docs":{},"动":{"docs":{},"时":{"docs":{},"配":{"docs":{},"置":{"docs":{},"`":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"`":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"都":{"docs":{},"会":{"docs":{},"被":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"f":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"o":{"docs":{},"d":{"docs":{},"u":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":10.001423149905124},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"l":{"docs":{},"u":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}},"f":{"docs":{},"l":{"docs":{},"u":{"docs":{},"e":{"docs":{},"n":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"v":{"docs":{},"o":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},".":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"m":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"k":{"docs":{},"e":{"docs":{},"y":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"n":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"b":{"docs":{},"/":{"docs":{},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"c":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}},"a":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"a":{"docs":{},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"i":{"docs":{},"n":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"u":{"docs":{},"a":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},":":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"s":{"docs":{},"a":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"d":{"docs":{},"d":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},")":{"docs":{},",":{"docs":{},"用":{"docs":{},"户":{"docs":{},"必":{"docs":{},"须":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"才":{"docs":{},"能":{"docs":{},"进":{"docs":{},"行":{"docs":{},"后":{"docs":{},"续":{"docs":{},"流":{"docs":{},"程":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.006731488406881077},"doc.html":{"ref":"doc.html","tf":0.01364522417153996}}}}},"u":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.009487666034155597},"doc.html":{"ref":"doc.html","tf":0.008284600389863547}},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"e":{"docs":{},"x":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.008227374719521317},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"p":{"docs":{},"k":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}},"a":{"docs":{},"k":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"a":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"u":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"n":{"docs":{},"e":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.004487658937920718},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},".":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},".":{"docs":{},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},"s":{"docs":{},"[":{"docs":{},"'":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"r":{"docs":{},"e":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.008975317875841436},"doc.html":{"ref":"doc.html","tf":0.005847953216374269}},"e":{"docs":{},".":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},"[":{"docs":{},"'":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"w":{"docs":{},"o":{"docs":{},"r":{"docs":{},"k":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"t":{"docs":{},"h":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"p":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}},"u":{"docs":{},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},"m":{"docs":{},"b":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"o":{"docs":{},"b":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"s":{"docs":{},"s":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"(":{"docs":{},"{":{"docs":{},"}":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}}}}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"a":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"v":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"f":{"docs":{},"f":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{},"w":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}}},"w":{"docs":{},"n":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"g":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"r":{"docs":{},"a":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"w":{"docs":{},"o":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},":":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}},"i":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"r":{"docs":{},"i":{"docs":{},"v":{"docs":{},"a":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"(":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"v":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}}}},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},",":{"docs":{},"i":{"docs":{},".":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.011219147344801795},"doc.html":{"ref":"doc.html","tf":0.016569200779727095}}},"y":{"docs":{},".":{"docs":{},"c":{"docs":{},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"c":{"docs":{},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"p":{"docs":{},"m":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"v":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"l":{"docs":{},"a":{"docs":{},"c":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"e":{"docs":{},"a":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"q":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"f":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.004487658937920718},"doc.html":{"ref":"doc.html","tf":0.024853801169590642}},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}},".":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},";":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}}}}}}}}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}},".":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},"(":{"docs":{},"'":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"'":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}},"c":{"docs":{},"h":{"docs":{},"i":{"docs":{},"l":{"docs":{},"d":{"docs":{},"_":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"e":{"docs":{},"x":{"docs":{},"e":{"docs":{},"c":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"m":{"docs":{},"o":{"docs":{},"d":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"'":{"docs":{},")":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{},"e":{"docs":{},"(":{"docs":{},"k":{"docs":{},"e":{"docs":{},"y":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"v":{"docs":{},"e":{"docs":{},"(":{"docs":{},"{":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0037397157816005983},"doc.html":{"ref":"doc.html","tf":0.01608187134502924}},"e":{"docs":{},"\"":{"docs":{},"}":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.011967090501121914},"doc.html":{"ref":"doc.html","tf":0.007797270955165692}}},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.004487658937920718},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},";":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"u":{"docs":{},"r":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.015706806282722512},"doc.html":{"ref":"doc.html","tf":0.01705653021442495}}}}}},"c":{"docs":{},"e":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.012715033657442034},"doc.html":{"ref":"doc.html","tf":0.021929824561403508}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"_":{"docs":{},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"/":{"docs":{},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"_":{"docs":{},"m":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{},"f":{"docs":{},"y":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"_":{"docs":{},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"_":{"docs":{},"l":{"docs":{},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"接":{"docs":{},"口":{"docs":{},"文":{"docs":{},"档":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"样":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"i":{"docs":{},"s":{"docs":{},"k":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},":":{"docs":{"./":{"ref":"./","tf":0.005983545250560957},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"v":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"f":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"t":{"docs":{},"i":{"docs":{},"m":{"docs":{},"e":{"docs":{},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{},"(":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}},"n":{"docs":{},"g":{"docs":{},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"t":{"docs":{},"y":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"f":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"r":{"docs":{},"v":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.008538899430740038},"doc.html":{"ref":"doc.html","tf":0.008771929824561403}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"(":{"docs":{},"s":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"d":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"g":{"docs":{},"n":{"docs":{},"e":{"docs":{},"d":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"s":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":5.002436647173489}}}},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"r":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"u":{"docs":{},"f":{"docs":{},"f":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"d":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"y":{"docs":{},"(":{"docs":{},")":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},":":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},"t":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"c":{"docs":{},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{},"o":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"k":{"docs":{},"i":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"u":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"e":{"docs":{},"c":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}},"t":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.005983545250560957},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}},"x":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"/":{"docs":{},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"i":{"docs":{},"n":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"t":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"a":{"docs":{},"t":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"i":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"w":{"docs":{},"a":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}}}},"!":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},";":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"或":{"docs":{},"者":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{},"e":{"docs":{},",":{"docs":{},"表":{"docs":{},"示":{"docs":{},"是":{"docs":{},"否":{"docs":{},"需":{"docs":{},"要":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"a":{"docs":{},"f":{"docs":{},"f":{"docs":{},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"y":{"docs":{},"p":{"docs":{},"e":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"]":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"a":{"docs":{},"k":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}}},"i":{"docs":{},"m":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"u":{"docs":{},"i":{"docs":{},"中":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"k":{"docs":{},"n":{"docs":{},"o":{"docs":{},"w":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"l":{"docs":{},"i":{"docs":{},"m":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"e":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.009746588693957114}},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"b":{"docs":{},"u":{"docs":{},"n":{"docs":{},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"v":{"docs":{},"a":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"l":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"u":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}},"i":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"e":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"s":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"i":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"f":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"版":{"docs":{},"界":{"docs":{},"面":{"docs":{},"端":{"docs":{},"口":{"docs":{},"号":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"重":{"docs":{},"构":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"a":{"docs":{},"l":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},"\"":{"docs":{},"s":{"docs":{},"o":{"docs":{},"r":{"docs":{},"r":{"docs":{},"y":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"s":{"docs":{},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"o":{"docs":{},"r":{"docs":{},"l":{"docs":{},"d":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"a":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"n":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"h":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"i":{"docs":{},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"y":{"docs":{},"i":{"docs":{},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}},"o":{"docs":{},"u":{"docs":{},"'":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"{":{"docs":{"./":{"ref":"./","tf":0.06581899775617053},"doc.html":{"ref":"doc.html","tf":0.04337231968810916}},"b":{"docs":{},"o":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},"a":{"docs":{},"n":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}},"n":{"docs":{},"u":{"docs":{},"m":{"docs":{},"b":{"docs":{},"e":{"docs":{},"r":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}},"o":{"docs":{},"b":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.005983545250560957},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"r":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.002243829468960359},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}},"|":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"}":{"docs":{"./":{"ref":"./","tf":0.02243829468960359},"doc.html":{"ref":"doc.html","tf":0.014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.006731488406881077},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}},",":{"docs":{"./":{"ref":"./","tf":0.015706806282722512},"doc.html":{"ref":"doc.html","tf":0.009746588693957114}}},";":{"docs":{"./":{"ref":"./","tf":0.020942408376963352},"doc.html":{"ref":"doc.html","tf":0.014132553606237816}}}},"不":{"docs":{},"做":{"docs":{},"任":{"docs":{},"何":{"docs":{},"处":{"docs":{},"理":{"docs":{},"。":{"docs":{},"此":{"docs":{},"时":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"返":{"docs":{},"回":{"docs":{},"一":{"docs":{},"个":{"docs":{},"默":{"docs":{},"认":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"页":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},",":{"docs":{},"返":{"docs":{},"回":{"docs":{},"n":{"docs":{},"u":{"docs":{},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}},"中":{"docs":{},"把":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"证":{"docs":{},"书":{"docs":{},"的":{"docs":{},"开":{"docs":{},"关":{"docs":{},"打":{"docs":{},"开":{"docs":{},",":{"docs":{},"否":{"docs":{},"则":{"docs":{},"s":{"docs":{},"a":{"docs":{},"f":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{},"将":{"docs":{},"报":{"docs":{},"错":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"文":{"docs":{},"文":{"docs":{},"档":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"为":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"什":{"docs":{},"么":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"能":{"docs":{},"进":{"docs":{},"入":{"docs":{},"处":{"docs":{},"理":{"docs":{},"函":{"docs":{},"数":{"docs":{},"?":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"主":{"docs":{},"要":{"docs":{},"特":{"docs":{},"性":{"docs":{},"包":{"docs":{},"括":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"举":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},",":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},":":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"代":{"docs":{},"理":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"启":{"docs":{},"动":{"docs":{},"完":{"docs":{},"成":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"都":{"docs":{},"在":{"docs":{},"w":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"设":{"docs":{},"置":{"docs":{},"中":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"以":{"docs":{},"c":{"docs":{},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"的":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"t":{"docs":{},"c":{"docs":{},"h":{"docs":{},"y":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"插":{"docs":{},"件":{"docs":{},"为":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"下":{"docs":{},"任":{"docs":{},"意":{"docs":{},"一":{"docs":{},"项":{"docs":{},"都":{"docs":{},"能":{"docs":{},"用":{"docs":{},"来":{"docs":{},"改":{"docs":{},"变":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"处":{"docs":{},"理":{"docs":{},"特":{"docs":{},"性":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"几":{"docs":{},"种":{"docs":{},"返":{"docs":{},"回":{"docs":{},"都":{"docs":{},"是":{"docs":{},"合":{"docs":{},"法":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.002243829468960359}}}}}}}}}}}}},"作":{"docs":{},"为":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"使":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"全":{"docs":{},"局":{"docs":{},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"通":{"docs":{},"过":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"使":{"docs":{},"用":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"举":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"在":{"docs":{},"线":{"docs":{},"地":{"docs":{},"址":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"本":{"docs":{},"地":{"docs":{},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"路":{"docs":{},"径":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"修":{"docs":{},"改":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}},"请":{"docs":{},"求":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"协":{"docs":{},"议":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},",":{"docs":{},"如":{"docs":{},"强":{"docs":{},"制":{"docs":{},"改":{"docs":{},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"发":{"docs":{},"起":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"头":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"的":{"docs":{},"目":{"docs":{},"标":{"docs":{},"地":{"docs":{},"址":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"内":{"docs":{},"容":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"头":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"的":{"docs":{},"内":{"docs":{},"容":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"关":{"docs":{},"闭":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"全":{"docs":{},"局":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"其":{"docs":{},"他":{"docs":{},"命":{"docs":{},"令":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"创":{"docs":{},"建":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"即":{"docs":{},"将":{"docs":{},"发":{"docs":{},"送":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"配":{"docs":{},"置":{"docs":{},",":{"docs":{},"供":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},"'":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"作":{"docs":{},"为":{"docs":{},"使":{"docs":{},"用":{"docs":{},"。":{"docs":{},"详":{"docs":{},"见":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"a":{"docs":{},"p":{"docs":{},"i":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"#":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"_":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"原":{"docs":{},"始":{"docs":{},"的":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"返":{"docs":{},"回":{"docs":{},"对":{"docs":{},"象":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"双":{"docs":{},"击":{"docs":{},"打":{"docs":{},"开":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"发":{"docs":{},"送":{"docs":{},"响":{"docs":{},"应":{"docs":{},"前":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},"拦":{"docs":{},"截":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"只":{"docs":{},"有":{"docs":{},"返":{"docs":{},"回":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"才":{"docs":{},"会":{"docs":{},"尝":{"docs":{},"试":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"、":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"。":{"docs":{},"否":{"docs":{},"则":{"docs":{},"只":{"docs":{},"做":{"docs":{},"数":{"docs":{},"据":{"docs":{},"流":{"docs":{},"转":{"docs":{},"发":{"docs":{},",":{"docs":{},"无":{"docs":{},"法":{"docs":{},"看":{"docs":{},"到":{"docs":{},"明":{"docs":{},"文":{"docs":{},"数":{"docs":{},"据":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"可":{"docs":{},"以":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"函":{"docs":{},"数":{"docs":{},",":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"普":{"docs":{},"通":{"docs":{},"的":{"docs":{},"字":{"docs":{},"符":{"docs":{},"串":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"修":{"docs":{},"改":{"docs":{},"内":{"docs":{},"容":{"docs":{},"包":{"docs":{},"括":{"docs":{},"请":{"docs":{},"求":{"docs":{},"头":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"的":{"docs":{},"内":{"docs":{},"容":{"docs":{},"包":{"docs":{},"括":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{},"(":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"同":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"中":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"中":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}}}}}}}}}}},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"加":{"docs":{},"载":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"后":{"docs":{},"将":{"docs":{},"终":{"docs":{},"端":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"配":{"docs":{},"置":{"docs":{},"为":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{},"即":{"docs":{},"可":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}}}},"命":{"docs":{},"令":{"docs":{},"行":{"docs":{},"启":{"docs":{},"动":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"时":{"docs":{},"配":{"docs":{},"置":{"docs":{},"`":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"端":{"docs":{},"口":{"docs":{},"号":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}},"直":{"docs":{},"接":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"在":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},"代":{"docs":{},"码":{"docs":{},"中":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"w":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"高":{"docs":{},"级":{"docs":{},"设":{"docs":{},"置":{"docs":{},"中":{"docs":{},",":{"docs":{},"配":{"docs":{},"置":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"即":{"docs":{},"可":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"构":{"docs":{},"造":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"实":{"docs":{},"例":{"docs":{},"的":{"docs":{},"时":{"docs":{},"候":{"docs":{},",":{"docs":{},"传":{"docs":{},"入":{"docs":{},"参":{"docs":{},"数":{"docs":{},"d":{"docs":{},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"u":{"docs":{},"s":{"docs":{},"l":{"docs":{},"y":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{},"i":{"docs":{},"z":{"docs":{},"e":{"docs":{},"d":{"docs":{},":":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"请":{"docs":{},"求":{"docs":{},"处":{"docs":{},"理":{"docs":{},"过":{"docs":{},"程":{"docs":{},"中":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"o":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"并":{"docs":{},"提":{"docs":{},"供":{"docs":{},"对":{"docs":{},"应":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"基":{"docs":{},"于":{"docs":{},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{},"s":{"docs":{},",":{"docs":{},"开":{"docs":{},"放":{"docs":{},"二":{"docs":{},"次":{"docs":{},"开":{"docs":{},"发":{"docs":{},"能":{"docs":{},"力":{"docs":{},",":{"docs":{},"允":{"docs":{},"许":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"请":{"docs":{},"求":{"docs":{},"处":{"docs":{},"理":{"docs":{},"逻":{"docs":{},"辑":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"处":{"docs":{},"理":{"docs":{},"流":{"docs":{},"程":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"图":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"多":{"docs":{},"数":{"docs":{},"场":{"docs":{},"景":{"docs":{},"下":{"docs":{},",":{"docs":{},"错":{"docs":{},"误":{"docs":{},"会":{"docs":{},"在":{"docs":{},"请":{"docs":{},"求":{"docs":{},"目":{"docs":{},"标":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"的":{"docs":{},"时":{"docs":{},"候":{"docs":{},"发":{"docs":{},"生":{"docs":{},",":{"docs":{},"比":{"docs":{},"如":{"docs":{},"d":{"docs":{},"n":{"docs":{},"s":{"docs":{},"解":{"docs":{},"析":{"docs":{},"失":{"docs":{},"败":{"docs":{},"、":{"docs":{},"请":{"docs":{},"求":{"docs":{},"超":{"docs":{},"时":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"如":{"docs":{},"下":{"docs":{},"几":{"docs":{},"种":{"docs":{},"方":{"docs":{},"案":{"docs":{},"都":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"来":{"docs":{},"引":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"何":{"docs":{},"引":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"果":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"返":{"docs":{},"回":{"docs":{},"了":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},",":{"docs":{},"则":{"docs":{},"立":{"docs":{},"即":{"docs":{},"把":{"docs":{},"此":{"docs":{},"响":{"docs":{},"应":{"docs":{},"返":{"docs":{},"回":{"docs":{},"到":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"(":{"docs":{},"而":{"docs":{},"不":{"docs":{},"再":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{},"真":{"docs":{},"正":{"docs":{},"的":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},")":{"docs":{},",":{"docs":{},"流":{"docs":{},"程":{"docs":{},"结":{"docs":{},"束":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"配":{"docs":{},"置":{"docs":{},"了":{"docs":{},"全":{"docs":{},"局":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"则":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"略":{"docs":{},"过":{"docs":{},"这":{"docs":{},"个":{"docs":{},"调":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"要":{"docs":{},"启":{"docs":{},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"解":{"docs":{},"析":{"docs":{},",":{"docs":{},"请":{"docs":{},"在":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"启":{"docs":{},"动":{"docs":{},"前":{"docs":{},"自":{"docs":{},"行":{"docs":{},"调":{"docs":{},"用":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{},"相":{"docs":{},"关":{"docs":{},"方":{"docs":{},"法":{"docs":{},"生":{"docs":{},"成":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"并":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"信":{"docs":{},"任":{"docs":{},"安":{"docs":{},"装":{"docs":{},"。":{"docs":{},"或":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"使":{"docs":{},"用":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"c":{"docs":{},"a":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"对":{"docs":{},"于":{"docs":{},"d":{"docs":{},"e":{"docs":{},"b":{"docs":{},"i":{"docs":{},"a":{"docs":{},"n":{"docs":{},"或":{"docs":{},"者":{"docs":{},"u":{"docs":{},"b":{"docs":{},"u":{"docs":{},"n":{"docs":{},"t":{"docs":{},"u":{"docs":{},"系":{"docs":{},"统":{"docs":{},",":{"docs":{},"在":{"docs":{},"安":{"docs":{},"装":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"之":{"docs":{},"前":{"docs":{},",":{"docs":{},"可":{"docs":{},"能":{"docs":{},"还":{"docs":{},"需":{"docs":{},"要":{"docs":{},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"开":{"docs":{},"发":{"docs":{},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"引":{"docs":{},"入":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"当":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"请":{"docs":{},"求":{"docs":{},"经":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"时":{"docs":{},",":{"docs":{},"具":{"docs":{},"体":{"docs":{},"处":{"docs":{},"理":{"docs":{},"过":{"docs":{},"程":{"docs":{},"是":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"收":{"docs":{},"到":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"可":{"docs":{},"以":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"对":{"docs":{},"请":{"docs":{},"求":{"docs":{},"做":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"访":{"docs":{},"问":{"docs":{},"特":{"docs":{},"定":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"站":{"docs":{},"点":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"提":{"docs":{},"示":{"docs":{},"该":{"docs":{},"站":{"docs":{},"点":{"docs":{},"不":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"安":{"docs":{},"全":{"docs":{},"的":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"这":{"docs":{},"通":{"docs":{},"常":{"docs":{},"是":{"docs":{},"因":{"docs":{},"为":{"docs":{},"站":{"docs":{},"点":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"设":{"docs":{},"置":{"docs":{},"不":{"docs":{},"能":{"docs":{},"被":{"docs":{},"正":{"docs":{},"确":{"docs":{},"识":{"docs":{},"别":{"docs":{},"导":{"docs":{},"致":{"docs":{},"的":{"docs":{},"(":{"docs":{},"比":{"docs":{},"如":{"docs":{},",":{"docs":{},"站":{"docs":{},"点":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"是":{"docs":{},"自":{"docs":{},"签":{"docs":{},"发":{"docs":{},"的":{"docs":{},")":{"docs":{},"。":{"docs":{},"如":{"docs":{},"果":{"docs":{},"您":{"docs":{},"信":{"docs":{},"任":{"docs":{},"该":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"以":{"docs":{},"下":{"docs":{},"方":{"docs":{},"式":{"docs":{},"来":{"docs":{},"继":{"docs":{},"续":{"docs":{},"访":{"docs":{},"问":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"必":{"docs":{},"选":{"docs":{},",":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"端":{"docs":{},"口":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"快":{"docs":{},"速":{"docs":{},"开":{"docs":{},"始":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"我":{"docs":{},"们":{"docs":{},"自":{"docs":{},"然":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"借":{"docs":{},"助":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"的":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"来":{"docs":{},"实":{"docs":{},"现":{"docs":{},"这":{"docs":{},"个":{"docs":{},"效":{"docs":{},"果":{"docs":{},",":{"docs":{},"而":{"docs":{},"且":{"docs":{},"我":{"docs":{},"们":{"docs":{},"还":{"docs":{},"可":{"docs":{},"以":{"docs":{},"控":{"docs":{},"制":{"docs":{},"到":{"docs":{},"只":{"docs":{},"允":{"docs":{},"许":{"docs":{},"指":{"docs":{},"定":{"docs":{},"网":{"docs":{},"址":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"对":{"docs":{},"不":{"docs":{},"在":{"docs":{},"列":{"docs":{},"表":{"docs":{},"的":{"docs":{},"网":{"docs":{},"址":{"docs":{},",":{"docs":{},"进":{"docs":{},"行":{"docs":{},"证":{"docs":{},"书":{"docs":{},"的":{"docs":{},"强":{"docs":{},"验":{"docs":{},"证":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"找":{"docs":{},"到":{"docs":{},"刚":{"docs":{},"刚":{"docs":{},"导":{"docs":{},"入":{"docs":{},"的":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"配":{"docs":{},"置":{"docs":{},"为":{"docs":{},"信":{"docs":{},"任":{"docs":{},"(":{"docs":{},"a":{"docs":{},"l":{"docs":{},"w":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"把":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"响":{"docs":{},"应":{"docs":{},"信":{"docs":{},"息":{"docs":{},"返":{"docs":{},"回":{"docs":{},"给":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"所":{"docs":{},"有":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"协":{"docs":{},"议":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"拦":{"docs":{},"截":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"对":{"docs":{},"内":{"docs":{},"容":{"docs":{},"做":{"docs":{},"修":{"docs":{},"改":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"并":{"docs":{},"修":{"docs":{},"改":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"响":{"docs":{},"应":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"正":{"docs":{},"在":{"docs":{},"发":{"docs":{},"送":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"提":{"docs":{},"供":{"docs":{},"g":{"docs":{},"u":{"docs":{},"i":{"docs":{},"界":{"docs":{},"面":{"docs":{},",":{"docs":{},"用":{"docs":{},"以":{"docs":{},"观":{"docs":{},"察":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"示":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"支":{"docs":{},"持":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"收":{"docs":{},"集":{"docs":{},"请":{"docs":{},"求":{"docs":{},"所":{"docs":{},"有":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"包":{"docs":{},"括":{"docs":{},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"改":{"docs":{},"成":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"并":{"docs":{},"发":{"docs":{},"送":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"时":{"docs":{},",":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"参":{"docs":{},"数":{"docs":{},"内":{"docs":{},"容":{"docs":{},"大":{"docs":{},"致":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"参":{"docs":{},"数":{"docs":{},"内":{"docs":{},"容":{"docs":{},"大":{"docs":{},"致":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"否":{"docs":{},"启":{"docs":{},"用":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"版":{"docs":{},"界":{"docs":{},"面":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"处":{"docs":{},"理":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"屏":{"docs":{},"蔽":{"docs":{},"所":{"docs":{},"有":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},"输":{"docs":{},"出":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"强":{"docs":{},"制":{"docs":{},"拦":{"docs":{},"截":{"docs":{},"所":{"docs":{},"有":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},",":{"docs":{},"忽":{"docs":{},"略":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"忽":{"docs":{},"略":{"docs":{},"请":{"docs":{},"求":{"docs":{},"中":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}},"必":{"docs":{},"选":{"docs":{},"字":{"docs":{},"段":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"包":{"docs":{},"括":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"本":{"docs":{},"文":{"docs":{},"档":{"docs":{},"的":{"docs":{},"适":{"docs":{},"用":{"docs":{},"范":{"docs":{},"围":{"docs":{},"是":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"质":{"docs":{},"是":{"docs":{},"中":{"docs":{},"间":{"docs":{},"人":{"docs":{},"攻":{"docs":{},"击":{"docs":{},"(":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"来":{"docs":{},"加":{"docs":{},"载":{"docs":{},"模":{"docs":{},"块":{"docs":{},"并":{"docs":{},"体":{"docs":{},"验":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"忽":{"docs":{},"略":{"docs":{},"证":{"docs":{},"书":{"docs":{},"认":{"docs":{},"证":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"。":{"docs":{},"需":{"docs":{},"要":{"docs":{},"注":{"docs":{},"意":{"docs":{},"的":{"docs":{},"是":{"docs":{},",":{"docs":{},"该":{"docs":{},"参":{"docs":{},"数":{"docs":{},"是":{"docs":{},"全":{"docs":{},"局":{"docs":{},"生":{"docs":{},"效":{"docs":{},"的":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"你":{"docs":{},"在":{"docs":{},"此":{"docs":{},"期":{"docs":{},"间":{"docs":{},"访":{"docs":{},"问":{"docs":{},"了":{"docs":{},"其":{"docs":{},"他":{"docs":{},"未":{"docs":{},"知":{"docs":{},"的":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"他":{"docs":{},"们":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"问":{"docs":{},"题":{"docs":{},"也":{"docs":{},"会":{"docs":{},"被":{"docs":{},"忽":{"docs":{},"略":{"docs":{},",":{"docs":{},"这":{"docs":{},"可":{"docs":{},"能":{"docs":{},"会":{"docs":{},"带":{"docs":{},"来":{"docs":{},"安":{"docs":{},"全":{"docs":{},"隐":{"docs":{},"患":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"查":{"docs":{},"看":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"校":{"docs":{},"验":{"docs":{},"系":{"docs":{},"统":{"docs":{},"内":{"docs":{},"是":{"docs":{},"否":{"docs":{},"存":{"docs":{},"在":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"根":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"样":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"根":{"docs":{},"据":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"向":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"发":{"docs":{},"出":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"接":{"docs":{},"收":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"响":{"docs":{},"应":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},"模":{"docs":{},"块":{"docs":{},"介":{"docs":{},"绍":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"此":{"docs":{},"处":{"docs":{},"无":{"docs":{},"法":{"docs":{},"控":{"docs":{},"制":{"docs":{},"向":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"无":{"docs":{},"需":{"docs":{},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"注":{"docs":{},"意":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"引":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"前":{"docs":{},",":{"docs":{},"请":{"docs":{},"务":{"docs":{},"必":{"docs":{},"确":{"docs":{},"保":{"docs":{},"文":{"docs":{},"件":{"docs":{},"来":{"docs":{},"源":{"docs":{},"可":{"docs":{},"靠":{"docs":{},",":{"docs":{},"以":{"docs":{},"免":{"docs":{},"发":{"docs":{},"生":{"docs":{},"安":{"docs":{},"全":{"docs":{},"问":{"docs":{},"题":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"测":{"docs":{},"试":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"访":{"docs":{},"问":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}},"点":{"docs":{},"击":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"然":{"docs":{},"后":{"docs":{},",":{"docs":{},"安":{"docs":{},"装":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"生":{"docs":{},"成":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},",":{"docs":{},"完":{"docs":{},"成":{"docs":{},"后":{"docs":{},"请":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"信":{"docs":{},"任":{"docs":{},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{},"文":{"docs":{},"件":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"用":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{},"发":{"docs":{},"请":{"docs":{},"求":{"docs":{},"测":{"docs":{},"试":{"docs":{},"的":{"docs":{},"方":{"docs":{},"法":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"测":{"docs":{},"试":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"测":{"docs":{},"试":{"docs":{},":":{"docs":{},"配":{"docs":{},"置":{"docs":{},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"为":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"会":{"docs":{},"被":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"请":{"docs":{},"求":{"docs":{},"全":{"docs":{},"部":{"docs":{},"改":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{},"使":{"docs":{},"用":{"docs":{},"本":{"docs":{},"地":{"docs":{},"数":{"docs":{},"据":{"docs":{},"代":{"docs":{},"替":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{},"里":{"docs":{},"加":{"docs":{},"上":{"docs":{},"测":{"docs":{},"试":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"5":{"docs":{},"秒":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"docs":{}}}}}}}}}}}}},"头":{"docs":{},"里":{"docs":{},"加":{"docs":{},"上":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"最":{"docs":{},"后":{"docs":{},"追":{"docs":{},"加":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"签":{"docs":{},"名":{"docs":{},",":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"5":{"docs":{},"秒":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"docs":{}}}}}}}}}}}}}}}}}}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{},"都":{"docs":{},"改":{"docs":{},"成":{"4":{"0":{"4":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}},"直":{"docs":{},"接":{"docs":{},"请":{"docs":{},"求":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},":":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},",":{"docs":{},"不":{"docs":{},"再":{"docs":{},"发":{"docs":{},"起":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"其":{"docs":{},"中":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}},"相":{"docs":{},"比":{"3":{"docs":{},".":{"docs":{},"x":{"docs":{},"版":{"docs":{},"本":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"docs":{}}},"确":{"docs":{},"认":{"docs":{},"将":{"docs":{},"证":{"docs":{},"书":{"docs":{},"添":{"docs":{},"加":{"docs":{},"到":{"docs":{},"l":{"docs":{},"o":{"docs":{},"g":{"docs":{},"i":{"docs":{},"n":{"docs":{},"或":{"docs":{},"s":{"docs":{},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786}}}},"简":{"docs":{},"介":{"docs":{"./":{"ref":"./","tf":10}}},"化":{"docs":{},"了":{"docs":{},"规":{"docs":{},"则":{"docs":{},"文":{"docs":{},"件":{"docs":{},"内":{"docs":{},"的":{"docs":{},"接":{"docs":{},"口":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"管":{"docs":{},"理":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"系":{"docs":{},"统":{"docs":{},"的":{"docs":{},"全":{"docs":{},"局":{"docs":{},"代":{"docs":{},"理":{"docs":{},"配":{"docs":{},"置":{"docs":{},",":{"docs":{},"方":{"docs":{},"法":{"docs":{},"调":{"docs":{},"用":{"docs":{},"时":{"docs":{},"可":{"docs":{},"能":{"docs":{},"会":{"docs":{},"弹":{"docs":{},"出":{"docs":{},"密":{"docs":{},"码":{"docs":{},"框":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"类":{"docs":{},"似":{"docs":{},"这":{"docs":{},"种":{"docs":{},"报":{"docs":{},"错":{"docs":{},"都":{"docs":{},"是":{"docs":{},"因":{"docs":{},"为":{"docs":{},"系":{"docs":{},"统":{"docs":{},"没":{"docs":{},"有":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{},"所":{"docs":{},"造":{"docs":{},"成":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"经":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"后":{"docs":{},",":{"docs":{},"期":{"docs":{},"望":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"被":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"后":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"处":{"docs":{},"理":{"docs":{},"流":{"docs":{},"程":{"docs":{},"同":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"一":{"docs":{},"致":{"docs":{},"。":{"docs":{},"未":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"会":{"docs":{},"再":{"docs":{},"进":{"docs":{},"入":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"规":{"docs":{},"则":{"docs":{},"文":{"docs":{},"件":{"docs":{},"中":{"docs":{},",":{"docs":{},"除":{"docs":{},"了":{"docs":{},"s":{"docs":{},"u":{"docs":{},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"r":{"docs":{},"y":{"docs":{},",":{"docs":{},"都":{"docs":{},"是":{"docs":{},"由":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"内":{"docs":{},"提":{"docs":{},"供":{"docs":{},"`":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"`":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"(":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},")":{"docs":{},"全":{"docs":{},"面":{"docs":{},"支":{"docs":{},"持":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"和":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"模":{"docs":{},"块":{"docs":{},"应":{"docs":{},"该":{"docs":{},"符":{"docs":{},"合":{"docs":{},"c":{"docs":{},"m":{"docs":{},"d":{"docs":{},"规":{"docs":{},"范":{"docs":{},",":{"docs":{},"一":{"docs":{},"个":{"docs":{},"典":{"docs":{},"型":{"docs":{},"的":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"代":{"docs":{},"码":{"docs":{},"结":{"docs":{},"构":{"docs":{},"如":{"docs":{},"下":{"docs":{},"。":{"docs":{},"模":{"docs":{},"块":{"docs":{},"中":{"docs":{},"所":{"docs":{},"有":{"docs":{},"方":{"docs":{},"法":{"docs":{},"都":{"docs":{},"是":{"docs":{},"可":{"docs":{},"选":{"docs":{},"的":{"docs":{},",":{"docs":{},"只":{"docs":{},"需":{"docs":{},"实":{"docs":{},"现":{"docs":{},"业":{"docs":{},"务":{"docs":{},"感":{"docs":{},"兴":{"docs":{},"趣":{"docs":{},"的":{"docs":{},"部":{"docs":{},"分":{"docs":{},"即":{"docs":{},"可":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"的":{"docs":{},"介":{"docs":{},"绍":{"docs":{},"文":{"docs":{},"案":{"docs":{},",":{"docs":{},"用":{"docs":{},"于":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"提":{"docs":{},"示":{"docs":{},"用":{"docs":{},"户":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"能":{"docs":{},"力":{"docs":{},"范":{"docs":{},"围":{"docs":{},"包":{"docs":{},"括":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"原":{"docs":{},"理":{"docs":{},"是":{"docs":{},"中":{"docs":{},"间":{"docs":{},"人":{"docs":{},"攻":{"docs":{},"击":{"docs":{},"(":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"警":{"docs":{},"告":{"docs":{},":":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},"和":{"docs":{},"系":{"docs":{},"统":{"docs":{},"安":{"docs":{},"全":{"docs":{},"息":{"docs":{},"息":{"docs":{},"相":{"docs":{},"关":{"docs":{},",":{"docs":{},"建":{"docs":{},"议":{"docs":{},"亲":{"docs":{},"自":{"docs":{},"生":{"docs":{},"成":{"docs":{},",":{"docs":{},"并":{"docs":{},"妥":{"docs":{},"善":{"docs":{},"保":{"docs":{},"管":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"设":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"属":{"docs":{},"性":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"访":{"docs":{},"问":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}},"证":{"docs":{},"书":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"请":{"docs":{},"求":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"使":{"docs":{},"用":{"docs":{},"的":{"docs":{},"协":{"docs":{},"议":{"docs":{},",":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"或":{"docs":{},"者":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"出":{"docs":{},"错":{"docs":{},"的":{"docs":{},"事":{"docs":{},"件":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"的":{"docs":{},"原":{"docs":{},"始":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}},"目":{"docs":{},"标":{"docs":{},"的":{"docs":{},"h":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"受":{"docs":{},"制":{"docs":{},"于":{"docs":{},"协":{"docs":{},"议":{"docs":{},",":{"docs":{},"这":{"docs":{},"里":{"docs":{},"无":{"docs":{},"法":{"docs":{},"获":{"docs":{},"取":{"docs":{},"完":{"docs":{},"整":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"调":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"返":{"docs":{},"回":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{},"会":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"这":{"docs":{},"个":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"其":{"docs":{},"他":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"由":{"docs":{},"模":{"docs":{},"块":{"docs":{},"对":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},"进":{"docs":{},"行":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"运":{"docs":{},"行":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"错":{"docs":{},"误":{"docs":{},"页":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"这":{"docs":{},"里":{"docs":{},"提":{"docs":{},"供":{"docs":{},"一":{"docs":{},"些":{"docs":{},"样":{"docs":{},"例":{"docs":{},",":{"docs":{},"来":{"docs":{},"讲":{"docs":{},"解":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"的":{"docs":{},"常":{"docs":{},"见":{"docs":{},"用":{"docs":{},"法":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"通":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"请":{"docs":{},"求":{"docs":{},":":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"启":{"docs":{},"动":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"的":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"来":{"docs":{},"修":{"docs":{},"改":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"这":{"docs":{},"种":{"docs":{},"方":{"docs":{},"式":{"docs":{},"初":{"docs":{},"始":{"docs":{},"化":{"docs":{},"的":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"其":{"docs":{},"配":{"docs":{},"置":{"docs":{},"也":{"docs":{},"是":{"docs":{},"全":{"docs":{},"局":{"docs":{},"性":{"docs":{},"的":{"docs":{},",":{"docs":{},"所":{"docs":{},"有":{"docs":{},"网":{"docs":{},"站":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"问":{"docs":{},"题":{"docs":{},"都":{"docs":{},"会":{"docs":{},"被":{"docs":{},"忽":{"docs":{},"略":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"配":{"docs":{},"置":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{},"为":{"docs":{},"全":{"docs":{},"局":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{},"i":{"docs":{},"o":{"docs":{},"s":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{},"系":{"docs":{},"统":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"o":{"docs":{},"s":{"docs":{},"x":{"docs":{},"系":{"docs":{},"统":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"启":{"docs":{},"动":{"docs":{},"端":{"docs":{},"口":{"docs":{},",":{"docs":{},"如":{"1":{"0":{"8":{"0":{"docs":{},"端":{"docs":{},"口":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"附":{"docs":{},"录":{"docs":{},":":{"docs":{},"如":{"docs":{},"何":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"限":{"docs":{},"速":{"docs":{},"值":{"docs":{},",":{"docs":{},"单":{"docs":{},"位":{"docs":{},"k":{"docs":{},"b":{"docs":{},"/":{"docs":{},"s":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"不":{"docs":{},"限":{"docs":{},"速":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"除":{"docs":{},"了":{"docs":{},"上":{"docs":{},"述":{"docs":{},"证":{"docs":{},"书":{"docs":{},"安":{"docs":{},"装":{"docs":{},"过":{"docs":{},"程":{"docs":{},",":{"docs":{},"还":{"docs":{},"需":{"docs":{},"要":{"docs":{},"在":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"需":{"docs":{},"要":{"docs":{},"编":{"docs":{},"写":{"docs":{},"一":{"docs":{},"个":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},",":{"docs":{},"在":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"驱":{"docs":{},"动":{"docs":{},"的":{"docs":{},",":{"docs":{},"函":{"docs":{},"数":{"docs":{},"需":{"docs":{},"要":{"docs":{},"满":{"docs":{},"足":{"docs":{},"y":{"docs":{},"i":{"docs":{},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"。":{"docs":{},"可":{"docs":{},"以":{"docs":{},"使":{"docs":{},"用":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"方":{"docs":{},"法":{"docs":{},"或":{"docs":{},"是":{"docs":{},"返":{"docs":{},"回":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"或":{"docs":{},"使":{"docs":{},"用":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"函":{"docs":{},"数":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},",":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"界":{"docs":{},"面":{"docs":{},"上":{"docs":{},"能":{"docs":{},"看":{"docs":{},"到":{"docs":{},"所":{"docs":{},"有":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"界":{"docs":{},"面":{"docs":{},"上":{"docs":{},"能":{"docs":{},"看":{"docs":{},"到":{"docs":{},"刚":{"docs":{},"才":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"[":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"t":{"docs":{},"c":{"docs":{},"h":{"docs":{},"y":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"j":{"docs":{},"a":{"docs":{},"v":{"docs":{},"a":{"docs":{},"s":{"docs":{},"c":{"docs":{},"r":{"docs":{},"i":{"docs":{},"p":{"docs":{},"t":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"length":2606},"corpusTokens":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#","#gener","#global","#launch","#local","#全局包","#启动anyproxy,并解析所有https请求","#本地包","#生成rootca证书,生成后需要手动信任","&&","'","'...'","'...',","'/',","'/index.html';","'/redirect/to/another/path';","'/user","'127.0.0.1'","'8001');","'8008';","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'hello","'http',","'http';","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'text/html';","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(){","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","*true*","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","//这里也可以同时加上requestopt","/^win/.test(process.platform);","0)","1.","10.3","10.3信任ca证书","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","127.0.0.1:8001/index.html","127.0.0.1:8001,访问","1,write","1,编写规则","2,","2.","200,","3,","3.x:","300);","4,","4.0","4.0的主要变化:","4.0,欢迎提供反馈","4.x","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>",">=",">关于本机",">证书信任设置",">通用","[switchyomega]","_re","_req","_req:","_res:","`","`;","a:","abil","abliti","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent';","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxy@beta","anyproxyin","anyproxyins.start();","anyproxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包","anyproxy可以作为一个npm模块使用,整合进其他工具。","anyproxy向客户端发送请求前,会调用beforesendresponse,并带上参数requestdetail","anyproxy向服务端发送请求前,会调用beforesendrequest,并带上参数requestdetail","anyproxy在与目标https服务器建立连接的过程中,如果发生错误,anyproxy会调用这个方法","anyproxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。","anyproxy收到https请求时,会调用beforedealhttpsrequest,并带上参数requestdetail","anyproxy是一个开放式的http代理服务器。","anyproxy调用规则模块beforesendrequest方法,由模块做处理,返回新的请求参数,或返回响应内容","anyproxy默认不对https请求做处理,如需看到明文信息,需要配置ca证书","apg","append","appendix:how","at',","attack","attack.","attack),需要客户端提前信任anyproxy生成的ca","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","beta","bodi","body,","body:","body三个字段","body等","body),甚至是请求的目标地址等","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","ca方法。","ca,按提示扫描二维码即可安装","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code)、响应头(respons","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","design","detect","determin","directli","disabl","dn","doc","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","english","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","features:","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","fulli","function","function()","function.","g","gener","get","github:","github主页:https://github.com/alibaba/anyproxy/tree/4.x","give","global","go,","gui","guid","h","hacked!","hacked...\")","handl","handling.","happen","header","header),请求体(request","header,","header:","headers:","headersar","header)、响应内容等","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http/http","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/ani","http://httpbin.org/post","http://httpbin.org/us","http://localhost:8008/index.html","httpbin.org","httpbin.org,","https://github.com/alibaba/anyproxy/tree/4.x","https://httpbin.org/us","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","https连接服务器出错","http代理配置","http的代理模式中,这里的request是connect请求","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","intercept`参数,按npm模块启动时配置`forceproxyhttps`参数,所有https请求都会被替换证书并解析","interest","interfac","interface,","introduct","invok","io","ios/android","ios系统信任ca证书","issu","iswin","it,","it.","javascript.","js","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","middle),用户必须信任anyproxy生成的ca证书,才能进行后续流程","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.hostnam","newrequestoptions.method","newrequestoptions.path","newrequestoptions.port","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['cont","newresponse.header['x","newresponse.statuscod","node.j","nodej","nodejs.","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","offer","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","osx系统信任ca证书","otherwire,","otherwise,","over","own.","page","param","part","pass","password","path","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.protocol","requestdetail.requestoptions);","requestdetail.requestoptions;","requestdetail;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","rule接口文档","rule样例","rule模块","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(","settimeout(()","settings.","side","signed.","silent","silent:","simpl","simplifi","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","string","stuff:","sudo","summari","summary():","summary,","summary:","summary:str","support","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","true;","true或者false,表示是否需要anyproxy替换证书并解析http","trust","trust)","type':","type']","type:","typic","ubuntu","ui,","ui中的","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","web版界面端口号,默认8002","web版界面配置","web版界面重构","whether","wifi","window","window.alert(\"sorry,","windows系统信任ca证书","without","work","world'","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","|","}","})","});","},","};","不做任何处理。此时anyproxy会返回一个默认的错误页。","不做任何处理,返回null","中把anyproxy证书的开关打开,否则safari将报错。","中文文档","为","为什么https请求不能进入处理函数?","主要特性包括:","举例","举例,请求","举例:请求","代理http","代理服务器发生错误","代理服务器启动完成","代理服务器都在wifi设置中配置","以chrome的switchyomega插件为例","以下任意一项都能用来改变https的处理特性:","以下几种返回都是合法的","作为npm模块使用","作为全局模块","你可以通过","使用npm包","使用举例","使用在线地址","使用本地数据","使用本地路径anyproxi","使用示例","修改发送到","修改请求bodi","修改请求协议","修改请求协议,如强制改用https发起请求","修改请求参数","修改请求头","修改请求数据","修改请求的目标地址","修改返回内容并延迟","修改返回头","修改返回状态码","修改返回的内容","修改返回的状态码","关闭代理服务器","关闭全局代理服务器","其他命令","创建代理服务器","即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback","原始的服务端返回对象","双击打开rootca.crt","发送响应前处理","发送请求前拦截处理","只有返回true时,anyproxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。","可以是一个函数,也可以是一个普通的字符串","可修改内容包括请求头(request","可修改的内容包括http状态码(statu","同beforedealhttpsrequest中的参数","同beforesendrequest中的参数","启动","启动anyproxy,加载规则","启动代理服务器","启动后将终端http代理服务器配置为127.0.0.1:8001即可","命令行启动anyproxy时配置`","命令行启动anyproxy,默认端口号8001","命令行直接启动","在","在nodejs代码中启动","在wifi高级设置中,配置http代理即可","在构造anyproxy实例的时候,传入参数dangerouslyignoreunauthorized:true,","在请求处理过程中发生错误时,anyproxy会调用onerror方法,并提供对应的错误信息","基于node.js,开放二次开发能力,允许自定义请求处理逻辑","处理流程","处理流程图如下","多数场景下,错误会在请求目标服务器的时候发生,比如dns解析失败、请求超时等","如下几种方案都可以用来引用规则模块:","如下:","如何引用","如果beforesendrequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。","如果配置了全局解析https的参数,则anyproxy会略过这个调用","如要启用https解析,请在代理服务器启动前自行调用anyproxy.utils.certmgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxi","安装","安装ca:","对于debian或者ubuntu系统,在安装anyproxy之前,可能还需要安装","开发示例","引入","当http请求经过代理服务器时,具体处理过程是:","当代理服务器收到https请求时,anyproxy可以替换证书,对请求做明文解析。","当访问特定的https站点,anyproxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问:","必选,代理服务器端口","快速开始","我们自然也可以借助自定义的rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。","所有http://httpbin.org","找到刚刚导入的anyproxy证书,配置为信任(alway","把","把响应信息返回给客户端","把所有发送到","把用http协议请求的","拦截https请求,对内容做修改","拦截发送到","拦截并修改服务端响应","拦截并修改正在发送的请求","提供gui界面,用以观察请求","提示","支持https的解析","收集请求所有请求参数,包括method,","改成https并发送","时,requestdetail参数内容大致如下","时,responsedetail参数内容大致如下","是否启用web版界面,默认fals","是否处理https请求","是否屏蔽所有console输出,默认fals","是否强制拦截所有的https,忽略规则模块的返回,默认fals","是否忽略请求中的证书错误,默认fals","是必选字段","服务端的返回信息,包括statuscod","本文档的适用范围是anyproxi","本质是中间人攻击(man","来加载模块并体验","来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。","查看请求信息","校验系统内是否存在anyproxy的根证书","样例","根据请求参数,向服务端发出请求,接收服务端响应。","模块介绍","此处无法控制向客户端的返回信息,无需返回值。","注意:http","注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题","测试规则","浏览器访问http://127.0.0.1:8002","点击web","然后,安装anyproxi","生成anyproxy的rootca,完成后请引导用户信任.crt文件","生成证书并解析所有https请求","用curl发请求测试的方法如下","用curl测试","用浏览器测试:配置浏览器http代理为","的https请求会被解析","的post数据","的user","的请求全部改到","的请求,使用本地数据代替服务端返回","的返回值里加上测试信息,并延迟5秒返回","的返回头里加上","的返回最后追加anyproxy的签名,并延迟5秒","的返回状态码都改成404","直接请求服务器:curl","直接返回客户端,不再发起请求,其中statuscod","相比3.x版本,anyproxi","确认将证书添加到login或system","示例","简介","简化了规则文件内的接口","管理anyproxy的证书","管理系统的全局代理配置,方法调用时可能会弹出密码框","类似这种报错都是因为系统没有信任anyproxy生成的ca所造成的","经过代理服务器后,期望的返回如下","自定义规则模块","被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。","规则文件中,除了summary,都是由","规则文件内提供`beforedealhttpsrequest`方法,返回","规则文件(rule)全面支持promise和gener","规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。","规则模块是用","规则模块的介绍文案,用于anyproxy提示用户,","规则模块的能力范围包括:","解析https请求的原理是中间人攻击(man","警告:ca证书和系统安全息息相关,建议亲自生成,并妥善保管","设置","设置属性","访问http://127.0.0.1:8002","证书配置","请求bodi","请求url","请求使用的协议,http或者http","请求出错的事件","请求的原始request","请求目标的host,受制于协议,这里无法获取完整url","调用规则模块beforedealhttpsrequest方法,如果返回true,会明文解析这个请求,其他请求不处理","调用规则模块beforesendresponse方法,由模块对响应内容进行处理","运行","返回值","返回自定义错误页","这里提供一些样例,来讲解规则模块的常见用法","通过代理服务器请求:curl","通过启动参数","通过自定义的rule来修改","通过这种方式初始化的anyproxy,其配置也是全局性的,所有网站的证书问题都会被忽略","配置127.0.0.1:8001为全局http代理服务器","配置ios/android系统代理","配置osx系统代理","配置启动端口,如1080端口启动","配置浏览器http代理","附录:如何信任ca证书","限速值,单位kb/s,默认不限速","除了上述证书安装过程,还需要在","需要编写一个规则模块,在","驱动的,函数需要满足yieldable。可以使用generator方法或是返回promise。","驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。",",web界面上能看到所有的请求信息",",界面上能看到刚才的请求信息",":"],"pipeline":["stopWordFilter","stemmer"]},"store":{"./":{"url":"./","title":"简介","keywords":"","body":"AnyProxy\n\n本文档的适用范围是AnyProxy 4.0,欢迎提供反馈\n\nRef: English Doc\nAnyProxy是一个开放式的HTTP代理服务器。\nGithub主页:https://github.com/alibaba/anyproxy/tree/4.x\n主要特性包括:\n\n基于Node.js,开放二次开发能力,允许自定义请求处理逻辑\n支持Https的解析\n提供GUI界面,用以观察请求\n相比3.x版本,AnyProxy 4.0的主要变化:\n\n规则文件(Rule)全面支持Promise和Generator\n\n简化了规则文件内的接口\nWeb版界面重构\n\n\n快速开始\n作为全局模块\n安装\n对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 nodejs-legacy\nsudo apg-get install nodejs-legacy\n\n然后,安装AnyProxy\nnpm install -g anyproxy\n\n启动\n\n命令行启动AnyProxy,默认端口号8001\n\nanyproxy\n\n\n启动后将终端http代理服务器配置为127.0.0.1:8001即可\n访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息\n\n其他命令\n\n配置启动端口,如1080端口启动\n\nanyproxy --port 1080\n\n作为npm模块使用\nAnyProxy可以作为一个npm模块使用,整合进其他工具。\n\n如要启用https解析,请在代理服务器启动前自行调用AnyProxy.utils.certMgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxy-ca方法。\n\n\n引入\n\nnpm i anyproxy --save\n\n\n使用举例\n\nconst AnyProxy = require('anyproxy');\nconst options = {\n port: 8001,\n rule: require('myRuleModule'),\n webInterface: {\n enable: true,\n webPort: 8002,\n wsPort: 8003,\n },\n throttle: 10000,\n forceProxyHttps: false,\n silent: false\n};\nconst proxyServer = new AnyProxy.ProxyServer(options);\n\nproxyServer.on('ready', () => { /* */ });\nproxyServer.on('error', (e) => { /* */ });\nproxyServer.start();\n\n//when finished\nproxyServer.close();\n\n\nClass: AnyProxy.proxyServer\n\n创建代理服务器\nconst proxy = new AnyProxy.proxyServer(options)\n\n\noptions\n\nport {number} 必选,代理服务器端口\nrule {object} 自定义规则模块\nthrottle {number} 限速值,单位kb/s,默认不限速\nforceProxyHttps {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认false\nsilent {boolean} 是否屏蔽所有console输出,默认false\ndangerouslyIgnoreUnauthorized {boolean} 是否忽略请求中的证书错误,默认false\nwebInterface {object} web版界面配置\nenable {boolean} 是否启用web版界面,默认false\nwebPort {number} web版界面端口号,默认8002\n\n\n\n\nEvent: ready\n\n代理服务器启动完成\n示例\n\nproxy.on('ready', function() { })\n\n\nEvent: error\n\n代理服务器发生错误\n示例\n\nproxy.on('error', function() { })\n\n\nMethod: start\n\n启动代理服务器\n示例\n\nproxy.start();\n\n\nMethod: close\n\n关闭代理服务器\n示例\n\nproxy.close();\n\n\n\n\nAnyProxy.utils.systemProxyMgr\n\n管理系统的全局代理配置,方法调用时可能会弹出密码框\n使用示例\n\n// 配置127.0.0.1:8001为全局http代理服务器\nAnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');\n\n// 关闭全局代理服务器\nAnyProxy.utils.systemProxyMgr.disableGlobalProxy();\n\n\nAnyProxy.utils.certMgr\n\n管理AnyProxy的证书\nAnyProxy.utils.certMgr.ifRootCAFileExists()\n校验系统内是否存在AnyProxy的根证书\n\n\nAnyProxy.utils.certMgr.generateRootCA(callback)\n生成AnyProxy的rootCA,完成后请引导用户信任.crt文件\n\n\n样例\n\n const AnyProxy = require('AnyProxy');\n const exec = require('child_process').exec;\n\n if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {\n AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {\n // let users to trust this CA before using proxy\n if (!error) {\n const certDir = require('path').dirname(keyPath);\n console.log('The cert is generated at', certDir);\n const isWin = /^win/.test(process.platform);\n if (isWin) {\n exec('start .', { cwd: certDir });\n } else {\n exec('open .', { cwd: certDir });\n }\n } else {\n console.error('error when generating rootCA', error);\n }\n });\n }\n\n\n\n代理HTTPS\n\nAnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书\n\n\n解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程\n\n\n生成证书并解析所有https请求\n\nanyproxy-ca #生成rootCA证书,生成后需要手动信任\nanyproxy --intercept #启动AnyProxy,并解析所有https请求\n\n\n附录:如何信任CA证书\n\nrule模块\nAnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。\n\n注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题\n\n规则模块的能力范围包括:\n\n拦截并修改正在发送的请求\n可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等\n\n\n拦截并修改服务端响应\n可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等\n\n\n拦截https请求,对内容做修改\n本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA\n\n\n\n开发示例\n\n举例\n\n需要编写一个规则模块,在 GET http://httpbin.org/user-agent 的返回值里加上测试信息,并延迟5秒返回\n\n\nStep 1,编写规则\n// file: sample.js\nmodule.exports = {\n summary: 'a rule to modify response',\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n\n newResponse.body += `\n setTimeout(\n function (){\n window.alert(\"Sorry, You Are Hacked...\")\n }, 300);\n `;\n newResponse.header['Content-Type'] = 'text/html';\n\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n\nStep 2, 启动AnyProxy,加载规则\n\n运行 anyproxy --rule sample.js\n\n\nStep 3, 测试规则\n\n用curl测试\ncurl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n\n\n用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent\n\n经过代理服务器后,期望的返回如下\n\n\n{\n \"user-agent\": \"curl/7.43.0\"\n}\n- AnyProxy Hacked!\n\nStep 4, 查看请求信息\n\n浏览器访问http://127.0.0.1:8002 ,界面上能看到刚才的请求信息\n\n\n\n处理流程\n\n处理流程图如下\n\n\n\n当http请求经过代理服务器时,具体处理过程是:\n\n收集请求所有请求参数,包括method, header, body等\nAnyProxy调用规则模块beforeSendRequest方法,由模块做处理,返回新的请求参数,或返回响应内容\n如果beforeSendRequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。\n根据请求参数,向服务端发出请求,接收服务端响应。\n调用规则模块beforeSendResponse方法,由模块对响应内容进行处理\n把响应信息返回给客户端\n\n\n当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。\n\n调用规则模块beforeDealHttpsRequest方法,如果返回true,会明文解析这个请求,其他请求不处理\n被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。\n\n\n\n如何引用\n如下几种方案都可以用来引用规则模块:\n\n使用本地路径anyproxy --rule ./rule.js\n\n\n使用在线地址\nanyproxy --rule https://sample.com/rule.js\n\n\n使用npm包\n\nAnyProxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包\n\nanyproxy --rule ./myRulePkg/ #本地包\nnpm i -g myRulePkg && anyproxy --rule myRulePkg #全局包\n\n\n\nrule接口文档\n规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。\nmodule.exports = {\n // 模块介绍\n summary: 'my customized rule for AnyProxy',\n // 发送请求前拦截处理\n *beforeSendRequest(requestDetail) { /* ... */ },\n // 发送响应前处理\n *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },\n // 是否处理https请求\n *beforeDealHttpsRequest(requestDetail) { /* ... */ },\n // 请求出错的事件\n *onError(requestDetail, error) { /* ... */ },\n // https连接服务器出错\n *onConnectError(requestDetail, error) { /* ... */ }\n};\n\n\n规则文件中,除了summary,都是由 co 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。\n\nsummary\nsummary(): string | summary:string\n\n规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串\n\nbeforeSendRequest\nbeforeSendRequest(requestDetail)\n\nAnyProxy向服务端发送请求前,会调用beforeSendRequest,并带上参数requestDetail\nrequestDetail\nprotocol {string} 请求使用的协议,http或者https\nrequestOptions {object} 即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback\nrequestData {object} 请求Body\nurl {string} 请求url\n_req {object} 请求的原始request\n\n\n举例:请求 anyproxy.io 时,requestDetail参数内容大致如下\n{\n protocol: 'http',\n url: 'http://anyproxy.io/',\n requestOptions: {\n hostname: 'anyproxy.io',\n port: 80,\n path: '/',\n method: 'GET',\n headers: {\n Host: 'anyproxy.io',\n 'Proxy-Connection': 'keep-alive',\n 'User-Agent': '...'\n }\n },\n requestData: '...',\n _req: { /* ... */}\n}\n\n\n以下几种返回都是合法的\n\n不做任何处理,返回null\n\nreturn null;\n\n\n修改请求协议,如强制改用https发起请求\n\nreturn {\n protocol: 'https'\n};\n\n\n修改请求参数\n\nvar newOption = Object.assign({}, requestDetail.requestOptions);\nnewOption.path = '/redirect/to/another/path';\nreturn {\n requestOptions: newOption\n};\n\n\n修改请求body\n\nreturn {\n requestData: 'my new request data'\n //这里也可以同时加上requestOptions\n};\n\n\n直接返回客户端,不再发起请求,其中statusCode header 是必选字段\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nbeforeSendResponse\nbeforeSendResponse(requestDetail, responseDetail)\n\nAnyProxy向客户端发送请求前,会调用beforeSendResponse,并带上参数requestDetail responseDetail\nrequestDetail 同beforeSendRequest中的参数\nresponseDetail\nresponse {object} 服务端的返回信息,包括statusCode header body三个字段\n_res {object} 原始的服务端返回对象\n\n\n举例,请求 anyproxy.io 时,responseDetail参数内容大致如下\n{\n response: {\n statusCode: 200,\n header: {\n 'Content-Type': 'image/gif',\n Connection: 'close',\n 'Cache-Control': '...'\n },\n body: '...'\n },\n _res: { /* ... */ }\n}\n\n\n以下几种返回都是合法的\n\n不做任何处理,返回null\n\nreturn null;\n\n\n修改返回的状态码\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.statusCode = 404;\nreturn {\n response: newResponse\n};\n\n\n修改返回的内容\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.body += '--from anyproxy--';\nreturn {\n response: newResponse\n};\n\n\n\nbeforeDealHttpsRequest\nbeforeDealHttpsRequest(requestDetail)\n\nAnyProxy收到https请求时,会调用beforeDealHttpsRequest,并带上参数requestDetail\n如果配置了全局解析https的参数,则AnyProxy会略过这个调用\n只有返回true时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。\n注意:https over http的代理模式中,这里的request是CONNECT请求\nrequestDetail\nhost {string} 请求目标的Host,受制于协议,这里无法获取完整url\n_req {object} 请求的原始request\n\n\n返回值\ntrue或者false,表示是否需要AnyProxy替换证书并解析https\n\n\n\nonError\nonError(requestDetail, error)\n\n在请求处理过程中发生错误时,AnyProxy会调用onError方法,并提供对应的错误信息\n多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等\nrequestDetail 同beforeSendRequest中的参数\n以下几种返回都是合法的\n\n不做任何处理。此时AnyProxy会返回一个默认的错误页。\n\nreturn null;\n\n\n返回自定义错误页\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nonConnectError\nonConnectError(requestDetail, error)\n\nAnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法\nrequestDetail 同beforeDealHttpsRequest中的参数\n此处无法控制向客户端的返回信息,无需返回值。\n\nrule样例\n\n这里提供一些样例,来讲解规则模块的常见用法\n你可以通过 anyproxy --rule http://....js 来加载模块并体验\n用curl发请求测试的方法如下\n直接请求服务器:curl http://httpbin.org/\n通过代理服务器请求:curl http://httpbin.org/ --proxy http://127.0.0.1:8001\n\n\n\n使用本地数据\n\n拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回\n\nanyproxy --rule rule_sample/sample_use_local_response.js\n\n/* \n sample: \n intercept all requests toward httpbin.org, use a local response\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n const localResponse = {\n statusCode: 200,\n header: { 'Content-Type': 'application/json' },\n body: '{\"hello\": \"this is local response\"}'\n };\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n return {\n response: localResponse\n };\n }\n },\n};\n\n修改请求头\n\n修改发送到 httpbin.org 的user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_header.js\n\n/* \n sample: \n modify the user-agent in requests toward httpbin.org\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\n修改请求数据\n\n修改发送到 http://httpbin.org/post 的post数据\n\nanyproxy --rule rule_sample/sample_modify_request_data.js\n\n/*\n sample:\n modify the post data towards http://httpbin.org/post\n test:\n curl -H \"Content-Type: text/plain\" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001\n expected response:\n { \"data\": \"i-am-anyproxy-modified-post-data\" }\n*/\nmodule.exports = {\n summary: 'Rule to modify request data',\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {\n return {\n requestData: 'i-am-anyproxy-modified-post-data'\n };\n }\n },\n};\n\n修改请求的目标地址\n\n把所有发送到 http://httpbin.org/ 的请求全部改到 http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_path.js\n\n/*\n sample:\n redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html\n test:\n curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n expected response:\n 'hello world' from 127.0.0.1:8001/index.html\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n requestDetail.protocol = 'http';\n newRequestOptions.hostname = '127.0.0.1'\n newRequestOptions.port = '8008';\n newRequestOptions.path = '/index.html';\n newRequestOptions.method = 'GET';\n return requestDetail;\n }\n },\n *beforeDealHttpsRequest(requestDetail) {\n return true;\n }\n};\n\n修改请求协议\n\n把用http协议请求的 http://httpbin.org 改成https并发送\n\nanyproxy --rule rule_sample/sample_modify_request_protocol.js\n\n/* \n sample: \n redirect all http requests of httpbin.org to https\n test:\n curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001\n expected response:\n { \"X-Forwarded-Protocol\": \"https\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newOption = requestDetail.requestOptions;\n newOption.port = 443;\n return {\n protocol: 'https',\n requestOptions: newOption\n };\n }\n }\n};\n\n修改返回状态码\n\n把 所有http://httpbin.org 的返回状态码都改成404\n\nanyproxy --rule rule_sample/sample_modify_response_statuscode.js\n\n/* \n sample: \n modify all status code of http://httpbin.org/ to 404\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n HTTP/1.1 404 Not Found\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newResponse = responseDetail.response;\n newResponse.statusCode = 404;\n return {\n response: newResponse\n };\n }\n }\n};\n\n修改返回头\n\n在 http://httpbin.org/user-agent 的返回头里加上 X-Proxy-By:AnyProxy\n\nanyproxy --rule rule_sample/sample_modify_response_header.js\n\n/* \n sample: \n modify response header of http://httpbin.org/user-agent\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n X-Proxy-By: AnyProxy\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {\n const newResponse = responseDetail.response;\n newResponse.header['X-Proxy-By'] = 'AnyProxy';\n return {\n response: newResponse\n };\n }\n }\n};\n\n修改返回内容并延迟\n\n在 http://httpbin.org/user-agent 的返回最后追加AnyProxy的签名,并延迟5秒\n\nanyproxy --rule rule_sample/sample_modify_response_data.js\n\n/* \n sample: \n modify response data of http://httpbin.org/user-agent\n test:\n curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" } -- AnyProxy Hacked! --\n*/\n\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay the response for 5s\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n证书配置\nOSX系统信任CA证书\n\n类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的\n\n\n\n警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管\n\n安装CA:\n\n双击打开rootCA.crt\n\n确认将证书添加到login或system\n\n\n\n\n找到刚刚导入的AnyProxy证书,配置为信任(Always Trust)\n\n\nWindows系统信任CA证书\n\n配置OSX系统代理\n\n在wifi高级设置中,配置http代理即可\n\n\n配置浏览器HTTP代理\n\n以Chrome的SwitchyOmega插件为例\n\n\niOS系统信任CA证书\n\n点击web ui中的 Root CA,按提示扫描二维码即可安装\n\n\niOS >= 10.3信任CA证书\n\n除了上述证书安装过程,还需要在 设置->通用->关于本机->证书信任设置 中把AnyProxy证书的开关打开,否则safari将报错。\n\n\n配置iOS/Android系统代理\n\n代理服务器都在wifi设置中配置\n\niOS HTTP代理配置\n\n\n\n\nAndroid HTTP代理配置\n\n\nFAQ\nQ: 为什么https请求不能进入处理函数?\n A: 以下任意一项都能用来改变https的处理特性:\n 1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析\n 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析\nQ: 提示 function is not yieldable\n\nA: 规则模块是用 co 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。\n\nQ: The connection is not private\n当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问:\n\n命令行直接启动\n通过启动参数 --ignore-unauthorized-ssl 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。\nanyproxy -i --ignore-unauthorized-ssl\n\n\n在Nodejs代码中启动\n在构造AnyProxy实例的时候,传入参数dangerouslyIgnoreUnauthorized:true, 如下:\n const options = {\n ...,\n dangerouslyIgnoreUnauthorized: true\n };\n\n const anyproxyIns = new AnyProxy.ProxyCore(options);\n anyproxyIns.start();\n\n通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略\n\n通过自定义的Rule来修改\n我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n // 设置属性 rejectUnauthorized 为 false\n newRequestOptions.rejectUnauthorized = false;\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\n\n\n"},"doc.html":{"url":"doc.html","title":"Getting-Start","keywords":"","body":"Getting Start\ninstall\nTo Debian and Ubuntu users, you may need to install nodejs-legacy at the same time\nsudo apg-get install nodejs-legacy\n\nThen install the AnyProxy\nnpm install -g anyproxy@beta # 4.x is in beta now\n\nlaunch\n\nstart AnyProxy in command line, with default port 8001\n\nanyproxy\n\n\nnow you can use http proxy server by 127.0.0.1:8001\nvisit http://127.0.0.1:8002 to see the http requests\n\noptions\n\nspecify the port of http proxy\n\nanyproxy --port 1080\n\nUse AnyProxy as an npm module\nAnyProxy can be used as an npm module\n\nTo enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.\n\n\ninstall\n\nnpm i anyproxy@beta --save # 4.0 is in beta now\n\n\nsample\n\nconst AnyProxy = require('anyproxy');\nconst options = {\n port: 8001,\n rule: require('myRuleModule'),\n webInterface: {\n enable: true,\n webPort: 8002,\n wsPort: 8003,\n },\n throttle: 10000,\n forceProxyHttps: false,\n silent: false\n};\nconst proxyServer = new AnyProxy.ProxyServer(options);\n\nproxyServer.on('ready', () => { /* */ });\nproxyServer.on('error', (e) => { /* */ });\nproxyServer.start();\n\n//when finished\nproxyServer.close();\n\n\nClass: AnyProxy.proxyServer\n\ncreate a proxy server\nconst proxy = new AnyProxy.proxyServer(options)\n\n\noptions\n\nport {number} required, port number of proxy server\nrule {object} your rule module\nthrottle {number} throttle in kb/s, unlimited for default\nforceProxyHttps {boolean} in force intercept all https request, false for default\nsilent {boolean} if keep silent in console, false for defaultfalse\ndangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default\nwebInterface {object} config for web interface\nenable {boolean} if enable web interface, false for default\nwebPort {number} port number for web interface\n\n\n\n\nEvent: ready\n\nemit when proxy server is ready\nsample\n\nproxy.on('ready', function() { })\n\n\nEvent: error\n\nemit when error happened inside proxy server\nsample\n\nproxy.on('error', function() { })\n\n\nMethod: start\n\nstart proxy server\nsample\n\nproxy.start();\n\n\nMethod: close\n\nclose proxy server\nsample\n\nproxy.close();\n\n\n\n\nAnyProxy.utils.systemProxyMgr\n\nmanage the system proxy config. sudo password may be required\nsample\n\n// set 127.0.0.1:8001 as system http server\nAnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');\n\n// disable global proxy server\nAnyProxy.utils.systemProxyMgr.disableGlobalProxy();\n\n\nAnyProxy.utils.certMgr\n\nManage certificates of AnyProxy\nAnyProxy.utils.certMgr.ifRootCAFileExists()\ndetect if AnyProx rootCA exists\n\n\nAnyProxy.utils.certMgr.generateRootCA(callback)\ngenerate a rootCA\n\n\nSample\n\n const AnyProxy = require('AnyProxy');\n const exec = require('child_process').exec;\n\n if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {\n AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {\n // let users to trust this CA before using proxy\n if (!error) {\n const certDir = require('path').dirname(keyPath);\n console.log('The cert is generated at', certDir);\n const isWin = /^win/.test(process.platform);\n if (isWin) {\n exec('start .', { cwd: certDir });\n } else {\n exec('open .', { cwd: certDir });\n }\n } else {\n console.error('error when generating rootCA', error);\n }\n });\n }\n\n\n\nProxy Https\n\nAnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.\n\n\nUnder the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.\n\n\ngenerate certifycates and intercept\n\nanyproxy-ca #generate root CA. manually trust it after that.\nanyproxy --intercept #launch anyproxy and intercept all https traffic\n\n\nAppendix:how to trust CA\n\nRule Introduction\nAnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.\n\nMake sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.\n\nRule module could do the following stuff:\n\nintercept and modify the request which is being sent\neditable fields include request header, body, target address\n\n\nintercept and modify the response from server\neditable fields include response status code, header, body\n\n\nintercept https requests, modify request and response\n\nsample\n\nTarget\n\nwrite a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds\n\n\nStep 1,Write the rule file, save as sample.js\n// file: sample.js\nmodule.exports = {\n summary: 'a rule to modify response',\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n\nStep 2, start AnyProxy and load the rule file\n\nrun anyproxy --rule sample.js\n\n\nStep 3, test\n\nuse curl\ncurl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n\n\nuse browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent\n\nthe expected response from proxy is\n\n\n{\n \"user-agent\": \"curl/7.43.0\"\n}\n- AnyProxy Hacked!\n\nStep 4, view the request log\n\nvisit http://127.0.0.1:8002, the request just sent should be listed here\n\n\n\nhow does it work\n\nThe flow chart is as follows\n\n\n\nWhen got an http request, the entire process of proxy server is\n\nAnyProxy collects all the quest info, include method, header, body\nAnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content\nIf beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.\nSend request to target server, collect response\nCall beforeSendResponse of the rule module. Rule module deal the response data\nSend response to client\n\n\nWhen AnyProxy get https request, it could replace the certificate and decrypt the request data\n\nAnyProxy calls beforeDealHttpsRequest of the rule module\nIf the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.\n\n\n\nhow to load rule module\n\nuse local file\nanyproxy --rule ./rule.js\n\n\nuse an online rule file\nanyproxy --rule https://sample.com/rule.js\n\n\nuse an npm module\n\nAnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.\n\nanyproxy --rule ./myRulePkg/ #local module\nnpm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module\n\n\n\nRule module interface\nA typical rule module is as follows. All the functions are optional, just write the part you are interested in.\nmodule.exports = {\n // introduction\n summary: 'my customized rule for AnyProxy',\n // intercept before send request to server\n *beforeSendRequest(requestDetail) { /* ... */ },\n // deal response before send to client\n *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },\n // if deal https request\n *beforeDealHttpsRequest(requestDetail) { /* ... */ },\n // error happened when dealing requests\n *onError(requestDetail, error) { /* ... */ },\n // error happened when connect to https server\n *onConnectError(requestDetail, error) { /* ... */ }\n};\n\n\nAll functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.\n\nsummary\nsummary\n\nIntroduction of this rule file. AnyProxy will read this field and give some tip to user.\n\nbeforeSendRequest\nbeforeSendRequest(requestDetail)\n\nBefore sending request to server, AnyProxy will call beforeSendRequest with param requestDetail\nrequestDetail\nprotocol {string} the protocol to use, http or https\nrequestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback\nrequestData {object} request body\nurl {string} request url\n_req {object} the native node.js request object\n\n\ne.g. When requesting anyproxy.io, requestDetail is something like the following\n{\n protocol: 'http',\n url: 'http://anyproxy.io/',\n requestOptions: {\n hostname: 'anyproxy.io',\n port: 80,\n path: '/',\n method: 'GET',\n headers: {\n Host: 'anyproxy.io',\n 'Proxy-Connection': 'keep-alive',\n 'User-Agent': '...'\n }\n },\n requestData: '...',\n _req: { /* ... */}\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the request protocol,i.e. force use https\n\nreturn {\n protocol: 'https'\n};\n\n\nmodify request param\n\nvar newOption = Object.assign({}, requestDetail.requestOptions);\nnewOption.path = '/redirect/to/another/path';\nreturn {\n requestOptions: newOption\n};\n\n\nmodify request body\n\nreturn {\n requestData: 'my new request data'\n // requestOptions can also be used here\n};\n\n\ngive response to the client, not sending request any longer. statusCode headersare required is this situation.\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nbeforeSendResponse\nbeforeSendResponse(requestDetail, responseDetail)\n\nBefore sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail\nrequestDetail is the same param as in beforeSendRequest\nresponseDetail\nresponse {object} the response from server, includes statusCode header body\n_res {object} the native node.js response object\n\n\ne.g. When requesting anyproxy.io, responseDetail is something like the following\n{\n response: {\n statusCode: 200,\n header: {\n 'Content-Type': 'image/gif',\n Connection: 'close',\n 'Cache-Control': '...'\n },\n body: '...'\n },\n _res: { /* ... */ }\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the response status code\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.statusCode = 404;\nreturn {\n response: newResponse\n};\n\n\nmodify the response content\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.body += '--from anyproxy--';\nreturn {\n response: newResponse\n};\n\n\n\nbeforeDealHttpsRequest\nbeforeDealHttpsRequest(requestDetail)\n\nWhen receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail\nIf configed with forceProxyHttps in launching, AnyProxy will skip calling this method\nOnly by returning true, AnyProxy will try to replace the certificate and intercept the https request.\nrequestDetail\nhost {string} the target host to request. Due to the request protocol, full url couldn't be got here\n_req {object} the native node.js request object. The _req here refers to the CONNECT request.\n\n\nreturn value\ntrue or false, whether AnyProxy should intercept the https request\n\n\n\nonError\nonError(requestDetail, error)\n\nAnyProxy will call this method when an error happened in request handling.\nErrors usually are issued during requesting, e.g. DNS failure, request timeout\nrequestDetail is the same one as in beforeSendRequest\nAny of these return values are valid\n\ndo nothing, and AnyProxy will response a default error page\n\nreturn null;\n\n\nreturn a customized error page\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nonConnectError\nonConnectError(requestDetail, error)\n\nAnyProxy will call this method when failed to connect target server in https request\nrequestDetail is the same one as in beforeDealHttpsRequest\nno return value is required\n\nRule Samples\n\nhere are some samples about frequently used rule file\ntry these samples by anyproxy --rule http://....js\nhow to test with curl:\nrequest the server directly curl http://httpbin.org/\nrequest the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001\n\n\n\nuse local response\n\nintercept the request towards http://httpbin.org , return the local-defined response\n\nanyproxy --rule rule_sample/sample_use_local_response.js\n\n/* \n sample: \n intercept all requests toward httpbin.org, use a local response\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n const localResponse = {\n statusCode: 200,\n header: { 'Content-Type': 'application/json' },\n body: '{\"hello\": \"this is local response\"}'\n };\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n return {\n response: localResponse\n };\n }\n },\n};\n\nmodify request header\n\nmodify the user-agent sent to httpbin.org\n\nanyproxy --rule rule_sample/sample_modify_request_header.js\n\n/* \n sample: \n modify the user-agent in requests toward httpbin.org\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request body\n\nmodify the post body of http://httpbin.org/post\n\nanyproxy --rule rule_sample/sample_modify_request_data.js\n\n/*\n sample:\n modify the post data towards http://httpbin.org/post\n test:\n curl -H \"Content-Type: text/plain\" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001\n expected response:\n { \"data\": \"i-am-anyproxy-modified-post-data\" }\n*/\nmodule.exports = {\n summary: 'Rule to modify request data',\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {\n return {\n requestData: 'i-am-anyproxy-modified-post-data'\n };\n }\n },\n};\n\nmodify the request target\n\nsend all the request towards http://httpbin.org/ to http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_path.js\n\n/* \n sample: \n redirect all httpbin.org requests to http://httpbin.org/user-agent\n test:\n curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.path = '/user-agent';\n newRequestOptions.method = 'GET';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request protocol\n\nmodify the http request towards http://httpbin.org to https\n\nanyproxy --rule rule_sample/sample_modify_request_protocol.js\n\n/* \n sample: \n redirect all http requests of httpbin.org to https\n test:\n curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001\n expected response:\n { \"X-Forwarded-Protocol\": \"https\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newOption = requestDetail.requestOptions;\n newOption.port = 443;\n return {\n protocol: 'https',\n requestOptions: newOption\n };\n }\n }\n};\n\nmodify response status code\n\nmodify all status code from http://httpbin.org to 404\n\nanyproxy --rule rule_sample/sample_modify_response_statuscode.js\n\n/* \n sample: \n modify all status code of http://httpbin.org/ to 404\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n HTTP/1.1 404 Not Found\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newResponse = responseDetail.response;\n newResponse.statusCode = 404;\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify the response header\n\nadd X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_response_header.js\n\n/* \n sample: \n modify response header of http://httpbin.org/user-agent\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n X-Proxy-By: AnyProxy\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {\n const newResponse = responseDetail.response;\n newResponse.header['X-Proxy-By'] = 'AnyProxy';\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify response data and delay\n\nappend some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds.\n\nanyproxy --rule rule_sample/sample_modify_response_data.js\n\n/* \n sample: \n modify response data of http://httpbin.org/user-agent\n test:\n curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" } -- AnyProxy Hacked! --\n*/\n\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay the response for 5s\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\nConfig Certification\nConfig root CA in OSX\n\nthis kind of errors is usually caused by untrusted root CA\n\n\n\nWarning: please keep your root CA safe since it may influence your system security.\n\ninstall :\n\ndouble click rootCA.crt\n\nadd cert into login or system\n\n\n\n\nfind the newly imported AnyProxy certificates, configured as Always Trust\n\n\nConfig root CA in windows\n\nConfig OSX system proxy\n\nthe config is in wifi - advanced\n\n\nconfig http proxy server\n\ntake Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例\n\n\ntrust root CA in iOS\n\nClick Root CA in web ui, and follow the instruction to install\n\n\ntrust root CA in iOS after 10.3\n\nBesides installing root CA, you have to \"turn on\" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.\n\n\nconfig iOS/Android proxy server\n\nproxy settings are placed in wifi setting\n\niOS\n\n\n\n\nAndroid\n\n\nFAQ\nQ: can not deal https request in rule module.\n\nA: Any of these options could be used to change the way AnyProxy deall https requests\nconfig --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module\nplace a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.\n\n\n\nQ: get an error says function is not yieldable\n\nA: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.\n\nQ: The connection is not private\nAnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.\n\nIf you run AnyProxy by command line\nPass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.\nanyproxy -i --ignore-unauthorized-ssl\n\n\nIf you run AnyProxy by Nodejs\nPass in the option dangerouslyIgnoreUnauthorized:true, like this:\nconst options = {\n ...,\n dangerouslyIgnoreUnauthorized: true\n};\n\nconst anyproxyIns = new AnyProxy.ProxyCore(options);\nanyproxyIns.start();\n\nThis is also a global option, all certification errors will be ignored\n\nWith the help of AnyProxy Rule\nYou can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n // set rejectUnauthorized as false\n newRequestOptions.rejectUnauthorized = false;\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nAnd we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!\n\n\n"}}} \ No newline at end of file diff --git a/docs/cn/src_doc.md b/docs/cn/src_doc.md new file mode 100644 index 0000000..432ea9c --- /dev/null +++ b/docs/cn/src_doc.md @@ -0,0 +1,686 @@ +AnyProxy +=================== + +> 本文档的适用范围是AnyProxy 4.0,欢迎提供反馈 + +Ref: [English Doc](../en) + +AnyProxy是一个开放式的HTTP代理服务器。 + +Github主页:https://github.com/alibaba/anyproxy/tree/4.x + +主要特性包括: + +* 基于Node.js,开放二次开发能力,允许自定义请求处理逻辑 +* 支持Https的解析 +* 提供GUI界面,用以观察请求 +相比3.x版本,AnyProxy 4.0的主要变化: + +* 规则文件(Rule)全面支持Promise和Generator +* 简化了规则文件内的接口 +* Web版界面重构 + + + +# 快速开始 +## 作为全局模块 +### 安装 + +对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 `nodejs-legacy` + +```bash +sudo apg-get install nodejs-legacy +``` + +然后,安装AnyProxy + +```bash +npm install -g anyproxy +``` + +### 启动 + +* 命令行启动AnyProxy,默认端口号8001 + +```bash +anyproxy +``` + +* 启动后将终端http代理服务器配置为127.0.0.1:8001即可 +* 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息 + +### 其他命令 + +* 配置启动端口,如1080端口启动 + +```bash +anyproxy --port 1080 +``` + +## 作为npm模块使用 + +AnyProxy可以作为一个npm模块使用,整合进其他工具。 + +> 如要启用https解析,请在代理服务器启动前自行调用`AnyProxy.utils.certMgr`相关方法生成证书,并引导用户信任安装。或引导用户使用`anyproxy-ca`方法。 + +* 引入 + +```bash +npm i anyproxy --save +``` + +* 使用举例 + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * 创建代理服务器 + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} 必选,代理服务器端口 + * `rule` {object} 自定义规则模块 + * `throttle` {number} 限速值,单位kb/s,默认不限速 + * `forceProxyHttps` {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认`false` + * `silent` {boolean} 是否屏蔽所有console输出,默认`false` + * `dangerouslyIgnoreUnauthorized` {boolean} 是否忽略请求中的证书错误,默认`false` + * `webInterface` {object} web版界面配置 + * `enable` {boolean} 是否启用web版界面,默认`false` + * `webPort` {number} web版界面端口号,默认`8002` + * Event: `ready` + * 代理服务器启动完成 + * 示例 + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * 代理服务器发生错误 + * 示例 + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * 启动代理服务器 + * 示例 + + ```js + proxy.start(); + ``` + * Method: `close` + * 关闭代理服务器 + * 示例 + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * 管理系统的全局代理配置,方法调用时可能会弹出密码框 + * 使用示例 + + ```js + // 配置127.0.0.1:8001为全局http代理服务器 + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // 关闭全局代理服务器 + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * 管理AnyProxy的证书 + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * 校验系统内是否存在AnyProxy的根证书 + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件 + * 样例 + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# 代理HTTPS +* AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书 + +> 解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程 + +* 生成证书并解析所有https请求 + +```bash +anyproxy-ca #生成rootCA证书,生成后需要手动信任 +anyproxy --intercept #启动AnyProxy,并解析所有https请求 +``` + +* [附录:如何信任CA证书](#证书配置) + +# rule模块 + +AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。 + +>注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题 + +规则模块的能力范围包括: + +* 拦截并修改正在发送的请求 + * 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等 +* 拦截并修改服务端响应 + * 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等 +* 拦截https请求,对内容做修改 + * 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA + +### 开发示例 + +* 举例 + * 需要编写一个规则模块,在 GET http://httpbin.org/user-agent 的返回值里加上测试信息,并延迟5秒返回 + +* Step 1,编写规则 + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '
      -- AnyProxy Hacked! --'; + + newResponse.body += ``; + newResponse.header['Content-Type'] = 'text/html'; + + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, 启动AnyProxy,加载规则 + * 运行 `anyproxy --rule sample.js` + +* Step 3, 测试规则 + + * 用curl测试 + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent + + * 经过代理服务器后,期望的返回如下 + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, 查看请求信息 + + * 浏览器访问http://127.0.0.1:8002 ,界面上能看到刚才的请求信息 + +### 处理流程 + +* 处理流程图如下 + + + +* 当http请求经过代理服务器时,具体处理过程是: + * 收集请求所有请求参数,包括method, header, body等 + * AnyProxy调用规则模块`beforeSendRequest`方法,由模块做处理,返回新的请求参数,或返回响应内容 + * 如果`beforeSendRequest`返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。 + * 根据请求参数,向服务端发出请求,接收服务端响应。 + * 调用规则模块`beforeSendResponse`方法,由模块对响应内容进行处理 + * 把响应信息返回给客户端 + +* 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。 + * 调用规则模块`beforeDealHttpsRequest`方法,如果返回`true`,会明文解析这个请求,其他请求不处理 + * 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。 + + +### 如何引用 + +如下几种方案都可以用来引用规则模块: + +* 使用本地路径 +```bash +anyproxy --rule ./rule.js +``` +* 使用在线地址 +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* 使用npm包 + * AnyProxy使用`require()`加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包 + + ```bash +anyproxy --rule ./myRulePkg/ #本地包 +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包 + ``` + + +# rule接口文档 + +规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。 + +```js +module.exports = { + // 模块介绍 + summary: 'my customized rule for AnyProxy', + // 发送请求前拦截处理 + *beforeSendRequest(requestDetail) { /* ... */ }, + // 发送响应前处理 + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // 是否处理https请求 + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // 请求出错的事件 + *onError(requestDetail, error) { /* ... */ }, + // https连接服务器出错 + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> 规则文件中,除了summary,都是由 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。 + +### summary + +#### summary(): string | summary:string + +* 规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串 + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* AnyProxy向服务端发送请求前,会调用`beforeSendRequest`,并带上参数`requestDetail` +* `requestDetail` + * `protocol` {string} 请求使用的协议,http或者https + * `requestOptions` {object} 即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} 请求Body + * `url` {string} 请求url + * `_req` {object} 请求的原始request +* 举例:请求 *anyproxy.io* 时,`requestDetail`参数内容大致如下 + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改请求协议,如强制改用https发起请求 + + ```js + return { + protocol: 'https' + }; + ``` + + * 修改请求参数 + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * 修改请求body + + ```js + return { + requestData: 'my new request data' + //这里也可以同时加上requestOptions + }; + ``` + * 直接返回客户端,不再发起请求,其中`statusCode` `header` 是必选字段 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* AnyProxy向客户端发送请求前,会调用`beforeSendResponse`,并带上参数`requestDetail` `responseDetail` +* `requestDetail` 同`beforeSendRequest`中的参数 +* `responseDetail` + * `response` {object} 服务端的返回信息,包括`statusCode` `header` `body`三个字段 + * `_res` {object} 原始的服务端返回对象 +* 举例,请求 *anyproxy.io* 时,`responseDetail`参数内容大致如下 + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` + +* 以下几种返回都是合法的 + * 不做任何处理,返回null + + ```js + return null; + ``` + + * 修改返回的状态码 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * 修改返回的内容 + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* AnyProxy收到https请求时,会调用`beforeDealHttpsRequest`,并带上参数`requestDetail` +* 如果配置了全局解析https的参数,则AnyProxy会略过这个调用 +* 只有返回`true`时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。 +* 注意:https over http的代理模式中,这里的request是CONNECT请求 +* `requestDetail` + * `host` {string} 请求目标的Host,受制于协议,这里无法获取完整url + * `_req` {object} 请求的原始request +* 返回值 + * `true`或者`false`,表示是否需要AnyProxy替换证书并解析https + +### onError + +#### onError(requestDetail, error) + +* 在请求处理过程中发生错误时,AnyProxy会调用`onError`方法,并提供对应的错误信息 +* 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等 +* `requestDetail` 同`beforeSendRequest`中的参数 +* 以下几种返回都是合法的 + * 不做任何处理。此时AnyProxy会返回一个默认的错误页。 + + ```js + return null; + ``` + + * 返回自定义错误页 + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法 +* `requestDetail` 同`beforeDealHttpsRequest`中的参数 +* 此处无法控制向客户端的返回信息,无需返回值。 + +# rule样例 + +* 这里提供一些样例,来讲解规则模块的常见用法 +* 你可以通过 `anyproxy --rule http://....js` 来加载模块并体验 +* 用curl发请求测试的方法如下 + * 直接请求服务器:`curl http://httpbin.org/` + * 通过代理服务器请求:`curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### 使用本地数据 + * 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回 + +{{sample-rule:rule_sample/sample_use_local_response.js}} + +### 修改请求头 + * 修改发送到 httpbin.org 的user-agent + +{{sample-rule:rule_sample/sample_modify_request_header.js}} + +### 修改请求数据 + * 修改发送到 http://httpbin.org/post 的post数据 + +{{sample-rule:rule_sample/sample_modify_request_data.js}} + +### 修改请求的目标地址 + * 把所有发送到 http://httpbin.org/ 的请求全部改到 http://httpbin.org/user-agent + +{{sample-rule:rule_sample/sample_modify_request_path.js}} + +### 修改请求协议 + * 把用http协议请求的 http://httpbin.org 改成https并发送 + +{{sample-rule:rule_sample/sample_modify_request_protocol.js}} + +### 修改返回状态码 + * 把 所有http://httpbin.org 的返回状态码都改成404 + +{{sample-rule:rule_sample/sample_modify_response_statuscode.js}} + +### 修改返回头 + * 在 http://httpbin.org/user-agent 的返回头里加上 X-Proxy-By:AnyProxy + +{{sample-rule:rule_sample/sample_modify_response_header.js}} + +### 修改返回内容并延迟 + * 在 http://httpbin.org/user-agent 的返回最后追加AnyProxy的签名,并延迟5秒 + +{{sample-rule:rule_sample/sample_modify_response_data.js}} + +# 证书配置 + +### OSX系统信任CA证书 + +* 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的 + + + +> 警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管 + +安装CA: + +* 双击打开*rootCA.crt* + +* 确认将证书添加到login或system + + + +* 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust) + + + +### Windows系统信任CA证书 + + + + +### 配置OSX系统代理 + +* 在wifi高级设置中,配置http代理即可 + + + +### 配置浏览器HTTP代理 + +* 以Chrome的[SwitchyOmega插件](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### iOS系统信任CA证书 + +* 点击web ui中的 *Root CA*,按提示扫描二维码即可安装 + + + +### iOS >= 10.3信任CA证书 + +* 除了上述证书安装过程,还需要在 *设置->通用->关于本机->证书信任设置* 中把AnyProxy证书的开关打开,否则safari将报错。 + + + +### 配置iOS/Android系统代理 + +* 代理服务器都在wifi设置中配置 + +* iOS HTTP代理配置 + + + +* Android HTTP代理配置 + + + +# FAQ + +#### Q: 为什么https请求不能进入处理函数? + A: 以下任意一项都能用来改变https的处理特性: + 1. 命令行启动AnyProxy时配置`--intercept`参数,按npm模块启动时配置`forceProxyHttps`参数,所有Https请求都会被替换证书并解析 + 2. 规则文件内提供`beforeDealHttpsRequest`方法,返回 *true* 的https请求会被解析 + +#### Q: 提示 *function is not yieldable* + * A: 规则模块是用 [co](https://www.npmjs.com/package/co) 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。 + +#### Q: The connection is not private +当访问特定的HTTPS站点,AnyProxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问: +- 命令行直接启动 + + 通过启动参数 `--ignore-unauthorized-ssl` 来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。 + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` + +- 在Nodejs代码中启动 + + 在构造AnyProxy实例的时候,传入参数`dangerouslyIgnoreUnauthorized:true`, 如下: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *通过这种方式初始化的AnyProxy,其配置也是全局性的,所有网站的证书问题都会被忽略* + +- 通过自定义的Rule来修改 + + 我们自然也可以借助自定义的Rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。 + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // 设置属性 rejectUnauthorized 为 false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` diff --git a/docs/en/doc.html b/docs/en/doc.html new file mode 100644 index 0000000..08a524b --- /dev/null +++ b/docs/en/doc.html @@ -0,0 +1,1635 @@ + + + + + + + Getting-Start · AnyProxy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + + + + + +
      + +
      + +
      + + + + + + + + +
      +
      + +
      +
      + +
      + +

      Getting Start

      +

      install

      +

      To Debian and Ubuntu users, you may need to install nodejs-legacy at the same time

      +
      sudo apg-get install nodejs-legacy
      +
      +

      Then install the AnyProxy

      +
      npm install -g anyproxy@beta # 4.x is in beta now
      +
      +

      launch

      +
        +
      • start AnyProxy in command line, with default port 8001
      • +
      +
      anyproxy
      +
      +
        +
      • now you can use http proxy server by 127.0.0.1:8001
      • +
      • visit http://127.0.0.1:8002 to see the http requests
      • +
      +

      options

      +
        +
      • specify the port of http proxy
      • +
      +
      anyproxy --port 1080
      +
      +

      Use AnyProxy as an npm module

      +

      AnyProxy can be used as an npm module

      +
      +

      To enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.

      +
      +
        +
      • install
      • +
      +
      npm i anyproxy@beta --save # 4.0 is in beta now
      +
      +
        +
      • sample
      • +
      +
      const AnyProxy = require('anyproxy');
      +const options = {
      +  port: 8001,
      +  rule: require('myRuleModule'),
      +  webInterface: {
      +    enable: true,
      +    webPort: 8002,
      +    wsPort: 8003,
      +  },
      +  throttle: 10000,
      +  forceProxyHttps: false,
      +  silent: false
      +};
      +const proxyServer = new AnyProxy.ProxyServer(options);
      +
      +proxyServer.on('ready', () => { /* */ });
      +proxyServer.on('error', (e) => { /* */ });
      +proxyServer.start();
      +
      +//when finished
      +proxyServer.close();
      +
      +
        +
      • Class: AnyProxy.proxyServer

        +
          +
        • create a proxy server

          +
          const proxy = new AnyProxy.proxyServer(options)
          +
          +
        • +
        • options

          +
            +
          • port {number} required, port number of proxy server
          • +
          • rule {object} your rule module
          • +
          • throttle {number} throttle in kb/s, unlimited for default
          • +
          • forceProxyHttps {boolean} in force intercept all https request, false for default
          • +
          • silent {boolean} if keep silent in console, false for defaultfalse
          • +
          • dangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default
          • +
          • webInterface {object} config for web interface
              +
            • enable {boolean} if enable web interface, false for default
            • +
            • webPort {number} port number for web interface
            • +
            +
          • +
          +
        • +
        • Event: ready

          +
            +
          • emit when proxy server is ready
          • +
          • sample
          • +
          +
          proxy.on('ready', function() { })
          +
          +
        • +
        • Event: error

          +
            +
          • emit when error happened inside proxy server
          • +
          • sample
          • +
          +
          proxy.on('error', function() { })
          +
          +
        • +
        • Method: start

          +
            +
          • start proxy server
          • +
          • sample
          • +
          +
          proxy.start();
          +
          +
        • +
        • Method: close

          +
            +
          • close proxy server
          • +
          • sample
          • +
          +
          proxy.close();
          +
          +
        • +
        +
      • +
      • AnyProxy.utils.systemProxyMgr

        +
          +
        • manage the system proxy config. sudo password may be required
        • +
        • sample
        • +
        +
        // set 127.0.0.1:8001 as system http server
        +AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');
        +
        +// disable global proxy server
        +AnyProxy.utils.systemProxyMgr.disableGlobalProxy();
        +
        +
      • +
      • AnyProxy.utils.certMgr

        +
          +
        • Manage certificates of AnyProxy
        • +
        • AnyProxy.utils.certMgr.ifRootCAFileExists()
            +
          • detect if AnyProx rootCA exists
          • +
          +
        • +
        • AnyProxy.utils.certMgr.generateRootCA(callback)
            +
          • generate a rootCA
          • +
          +
        • +
        • Sample
        • +
        +
          const AnyProxy = require('AnyProxy');
        +  const exec = require('child_process').exec;
        +
        +  if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {
        +    AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {
        +      // let users to trust this CA before using proxy
        +      if (!error) {
        +        const certDir = require('path').dirname(keyPath);
        +        console.log('The cert is generated at', certDir);
        +        const isWin = /^win/.test(process.platform);
        +        if (isWin) {
        +          exec('start .', { cwd: certDir });
        +        } else {
        +          exec('open .', { cwd: certDir });
        +        }
        +      } else {
        +        console.error('error when generating rootCA', error);
        +      }
        +    });
        +  }
        +
        +
      • +
      +

      Proxy Https

      +
        +
      • AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.
      • +
      +
      +

      Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.

      +
      +
        +
      • generate certifycates and intercept
      • +
      +
      anyproxy-ca #generate root CA. manually trust it after that.
      +anyproxy --intercept #launch anyproxy and intercept all https traffic
      +
      + +

      Rule Introduction

      +

      AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.

      +
      +

      Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.

      +
      +

      Rule module could do the following stuff:

      +
        +
      • intercept and modify the request which is being sent
          +
        • editable fields include request header, body, target address
        • +
        +
      • +
      • intercept and modify the response from server
          +
        • editable fields include response status code, header, body
        • +
        +
      • +
      • intercept https requests, modify request and response
      • +
      +

      sample

      +
        +
      • Target

        + +
      • +
      • Step 1,Write the rule file, save as sample.js

        +
        // file: sample.js
        +module.exports = {
        +  summary: 'a rule to modify response',
        +  *beforeSendResponse(requestDetail, responseDetail) {
        +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
        +      const newResponse = responseDetail.response;
        +      newResponse.body += '-- AnyProxy Hacked! --';
        +      return new Promise((resolve, reject) => {
        +        setTimeout(() => { // delay
        +          resolve({ response: newResponse });
        +        }, 5000);
        +      });
        +    }
        +  },
        +};
        +
        +
      • +
      • Step 2, start AnyProxy and load the rule file

        +
          +
        • run anyproxy --rule sample.js
        • +
        +
      • +
      • Step 3, test

        +
          +
        • use curl

          +
          curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
          +
          +
        • +
        • use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent

          +
        • +
        • the expected response from proxy is

          +
        • +
        +
        {
        +  "user-agent": "curl/7.43.0"
        +}
        +- AnyProxy Hacked!
        +
      • +
      • Step 4, view the request log

        + +
      • +
      +

      how does it work

      +
        +
      • The flow chart is as follows
      • +
      +

      +
        +
      • When got an http request, the entire process of proxy server is

        +
          +
        • AnyProxy collects all the quest info, include method, header, body
        • +
        • AnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content
        • +
        • If beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.
        • +
        • Send request to target server, collect response
        • +
        • Call beforeSendResponse of the rule module. Rule module deal the response data
        • +
        • Send response to client
        • +
        +
      • +
      • When AnyProxy get https request, it could replace the certificate and decrypt the request data

        +
          +
        • AnyProxy calls beforeDealHttpsRequest of the rule module
        • +
        • If the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.
        • +
        +
      • +
      +

      how to load rule module

      +
        +
      • use local file

        +
        anyproxy --rule ./rule.js
        +
        +
      • +
      • use an online rule file

        +
        anyproxy --rule https://sample.com/rule.js
        +
        +
      • +
      • use an npm module

        +
          +
        • AnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.
        • +
        +
        anyproxy --rule ./myRulePkg/ #local module
        +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module
        +
        +
      • +
      +

      Rule module interface

      +

      A typical rule module is as follows. All the functions are optional, just write the part you are interested in.

      +
      module.exports = {
      +  // introduction
      +  summary: 'my customized rule for AnyProxy',
      +  // intercept before send request to server
      +  *beforeSendRequest(requestDetail) { /* ... */ },
      +  // deal response before send to client
      +  *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },
      +  // if deal https request
      +  *beforeDealHttpsRequest(requestDetail) { /* ... */ },
      +  // error happened when dealing requests
      +  *onError(requestDetail, error) { /* ... */ },
      +  // error happened when connect to https server
      +  *onConnectError(requestDetail, error) { /* ... */ }
      +};
      +
      +
      +

      All functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.

      +
      +

      summary

      +

      summary

      +
        +
      • Introduction of this rule file. AnyProxy will read this field and give some tip to user.
      • +
      +

      beforeSendRequest

      +

      beforeSendRequest(requestDetail)

      +
        +
      • Before sending request to server, AnyProxy will call beforeSendRequest with param requestDetail
      • +
      • requestDetail
          +
        • protocol {string} the protocol to use, http or https
        • +
        • requestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback
        • +
        • requestData {object} request body
        • +
        • url {string} request url
        • +
        • _req {object} the native node.js request object
        • +
        +
      • +
      • e.g. When requesting anyproxy.io, requestDetail is something like the following

        +
        {
        +  protocol: 'http',
        +  url: 'http://anyproxy.io/',
        +  requestOptions: {
        +    hostname: 'anyproxy.io',
        +    port: 80,
        +    path: '/',
        +    method: 'GET',
        +    headers: {
        +      Host: 'anyproxy.io',
        +      'Proxy-Connection': 'keep-alive',
        +      'User-Agent': '...'
        +    }
        +  },
        +  requestData: '...',
        +  _req: { /* ... */}
        +}
        +
        +
      • +
      • Any of these return values are valid

        +
          +
        • do nothing, and return null
        • +
        +
        return null;
        +
        +
          +
        • modify the request protocol,i.e. force use https
        • +
        +
        return {
        +  protocol: 'https'
        +};
        +
        +
          +
        • modify request param
        • +
        +
        var newOption = Object.assign({}, requestDetail.requestOptions);
        +newOption.path = '/redirect/to/another/path';
        +return {
        +  requestOptions: newOption
        +};
        +
        +
          +
        • modify request body
        • +
        +
        return {
        +  requestData: 'my new request data'
        +  // requestOptions can also be used here
        +};
        +
        +
          +
        • give response to the client, not sending request any longer. statusCode headersare required is this situation.
        • +
        +
        return {
        +  response: {
        +    statusCode: 200,
        +    header: { 'content-type': 'text/html' },
        +    body: 'this could be a <string> or <buffer>'
        +  }
        +};
        +
        +
      • +
      +

      beforeSendResponse

      +

      beforeSendResponse(requestDetail, responseDetail)

      +
        +
      • Before sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail
      • +
      • requestDetail is the same param as in beforeSendRequest
      • +
      • responseDetail
          +
        • response {object} the response from server, includes statusCode header body
        • +
        • _res {object} the native node.js response object
        • +
        +
      • +
      • e.g. When requesting anyproxy.io, responseDetail is something like the following

        +
        {
        +  response: {
        +    statusCode: 200,
        +    header: {
        +      'Content-Type': 'image/gif',
        +      Connection: 'close',
        +      'Cache-Control': '...'
        +    },
        +    body: '...'
        +  },
        +  _res: { /* ... */ }
        +}
        +
        +
      • +
      • Any of these return values are valid

        +
          +
        • do nothing, and return null
        • +
        +
        return null;
        +
        +
          +
        • modify the response status code
        • +
        +
        var newResponse = Object.assign({}, responseDetail.response);
        +newResponse.statusCode = 404;
        +return {
        +  response: newResponse
        +};
        +
        +
          +
        • modify the response content
        • +
        +
        var newResponse = Object.assign({}, responseDetail.response);
        +newResponse.body += '--from anyproxy--';
        +return {
        +  response: newResponse
        +};
        +
        +
      • +
      +

      beforeDealHttpsRequest

      +

      beforeDealHttpsRequest(requestDetail)

      +
        +
      • When receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail
      • +
      • If configed with forceProxyHttps in launching, AnyProxy will skip calling this method
      • +
      • Only by returning true, AnyProxy will try to replace the certificate and intercept the https request.
      • +
      • requestDetail
          +
        • host {string} the target host to request. Due to the request protocol, full url couldn't be got here
        • +
        • _req {object} the native node.js request object. The _req here refers to the CONNECT request.
        • +
        +
      • +
      • return value
          +
        • true or false, whether AnyProxy should intercept the https request
        • +
        +
      • +
      +

      onError

      +

      onError(requestDetail, error)

      +
        +
      • AnyProxy will call this method when an error happened in request handling.
      • +
      • Errors usually are issued during requesting, e.g. DNS failure, request timeout
      • +
      • requestDetail is the same one as in beforeSendRequest
      • +
      • Any of these return values are valid

        +
          +
        • do nothing, and AnyProxy will response a default error page
        • +
        +
        return null;
        +
        +
          +
        • return a customized error page
        • +
        +
        return {
        +  response: {
        +    statusCode: 200,
        +    header: { 'content-type': 'text/html' },
        +    body: 'this could be a <string> or <buffer>'
        +  }
        +};
        +
        +
      • +
      +

      onConnectError

      +

      onConnectError(requestDetail, error)

      +
        +
      • AnyProxy will call this method when failed to connect target server in https request
      • +
      • requestDetail is the same one as in beforeDealHttpsRequest
      • +
      • no return value is required
      • +
      +

      Rule Samples

      +
        +
      • here are some samples about frequently used rule file
      • +
      • try these samples by anyproxy --rule http://....js
      • +
      • how to test with curl:
          +
        • request the server directly curl http://httpbin.org/
        • +
        • request the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001
        • +
        +
      • +
      +

      use local response

      + +
      anyproxy --rule rule_sample/sample_use_local_response.js
      +
      +
      /* 
      +  sample: 
      +    intercept all requests toward httpbin.org, use a local response
      +  test:
      +    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
      +*/
      +module.exports = {
      +  *beforeSendRequest(requestDetail) {
      +    const localResponse = {
      +      statusCode: 200,
      +      header: { 'Content-Type': 'application/json' },
      +      body: '{"hello": "this is local response"}'
      +    };
      +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      +      return {
      +        response: localResponse
      +      };
      +    }
      +  },
      +};
      +
      +

      modify request header

      +
        +
      • modify the user-agent sent to httpbin.org
      • +
      +
      anyproxy --rule rule_sample/sample_modify_request_header.js
      +
      +
      /* 
      +  sample: 
      +    modify the user-agent in requests toward httpbin.org
      +  test:
      +    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
      +*/
      +module.exports = {
      +  *beforeSendRequest(requestDetail) {
      +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      +      const newRequestOptions = requestDetail.requestOptions;
      +      newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';
      +      return {
      +        requestOptions: newRequestOptions
      +      };
      +    }
      +  },
      +};
      +
      +

      modify request body

      + +
      anyproxy --rule rule_sample/sample_modify_request_data.js
      +
      +
      /*
      +  sample:
      +    modify the post data towards http://httpbin.org/post
      +  test:
      +    curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001
      +  expected response:
      +    { "data": "i-am-anyproxy-modified-post-data" }
      +*/
      +module.exports = {
      +  summary: 'Rule to modify request data',
      +  *beforeSendRequest(requestDetail) {
      +    if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {
      +      return {
      +        requestData: 'i-am-anyproxy-modified-post-data'
      +      };
      +    }
      +  },
      +};
      +
      +

      modify the request target

      + +
      anyproxy --rule rule_sample/sample_modify_request_path.js
      +
      +
      /* 
      +  sample: 
      +    redirect all httpbin.org requests to http://httpbin.org/user-agent
      +  test:
      +    curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001
      +  expected response:
      +    { "user-agent": "curl/7.43.0" }
      +*/
      +module.exports = {
      +  *beforeSendRequest(requestDetail) {
      +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      +      const newRequestOptions = requestDetail.requestOptions;
      +      newRequestOptions.path = '/user-agent';
      +      newRequestOptions.method = 'GET';
      +      return {
      +        requestOptions: newRequestOptions
      +      };
      +    }
      +  },
      +};
      +
      +

      modify request protocol

      + +
      anyproxy --rule rule_sample/sample_modify_request_protocol.js
      +
      +
      /* 
      +  sample: 
      +    redirect all http requests of httpbin.org to https
      +  test:
      +    curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001
      +  expected response:
      +    { "X-Forwarded-Protocol": "https" }
      +*/
      +module.exports = {
      +  *beforeSendRequest(requestDetail) {
      +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      +      const newOption = requestDetail.requestOptions;
      +      newOption.port = 443;
      +      return {
      +        protocol: 'https',
      +        requestOptions: newOption
      +      };
      +    }
      +  }
      +};
      +
      +

      modify response status code

      + +
      anyproxy --rule rule_sample/sample_modify_response_statuscode.js
      +
      +
      /* 
      +  sample: 
      +    modify all status code of http://httpbin.org/ to 404
      +  test:
      +    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
      +  expected response:
      +    HTTP/1.1 404 Not Found
      +*/
      +module.exports = {
      +  *beforeSendResponse(requestDetail, responseDetail) {
      +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
      +      const newResponse = responseDetail.response;
      +      newResponse.statusCode = 404;
      +      return {
      +        response: newResponse
      +      };
      +    }
      +  }
      +};
      +
      +

      modify the response header

      + +
      anyproxy --rule rule_sample/sample_modify_response_header.js
      +
      +
      /* 
      +  sample: 
      +    modify response header of http://httpbin.org/user-agent
      +  test:
      +    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
      +  expected response:
      +    X-Proxy-By: AnyProxy
      +*/
      +module.exports = {
      +  *beforeSendResponse(requestDetail, responseDetail) {
      +    if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {
      +      const newResponse = responseDetail.response;
      +      newResponse.header['X-Proxy-By'] = 'AnyProxy';
      +      return {
      +        response: newResponse
      +      };
      +    }
      +  }
      +};
      +
      +

      modify response data and delay

      + +
      anyproxy --rule rule_sample/sample_modify_response_data.js
      +
      +
      /* 
      +  sample: 
      +    modify response data of http://httpbin.org/user-agent
      +  test:
      +    curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
      +  expected response:
      +    { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! --
      +*/
      +
      +module.exports = {
      +  *beforeSendResponse(requestDetail, responseDetail) {
      +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
      +      const newResponse = responseDetail.response;
      +      newResponse.body += '-- AnyProxy Hacked! --';
      +      return new Promise((resolve, reject) => {
      +        setTimeout(() => { // delay the response for 5s
      +          resolve({ response: newResponse });
      +        }, 5000);
      +      });
      +    }
      +  },
      +};
      +
      +

      Config Certification

      +

      Config root CA in OSX

      +
        +
      • this kind of errors is usually caused by untrusted root CA
      • +
      +

      +
      +

      Warning: please keep your root CA safe since it may influence your system security.

      +
      +

      install :

      +
        +
      • double click rootCA.crt

        +
      • +
      • add cert into login or system

        +
      • +
      +

      +
        +
      • find the newly imported AnyProxy certificates, configured as Always Trust
      • +
      +

      +

      Config root CA in windows

      +

      +

      Config OSX system proxy

      +
        +
      • the config is in wifi - advanced
      • +
      +

      +

      config http proxy server

      + +

      +

      trust root CA in iOS

      +
        +
      • Click Root CA in web ui, and follow the instruction to install
      • +
      +

      +

      trust root CA in iOS after 10.3

      +
        +
      • Besides installing root CA, you have to "turn on" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.
      • +
      +

      +

      config iOS/Android proxy server

      +
        +
      • proxy settings are placed in wifi setting

        +
      • +
      • iOS

        +
      • +
      +

      +
        +
      • Android
      • +
      +

      +

      FAQ

      +

      Q: can not deal https request in rule module.

      +
        +
      • A: Any of these options could be used to change the way AnyProxy deall https requests
          +
        1. config --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module
        2. +
        3. place a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.
        4. +
        +
      • +
      +

      Q: get an error says function is not yieldable

      +
        +
      • A: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.
      • +
      +

      Q: The connection is not private

      +

      AnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.

      +
        +
      • If you run AnyProxy by command line +Pass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.

        +
        anyproxy -i --ignore-unauthorized-ssl
        +
        +
      • +
      • If you run AnyProxy by Nodejs +Pass in the option dangerouslyIgnoreUnauthorized:true, like this:

        +
        const options = {
        + ...,
        + dangerouslyIgnoreUnauthorized: true
        +};
        +
        +const anyproxyIns = new AnyProxy.ProxyCore(options);
        +anyproxyIns.start();
        +
        +

        This is also a global option, all certification errors will be ignored

        +
      • +
      • With the help of AnyProxy Rule +You can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:

        +
        module.exports = {
        +  *beforeSendRequest(requestDetail) {
        +    if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {
        +      const newRequestOptions = requestDetail.requestOptions;
        +      // set rejectUnauthorized as false
        +      newRequestOptions.rejectUnauthorized = false;
        +      return {
        +        requestOptions: newRequestOptions
        +      };
        +    }
        +  },
        +};
        +
        +

        And we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!

        +
      • +
      + + +
      + +
      +
      +
      + +

      results matching ""

      +
        + +
        +
        + +

        No results matching ""

        + +
        +
        +
        + +
        +
        + +
        + + + + + + + + + + + + + + +
        + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/en/index.html b/docs/en/index.html new file mode 100644 index 0000000..d7514f1 --- /dev/null +++ b/docs/en/index.html @@ -0,0 +1,1663 @@ + + + + + + + Introduction · AnyProxy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        +
        + + + + + + + + +
        + +
        + +
        + + + + + + + + +
        +
        + +
        +
        + +
        + +

        AnyProxy

        +

        AnyProxy is a fully configurable http/https proxy in NodeJS.

        +

        Ref: 中文文档

        +

        Github:

        + +

        Features:

        +
          +
        • Offer you the ablity to handle http traffic by invoking a js module
        • +
        • Intercept https
        • +
        • GUI webinterface
        • +
        +

        Change Logs since 3.x:

        +
          +
        • Support Promise and Generator in rule module
        • +
        • Simplified interface in rule module
        • +
        • A newly designed web interface
        • +
        +

        +

        Getting Start

        +

        install

        +

        To Debian and Ubuntu users, you may need to install nodejs-legacy at the same time

        +
        sudo apg-get install nodejs-legacy
        +
        +

        Then install the AnyProxy

        +
        npm install -g anyproxy
        +
        +

        launch

        +
          +
        • start AnyProxy in command line, with default port 8001
        • +
        +
        anyproxy
        +
        +
          +
        • now you can use http proxy server by 127.0.0.1:8001
        • +
        • visit http://127.0.0.1:8002 to see the http requests
        • +
        +

        options

        +
          +
        • specify the port of http proxy
        • +
        +
        anyproxy --port 1080
        +
        +

        Use AnyProxy as an npm module

        +

        AnyProxy can be used as an npm module

        +
        +

        To enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.

        +
        +
          +
        • install
        • +
        +
        npm i anyproxy --save
        +
        +
          +
        • sample
        • +
        +
        const AnyProxy = require('anyproxy');
        +const options = {
        +  port: 8001,
        +  rule: require('myRuleModule'),
        +  webInterface: {
        +    enable: true,
        +    webPort: 8002,
        +    wsPort: 8003,
        +  },
        +  throttle: 10000,
        +  forceProxyHttps: false,
        +  silent: false
        +};
        +const proxyServer = new AnyProxy.ProxyServer(options);
        +
        +proxyServer.on('ready', () => { /* */ });
        +proxyServer.on('error', (e) => { /* */ });
        +proxyServer.start();
        +
        +//when finished
        +proxyServer.close();
        +
        +
          +
        • Class: AnyProxy.proxyServer

          +
            +
          • create a proxy server

            +
            const proxy = new AnyProxy.proxyServer(options)
            +
            +
          • +
          • options

            +
              +
            • port {number} required, port number of proxy server
            • +
            • rule {object} your rule module
            • +
            • throttle {number} throttle in kb/s, unlimited for default
            • +
            • forceProxyHttps {boolean} in force intercept all https request, false for default
            • +
            • silent {boolean} if keep silent in console, false for defaultfalse
            • +
            • dangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default
            • +
            • webInterface {object} config for web interface
                +
              • enable {boolean} if enable web interface, false for default
              • +
              • webPort {number} port number for web interface
              • +
              +
            • +
            +
          • +
          • Event: ready

            +
              +
            • emit when proxy server is ready
            • +
            • sample
            • +
            +
            proxy.on('ready', function() { })
            +
            +
          • +
          • Event: error

            +
              +
            • emit when error happened inside proxy server
            • +
            • sample
            • +
            +
            proxy.on('error', function() { })
            +
            +
          • +
          • Method: start

            +
              +
            • start proxy server
            • +
            • sample
            • +
            +
            proxy.start();
            +
            +
          • +
          • Method: close

            +
              +
            • close proxy server
            • +
            • sample
            • +
            +
            proxy.close();
            +
            +
          • +
          +
        • +
        • AnyProxy.utils.systemProxyMgr

          +
            +
          • manage the system proxy config. sudo password may be required
          • +
          • sample
          • +
          +
          // set 127.0.0.1:8001 as system http server
          +AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');
          +
          +// disable global proxy server
          +AnyProxy.utils.systemProxyMgr.disableGlobalProxy();
          +
          +
        • +
        • AnyProxy.utils.certMgr

          +
            +
          • Manage certificates of AnyProxy
          • +
          • AnyProxy.utils.certMgr.ifRootCAFileExists()
              +
            • detect if AnyProx rootCA exists
            • +
            +
          • +
          • AnyProxy.utils.certMgr.generateRootCA(callback)
              +
            • generate a rootCA
            • +
            +
          • +
          • Sample
          • +
          +
            const AnyProxy = require('AnyProxy');
          +  const exec = require('child_process').exec;
          +
          +  if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {
          +    AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {
          +      // let users to trust this CA before using proxy
          +      if (!error) {
          +        const certDir = require('path').dirname(keyPath);
          +        console.log('The cert is generated at', certDir);
          +        const isWin = /^win/.test(process.platform);
          +        if (isWin) {
          +          exec('start .', { cwd: certDir });
          +        } else {
          +          exec('open .', { cwd: certDir });
          +        }
          +      } else {
          +        console.error('error when generating rootCA', error);
          +      }
          +    });
          +  }
          +
          +
        • +
        +

        Proxy Https

        +
          +
        • AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.
        • +
        +
        +

        Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.

        +
        +
          +
        • generate certifycates and intercept
        • +
        +
        anyproxy-ca #generate root CA. manually trust it after that.
        +anyproxy --intercept #launch anyproxy and intercept all https traffic
        +
        + +

        Rule Introduction

        +

        AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.

        +
        +

        Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.

        +
        +

        Rule module could do the following stuff:

        +
          +
        • intercept and modify the request which is being sent
            +
          • editable fields include request header, body, target address
          • +
          +
        • +
        • intercept and modify the response from server
            +
          • editable fields include response status code, header, body
          • +
          +
        • +
        • intercept https requests, modify request and response
        • +
        +

        sample

        +
          +
        • Target

          + +
        • +
        • Step 1,Write the rule file, save as sample.js

          +
          // file: sample.js
          +module.exports = {
          +  summary: 'a rule to modify response',
          +  *beforeSendResponse(requestDetail, responseDetail) {
          +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
          +      const newResponse = responseDetail.response;
          +
          +      newResponse.body += '<br/><span style="color:blue">-- AnyProxy Hacked! --</span>';
          +
          +      newResponse.body += `<script>
          +        setTimeout(
          +          function (){
          +            window.alert("Sorry, You Are Hacked...")
          +          }, 300);
          +        </script>`;
          +      newResponse.header['Content-Type'] = 'text/html';
          +
          +      return new Promise((resolve, reject) => {
          +        setTimeout(() => { // delay
          +          resolve({ response: newResponse });
          +        }, 5000);
          +      });
          +    }
          +  },
          +};
          +
          +
        • +
        • Step 2, start AnyProxy and load the rule file

          +
            +
          • run anyproxy --rule sample.js
          • +
          +
        • +
        • Step 3, test

          +
            +
          • use curl

            +
            curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
            +
            +
          • +
          • use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent

            +
          • +
          • the expected response from proxy is

            +
          • +
          +
          {
          +  "user-agent": "curl/7.43.0"
          +}
          +- AnyProxy Hacked!
          +
        • +
        • Step 4, view the request log

          + +
        • +
        +

        how does it work

        +
          +
        • The flow chart is as follows
        • +
        +

        +
          +
        • When got an http request, the entire process of proxy server is

          +
            +
          • AnyProxy collects all the quest info, include method, header, body
          • +
          • AnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content
          • +
          • If beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.
          • +
          • Send request to target server, collect response
          • +
          • Call beforeSendResponse of the rule module. Rule module deal the response data
          • +
          • Send response to client
          • +
          +
        • +
        • When AnyProxy get https request, it could replace the certificate and decrypt the request data

          +
            +
          • AnyProxy calls beforeDealHttpsRequest of the rule module
          • +
          • If the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.
          • +
          +
        • +
        +

        how to load rule module

        +
          +
        • use local file

          +
          anyproxy --rule ./rule.js
          +
          +
        • +
        • use an online rule file

          +
          anyproxy --rule https://sample.com/rule.js
          +
          +
        • +
        • use an npm module

          +
            +
          • AnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.
          • +
          +
          anyproxy --rule ./myRulePkg/ #local module
          +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module
          +
          +
        • +
        +

        Rule module interface

        +

        A typical rule module is as follows. All the functions are optional, just write the part you are interested in.

        +
        module.exports = {
        +  // introduction
        +  summary: 'my customized rule for AnyProxy',
        +  // intercept before send request to server
        +  *beforeSendRequest(requestDetail) { /* ... */ },
        +  // deal response before send to client
        +  *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },
        +  // if deal https request
        +  *beforeDealHttpsRequest(requestDetail) { /* ... */ },
        +  // error happened when dealing requests
        +  *onError(requestDetail, error) { /* ... */ },
        +  // error happened when connect to https server
        +  *onConnectError(requestDetail, error) { /* ... */ }
        +};
        +
        +
        +

        All functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.

        +
        +

        summary

        +

        summary

        +
          +
        • Introduction of this rule file. AnyProxy will read this field and give some tip to user.
        • +
        +

        beforeSendRequest

        +

        beforeSendRequest(requestDetail)

        +
          +
        • Before sending request to server, AnyProxy will call beforeSendRequest with param requestDetail
        • +
        • requestDetail
            +
          • protocol {string} the protocol to use, http or https
          • +
          • requestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback
          • +
          • requestData {object} request body
          • +
          • url {string} request url
          • +
          • _req {object} the native node.js request object
          • +
          +
        • +
        • e.g. When requesting anyproxy.io, requestDetail is something like the following

          +
          {
          +  protocol: 'http',
          +  url: 'http://anyproxy.io/',
          +  requestOptions: {
          +    hostname: 'anyproxy.io',
          +    port: 80,
          +    path: '/',
          +    method: 'GET',
          +    headers: {
          +      Host: 'anyproxy.io',
          +      'Proxy-Connection': 'keep-alive',
          +      'User-Agent': '...'
          +    }
          +  },
          +  requestData: '...',
          +  _req: { /* ... */}
          +}
          +
          +
        • +
        • Any of these return values are valid

          +
            +
          • do nothing, and return null
          • +
          +
          return null;
          +
          +
            +
          • modify the request protocol,i.e. force use https
          • +
          +
          return {
          +  protocol: 'https'
          +};
          +
          +
            +
          • modify request param
          • +
          +
          var newOption = Object.assign({}, requestDetail.requestOptions);
          +newOption.path = '/redirect/to/another/path';
          +return {
          +  requestOptions: newOption
          +};
          +
          +
            +
          • modify request body
          • +
          +
          return {
          +  requestData: 'my new request data'
          +  // requestOptions can also be used here
          +};
          +
          +
            +
          • give response to the client, not sending request any longer. statusCode headersare required is this situation.
          • +
          +
          return {
          +  response: {
          +    statusCode: 200,
          +    header: { 'content-type': 'text/html' },
          +    body: 'this could be a <string> or <buffer>'
          +  }
          +};
          +
          +
        • +
        +

        beforeSendResponse

        +

        beforeSendResponse(requestDetail, responseDetail)

        +
          +
        • Before sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail
        • +
        • requestDetail is the same param as in beforeSendRequest
        • +
        • responseDetail
            +
          • response {object} the response from server, includes statusCode header body
          • +
          • _res {object} the native node.js response object
          • +
          +
        • +
        • e.g. When requesting anyproxy.io, responseDetail is something like the following

          +
          {
          +  response: {
          +    statusCode: 200,
          +    header: {
          +      'Content-Type': 'image/gif',
          +      Connection: 'close',
          +      'Cache-Control': '...'
          +    },
          +    body: '...'
          +  },
          +  _res: { /* ... */ }
          +}
          +
          +
        • +
        • Any of these return values are valid

          +
            +
          • do nothing, and return null
          • +
          +
          return null;
          +
          +
            +
          • modify the response status code
          • +
          +
          var newResponse = Object.assign({}, responseDetail.response);
          +newResponse.statusCode = 404;
          +return {
          +  response: newResponse
          +};
          +
          +
            +
          • modify the response content
          • +
          +
          var newResponse = Object.assign({}, responseDetail.response);
          +newResponse.body += '--from anyproxy--';
          +return {
          +  response: newResponse
          +};
          +
          +
        • +
        +

        beforeDealHttpsRequest

        +

        beforeDealHttpsRequest(requestDetail)

        +
          +
        • When receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail
        • +
        • If configed with forceProxyHttps in launching, AnyProxy will skip calling this method
        • +
        • Only by returning true, AnyProxy will try to replace the certificate and intercept the https request.
        • +
        • requestDetail
            +
          • host {string} the target host to request. Due to the request protocol, full url couldn't be got here
          • +
          • _req {object} the native node.js request object. The _req here refers to the CONNECT request.
          • +
          +
        • +
        • return value
            +
          • true or false, whether AnyProxy should intercept the https request
          • +
          +
        • +
        +

        onError

        +

        onError(requestDetail, error)

        +
          +
        • AnyProxy will call this method when an error happened in request handling.
        • +
        • Errors usually are issued during requesting, e.g. DNS failure, request timeout
        • +
        • requestDetail is the same one as in beforeSendRequest
        • +
        • Any of these return values are valid

          +
            +
          • do nothing, and AnyProxy will response a default error page
          • +
          +
          return null;
          +
          +
            +
          • return a customized error page
          • +
          +
          return {
          +  response: {
          +    statusCode: 200,
          +    header: { 'content-type': 'text/html' },
          +    body: 'this could be a <string> or <buffer>'
          +  }
          +};
          +
          +
        • +
        +

        onConnectError

        +

        onConnectError(requestDetail, error)

        +
          +
        • AnyProxy will call this method when failed to connect target server in https request
        • +
        • requestDetail is the same one as in beforeDealHttpsRequest
        • +
        • no return value is required
        • +
        +

        Rule Samples

        +
          +
        • here are some samples about frequently used rule file
        • +
        • try these samples by anyproxy --rule http://....js
        • +
        • how to test with curl:
            +
          • request the server directly curl http://httpbin.org/
          • +
          • request the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001
          • +
          +
        • +
        +

        use local response

        + +
        anyproxy --rule rule_sample/sample_use_local_response.js
        +
        +
        /* 
        +  sample: 
        +    intercept all requests toward httpbin.org, use a local response
        +  test:
        +    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
        +*/
        +module.exports = {
        +  *beforeSendRequest(requestDetail) {
        +    const localResponse = {
        +      statusCode: 200,
        +      header: { 'Content-Type': 'application/json' },
        +      body: '{"hello": "this is local response"}'
        +    };
        +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
        +      return {
        +        response: localResponse
        +      };
        +    }
        +  },
        +};
        +
        +

        modify request header

        +
          +
        • modify the user-agent sent to httpbin.org
        • +
        +
        anyproxy --rule rule_sample/sample_modify_request_header.js
        +
        +
        /* 
        +  sample: 
        +    modify the user-agent in requests toward httpbin.org
        +  test:
        +    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
        +*/
        +module.exports = {
        +  *beforeSendRequest(requestDetail) {
        +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
        +      const newRequestOptions = requestDetail.requestOptions;
        +      newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';
        +      return {
        +        requestOptions: newRequestOptions
        +      };
        +    }
        +  },
        +};
        +
        +

        modify request body

        + +
        anyproxy --rule rule_sample/sample_modify_request_data.js
        +
        +
        /*
        +  sample:
        +    modify the post data towards http://httpbin.org/post
        +  test:
        +    curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001
        +  expected response:
        +    { "data": "i-am-anyproxy-modified-post-data" }
        +*/
        +module.exports = {
        +  summary: 'Rule to modify request data',
        +  *beforeSendRequest(requestDetail) {
        +    if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {
        +      return {
        +        requestData: 'i-am-anyproxy-modified-post-data'
        +      };
        +    }
        +  },
        +};
        +
        +

        modify the request target

        + +
        anyproxy --rule rule_sample/sample_modify_request_path.js
        +
        +
        /*
        +  sample:
        +    redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html
        +  test:
        +    curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001
        +  expected response:
        +    'hello world' from 127.0.0.1:8001/index.html
        +*/
        +module.exports = {
        +  *beforeSendRequest(requestDetail) {
        +    if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) {
        +      const newRequestOptions = requestDetail.requestOptions;
        +      requestDetail.protocol = 'http';
        +      newRequestOptions.hostname = '127.0.0.1'
        +      newRequestOptions.port = '8008';
        +      newRequestOptions.path = '/index.html';
        +      newRequestOptions.method = 'GET';
        +      return requestDetail;
        +    }
        +  },
        +  *beforeDealHttpsRequest(requestDetail) {
        +    return true;
        +  }
        +};
        +
        +

        modify request protocol

        + +
        anyproxy --rule rule_sample/sample_modify_request_protocol.js
        +
        +
        /* 
        +  sample: 
        +    redirect all http requests of httpbin.org to https
        +  test:
        +    curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001
        +  expected response:
        +    { "X-Forwarded-Protocol": "https" }
        +*/
        +module.exports = {
        +  *beforeSendRequest(requestDetail) {
        +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
        +      const newOption = requestDetail.requestOptions;
        +      newOption.port = 443;
        +      return {
        +        protocol: 'https',
        +        requestOptions: newOption
        +      };
        +    }
        +  }
        +};
        +
        +

        modify response status code

        + +
        anyproxy --rule rule_sample/sample_modify_response_statuscode.js
        +
        +
        /* 
        +  sample: 
        +    modify all status code of http://httpbin.org/ to 404
        +  test:
        +    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
        +  expected response:
        +    HTTP/1.1 404 Not Found
        +*/
        +module.exports = {
        +  *beforeSendResponse(requestDetail, responseDetail) {
        +    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
        +      const newResponse = responseDetail.response;
        +      newResponse.statusCode = 404;
        +      return {
        +        response: newResponse
        +      };
        +    }
        +  }
        +};
        +
        +

        modify the response header

        + +
        anyproxy --rule rule_sample/sample_modify_response_header.js
        +
        +
        /* 
        +  sample: 
        +    modify response header of http://httpbin.org/user-agent
        +  test:
        +    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
        +  expected response:
        +    X-Proxy-By: AnyProxy
        +*/
        +module.exports = {
        +  *beforeSendResponse(requestDetail, responseDetail) {
        +    if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {
        +      const newResponse = responseDetail.response;
        +      newResponse.header['X-Proxy-By'] = 'AnyProxy';
        +      return {
        +        response: newResponse
        +      };
        +    }
        +  }
        +};
        +
        +

        modify response data and delay

        + +
        anyproxy --rule rule_sample/sample_modify_response_data.js
        +
        +
        /* 
        +  sample: 
        +    modify response data of http://httpbin.org/user-agent
        +  test:
        +    curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
        +  expected response:
        +    { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! --
        +*/
        +
        +module.exports = {
        +  *beforeSendResponse(requestDetail, responseDetail) {
        +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
        +      const newResponse = responseDetail.response;
        +      newResponse.body += '-- AnyProxy Hacked! --';
        +      return new Promise((resolve, reject) => {
        +        setTimeout(() => { // delay the response for 5s
        +          resolve({ response: newResponse });
        +        }, 5000);
        +      });
        +    }
        +  },
        +};
        +
        +

        Config Certification

        +

        Config root CA in OSX

        +
          +
        • this kind of errors is usually caused by untrusted root CA
        • +
        +

        +
        +

        Warning: please keep your root CA safe since it may influence your system security.

        +
        +

        install :

        +
          +
        • double click rootCA.crt

          +
        • +
        • add cert into login or system

          +
        • +
        +

        +
          +
        • find the newly imported AnyProxy certificates, configured as Always Trust
        • +
        +

        +

        Config root CA in windows

        +

        +

        Config OSX system proxy

        +
          +
        • the config is in wifi - advanced
        • +
        +

        +

        config http proxy server

        + +

        +

        trust root CA in iOS

        +
          +
        • Click Root CA in web ui, and follow the instruction to install
        • +
        +

        +

        trust root CA in iOS after 10.3

        +
          +
        • Besides installing root CA, you have to "turn on" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.
        • +
        +

        +

        config iOS/Android proxy server

        +
          +
        • proxy settings are placed in wifi setting

          +
        • +
        • iOS

          +
        • +
        +

        +
          +
        • Android
        • +
        +

        +

        FAQ

        +

        Q: can not deal https request in rule module.

        +
          +
        • A: Any of these options could be used to change the way AnyProxy deall https requests
            +
          1. config --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module
          2. +
          3. place a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.
          4. +
          +
        • +
        +

        Q: get an error says function is not yieldable

        +
          +
        • A: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.
        • +
        +

        Q: The connection is not private

        +

        AnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.

        +
          +
        • If you run AnyProxy by command line +Pass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.

          +
          anyproxy -i --ignore-unauthorized-ssl
          +
          +
        • +
        • If you run AnyProxy by Nodejs +Pass in the option dangerouslyIgnoreUnauthorized:true, like this:

          +
          const options = {
          + ...,
          + dangerouslyIgnoreUnauthorized: true
          +};
          +
          +const anyproxyIns = new AnyProxy.ProxyCore(options);
          +anyproxyIns.start();
          +
          +

          This is also a global option, all certification errors will be ignored

          +
        • +
        • With the help of AnyProxy Rule +You can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:

          +
          module.exports = {
          +  *beforeSendRequest(requestDetail) {
          +    if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {
          +      const newRequestOptions = requestDetail.requestOptions;
          +      // set rejectUnauthorized as false
          +      newRequestOptions.rejectUnauthorized = false;
          +      return {
          +        requestOptions: newRequestOptions
          +      };
          +    }
          +  },
          +};
          +
          +

          And we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!

          +
        • +
        + + +
        + +
        +
        +
        + +

        results matching ""

        +
          + +
          +
          + +

          No results matching ""

          + +
          +
          +
          + +
          +
          + +
          + + + + + + + + + + +
          + + +
          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/en/search_index.json b/docs/en/search_index.json new file mode 100644 index 0000000..0370d2f --- /dev/null +++ b/docs/en/search_index.json @@ -0,0 +1 @@ +{"index":{"version":"0.5.12","fields":[{"name":"title","boost":10},{"name":"keywords","boost":15},{"name":"body","boost":1}],"ref":"url","documentStore":{"store":{"./":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#gener","#global","#launch","#local","&&","'","'...'","'...',","'/',","'/index.html';","'/redirect/to/another/path';","'127.0.0.1'","'8001');","'8008';","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'hello","'http',","'http';","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'text/html';","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(){","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","/^win/.test(process.platform);","0)","10.3","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","127.0.0.1:8001/index.html","1,write","2,","200,","3,","3.x:","300);","4,","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>","[switchyomega]","_re","_req","_req:","_res:","`","`;","a:","abil","abliti","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxyin","anyproxyins.start();","apg","append","appendix:how","at',","attack","attack.","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","bodi","body,","body:","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","design","detect","determin","directli","disabl","dn","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","features:","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","fulli","function","function()","function.","g","gener","get","github:","give","global","go,","gui","guid","h","hacked!","hacked...\")","handl","handling.","happen","header","header,","header:","headers:","headersar","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http/http","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/post","http://httpbin.org/us","http://localhost:8008/index.html","httpbin.org","httpbin.org,","https://github.com/alibaba/anyproxy/tree/4.x","https://httpbin.org/us","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","interest","interfac","interface,","introduct","invok","io","ios/android","issu","iswin","it,","it.","javascript.","js","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.hostnam","newrequestoptions.method","newrequestoptions.path","newrequestoptions.port","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['cont","newresponse.header['x","newresponse.statuscod","node.j","nodej","nodejs.","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","offer","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","otherwire,","otherwise,","own.","page","param","part","pass","password","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.protocol","requestdetail.requestoptions);","requestdetail.requestoptions;","requestdetail;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(","settimeout(()","settings.","side","signed.","silent","silent:","simpl","simplifi","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","stuff:","sudo","summari","summary,","summary:","support","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","true;","trust","type':","type']","type:","typic","ubuntu","ui,","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","whether","wifi","window","window.alert(\"sorry,","without","work","world'","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","}","})","});","},","};","中文文档",":"],"doc.html":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#","#gener","#global","#launch","#local","&&","'","'...'","'...',","'/',","'/redirect/to/another/path';","'/user","'8001');","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'http',","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","/^win/.test(process.platform);","0)","10.3","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","1,write","2,","200,","3,","4,","4.0","4.x","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>","[switchyomega]","_re","_req","_req:","_res:","a:","abil","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent';","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxy@beta","anyproxyin","anyproxyins.start();","apg","append","appendix:how","at',","attack","attack.","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","beta","bodi","body,","body:","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","detect","determin","directli","disabl","dn","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","function","function()","function.","g","gener","get","give","global","go,","guid","h","hacked!","handl","handling.","happen","header","header,","header:","headers:","headersar","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/ani","http://httpbin.org/post","http://httpbin.org/us","httpbin.org","httpbin.org,","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","interest","interfac","interface,","introduct","io","ios/android","issu","iswin","it,","it.","javascript.","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.method","newrequestoptions.path","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['x","newresponse.statuscod","node.j","nodej","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","otherwire,","otherwise,","own.","page","param","part","pass","password","path","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.requestoptions);","requestdetail.requestoptions;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(()","settings.","side","signed.","silent","silent:","simpl","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","stuff:","sudo","summari","summary,","summary:","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","trust","type':","type:","typic","ubuntu","ui,","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","whether","wifi","window","without","work","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","}","})","});","},","};",":"]},"length":2},"tokenStore":{"root":{"0":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}},"1":{"0":{"0":{"0":{"0":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"8":{"0":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"docs":{}},"docs":{},".":{"3":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"docs":{}}},"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"/":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}},",":{"docs":{},"访":{"docs":{},"问":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},",":{"docs":{},"编":{"docs":{},"写":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"w":{"docs":{},"r":{"docs":{},"i":{"docs":{},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"2":{"0":{"0":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"3":{"0":{"0":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{},"x":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"4":{"0":{"4":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"docs":{}},"4":{"3":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"0":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"的":{"docs":{},"主":{"docs":{},"要":{"docs":{},"变":{"docs":{},"化":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},",":{"docs":{},"欢":{"docs":{},"迎":{"docs":{},"提":{"docs":{},"供":{"docs":{},"反":{"docs":{},"馈":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"docs":{},"x":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"5":{"0":{"0":{"0":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"docs":{}},"docs":{}},"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"2":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"3":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},"\"":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{},"/":{"7":{"docs":{},".":{"4":{"3":{"docs":{},".":{"0":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"docs":{}}},"docs":{}},"docs":{}}},"docs":{}}}}}},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"t":{"docs":{},"h":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"r":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"#":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"全":{"docs":{},"局":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"启":{"docs":{},"动":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"本":{"docs":{},"地":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"生":{"docs":{},"成":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"生":{"docs":{},"成":{"docs":{},"后":{"docs":{},"需":{"docs":{},"要":{"docs":{},"手":{"docs":{},"动":{"docs":{},"信":{"docs":{},"任":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"l":{"docs":{},"a":{"docs":{},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"&":{"docs":{},"&":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"'":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"8":{"0":{"0":{"1":{"docs":{},"'":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"8":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},".":{"docs":{},".":{"docs":{},".":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"/":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"/":{"docs":{},"t":{"docs":{},"o":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"o":{"docs":{},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{},"/":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},";":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"a":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{},"i":{"docs":{},"o":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"/":{"0":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"docs":{}}},"docs":{}}},"docs":{}}}}}}}}},"p":{"docs":{},"p":{"docs":{},"l":{"docs":{},"i":{"docs":{},"c":{"docs":{},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"/":{"docs":{},"j":{"docs":{},"s":{"docs":{},"o":{"docs":{},"n":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"a":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"h":{"docs":{},"e":{"docs":{},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"i":{"docs":{},"o":{"docs":{},"/":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{},"?":{"docs":{},"s":{"docs":{},"h":{"docs":{},"o":{"docs":{},"w":{"docs":{},"_":{"docs":{},"e":{"docs":{},"n":{"docs":{},"v":{"docs":{},"=":{"1":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"m":{"docs":{},"a":{"docs":{},"g":{"docs":{},"e":{"docs":{},"/":{"docs":{},"g":{"docs":{},"i":{"docs":{},"f":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"k":{"docs":{},"e":{"docs":{},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"m":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"o":{"docs":{},"r":{"docs":{},"i":{"docs":{},"g":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{},"e":{"docs":{},"x":{"docs":{},"t":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}},"h":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"{":{"docs":{},"\"":{"docs":{},"h":{"docs":{},"e":{"docs":{},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"(":{"docs":{},"!":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{},".":{"docs":{},"i":{"docs":{},"f":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"f":{"docs":{},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"e":{"docs":{},"x":{"docs":{},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{},"s":{"docs":{},"(":{"docs":{},")":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"{":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},"e":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},".":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},"o":{"docs":{},"f":{"docs":{},"(":{"docs":{},"'":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}},"/":{"docs":{},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}}}},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"*":{"docs":{},"/":{"docs":{"./":{"ref":"./","tf":0.007590132827324478},"doc.html":{"ref":"doc.html","tf":0.007797270955165692}},"}":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},"*":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"+":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},".":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"/":{"docs":{},"m":{"docs":{},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"p":{"docs":{},"k":{"docs":{},"g":{"docs":{},"/":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"/":{"docs":{},"*":{"docs":{"./":{"ref":"./","tf":0.008064516129032258},"doc.html":{"ref":"doc.html","tf":0.008284600389863547}}},"/":{"docs":{"./":{"ref":"./","tf":0.006641366223908918},"doc.html":{"ref":"doc.html","tf":0.00682261208576998}},"w":{"docs":{},"h":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"这":{"docs":{},"里":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"同":{"docs":{},"时":{"docs":{},"加":{"docs":{},"上":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"^":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{},"/":{"docs":{},".":{"docs":{},"t":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{},".":{"docs":{},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"t":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"m":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"=":{"docs":{"./":{"ref":"./","tf":0.021821631878557873},"doc.html":{"ref":"doc.html","tf":0.02046783625730994}},"=":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}}},">":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},">":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"关":{"docs":{},"于":{"docs":{},"本":{"docs":{},"机":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"证":{"docs":{},"书":{"docs":{},"信":{"docs":{},"任":{"docs":{},"设":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"通":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"q":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"`":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},"a":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0056925996204933585},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"'":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"l":{"docs":{},"i":{"docs":{},"v":{"docs":{},"e":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"w":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.03273244781783681},"doc.html":{"ref":"doc.html","tf":0.03167641325536062}}},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"c":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},"r":{"docs":{},"(":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"(":{"docs":{},"(":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"f":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"f":{"docs":{},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"e":{"docs":{},"x":{"docs":{},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{},"s":{"docs":{},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"d":{"docs":{},"i":{"docs":{},"s":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"(":{"docs":{},"'":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"s":{"docs":{},".":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"使":{"docs":{},"用":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},"加":{"docs":{},"载":{"docs":{},"本":{"docs":{},"地":{"docs":{},"规":{"docs":{},"则":{"docs":{},",":{"docs":{},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"在":{"docs":{},"参":{"docs":{},"数":{"docs":{},"里":{"docs":{},"传":{"docs":{},"入":{"docs":{},"一":{"docs":{},"个":{"docs":{},"本":{"docs":{},"地":{"docs":{},"的":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{},"路":{"docs":{},"径":{"docs":{},",":{"docs":{},"或":{"docs":{},"是":{"docs":{},"某":{"docs":{},"个":{"docs":{},"全":{"docs":{},"局":{"docs":{},"安":{"docs":{},"装":{"docs":{},"的":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"可":{"docs":{},"以":{"docs":{},"作":{"docs":{},"为":{"docs":{},"一":{"docs":{},"个":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"使":{"docs":{},"用":{"docs":{},",":{"docs":{},"整":{"docs":{},"合":{"docs":{},"进":{"docs":{},"其":{"docs":{},"他":{"docs":{},"工":{"docs":{},"具":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"向":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"发":{"docs":{},"送":{"docs":{},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"发":{"docs":{},"送":{"docs":{},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"在":{"docs":{},"与":{"docs":{},"目":{"docs":{},"标":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"建":{"docs":{},"立":{"docs":{},"连":{"docs":{},"接":{"docs":{},"的":{"docs":{},"过":{"docs":{},"程":{"docs":{},"中":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"这":{"docs":{},"个":{"docs":{},"方":{"docs":{},"法":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"提":{"docs":{},"供":{"docs":{},"了":{"docs":{},"二":{"docs":{},"次":{"docs":{},"开":{"docs":{},"发":{"docs":{},"的":{"docs":{},"能":{"docs":{},"力":{"docs":{},",":{"docs":{},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"j":{"docs":{},"s":{"docs":{},"编":{"docs":{},"写":{"docs":{},"自":{"docs":{},"己":{"docs":{},"的":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"(":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},")":{"docs":{},",":{"docs":{},"来":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"网":{"docs":{},"络":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"处":{"docs":{},"理":{"docs":{},"逻":{"docs":{},"辑":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"收":{"docs":{},"到":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"时":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"开":{"docs":{},"放":{"docs":{},"式":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}},"调":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"由":{"docs":{},"模":{"docs":{},"块":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},",":{"docs":{},"返":{"docs":{},"回":{"docs":{},"新":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"或":{"docs":{},"返":{"docs":{},"回":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"默":{"docs":{},"认":{"docs":{},"不":{"docs":{},"对":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},",":{"docs":{},"如":{"docs":{},"需":{"docs":{},"看":{"docs":{},"到":{"docs":{},"明":{"docs":{},"文":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"需":{"docs":{},"要":{"docs":{},"配":{"docs":{},"置":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"@":{"docs":{},"b":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}},"p":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"x":{"docs":{},":":{"docs":{},"h":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"t":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"t":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},")":{"docs":{},",":{"docs":{},"需":{"docs":{},"要":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"提":{"docs":{},"前":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"b":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"l":{"docs":{},"i":{"docs":{},"t":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"c":{"docs":{},"t":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"d":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"v":{"docs":{},"a":{"docs":{},"n":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"b":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"t":{"docs":{},"a":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"o":{"docs":{},"d":{"docs":{},"y":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"三":{"docs":{},"个":{"docs":{},"字":{"docs":{},"段":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},")":{"docs":{},",":{"docs":{},"甚":{"docs":{},"至":{"docs":{},"是":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"目":{"docs":{},"标":{"docs":{},"地":{"docs":{},"址":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},"n":{"docs":{},"o":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"y":{"docs":{},"'":{"docs":{},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"r":{"docs":{},"o":{"docs":{},"w":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.006641366223908918},"doc.html":{"ref":"doc.html","tf":0.00682261208576998}},"方":{"docs":{},"法":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},",":{"docs":{},"按":{"docs":{},"提":{"docs":{},"示":{"docs":{},"扫":{"docs":{},"描":{"docs":{},"二":{"docs":{},"维":{"docs":{},"码":{"docs":{},"即":{"docs":{},"可":{"docs":{},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"f":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"i":{"docs":{},"c":{"docs":{},"a":{"docs":{},"t":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"y":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"l":{"docs":{},"a":{"docs":{},"s":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"i":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"d":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},")":{"docs":{},"、":{"docs":{},"响":{"docs":{},"应":{"docs":{},"头":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"g":{"docs":{},"(":{"docs":{},"'":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{"./":{"ref":"./","tf":0.009013282732447819},"doc.html":{"ref":"doc.html","tf":0.009259259259259259}}}},"t":{"docs":{},"r":{"docs":{},"o":{"docs":{},"l":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"f":{"docs":{},"i":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"l":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"l":{"docs":{},"d":{"docs":{},"n":{"docs":{},"'":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"s":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0056925996204933585},"doc.html":{"ref":"doc.html","tf":0.005847953216374269}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"w":{"docs":{},"d":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"h":{"docs":{},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"u":{"docs":{},"s":{"docs":{},"l":{"docs":{},"y":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"z":{"docs":{},"e":{"docs":{},"d":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"'":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"e":{"docs":{},"l":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"e":{"docs":{},"d":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"b":{"docs":{},"i":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"c":{"docs":{},"r":{"docs":{},"y":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"f":{"docs":{},"a":{"docs":{},"u":{"docs":{},"l":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"m":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"m":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"u":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"l":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"s":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"r":{"docs":{},"i":{"docs":{},"v":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"u":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"g":{"docs":{},"l":{"docs":{},"i":{"docs":{},"s":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"t":{"docs":{},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.007115749525616698},"doc.html":{"ref":"doc.html","tf":0.007309941520467836}},")":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"v":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"x":{"docs":{},"e":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"'":{"docs":{},"o":{"docs":{},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"p":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"(":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"c":{"docs":{},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},".":{"docs":{},"g":{"docs":{},"o":{"docs":{},"o":{"docs":{},"g":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"/":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"/":{"docs":{},"p":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"k":{"docs":{},"g":{"docs":{},"c":{"docs":{},"e":{"docs":{},"m":{"docs":{},"l":{"docs":{},"o":{"docs":{},"k":{"docs":{},"b":{"docs":{},"a":{"docs":{},"d":{"docs":{},"o":{"docs":{},"h":{"docs":{},"g":{"docs":{},"k":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"j":{"docs":{},"o":{"docs":{},"m":{"docs":{},"c":{"docs":{},"l":{"docs":{},"g":{"docs":{},"j":{"docs":{},"g":{"docs":{},"i":{"docs":{},"f":{"docs":{},")":{"docs":{},"为":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},".":{"docs":{},"g":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},"d":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"m":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"q":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"u":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"i":{"docs":{},"s":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"o":{"docs":{},"r":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}},"w":{"docs":{},"a":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"e":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}}},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":5.000487329434698}}}},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},"主":{"docs":{},"页":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"g":{"docs":{},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"a":{"docs":{},"l":{"docs":{},"i":{"docs":{},"b":{"docs":{},"a":{"docs":{},"b":{"docs":{},"a":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"/":{"docs":{},"t":{"docs":{},"r":{"docs":{},"e":{"docs":{},"e":{"docs":{},"/":{"4":{"docs":{},".":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"v":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"o":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{},"e":{"docs":{},"d":{"docs":{},"!":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},".":{"docs":{},".":{"docs":{},".":{"docs":{},"\"":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}},"n":{"docs":{},"d":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"p":{"docs":{},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},")":{"docs":{},",":{"docs":{},"请":{"docs":{},"求":{"docs":{},"体":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"a":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},")":{"docs":{},"、":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"l":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.015180265654648957},"doc.html":{"ref":"doc.html","tf":0.014619883040935672}},"/":{"1":{"docs":{},".":{"1":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"docs":{}}},"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}},"2":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{},".":{"docs":{},".":{"docs":{},".":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"/":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}},"a":{"docs":{},"n":{"docs":{},"i":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},":":{"8":{"0":{"0":{"8":{"docs":{},"/":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598}}}}}}}}}}}}}}}},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"a":{"docs":{},"l":{"docs":{},"i":{"docs":{},"b":{"docs":{},"a":{"docs":{},"b":{"docs":{},"a":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"/":{"docs":{},"t":{"docs":{},"r":{"docs":{},"e":{"docs":{},"e":{"docs":{},"/":{"4":{"docs":{},".":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"a":{"docs":{},"p":{"docs":{},"i":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"#":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"_":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"连":{"docs":{},"接":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"出":{"docs":{},"错":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}},"的":{"docs":{},"代":{"docs":{},"理":{"docs":{},"模":{"docs":{},"式":{"docs":{},"中":{"docs":{},",":{"docs":{},"这":{"docs":{},"里":{"docs":{},"的":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"是":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}},".":{"docs":{},"r":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}},"n":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}},"r":{"docs":{},"u":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.007590132827324478},"doc.html":{"ref":"doc.html","tf":0.007309941520467836}},"`":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"按":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"启":{"docs":{},"动":{"docs":{},"时":{"docs":{},"配":{"docs":{},"置":{"docs":{},"`":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"`":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"都":{"docs":{},"会":{"docs":{},"被":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"f":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"o":{"docs":{},"d":{"docs":{},"u":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":10.001423149905124},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"l":{"docs":{},"u":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}},"f":{"docs":{},"l":{"docs":{},"u":{"docs":{},"e":{"docs":{},"n":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"v":{"docs":{},"o":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},".":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"m":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"k":{"docs":{},"e":{"docs":{},"y":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"n":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"b":{"docs":{},"/":{"docs":{},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"c":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}},"a":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"a":{"docs":{},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"i":{"docs":{},"n":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"u":{"docs":{},"a":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"s":{"docs":{},"a":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"d":{"docs":{},"d":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},")":{"docs":{},",":{"docs":{},"用":{"docs":{},"户":{"docs":{},"必":{"docs":{},"须":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"才":{"docs":{},"能":{"docs":{},"进":{"docs":{},"行":{"docs":{},"后":{"docs":{},"续":{"docs":{},"流":{"docs":{},"程":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.013282732447817837},"doc.html":{"ref":"doc.html","tf":0.01364522417153996}}}}},"u":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.009487666034155597},"doc.html":{"ref":"doc.html","tf":0.008284600389863547}},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"e":{"docs":{},"x":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"p":{"docs":{},"k":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}},"a":{"docs":{},"k":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"a":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"u":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"n":{"docs":{},"e":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},".":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},".":{"docs":{},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},"s":{"docs":{},"[":{"docs":{},"'":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"r":{"docs":{},"e":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0056925996204933585},"doc.html":{"ref":"doc.html","tf":0.005847953216374269}},"e":{"docs":{},".":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},"[":{"docs":{},"'":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"w":{"docs":{},"o":{"docs":{},"r":{"docs":{},"k":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"t":{"docs":{},"h":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"p":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}},"u":{"docs":{},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},"m":{"docs":{},"b":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"o":{"docs":{},"b":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"s":{"docs":{},"s":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"(":{"docs":{},"{":{"docs":{},"}":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}}}}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"a":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"v":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"f":{"docs":{},"f":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{},"w":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}}},"w":{"docs":{},"n":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"g":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"r":{"docs":{},"a":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"w":{"docs":{},"o":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}},"i":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"r":{"docs":{},"i":{"docs":{},"v":{"docs":{},"a":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"(":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"v":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}}}},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},",":{"docs":{},"i":{"docs":{},".":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.016603415559772294},"doc.html":{"ref":"doc.html","tf":0.016569200779727095}}},"y":{"docs":{},".":{"docs":{},"c":{"docs":{},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"c":{"docs":{},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"p":{"docs":{},"m":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"v":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"l":{"docs":{},"a":{"docs":{},"c":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"e":{"docs":{},"a":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"q":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"f":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.024193548387096774},"doc.html":{"ref":"doc.html","tf":0.024853801169590642}},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}},".":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},";":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}}}}}}}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}},".":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},"(":{"docs":{},"'":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"'":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}},"c":{"docs":{},"h":{"docs":{},"i":{"docs":{},"l":{"docs":{},"d":{"docs":{},"_":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"e":{"docs":{},"x":{"docs":{},"e":{"docs":{},"c":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"m":{"docs":{},"o":{"docs":{},"d":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"'":{"docs":{},")":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{},"e":{"docs":{},"(":{"docs":{},"k":{"docs":{},"e":{"docs":{},"y":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"v":{"docs":{},"e":{"docs":{},"(":{"docs":{},"{":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.015654648956356737},"doc.html":{"ref":"doc.html","tf":0.01608187134502924}},"e":{"docs":{},"\"":{"docs":{},"}":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.007590132827324478},"doc.html":{"ref":"doc.html","tf":0.007797270955165692}}},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},";":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"u":{"docs":{},"r":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.017077798861480076},"doc.html":{"ref":"doc.html","tf":0.01705653021442495}}}}}},"c":{"docs":{},"e":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.022296015180265655},"doc.html":{"ref":"doc.html","tf":0.021929824561403508}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"_":{"docs":{},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"/":{"docs":{},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"_":{"docs":{},"m":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{},"f":{"docs":{},"y":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"_":{"docs":{},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"_":{"docs":{},"l":{"docs":{},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"接":{"docs":{},"口":{"docs":{},"文":{"docs":{},"档":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"样":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"i":{"docs":{},"s":{"docs":{},"k":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},":":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"v":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"f":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"t":{"docs":{},"i":{"docs":{},"m":{"docs":{},"e":{"docs":{},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{},"(":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}},"n":{"docs":{},"g":{"docs":{},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"t":{"docs":{},"y":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"f":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"r":{"docs":{},"v":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.008538899430740038},"doc.html":{"ref":"doc.html","tf":0.008771929824561403}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"(":{"docs":{},"s":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"d":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"g":{"docs":{},"n":{"docs":{},"e":{"docs":{},"d":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"s":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":5.002436647173489}}}},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"r":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"u":{"docs":{},"f":{"docs":{},"f":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"d":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"y":{"docs":{},"(":{"docs":{},")":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},"t":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"c":{"docs":{},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{},"o":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"k":{"docs":{},"i":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"u":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"e":{"docs":{},"c":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}},"t":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}},"x":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"/":{"docs":{},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"i":{"docs":{},"n":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"t":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"a":{"docs":{},"t":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"i":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"w":{"docs":{},"a":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}}}},"!":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}},"或":{"docs":{},"者":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{},"e":{"docs":{},",":{"docs":{},"表":{"docs":{},"示":{"docs":{},"是":{"docs":{},"否":{"docs":{},"需":{"docs":{},"要":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"a":{"docs":{},"f":{"docs":{},"f":{"docs":{},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"y":{"docs":{},"p":{"docs":{},"e":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"a":{"docs":{},"k":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}}},"i":{"docs":{},"m":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"u":{"docs":{},"i":{"docs":{},"中":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"k":{"docs":{},"n":{"docs":{},"o":{"docs":{},"w":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"l":{"docs":{},"i":{"docs":{},"m":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"e":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{"./":{"ref":"./","tf":0.009487666034155597},"doc.html":{"ref":"doc.html","tf":0.009746588693957114}},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"b":{"docs":{},"u":{"docs":{},"n":{"docs":{},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"v":{"docs":{},"a":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"l":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"u":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}},"i":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"e":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"s":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"i":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"f":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"版":{"docs":{},"界":{"docs":{},"面":{"docs":{},"端":{"docs":{},"口":{"docs":{},"号":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"重":{"docs":{},"构":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"a":{"docs":{},"l":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},"\"":{"docs":{},"s":{"docs":{},"o":{"docs":{},"r":{"docs":{},"r":{"docs":{},"y":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}}}},"s":{"docs":{},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"o":{"docs":{},"r":{"docs":{},"l":{"docs":{},"d":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"a":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"n":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"h":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"i":{"docs":{},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"y":{"docs":{},"i":{"docs":{},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}},"o":{"docs":{},"u":{"docs":{},"'":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"{":{"docs":{"./":{"ref":"./","tf":0.04174573055028463},"doc.html":{"ref":"doc.html","tf":0.04337231968810916}},"b":{"docs":{},"o":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},"a":{"docs":{},"n":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}},"n":{"docs":{},"u":{"docs":{},"m":{"docs":{},"b":{"docs":{},"e":{"docs":{},"r":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}},"o":{"docs":{},"b":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"r":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}},"|":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"}":{"docs":{"./":{"ref":"./","tf":0.014231499051233396},"doc.html":{"ref":"doc.html","tf":0.014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}},",":{"docs":{"./":{"ref":"./","tf":0.009962049335863378},"doc.html":{"ref":"doc.html","tf":0.009746588693957114}}},";":{"docs":{"./":{"ref":"./","tf":0.013282732447817837},"doc.html":{"ref":"doc.html","tf":0.014132553606237816}}}},"不":{"docs":{},"做":{"docs":{},"任":{"docs":{},"何":{"docs":{},"处":{"docs":{},"理":{"docs":{},"。":{"docs":{},"此":{"docs":{},"时":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"返":{"docs":{},"回":{"docs":{},"一":{"docs":{},"个":{"docs":{},"默":{"docs":{},"认":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"页":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},",":{"docs":{},"返":{"docs":{},"回":{"docs":{},"n":{"docs":{},"u":{"docs":{},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}},"中":{"docs":{},"把":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"证":{"docs":{},"书":{"docs":{},"的":{"docs":{},"开":{"docs":{},"关":{"docs":{},"打":{"docs":{},"开":{"docs":{},",":{"docs":{},"否":{"docs":{},"则":{"docs":{},"s":{"docs":{},"a":{"docs":{},"f":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{},"将":{"docs":{},"报":{"docs":{},"错":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"文":{"docs":{},"文":{"docs":{},"档":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"为":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"什":{"docs":{},"么":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"能":{"docs":{},"进":{"docs":{},"入":{"docs":{},"处":{"docs":{},"理":{"docs":{},"函":{"docs":{},"数":{"docs":{},"?":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"主":{"docs":{},"要":{"docs":{},"特":{"docs":{},"性":{"docs":{},"包":{"docs":{},"括":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"举":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},",":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},":":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"代":{"docs":{},"理":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"启":{"docs":{},"动":{"docs":{},"完":{"docs":{},"成":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"都":{"docs":{},"在":{"docs":{},"w":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"设":{"docs":{},"置":{"docs":{},"中":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"以":{"docs":{},"c":{"docs":{},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"的":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"t":{"docs":{},"c":{"docs":{},"h":{"docs":{},"y":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"插":{"docs":{},"件":{"docs":{},"为":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"下":{"docs":{},"任":{"docs":{},"意":{"docs":{},"一":{"docs":{},"项":{"docs":{},"都":{"docs":{},"能":{"docs":{},"用":{"docs":{},"来":{"docs":{},"改":{"docs":{},"变":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"处":{"docs":{},"理":{"docs":{},"特":{"docs":{},"性":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"几":{"docs":{},"种":{"docs":{},"返":{"docs":{},"回":{"docs":{},"都":{"docs":{},"是":{"docs":{},"合":{"docs":{},"法":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.002243829468960359}}}}}}}}}}}}},"作":{"docs":{},"为":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"使":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"全":{"docs":{},"局":{"docs":{},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"通":{"docs":{},"过":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"使":{"docs":{},"用":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"举":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"在":{"docs":{},"线":{"docs":{},"地":{"docs":{},"址":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"本":{"docs":{},"地":{"docs":{},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"路":{"docs":{},"径":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"修":{"docs":{},"改":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}},"请":{"docs":{},"求":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"协":{"docs":{},"议":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},",":{"docs":{},"如":{"docs":{},"强":{"docs":{},"制":{"docs":{},"改":{"docs":{},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"发":{"docs":{},"起":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"头":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"的":{"docs":{},"目":{"docs":{},"标":{"docs":{},"地":{"docs":{},"址":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"内":{"docs":{},"容":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"头":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"的":{"docs":{},"内":{"docs":{},"容":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"关":{"docs":{},"闭":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"全":{"docs":{},"局":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"其":{"docs":{},"他":{"docs":{},"命":{"docs":{},"令":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"创":{"docs":{},"建":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"即":{"docs":{},"将":{"docs":{},"发":{"docs":{},"送":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"配":{"docs":{},"置":{"docs":{},",":{"docs":{},"供":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},"'":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"作":{"docs":{},"为":{"docs":{},"使":{"docs":{},"用":{"docs":{},"。":{"docs":{},"详":{"docs":{},"见":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"a":{"docs":{},"p":{"docs":{},"i":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"#":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"_":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"原":{"docs":{},"始":{"docs":{},"的":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"返":{"docs":{},"回":{"docs":{},"对":{"docs":{},"象":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"双":{"docs":{},"击":{"docs":{},"打":{"docs":{},"开":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"发":{"docs":{},"送":{"docs":{},"响":{"docs":{},"应":{"docs":{},"前":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},"拦":{"docs":{},"截":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"只":{"docs":{},"有":{"docs":{},"返":{"docs":{},"回":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"才":{"docs":{},"会":{"docs":{},"尝":{"docs":{},"试":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"、":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"。":{"docs":{},"否":{"docs":{},"则":{"docs":{},"只":{"docs":{},"做":{"docs":{},"数":{"docs":{},"据":{"docs":{},"流":{"docs":{},"转":{"docs":{},"发":{"docs":{},",":{"docs":{},"无":{"docs":{},"法":{"docs":{},"看":{"docs":{},"到":{"docs":{},"明":{"docs":{},"文":{"docs":{},"数":{"docs":{},"据":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"可":{"docs":{},"以":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"函":{"docs":{},"数":{"docs":{},",":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"普":{"docs":{},"通":{"docs":{},"的":{"docs":{},"字":{"docs":{},"符":{"docs":{},"串":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"修":{"docs":{},"改":{"docs":{},"内":{"docs":{},"容":{"docs":{},"包":{"docs":{},"括":{"docs":{},"请":{"docs":{},"求":{"docs":{},"头":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"的":{"docs":{},"内":{"docs":{},"容":{"docs":{},"包":{"docs":{},"括":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{},"(":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"同":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"中":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"中":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}}}}}}}}}}},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"加":{"docs":{},"载":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"后":{"docs":{},"将":{"docs":{},"终":{"docs":{},"端":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"配":{"docs":{},"置":{"docs":{},"为":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{},"即":{"docs":{},"可":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}}}},"命":{"docs":{},"令":{"docs":{},"行":{"docs":{},"启":{"docs":{},"动":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"时":{"docs":{},"配":{"docs":{},"置":{"docs":{},"`":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"端":{"docs":{},"口":{"docs":{},"号":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}},"直":{"docs":{},"接":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"在":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},"代":{"docs":{},"码":{"docs":{},"中":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"w":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"高":{"docs":{},"级":{"docs":{},"设":{"docs":{},"置":{"docs":{},"中":{"docs":{},",":{"docs":{},"配":{"docs":{},"置":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"即":{"docs":{},"可":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"构":{"docs":{},"造":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"实":{"docs":{},"例":{"docs":{},"的":{"docs":{},"时":{"docs":{},"候":{"docs":{},",":{"docs":{},"传":{"docs":{},"入":{"docs":{},"参":{"docs":{},"数":{"docs":{},"d":{"docs":{},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"u":{"docs":{},"s":{"docs":{},"l":{"docs":{},"y":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{},"i":{"docs":{},"z":{"docs":{},"e":{"docs":{},"d":{"docs":{},":":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"请":{"docs":{},"求":{"docs":{},"处":{"docs":{},"理":{"docs":{},"过":{"docs":{},"程":{"docs":{},"中":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"o":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"并":{"docs":{},"提":{"docs":{},"供":{"docs":{},"对":{"docs":{},"应":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"基":{"docs":{},"于":{"docs":{},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{},"s":{"docs":{},",":{"docs":{},"开":{"docs":{},"放":{"docs":{},"二":{"docs":{},"次":{"docs":{},"开":{"docs":{},"发":{"docs":{},"能":{"docs":{},"力":{"docs":{},",":{"docs":{},"允":{"docs":{},"许":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"请":{"docs":{},"求":{"docs":{},"处":{"docs":{},"理":{"docs":{},"逻":{"docs":{},"辑":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"处":{"docs":{},"理":{"docs":{},"流":{"docs":{},"程":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"图":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"多":{"docs":{},"数":{"docs":{},"场":{"docs":{},"景":{"docs":{},"下":{"docs":{},",":{"docs":{},"错":{"docs":{},"误":{"docs":{},"会":{"docs":{},"在":{"docs":{},"请":{"docs":{},"求":{"docs":{},"目":{"docs":{},"标":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"的":{"docs":{},"时":{"docs":{},"候":{"docs":{},"发":{"docs":{},"生":{"docs":{},",":{"docs":{},"比":{"docs":{},"如":{"docs":{},"d":{"docs":{},"n":{"docs":{},"s":{"docs":{},"解":{"docs":{},"析":{"docs":{},"失":{"docs":{},"败":{"docs":{},"、":{"docs":{},"请":{"docs":{},"求":{"docs":{},"超":{"docs":{},"时":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"如":{"docs":{},"下":{"docs":{},"几":{"docs":{},"种":{"docs":{},"方":{"docs":{},"案":{"docs":{},"都":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"来":{"docs":{},"引":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"何":{"docs":{},"引":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"果":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"返":{"docs":{},"回":{"docs":{},"了":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},",":{"docs":{},"则":{"docs":{},"立":{"docs":{},"即":{"docs":{},"把":{"docs":{},"此":{"docs":{},"响":{"docs":{},"应":{"docs":{},"返":{"docs":{},"回":{"docs":{},"到":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"(":{"docs":{},"而":{"docs":{},"不":{"docs":{},"再":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{},"真":{"docs":{},"正":{"docs":{},"的":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},")":{"docs":{},",":{"docs":{},"流":{"docs":{},"程":{"docs":{},"结":{"docs":{},"束":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"配":{"docs":{},"置":{"docs":{},"了":{"docs":{},"全":{"docs":{},"局":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"则":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"略":{"docs":{},"过":{"docs":{},"这":{"docs":{},"个":{"docs":{},"调":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"要":{"docs":{},"启":{"docs":{},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"解":{"docs":{},"析":{"docs":{},",":{"docs":{},"请":{"docs":{},"在":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"启":{"docs":{},"动":{"docs":{},"前":{"docs":{},"自":{"docs":{},"行":{"docs":{},"调":{"docs":{},"用":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{},"相":{"docs":{},"关":{"docs":{},"方":{"docs":{},"法":{"docs":{},"生":{"docs":{},"成":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"并":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"信":{"docs":{},"任":{"docs":{},"安":{"docs":{},"装":{"docs":{},"。":{"docs":{},"或":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"使":{"docs":{},"用":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"c":{"docs":{},"a":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"对":{"docs":{},"于":{"docs":{},"d":{"docs":{},"e":{"docs":{},"b":{"docs":{},"i":{"docs":{},"a":{"docs":{},"n":{"docs":{},"或":{"docs":{},"者":{"docs":{},"u":{"docs":{},"b":{"docs":{},"u":{"docs":{},"n":{"docs":{},"t":{"docs":{},"u":{"docs":{},"系":{"docs":{},"统":{"docs":{},",":{"docs":{},"在":{"docs":{},"安":{"docs":{},"装":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"之":{"docs":{},"前":{"docs":{},",":{"docs":{},"可":{"docs":{},"能":{"docs":{},"还":{"docs":{},"需":{"docs":{},"要":{"docs":{},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"开":{"docs":{},"发":{"docs":{},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"引":{"docs":{},"入":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"当":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"请":{"docs":{},"求":{"docs":{},"经":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"时":{"docs":{},",":{"docs":{},"具":{"docs":{},"体":{"docs":{},"处":{"docs":{},"理":{"docs":{},"过":{"docs":{},"程":{"docs":{},"是":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"收":{"docs":{},"到":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"可":{"docs":{},"以":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"对":{"docs":{},"请":{"docs":{},"求":{"docs":{},"做":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"访":{"docs":{},"问":{"docs":{},"特":{"docs":{},"定":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"站":{"docs":{},"点":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"提":{"docs":{},"示":{"docs":{},"该":{"docs":{},"站":{"docs":{},"点":{"docs":{},"不":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"安":{"docs":{},"全":{"docs":{},"的":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"这":{"docs":{},"通":{"docs":{},"常":{"docs":{},"是":{"docs":{},"因":{"docs":{},"为":{"docs":{},"站":{"docs":{},"点":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"设":{"docs":{},"置":{"docs":{},"不":{"docs":{},"能":{"docs":{},"被":{"docs":{},"正":{"docs":{},"确":{"docs":{},"识":{"docs":{},"别":{"docs":{},"导":{"docs":{},"致":{"docs":{},"的":{"docs":{},"(":{"docs":{},"比":{"docs":{},"如":{"docs":{},",":{"docs":{},"站":{"docs":{},"点":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"是":{"docs":{},"自":{"docs":{},"签":{"docs":{},"发":{"docs":{},"的":{"docs":{},")":{"docs":{},"。":{"docs":{},"如":{"docs":{},"果":{"docs":{},"您":{"docs":{},"信":{"docs":{},"任":{"docs":{},"该":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"以":{"docs":{},"下":{"docs":{},"方":{"docs":{},"式":{"docs":{},"来":{"docs":{},"继":{"docs":{},"续":{"docs":{},"访":{"docs":{},"问":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"必":{"docs":{},"选":{"docs":{},",":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"端":{"docs":{},"口":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"快":{"docs":{},"速":{"docs":{},"开":{"docs":{},"始":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"我":{"docs":{},"们":{"docs":{},"自":{"docs":{},"然":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"借":{"docs":{},"助":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"的":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"来":{"docs":{},"实":{"docs":{},"现":{"docs":{},"这":{"docs":{},"个":{"docs":{},"效":{"docs":{},"果":{"docs":{},",":{"docs":{},"而":{"docs":{},"且":{"docs":{},"我":{"docs":{},"们":{"docs":{},"还":{"docs":{},"可":{"docs":{},"以":{"docs":{},"控":{"docs":{},"制":{"docs":{},"到":{"docs":{},"只":{"docs":{},"允":{"docs":{},"许":{"docs":{},"指":{"docs":{},"定":{"docs":{},"网":{"docs":{},"址":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"对":{"docs":{},"不":{"docs":{},"在":{"docs":{},"列":{"docs":{},"表":{"docs":{},"的":{"docs":{},"网":{"docs":{},"址":{"docs":{},",":{"docs":{},"进":{"docs":{},"行":{"docs":{},"证":{"docs":{},"书":{"docs":{},"的":{"docs":{},"强":{"docs":{},"验":{"docs":{},"证":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"找":{"docs":{},"到":{"docs":{},"刚":{"docs":{},"刚":{"docs":{},"导":{"docs":{},"入":{"docs":{},"的":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"配":{"docs":{},"置":{"docs":{},"为":{"docs":{},"信":{"docs":{},"任":{"docs":{},"(":{"docs":{},"a":{"docs":{},"l":{"docs":{},"w":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"把":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"响":{"docs":{},"应":{"docs":{},"信":{"docs":{},"息":{"docs":{},"返":{"docs":{},"回":{"docs":{},"给":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"所":{"docs":{},"有":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"协":{"docs":{},"议":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"拦":{"docs":{},"截":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"对":{"docs":{},"内":{"docs":{},"容":{"docs":{},"做":{"docs":{},"修":{"docs":{},"改":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"并":{"docs":{},"修":{"docs":{},"改":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"响":{"docs":{},"应":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"正":{"docs":{},"在":{"docs":{},"发":{"docs":{},"送":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"提":{"docs":{},"供":{"docs":{},"g":{"docs":{},"u":{"docs":{},"i":{"docs":{},"界":{"docs":{},"面":{"docs":{},",":{"docs":{},"用":{"docs":{},"以":{"docs":{},"观":{"docs":{},"察":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"示":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"支":{"docs":{},"持":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"收":{"docs":{},"集":{"docs":{},"请":{"docs":{},"求":{"docs":{},"所":{"docs":{},"有":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"包":{"docs":{},"括":{"docs":{},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"改":{"docs":{},"成":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"并":{"docs":{},"发":{"docs":{},"送":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"时":{"docs":{},",":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"参":{"docs":{},"数":{"docs":{},"内":{"docs":{},"容":{"docs":{},"大":{"docs":{},"致":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"参":{"docs":{},"数":{"docs":{},"内":{"docs":{},"容":{"docs":{},"大":{"docs":{},"致":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"否":{"docs":{},"启":{"docs":{},"用":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"版":{"docs":{},"界":{"docs":{},"面":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"处":{"docs":{},"理":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"屏":{"docs":{},"蔽":{"docs":{},"所":{"docs":{},"有":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},"输":{"docs":{},"出":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"强":{"docs":{},"制":{"docs":{},"拦":{"docs":{},"截":{"docs":{},"所":{"docs":{},"有":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},",":{"docs":{},"忽":{"docs":{},"略":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"忽":{"docs":{},"略":{"docs":{},"请":{"docs":{},"求":{"docs":{},"中":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}},"必":{"docs":{},"选":{"docs":{},"字":{"docs":{},"段":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"包":{"docs":{},"括":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"本":{"docs":{},"文":{"docs":{},"档":{"docs":{},"的":{"docs":{},"适":{"docs":{},"用":{"docs":{},"范":{"docs":{},"围":{"docs":{},"是":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"质":{"docs":{},"是":{"docs":{},"中":{"docs":{},"间":{"docs":{},"人":{"docs":{},"攻":{"docs":{},"击":{"docs":{},"(":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"来":{"docs":{},"加":{"docs":{},"载":{"docs":{},"模":{"docs":{},"块":{"docs":{},"并":{"docs":{},"体":{"docs":{},"验":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"忽":{"docs":{},"略":{"docs":{},"证":{"docs":{},"书":{"docs":{},"认":{"docs":{},"证":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"。":{"docs":{},"需":{"docs":{},"要":{"docs":{},"注":{"docs":{},"意":{"docs":{},"的":{"docs":{},"是":{"docs":{},",":{"docs":{},"该":{"docs":{},"参":{"docs":{},"数":{"docs":{},"是":{"docs":{},"全":{"docs":{},"局":{"docs":{},"生":{"docs":{},"效":{"docs":{},"的":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"你":{"docs":{},"在":{"docs":{},"此":{"docs":{},"期":{"docs":{},"间":{"docs":{},"访":{"docs":{},"问":{"docs":{},"了":{"docs":{},"其":{"docs":{},"他":{"docs":{},"未":{"docs":{},"知":{"docs":{},"的":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"他":{"docs":{},"们":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"问":{"docs":{},"题":{"docs":{},"也":{"docs":{},"会":{"docs":{},"被":{"docs":{},"忽":{"docs":{},"略":{"docs":{},",":{"docs":{},"这":{"docs":{},"可":{"docs":{},"能":{"docs":{},"会":{"docs":{},"带":{"docs":{},"来":{"docs":{},"安":{"docs":{},"全":{"docs":{},"隐":{"docs":{},"患":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"查":{"docs":{},"看":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"校":{"docs":{},"验":{"docs":{},"系":{"docs":{},"统":{"docs":{},"内":{"docs":{},"是":{"docs":{},"否":{"docs":{},"存":{"docs":{},"在":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"根":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"样":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"根":{"docs":{},"据":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"向":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"发":{"docs":{},"出":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"接":{"docs":{},"收":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"响":{"docs":{},"应":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},"模":{"docs":{},"块":{"docs":{},"介":{"docs":{},"绍":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"此":{"docs":{},"处":{"docs":{},"无":{"docs":{},"法":{"docs":{},"控":{"docs":{},"制":{"docs":{},"向":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"无":{"docs":{},"需":{"docs":{},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"注":{"docs":{},"意":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"引":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"前":{"docs":{},",":{"docs":{},"请":{"docs":{},"务":{"docs":{},"必":{"docs":{},"确":{"docs":{},"保":{"docs":{},"文":{"docs":{},"件":{"docs":{},"来":{"docs":{},"源":{"docs":{},"可":{"docs":{},"靠":{"docs":{},",":{"docs":{},"以":{"docs":{},"免":{"docs":{},"发":{"docs":{},"生":{"docs":{},"安":{"docs":{},"全":{"docs":{},"问":{"docs":{},"题":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"测":{"docs":{},"试":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"访":{"docs":{},"问":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}},"点":{"docs":{},"击":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"然":{"docs":{},"后":{"docs":{},",":{"docs":{},"安":{"docs":{},"装":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"生":{"docs":{},"成":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},",":{"docs":{},"完":{"docs":{},"成":{"docs":{},"后":{"docs":{},"请":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"信":{"docs":{},"任":{"docs":{},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{},"文":{"docs":{},"件":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"用":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{},"发":{"docs":{},"请":{"docs":{},"求":{"docs":{},"测":{"docs":{},"试":{"docs":{},"的":{"docs":{},"方":{"docs":{},"法":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"测":{"docs":{},"试":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"测":{"docs":{},"试":{"docs":{},":":{"docs":{},"配":{"docs":{},"置":{"docs":{},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"为":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"会":{"docs":{},"被":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"请":{"docs":{},"求":{"docs":{},"全":{"docs":{},"部":{"docs":{},"改":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{},"使":{"docs":{},"用":{"docs":{},"本":{"docs":{},"地":{"docs":{},"数":{"docs":{},"据":{"docs":{},"代":{"docs":{},"替":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{},"里":{"docs":{},"加":{"docs":{},"上":{"docs":{},"测":{"docs":{},"试":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"5":{"docs":{},"秒":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"docs":{}}}}}}}}}}}}},"头":{"docs":{},"里":{"docs":{},"加":{"docs":{},"上":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"最":{"docs":{},"后":{"docs":{},"追":{"docs":{},"加":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"签":{"docs":{},"名":{"docs":{},",":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"5":{"docs":{},"秒":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"docs":{}}}}}}}}}}}}}}}}}}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{},"都":{"docs":{},"改":{"docs":{},"成":{"4":{"0":{"4":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}},"直":{"docs":{},"接":{"docs":{},"请":{"docs":{},"求":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},":":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},",":{"docs":{},"不":{"docs":{},"再":{"docs":{},"发":{"docs":{},"起":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"其":{"docs":{},"中":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}},"相":{"docs":{},"比":{"3":{"docs":{},".":{"docs":{},"x":{"docs":{},"版":{"docs":{},"本":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"docs":{}}},"确":{"docs":{},"认":{"docs":{},"将":{"docs":{},"证":{"docs":{},"书":{"docs":{},"添":{"docs":{},"加":{"docs":{},"到":{"docs":{},"l":{"docs":{},"o":{"docs":{},"g":{"docs":{},"i":{"docs":{},"n":{"docs":{},"或":{"docs":{},"s":{"docs":{},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786}}}},"简":{"docs":{},"介":{"docs":{"./":{"ref":"./","tf":10}}},"化":{"docs":{},"了":{"docs":{},"规":{"docs":{},"则":{"docs":{},"文":{"docs":{},"件":{"docs":{},"内":{"docs":{},"的":{"docs":{},"接":{"docs":{},"口":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"管":{"docs":{},"理":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"系":{"docs":{},"统":{"docs":{},"的":{"docs":{},"全":{"docs":{},"局":{"docs":{},"代":{"docs":{},"理":{"docs":{},"配":{"docs":{},"置":{"docs":{},",":{"docs":{},"方":{"docs":{},"法":{"docs":{},"调":{"docs":{},"用":{"docs":{},"时":{"docs":{},"可":{"docs":{},"能":{"docs":{},"会":{"docs":{},"弹":{"docs":{},"出":{"docs":{},"密":{"docs":{},"码":{"docs":{},"框":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"类":{"docs":{},"似":{"docs":{},"这":{"docs":{},"种":{"docs":{},"报":{"docs":{},"错":{"docs":{},"都":{"docs":{},"是":{"docs":{},"因":{"docs":{},"为":{"docs":{},"系":{"docs":{},"统":{"docs":{},"没":{"docs":{},"有":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{},"所":{"docs":{},"造":{"docs":{},"成":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"经":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"后":{"docs":{},",":{"docs":{},"期":{"docs":{},"望":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"被":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"后":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"处":{"docs":{},"理":{"docs":{},"流":{"docs":{},"程":{"docs":{},"同":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"一":{"docs":{},"致":{"docs":{},"。":{"docs":{},"未":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"会":{"docs":{},"再":{"docs":{},"进":{"docs":{},"入":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"规":{"docs":{},"则":{"docs":{},"文":{"docs":{},"件":{"docs":{},"中":{"docs":{},",":{"docs":{},"除":{"docs":{},"了":{"docs":{},"s":{"docs":{},"u":{"docs":{},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"r":{"docs":{},"y":{"docs":{},",":{"docs":{},"都":{"docs":{},"是":{"docs":{},"由":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"内":{"docs":{},"提":{"docs":{},"供":{"docs":{},"`":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"`":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"(":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},")":{"docs":{},"全":{"docs":{},"面":{"docs":{},"支":{"docs":{},"持":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"和":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"模":{"docs":{},"块":{"docs":{},"应":{"docs":{},"该":{"docs":{},"符":{"docs":{},"合":{"docs":{},"c":{"docs":{},"m":{"docs":{},"d":{"docs":{},"规":{"docs":{},"范":{"docs":{},",":{"docs":{},"一":{"docs":{},"个":{"docs":{},"典":{"docs":{},"型":{"docs":{},"的":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"代":{"docs":{},"码":{"docs":{},"结":{"docs":{},"构":{"docs":{},"如":{"docs":{},"下":{"docs":{},"。":{"docs":{},"模":{"docs":{},"块":{"docs":{},"中":{"docs":{},"所":{"docs":{},"有":{"docs":{},"方":{"docs":{},"法":{"docs":{},"都":{"docs":{},"是":{"docs":{},"可":{"docs":{},"选":{"docs":{},"的":{"docs":{},",":{"docs":{},"只":{"docs":{},"需":{"docs":{},"实":{"docs":{},"现":{"docs":{},"业":{"docs":{},"务":{"docs":{},"感":{"docs":{},"兴":{"docs":{},"趣":{"docs":{},"的":{"docs":{},"部":{"docs":{},"分":{"docs":{},"即":{"docs":{},"可":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"的":{"docs":{},"介":{"docs":{},"绍":{"docs":{},"文":{"docs":{},"案":{"docs":{},",":{"docs":{},"用":{"docs":{},"于":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"提":{"docs":{},"示":{"docs":{},"用":{"docs":{},"户":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"能":{"docs":{},"力":{"docs":{},"范":{"docs":{},"围":{"docs":{},"包":{"docs":{},"括":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"原":{"docs":{},"理":{"docs":{},"是":{"docs":{},"中":{"docs":{},"间":{"docs":{},"人":{"docs":{},"攻":{"docs":{},"击":{"docs":{},"(":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"警":{"docs":{},"告":{"docs":{},":":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},"和":{"docs":{},"系":{"docs":{},"统":{"docs":{},"安":{"docs":{},"全":{"docs":{},"息":{"docs":{},"息":{"docs":{},"相":{"docs":{},"关":{"docs":{},",":{"docs":{},"建":{"docs":{},"议":{"docs":{},"亲":{"docs":{},"自":{"docs":{},"生":{"docs":{},"成":{"docs":{},",":{"docs":{},"并":{"docs":{},"妥":{"docs":{},"善":{"docs":{},"保":{"docs":{},"管":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"设":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"属":{"docs":{},"性":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"访":{"docs":{},"问":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}},"证":{"docs":{},"书":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"请":{"docs":{},"求":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"使":{"docs":{},"用":{"docs":{},"的":{"docs":{},"协":{"docs":{},"议":{"docs":{},",":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"或":{"docs":{},"者":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"出":{"docs":{},"错":{"docs":{},"的":{"docs":{},"事":{"docs":{},"件":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"的":{"docs":{},"原":{"docs":{},"始":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}},"目":{"docs":{},"标":{"docs":{},"的":{"docs":{},"h":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"受":{"docs":{},"制":{"docs":{},"于":{"docs":{},"协":{"docs":{},"议":{"docs":{},",":{"docs":{},"这":{"docs":{},"里":{"docs":{},"无":{"docs":{},"法":{"docs":{},"获":{"docs":{},"取":{"docs":{},"完":{"docs":{},"整":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"调":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"返":{"docs":{},"回":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{},"会":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"这":{"docs":{},"个":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"其":{"docs":{},"他":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"由":{"docs":{},"模":{"docs":{},"块":{"docs":{},"对":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},"进":{"docs":{},"行":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"运":{"docs":{},"行":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"错":{"docs":{},"误":{"docs":{},"页":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"这":{"docs":{},"里":{"docs":{},"提":{"docs":{},"供":{"docs":{},"一":{"docs":{},"些":{"docs":{},"样":{"docs":{},"例":{"docs":{},",":{"docs":{},"来":{"docs":{},"讲":{"docs":{},"解":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"的":{"docs":{},"常":{"docs":{},"见":{"docs":{},"用":{"docs":{},"法":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"通":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"请":{"docs":{},"求":{"docs":{},":":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"启":{"docs":{},"动":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"的":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"来":{"docs":{},"修":{"docs":{},"改":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"这":{"docs":{},"种":{"docs":{},"方":{"docs":{},"式":{"docs":{},"初":{"docs":{},"始":{"docs":{},"化":{"docs":{},"的":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"其":{"docs":{},"配":{"docs":{},"置":{"docs":{},"也":{"docs":{},"是":{"docs":{},"全":{"docs":{},"局":{"docs":{},"性":{"docs":{},"的":{"docs":{},",":{"docs":{},"所":{"docs":{},"有":{"docs":{},"网":{"docs":{},"站":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"问":{"docs":{},"题":{"docs":{},"都":{"docs":{},"会":{"docs":{},"被":{"docs":{},"忽":{"docs":{},"略":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"配":{"docs":{},"置":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{},"为":{"docs":{},"全":{"docs":{},"局":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{},"i":{"docs":{},"o":{"docs":{},"s":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{},"系":{"docs":{},"统":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"o":{"docs":{},"s":{"docs":{},"x":{"docs":{},"系":{"docs":{},"统":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"启":{"docs":{},"动":{"docs":{},"端":{"docs":{},"口":{"docs":{},",":{"docs":{},"如":{"1":{"0":{"8":{"0":{"docs":{},"端":{"docs":{},"口":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"附":{"docs":{},"录":{"docs":{},":":{"docs":{},"如":{"docs":{},"何":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"限":{"docs":{},"速":{"docs":{},"值":{"docs":{},",":{"docs":{},"单":{"docs":{},"位":{"docs":{},"k":{"docs":{},"b":{"docs":{},"/":{"docs":{},"s":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"不":{"docs":{},"限":{"docs":{},"速":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"除":{"docs":{},"了":{"docs":{},"上":{"docs":{},"述":{"docs":{},"证":{"docs":{},"书":{"docs":{},"安":{"docs":{},"装":{"docs":{},"过":{"docs":{},"程":{"docs":{},",":{"docs":{},"还":{"docs":{},"需":{"docs":{},"要":{"docs":{},"在":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"需":{"docs":{},"要":{"docs":{},"编":{"docs":{},"写":{"docs":{},"一":{"docs":{},"个":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},",":{"docs":{},"在":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"驱":{"docs":{},"动":{"docs":{},"的":{"docs":{},",":{"docs":{},"函":{"docs":{},"数":{"docs":{},"需":{"docs":{},"要":{"docs":{},"满":{"docs":{},"足":{"docs":{},"y":{"docs":{},"i":{"docs":{},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"。":{"docs":{},"可":{"docs":{},"以":{"docs":{},"使":{"docs":{},"用":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"方":{"docs":{},"法":{"docs":{},"或":{"docs":{},"是":{"docs":{},"返":{"docs":{},"回":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"或":{"docs":{},"使":{"docs":{},"用":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"函":{"docs":{},"数":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},",":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"界":{"docs":{},"面":{"docs":{},"上":{"docs":{},"能":{"docs":{},"看":{"docs":{},"到":{"docs":{},"所":{"docs":{},"有":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"界":{"docs":{},"面":{"docs":{},"上":{"docs":{},"能":{"docs":{},"看":{"docs":{},"到":{"docs":{},"刚":{"docs":{},"才":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"[":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"t":{"docs":{},"c":{"docs":{},"h":{"docs":{},"y":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"j":{"docs":{},"a":{"docs":{},"v":{"docs":{},"a":{"docs":{},"s":{"docs":{},"c":{"docs":{},"r":{"docs":{},"i":{"docs":{},"p":{"docs":{},"t":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"length":3960},"corpusTokens":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#","#gener","#global","#launch","#local","#全局包","#启动anyproxy,并解析所有https请求","#本地包","#生成rootca证书,生成后需要手动信任","&&","'","'...'","'...',","'/',","'/index.html';","'/redirect/to/another/path';","'/user","'127.0.0.1'","'8001');","'8008';","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'hello","'http',","'http';","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'text/html';","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(){","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","*true*","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","//这里也可以同时加上requestopt","/^win/.test(process.platform);","0)","1.","10.3","10.3信任ca证书","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","127.0.0.1:8001/index.html","127.0.0.1:8001,访问","1,write","1,编写规则","2,","2.","200,","3,","3.x:","300);","4,","4.0","4.0的主要变化:","4.0,欢迎提供反馈","4.x","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>",">=",">关于本机",">证书信任设置",">通用","[switchyomega]","_re","_req","_req:","_res:","`","`;","a:","abil","abliti","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent';","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxy@beta","anyproxyin","anyproxyins.start();","anyproxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包","anyproxy可以作为一个npm模块使用,整合进其他工具。","anyproxy向客户端发送请求前,会调用beforesendresponse,并带上参数requestdetail","anyproxy向服务端发送请求前,会调用beforesendrequest,并带上参数requestdetail","anyproxy在与目标https服务器建立连接的过程中,如果发生错误,anyproxy会调用这个方法","anyproxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。","anyproxy收到https请求时,会调用beforedealhttpsrequest,并带上参数requestdetail","anyproxy是一个开放式的http代理服务器。","anyproxy调用规则模块beforesendrequest方法,由模块做处理,返回新的请求参数,或返回响应内容","anyproxy默认不对https请求做处理,如需看到明文信息,需要配置ca证书","apg","append","appendix:how","at',","attack","attack.","attack),需要客户端提前信任anyproxy生成的ca","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","beta","bodi","body,","body:","body三个字段","body等","body),甚至是请求的目标地址等","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","ca方法。","ca,按提示扫描二维码即可安装","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code)、响应头(respons","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","design","detect","determin","directli","disabl","dn","doc","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","english","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","features:","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","fulli","function","function()","function.","g","gener","get","github:","github主页:https://github.com/alibaba/anyproxy/tree/4.x","give","global","go,","gui","guid","h","hacked!","hacked...\")","handl","handling.","happen","header","header),请求体(request","header,","header:","headers:","headersar","header)、响应内容等","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http/http","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/ani","http://httpbin.org/post","http://httpbin.org/us","http://localhost:8008/index.html","httpbin.org","httpbin.org,","https://github.com/alibaba/anyproxy/tree/4.x","https://httpbin.org/us","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","https连接服务器出错","http代理配置","http的代理模式中,这里的request是connect请求","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","intercept`参数,按npm模块启动时配置`forceproxyhttps`参数,所有https请求都会被替换证书并解析","interest","interfac","interface,","introduct","invok","io","ios/android","ios系统信任ca证书","issu","iswin","it,","it.","javascript.","js","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","middle),用户必须信任anyproxy生成的ca证书,才能进行后续流程","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.hostnam","newrequestoptions.method","newrequestoptions.path","newrequestoptions.port","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['cont","newresponse.header['x","newresponse.statuscod","node.j","nodej","nodejs.","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","offer","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","osx系统信任ca证书","otherwire,","otherwise,","over","own.","page","param","part","pass","password","path","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.protocol","requestdetail.requestoptions);","requestdetail.requestoptions;","requestdetail;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","rule接口文档","rule样例","rule模块","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(","settimeout(()","settings.","side","signed.","silent","silent:","simpl","simplifi","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","string","stuff:","sudo","summari","summary():","summary,","summary:","summary:str","support","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","true;","true或者false,表示是否需要anyproxy替换证书并解析http","trust","trust)","type':","type']","type:","typic","ubuntu","ui,","ui中的","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","web版界面端口号,默认8002","web版界面配置","web版界面重构","whether","wifi","window","window.alert(\"sorry,","windows系统信任ca证书","without","work","world'","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","|","}","})","});","},","};","不做任何处理。此时anyproxy会返回一个默认的错误页。","不做任何处理,返回null","中把anyproxy证书的开关打开,否则safari将报错。","中文文档","为","为什么https请求不能进入处理函数?","主要特性包括:","举例","举例,请求","举例:请求","代理http","代理服务器发生错误","代理服务器启动完成","代理服务器都在wifi设置中配置","以chrome的switchyomega插件为例","以下任意一项都能用来改变https的处理特性:","以下几种返回都是合法的","作为npm模块使用","作为全局模块","你可以通过","使用npm包","使用举例","使用在线地址","使用本地数据","使用本地路径anyproxi","使用示例","修改发送到","修改请求bodi","修改请求协议","修改请求协议,如强制改用https发起请求","修改请求参数","修改请求头","修改请求数据","修改请求的目标地址","修改返回内容并延迟","修改返回头","修改返回状态码","修改返回的内容","修改返回的状态码","关闭代理服务器","关闭全局代理服务器","其他命令","创建代理服务器","即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback","原始的服务端返回对象","双击打开rootca.crt","发送响应前处理","发送请求前拦截处理","只有返回true时,anyproxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。","可以是一个函数,也可以是一个普通的字符串","可修改内容包括请求头(request","可修改的内容包括http状态码(statu","同beforedealhttpsrequest中的参数","同beforesendrequest中的参数","启动","启动anyproxy,加载规则","启动代理服务器","启动后将终端http代理服务器配置为127.0.0.1:8001即可","命令行启动anyproxy时配置`","命令行启动anyproxy,默认端口号8001","命令行直接启动","在","在nodejs代码中启动","在wifi高级设置中,配置http代理即可","在构造anyproxy实例的时候,传入参数dangerouslyignoreunauthorized:true,","在请求处理过程中发生错误时,anyproxy会调用onerror方法,并提供对应的错误信息","基于node.js,开放二次开发能力,允许自定义请求处理逻辑","处理流程","处理流程图如下","多数场景下,错误会在请求目标服务器的时候发生,比如dns解析失败、请求超时等","如下几种方案都可以用来引用规则模块:","如下:","如何引用","如果beforesendrequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。","如果配置了全局解析https的参数,则anyproxy会略过这个调用","如要启用https解析,请在代理服务器启动前自行调用anyproxy.utils.certmgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxi","安装","安装ca:","对于debian或者ubuntu系统,在安装anyproxy之前,可能还需要安装","开发示例","引入","当http请求经过代理服务器时,具体处理过程是:","当代理服务器收到https请求时,anyproxy可以替换证书,对请求做明文解析。","当访问特定的https站点,anyproxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问:","必选,代理服务器端口","快速开始","我们自然也可以借助自定义的rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。","所有http://httpbin.org","找到刚刚导入的anyproxy证书,配置为信任(alway","把","把响应信息返回给客户端","把所有发送到","把用http协议请求的","拦截https请求,对内容做修改","拦截发送到","拦截并修改服务端响应","拦截并修改正在发送的请求","提供gui界面,用以观察请求","提示","支持https的解析","收集请求所有请求参数,包括method,","改成https并发送","时,requestdetail参数内容大致如下","时,responsedetail参数内容大致如下","是否启用web版界面,默认fals","是否处理https请求","是否屏蔽所有console输出,默认fals","是否强制拦截所有的https,忽略规则模块的返回,默认fals","是否忽略请求中的证书错误,默认fals","是必选字段","服务端的返回信息,包括statuscod","本文档的适用范围是anyproxi","本质是中间人攻击(man","来加载模块并体验","来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。","查看请求信息","校验系统内是否存在anyproxy的根证书","样例","根据请求参数,向服务端发出请求,接收服务端响应。","模块介绍","此处无法控制向客户端的返回信息,无需返回值。","注意:http","注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题","测试规则","浏览器访问http://127.0.0.1:8002","点击web","然后,安装anyproxi","生成anyproxy的rootca,完成后请引导用户信任.crt文件","生成证书并解析所有https请求","用curl发请求测试的方法如下","用curl测试","用浏览器测试:配置浏览器http代理为","的https请求会被解析","的post数据","的user","的请求全部改到","的请求,使用本地数据代替服务端返回","的返回值里加上测试信息,并延迟5秒返回","的返回头里加上","的返回最后追加anyproxy的签名,并延迟5秒","的返回状态码都改成404","直接请求服务器:curl","直接返回客户端,不再发起请求,其中statuscod","相比3.x版本,anyproxi","确认将证书添加到login或system","示例","简介","简化了规则文件内的接口","管理anyproxy的证书","管理系统的全局代理配置,方法调用时可能会弹出密码框","类似这种报错都是因为系统没有信任anyproxy生成的ca所造成的","经过代理服务器后,期望的返回如下","自定义规则模块","被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。","规则文件中,除了summary,都是由","规则文件内提供`beforedealhttpsrequest`方法,返回","规则文件(rule)全面支持promise和gener","规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。","规则模块是用","规则模块的介绍文案,用于anyproxy提示用户,","规则模块的能力范围包括:","解析https请求的原理是中间人攻击(man","警告:ca证书和系统安全息息相关,建议亲自生成,并妥善保管","设置","设置属性","访问http://127.0.0.1:8002","证书配置","请求bodi","请求url","请求使用的协议,http或者http","请求出错的事件","请求的原始request","请求目标的host,受制于协议,这里无法获取完整url","调用规则模块beforedealhttpsrequest方法,如果返回true,会明文解析这个请求,其他请求不处理","调用规则模块beforesendresponse方法,由模块对响应内容进行处理","运行","返回值","返回自定义错误页","这里提供一些样例,来讲解规则模块的常见用法","通过代理服务器请求:curl","通过启动参数","通过自定义的rule来修改","通过这种方式初始化的anyproxy,其配置也是全局性的,所有网站的证书问题都会被忽略","配置127.0.0.1:8001为全局http代理服务器","配置ios/android系统代理","配置osx系统代理","配置启动端口,如1080端口启动","配置浏览器http代理","附录:如何信任ca证书","限速值,单位kb/s,默认不限速","除了上述证书安装过程,还需要在","需要编写一个规则模块,在","驱动的,函数需要满足yieldable。可以使用generator方法或是返回promise。","驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。",",web界面上能看到所有的请求信息",",界面上能看到刚才的请求信息",":"],"pipeline":["stopWordFilter","stemmer"]},"store":{"./":{"url":"./","title":"Introduction","keywords":"","body":"AnyProxy\nAnyProxy is a fully configurable http/https proxy in NodeJS.\nRef: 中文文档\nGithub:\n\nhttps://github.com/alibaba/anyproxy/tree/4.x\n\nFeatures:\n\nOffer you the ablity to handle http traffic by invoking a js module\nIntercept https\nGUI webinterface\n\nChange Logs since 3.x:\n\nSupport Promise and Generator in rule module\nSimplified interface in rule module\nA newly designed web interface\n\n\nGetting Start\ninstall\nTo Debian and Ubuntu users, you may need to install nodejs-legacy at the same time\nsudo apg-get install nodejs-legacy\n\nThen install the AnyProxy\nnpm install -g anyproxy\n\nlaunch\n\nstart AnyProxy in command line, with default port 8001\n\nanyproxy\n\n\nnow you can use http proxy server by 127.0.0.1:8001\nvisit http://127.0.0.1:8002 to see the http requests\n\noptions\n\nspecify the port of http proxy\n\nanyproxy --port 1080\n\nUse AnyProxy as an npm module\nAnyProxy can be used as an npm module\n\nTo enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.\n\n\ninstall\n\nnpm i anyproxy --save\n\n\nsample\n\nconst AnyProxy = require('anyproxy');\nconst options = {\n port: 8001,\n rule: require('myRuleModule'),\n webInterface: {\n enable: true,\n webPort: 8002,\n wsPort: 8003,\n },\n throttle: 10000,\n forceProxyHttps: false,\n silent: false\n};\nconst proxyServer = new AnyProxy.ProxyServer(options);\n\nproxyServer.on('ready', () => { /* */ });\nproxyServer.on('error', (e) => { /* */ });\nproxyServer.start();\n\n//when finished\nproxyServer.close();\n\n\nClass: AnyProxy.proxyServer\n\ncreate a proxy server\nconst proxy = new AnyProxy.proxyServer(options)\n\n\noptions\n\nport {number} required, port number of proxy server\nrule {object} your rule module\nthrottle {number} throttle in kb/s, unlimited for default\nforceProxyHttps {boolean} in force intercept all https request, false for default\nsilent {boolean} if keep silent in console, false for defaultfalse\ndangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default\nwebInterface {object} config for web interface\nenable {boolean} if enable web interface, false for default\nwebPort {number} port number for web interface\n\n\n\n\nEvent: ready\n\nemit when proxy server is ready\nsample\n\nproxy.on('ready', function() { })\n\n\nEvent: error\n\nemit when error happened inside proxy server\nsample\n\nproxy.on('error', function() { })\n\n\nMethod: start\n\nstart proxy server\nsample\n\nproxy.start();\n\n\nMethod: close\n\nclose proxy server\nsample\n\nproxy.close();\n\n\n\n\nAnyProxy.utils.systemProxyMgr\n\nmanage the system proxy config. sudo password may be required\nsample\n\n// set 127.0.0.1:8001 as system http server\nAnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');\n\n// disable global proxy server\nAnyProxy.utils.systemProxyMgr.disableGlobalProxy();\n\n\nAnyProxy.utils.certMgr\n\nManage certificates of AnyProxy\nAnyProxy.utils.certMgr.ifRootCAFileExists()\ndetect if AnyProx rootCA exists\n\n\nAnyProxy.utils.certMgr.generateRootCA(callback)\ngenerate a rootCA\n\n\nSample\n\n const AnyProxy = require('AnyProxy');\n const exec = require('child_process').exec;\n\n if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {\n AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {\n // let users to trust this CA before using proxy\n if (!error) {\n const certDir = require('path').dirname(keyPath);\n console.log('The cert is generated at', certDir);\n const isWin = /^win/.test(process.platform);\n if (isWin) {\n exec('start .', { cwd: certDir });\n } else {\n exec('open .', { cwd: certDir });\n }\n } else {\n console.error('error when generating rootCA', error);\n }\n });\n }\n\n\n\nProxy Https\n\nAnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.\n\n\nUnder the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.\n\n\ngenerate certifycates and intercept\n\nanyproxy-ca #generate root CA. manually trust it after that.\nanyproxy --intercept #launch anyproxy and intercept all https traffic\n\n\nAppendix:how to trust CA\n\nRule Introduction\nAnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.\n\nMake sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.\n\nRule module could do the following stuff:\n\nintercept and modify the request which is being sent\neditable fields include request header, body, target address\n\n\nintercept and modify the response from server\neditable fields include response status code, header, body\n\n\nintercept https requests, modify request and response\n\nsample\n\nTarget\n\nwrite a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds\n\n\nStep 1,Write the rule file, save as sample.js\n// file: sample.js\nmodule.exports = {\n summary: 'a rule to modify response',\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n\n newResponse.body += '-- AnyProxy Hacked! --';\n\n newResponse.body += `\n setTimeout(\n function (){\n window.alert(\"Sorry, You Are Hacked...\")\n }, 300);\n `;\n newResponse.header['Content-Type'] = 'text/html';\n\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n\nStep 2, start AnyProxy and load the rule file\n\nrun anyproxy --rule sample.js\n\n\nStep 3, test\n\nuse curl\ncurl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n\n\nuse browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent\n\nthe expected response from proxy is\n\n\n{\n \"user-agent\": \"curl/7.43.0\"\n}\n- AnyProxy Hacked!\n\nStep 4, view the request log\n\nvisit http://127.0.0.1:8002, the request just sent should be listed here\n\n\n\nhow does it work\n\nThe flow chart is as follows\n\n\n\nWhen got an http request, the entire process of proxy server is\n\nAnyProxy collects all the quest info, include method, header, body\nAnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content\nIf beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.\nSend request to target server, collect response\nCall beforeSendResponse of the rule module. Rule module deal the response data\nSend response to client\n\n\nWhen AnyProxy get https request, it could replace the certificate and decrypt the request data\n\nAnyProxy calls beforeDealHttpsRequest of the rule module\nIf the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.\n\n\n\nhow to load rule module\n\nuse local file\nanyproxy --rule ./rule.js\n\n\nuse an online rule file\nanyproxy --rule https://sample.com/rule.js\n\n\nuse an npm module\n\nAnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.\n\nanyproxy --rule ./myRulePkg/ #local module\nnpm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module\n\n\n\nRule module interface\nA typical rule module is as follows. All the functions are optional, just write the part you are interested in.\nmodule.exports = {\n // introduction\n summary: 'my customized rule for AnyProxy',\n // intercept before send request to server\n *beforeSendRequest(requestDetail) { /* ... */ },\n // deal response before send to client\n *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },\n // if deal https request\n *beforeDealHttpsRequest(requestDetail) { /* ... */ },\n // error happened when dealing requests\n *onError(requestDetail, error) { /* ... */ },\n // error happened when connect to https server\n *onConnectError(requestDetail, error) { /* ... */ }\n};\n\n\nAll functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.\n\nsummary\nsummary\n\nIntroduction of this rule file. AnyProxy will read this field and give some tip to user.\n\nbeforeSendRequest\nbeforeSendRequest(requestDetail)\n\nBefore sending request to server, AnyProxy will call beforeSendRequest with param requestDetail\nrequestDetail\nprotocol {string} the protocol to use, http or https\nrequestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback\nrequestData {object} request body\nurl {string} request url\n_req {object} the native node.js request object\n\n\ne.g. When requesting anyproxy.io, requestDetail is something like the following\n{\n protocol: 'http',\n url: 'http://anyproxy.io/',\n requestOptions: {\n hostname: 'anyproxy.io',\n port: 80,\n path: '/',\n method: 'GET',\n headers: {\n Host: 'anyproxy.io',\n 'Proxy-Connection': 'keep-alive',\n 'User-Agent': '...'\n }\n },\n requestData: '...',\n _req: { /* ... */}\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the request protocol,i.e. force use https\n\nreturn {\n protocol: 'https'\n};\n\n\nmodify request param\n\nvar newOption = Object.assign({}, requestDetail.requestOptions);\nnewOption.path = '/redirect/to/another/path';\nreturn {\n requestOptions: newOption\n};\n\n\nmodify request body\n\nreturn {\n requestData: 'my new request data'\n // requestOptions can also be used here\n};\n\n\ngive response to the client, not sending request any longer. statusCode headersare required is this situation.\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nbeforeSendResponse\nbeforeSendResponse(requestDetail, responseDetail)\n\nBefore sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail\nrequestDetail is the same param as in beforeSendRequest\nresponseDetail\nresponse {object} the response from server, includes statusCode header body\n_res {object} the native node.js response object\n\n\ne.g. When requesting anyproxy.io, responseDetail is something like the following\n{\n response: {\n statusCode: 200,\n header: {\n 'Content-Type': 'image/gif',\n Connection: 'close',\n 'Cache-Control': '...'\n },\n body: '...'\n },\n _res: { /* ... */ }\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the response status code\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.statusCode = 404;\nreturn {\n response: newResponse\n};\n\n\nmodify the response content\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.body += '--from anyproxy--';\nreturn {\n response: newResponse\n};\n\n\n\nbeforeDealHttpsRequest\nbeforeDealHttpsRequest(requestDetail)\n\nWhen receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail\nIf configed with forceProxyHttps in launching, AnyProxy will skip calling this method\nOnly by returning true, AnyProxy will try to replace the certificate and intercept the https request.\nrequestDetail\nhost {string} the target host to request. Due to the request protocol, full url couldn't be got here\n_req {object} the native node.js request object. The _req here refers to the CONNECT request.\n\n\nreturn value\ntrue or false, whether AnyProxy should intercept the https request\n\n\n\nonError\nonError(requestDetail, error)\n\nAnyProxy will call this method when an error happened in request handling.\nErrors usually are issued during requesting, e.g. DNS failure, request timeout\nrequestDetail is the same one as in beforeSendRequest\nAny of these return values are valid\n\ndo nothing, and AnyProxy will response a default error page\n\nreturn null;\n\n\nreturn a customized error page\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nonConnectError\nonConnectError(requestDetail, error)\n\nAnyProxy will call this method when failed to connect target server in https request\nrequestDetail is the same one as in beforeDealHttpsRequest\nno return value is required\n\nRule Samples\n\nhere are some samples about frequently used rule file\ntry these samples by anyproxy --rule http://....js\nhow to test with curl:\nrequest the server directly curl http://httpbin.org/\nrequest the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001\n\n\n\nuse local response\n\nintercept the request towards http://httpbin.org , return the local-defined response\n\nanyproxy --rule rule_sample/sample_use_local_response.js\n\n/* \n sample: \n intercept all requests toward httpbin.org, use a local response\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n const localResponse = {\n statusCode: 200,\n header: { 'Content-Type': 'application/json' },\n body: '{\"hello\": \"this is local response\"}'\n };\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n return {\n response: localResponse\n };\n }\n },\n};\n\nmodify request header\n\nmodify the user-agent sent to httpbin.org\n\nanyproxy --rule rule_sample/sample_modify_request_header.js\n\n/* \n sample: \n modify the user-agent in requests toward httpbin.org\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request body\n\nmodify the post body of http://httpbin.org/post\n\nanyproxy --rule rule_sample/sample_modify_request_data.js\n\n/*\n sample:\n modify the post data towards http://httpbin.org/post\n test:\n curl -H \"Content-Type: text/plain\" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001\n expected response:\n { \"data\": \"i-am-anyproxy-modified-post-data\" }\n*/\nmodule.exports = {\n summary: 'Rule to modify request data',\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {\n return {\n requestData: 'i-am-anyproxy-modified-post-data'\n };\n }\n },\n};\n\nmodify the request target\n\nsend all the request towards http://httpbin.org/ to http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_path.js\n\n/*\n sample:\n redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html\n test:\n curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n expected response:\n 'hello world' from 127.0.0.1:8001/index.html\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n requestDetail.protocol = 'http';\n newRequestOptions.hostname = '127.0.0.1'\n newRequestOptions.port = '8008';\n newRequestOptions.path = '/index.html';\n newRequestOptions.method = 'GET';\n return requestDetail;\n }\n },\n *beforeDealHttpsRequest(requestDetail) {\n return true;\n }\n};\n\nmodify request protocol\n\nmodify the http request towards http://httpbin.org to https\n\nanyproxy --rule rule_sample/sample_modify_request_protocol.js\n\n/* \n sample: \n redirect all http requests of httpbin.org to https\n test:\n curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001\n expected response:\n { \"X-Forwarded-Protocol\": \"https\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newOption = requestDetail.requestOptions;\n newOption.port = 443;\n return {\n protocol: 'https',\n requestOptions: newOption\n };\n }\n }\n};\n\nmodify response status code\n\nmodify all status code from http://httpbin.org to 404\n\nanyproxy --rule rule_sample/sample_modify_response_statuscode.js\n\n/* \n sample: \n modify all status code of http://httpbin.org/ to 404\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n HTTP/1.1 404 Not Found\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newResponse = responseDetail.response;\n newResponse.statusCode = 404;\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify the response header\n\nadd X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_response_header.js\n\n/* \n sample: \n modify response header of http://httpbin.org/user-agent\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n X-Proxy-By: AnyProxy\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {\n const newResponse = responseDetail.response;\n newResponse.header['X-Proxy-By'] = 'AnyProxy';\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify response data and delay\n\nappend some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds.\n\nanyproxy --rule rule_sample/sample_modify_response_data.js\n\n/* \n sample: \n modify response data of http://httpbin.org/user-agent\n test:\n curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" } -- AnyProxy Hacked! --\n*/\n\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay the response for 5s\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\nConfig Certification\nConfig root CA in OSX\n\nthis kind of errors is usually caused by untrusted root CA\n\n\n\nWarning: please keep your root CA safe since it may influence your system security.\n\ninstall :\n\ndouble click rootCA.crt\n\nadd cert into login or system\n\n\n\n\nfind the newly imported AnyProxy certificates, configured as Always Trust\n\n\nConfig root CA in windows\n\nConfig OSX system proxy\n\nthe config is in wifi - advanced\n\n\nconfig http proxy server\n\ntake Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例\n\n\ntrust root CA in iOS\n\nClick Root CA in web ui, and follow the instruction to install\n\n\ntrust root CA in iOS after 10.3\n\nBesides installing root CA, you have to \"turn on\" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.\n\n\nconfig iOS/Android proxy server\n\nproxy settings are placed in wifi setting\n\niOS\n\n\n\n\nAndroid\n\n\nFAQ\nQ: can not deal https request in rule module.\n\nA: Any of these options could be used to change the way AnyProxy deall https requests\nconfig --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module\nplace a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.\n\n\n\nQ: get an error says function is not yieldable\n\nA: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.\n\nQ: The connection is not private\nAnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.\n\nIf you run AnyProxy by command line\nPass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.\nanyproxy -i --ignore-unauthorized-ssl\n\n\nIf you run AnyProxy by Nodejs\nPass in the option dangerouslyIgnoreUnauthorized:true, like this:\nconst options = {\n ...,\n dangerouslyIgnoreUnauthorized: true\n};\n\nconst anyproxyIns = new AnyProxy.ProxyCore(options);\nanyproxyIns.start();\n\nThis is also a global option, all certification errors will be ignored\n\nWith the help of AnyProxy Rule\nYou can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n // set rejectUnauthorized as false\n newRequestOptions.rejectUnauthorized = false;\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nAnd we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!\n\n\n"},"doc.html":{"url":"doc.html","title":"Getting-Start","keywords":"","body":"Getting Start\ninstall\nTo Debian and Ubuntu users, you may need to install nodejs-legacy at the same time\nsudo apg-get install nodejs-legacy\n\nThen install the AnyProxy\nnpm install -g anyproxy@beta # 4.x is in beta now\n\nlaunch\n\nstart AnyProxy in command line, with default port 8001\n\nanyproxy\n\n\nnow you can use http proxy server by 127.0.0.1:8001\nvisit http://127.0.0.1:8002 to see the http requests\n\noptions\n\nspecify the port of http proxy\n\nanyproxy --port 1080\n\nUse AnyProxy as an npm module\nAnyProxy can be used as an npm module\n\nTo enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.\n\n\ninstall\n\nnpm i anyproxy@beta --save # 4.0 is in beta now\n\n\nsample\n\nconst AnyProxy = require('anyproxy');\nconst options = {\n port: 8001,\n rule: require('myRuleModule'),\n webInterface: {\n enable: true,\n webPort: 8002,\n wsPort: 8003,\n },\n throttle: 10000,\n forceProxyHttps: false,\n silent: false\n};\nconst proxyServer = new AnyProxy.ProxyServer(options);\n\nproxyServer.on('ready', () => { /* */ });\nproxyServer.on('error', (e) => { /* */ });\nproxyServer.start();\n\n//when finished\nproxyServer.close();\n\n\nClass: AnyProxy.proxyServer\n\ncreate a proxy server\nconst proxy = new AnyProxy.proxyServer(options)\n\n\noptions\n\nport {number} required, port number of proxy server\nrule {object} your rule module\nthrottle {number} throttle in kb/s, unlimited for default\nforceProxyHttps {boolean} in force intercept all https request, false for default\nsilent {boolean} if keep silent in console, false for defaultfalse\ndangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default\nwebInterface {object} config for web interface\nenable {boolean} if enable web interface, false for default\nwebPort {number} port number for web interface\n\n\n\n\nEvent: ready\n\nemit when proxy server is ready\nsample\n\nproxy.on('ready', function() { })\n\n\nEvent: error\n\nemit when error happened inside proxy server\nsample\n\nproxy.on('error', function() { })\n\n\nMethod: start\n\nstart proxy server\nsample\n\nproxy.start();\n\n\nMethod: close\n\nclose proxy server\nsample\n\nproxy.close();\n\n\n\n\nAnyProxy.utils.systemProxyMgr\n\nmanage the system proxy config. sudo password may be required\nsample\n\n// set 127.0.0.1:8001 as system http server\nAnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');\n\n// disable global proxy server\nAnyProxy.utils.systemProxyMgr.disableGlobalProxy();\n\n\nAnyProxy.utils.certMgr\n\nManage certificates of AnyProxy\nAnyProxy.utils.certMgr.ifRootCAFileExists()\ndetect if AnyProx rootCA exists\n\n\nAnyProxy.utils.certMgr.generateRootCA(callback)\ngenerate a rootCA\n\n\nSample\n\n const AnyProxy = require('AnyProxy');\n const exec = require('child_process').exec;\n\n if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {\n AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {\n // let users to trust this CA before using proxy\n if (!error) {\n const certDir = require('path').dirname(keyPath);\n console.log('The cert is generated at', certDir);\n const isWin = /^win/.test(process.platform);\n if (isWin) {\n exec('start .', { cwd: certDir });\n } else {\n exec('open .', { cwd: certDir });\n }\n } else {\n console.error('error when generating rootCA', error);\n }\n });\n }\n\n\n\nProxy Https\n\nAnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.\n\n\nUnder the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.\n\n\ngenerate certifycates and intercept\n\nanyproxy-ca #generate root CA. manually trust it after that.\nanyproxy --intercept #launch anyproxy and intercept all https traffic\n\n\nAppendix:how to trust CA\n\nRule Introduction\nAnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.\n\nMake sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.\n\nRule module could do the following stuff:\n\nintercept and modify the request which is being sent\neditable fields include request header, body, target address\n\n\nintercept and modify the response from server\neditable fields include response status code, header, body\n\n\nintercept https requests, modify request and response\n\nsample\n\nTarget\n\nwrite a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds\n\n\nStep 1,Write the rule file, save as sample.js\n// file: sample.js\nmodule.exports = {\n summary: 'a rule to modify response',\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n\nStep 2, start AnyProxy and load the rule file\n\nrun anyproxy --rule sample.js\n\n\nStep 3, test\n\nuse curl\ncurl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n\n\nuse browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent\n\nthe expected response from proxy is\n\n\n{\n \"user-agent\": \"curl/7.43.0\"\n}\n- AnyProxy Hacked!\n\nStep 4, view the request log\n\nvisit http://127.0.0.1:8002, the request just sent should be listed here\n\n\n\nhow does it work\n\nThe flow chart is as follows\n\n\n\nWhen got an http request, the entire process of proxy server is\n\nAnyProxy collects all the quest info, include method, header, body\nAnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content\nIf beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.\nSend request to target server, collect response\nCall beforeSendResponse of the rule module. Rule module deal the response data\nSend response to client\n\n\nWhen AnyProxy get https request, it could replace the certificate and decrypt the request data\n\nAnyProxy calls beforeDealHttpsRequest of the rule module\nIf the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.\n\n\n\nhow to load rule module\n\nuse local file\nanyproxy --rule ./rule.js\n\n\nuse an online rule file\nanyproxy --rule https://sample.com/rule.js\n\n\nuse an npm module\n\nAnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.\n\nanyproxy --rule ./myRulePkg/ #local module\nnpm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module\n\n\n\nRule module interface\nA typical rule module is as follows. All the functions are optional, just write the part you are interested in.\nmodule.exports = {\n // introduction\n summary: 'my customized rule for AnyProxy',\n // intercept before send request to server\n *beforeSendRequest(requestDetail) { /* ... */ },\n // deal response before send to client\n *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },\n // if deal https request\n *beforeDealHttpsRequest(requestDetail) { /* ... */ },\n // error happened when dealing requests\n *onError(requestDetail, error) { /* ... */ },\n // error happened when connect to https server\n *onConnectError(requestDetail, error) { /* ... */ }\n};\n\n\nAll functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.\n\nsummary\nsummary\n\nIntroduction of this rule file. AnyProxy will read this field and give some tip to user.\n\nbeforeSendRequest\nbeforeSendRequest(requestDetail)\n\nBefore sending request to server, AnyProxy will call beforeSendRequest with param requestDetail\nrequestDetail\nprotocol {string} the protocol to use, http or https\nrequestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback\nrequestData {object} request body\nurl {string} request url\n_req {object} the native node.js request object\n\n\ne.g. When requesting anyproxy.io, requestDetail is something like the following\n{\n protocol: 'http',\n url: 'http://anyproxy.io/',\n requestOptions: {\n hostname: 'anyproxy.io',\n port: 80,\n path: '/',\n method: 'GET',\n headers: {\n Host: 'anyproxy.io',\n 'Proxy-Connection': 'keep-alive',\n 'User-Agent': '...'\n }\n },\n requestData: '...',\n _req: { /* ... */}\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the request protocol,i.e. force use https\n\nreturn {\n protocol: 'https'\n};\n\n\nmodify request param\n\nvar newOption = Object.assign({}, requestDetail.requestOptions);\nnewOption.path = '/redirect/to/another/path';\nreturn {\n requestOptions: newOption\n};\n\n\nmodify request body\n\nreturn {\n requestData: 'my new request data'\n // requestOptions can also be used here\n};\n\n\ngive response to the client, not sending request any longer. statusCode headersare required is this situation.\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nbeforeSendResponse\nbeforeSendResponse(requestDetail, responseDetail)\n\nBefore sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail\nrequestDetail is the same param as in beforeSendRequest\nresponseDetail\nresponse {object} the response from server, includes statusCode header body\n_res {object} the native node.js response object\n\n\ne.g. When requesting anyproxy.io, responseDetail is something like the following\n{\n response: {\n statusCode: 200,\n header: {\n 'Content-Type': 'image/gif',\n Connection: 'close',\n 'Cache-Control': '...'\n },\n body: '...'\n },\n _res: { /* ... */ }\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the response status code\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.statusCode = 404;\nreturn {\n response: newResponse\n};\n\n\nmodify the response content\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.body += '--from anyproxy--';\nreturn {\n response: newResponse\n};\n\n\n\nbeforeDealHttpsRequest\nbeforeDealHttpsRequest(requestDetail)\n\nWhen receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail\nIf configed with forceProxyHttps in launching, AnyProxy will skip calling this method\nOnly by returning true, AnyProxy will try to replace the certificate and intercept the https request.\nrequestDetail\nhost {string} the target host to request. Due to the request protocol, full url couldn't be got here\n_req {object} the native node.js request object. The _req here refers to the CONNECT request.\n\n\nreturn value\ntrue or false, whether AnyProxy should intercept the https request\n\n\n\nonError\nonError(requestDetail, error)\n\nAnyProxy will call this method when an error happened in request handling.\nErrors usually are issued during requesting, e.g. DNS failure, request timeout\nrequestDetail is the same one as in beforeSendRequest\nAny of these return values are valid\n\ndo nothing, and AnyProxy will response a default error page\n\nreturn null;\n\n\nreturn a customized error page\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nonConnectError\nonConnectError(requestDetail, error)\n\nAnyProxy will call this method when failed to connect target server in https request\nrequestDetail is the same one as in beforeDealHttpsRequest\nno return value is required\n\nRule Samples\n\nhere are some samples about frequently used rule file\ntry these samples by anyproxy --rule http://....js\nhow to test with curl:\nrequest the server directly curl http://httpbin.org/\nrequest the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001\n\n\n\nuse local response\n\nintercept the request towards http://httpbin.org , return the local-defined response\n\nanyproxy --rule rule_sample/sample_use_local_response.js\n\n/* \n sample: \n intercept all requests toward httpbin.org, use a local response\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n const localResponse = {\n statusCode: 200,\n header: { 'Content-Type': 'application/json' },\n body: '{\"hello\": \"this is local response\"}'\n };\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n return {\n response: localResponse\n };\n }\n },\n};\n\nmodify request header\n\nmodify the user-agent sent to httpbin.org\n\nanyproxy --rule rule_sample/sample_modify_request_header.js\n\n/* \n sample: \n modify the user-agent in requests toward httpbin.org\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request body\n\nmodify the post body of http://httpbin.org/post\n\nanyproxy --rule rule_sample/sample_modify_request_data.js\n\n/*\n sample:\n modify the post data towards http://httpbin.org/post\n test:\n curl -H \"Content-Type: text/plain\" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001\n expected response:\n { \"data\": \"i-am-anyproxy-modified-post-data\" }\n*/\nmodule.exports = {\n summary: 'Rule to modify request data',\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {\n return {\n requestData: 'i-am-anyproxy-modified-post-data'\n };\n }\n },\n};\n\nmodify the request target\n\nsend all the request towards http://httpbin.org/ to http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_path.js\n\n/* \n sample: \n redirect all httpbin.org requests to http://httpbin.org/user-agent\n test:\n curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.path = '/user-agent';\n newRequestOptions.method = 'GET';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request protocol\n\nmodify the http request towards http://httpbin.org to https\n\nanyproxy --rule rule_sample/sample_modify_request_protocol.js\n\n/* \n sample: \n redirect all http requests of httpbin.org to https\n test:\n curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001\n expected response:\n { \"X-Forwarded-Protocol\": \"https\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newOption = requestDetail.requestOptions;\n newOption.port = 443;\n return {\n protocol: 'https',\n requestOptions: newOption\n };\n }\n }\n};\n\nmodify response status code\n\nmodify all status code from http://httpbin.org to 404\n\nanyproxy --rule rule_sample/sample_modify_response_statuscode.js\n\n/* \n sample: \n modify all status code of http://httpbin.org/ to 404\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n HTTP/1.1 404 Not Found\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newResponse = responseDetail.response;\n newResponse.statusCode = 404;\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify the response header\n\nadd X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_response_header.js\n\n/* \n sample: \n modify response header of http://httpbin.org/user-agent\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n X-Proxy-By: AnyProxy\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {\n const newResponse = responseDetail.response;\n newResponse.header['X-Proxy-By'] = 'AnyProxy';\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify response data and delay\n\nappend some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds.\n\nanyproxy --rule rule_sample/sample_modify_response_data.js\n\n/* \n sample: \n modify response data of http://httpbin.org/user-agent\n test:\n curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" } -- AnyProxy Hacked! --\n*/\n\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay the response for 5s\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\nConfig Certification\nConfig root CA in OSX\n\nthis kind of errors is usually caused by untrusted root CA\n\n\n\nWarning: please keep your root CA safe since it may influence your system security.\n\ninstall :\n\ndouble click rootCA.crt\n\nadd cert into login or system\n\n\n\n\nfind the newly imported AnyProxy certificates, configured as Always Trust\n\n\nConfig root CA in windows\n\nConfig OSX system proxy\n\nthe config is in wifi - advanced\n\n\nconfig http proxy server\n\ntake Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例\n\n\ntrust root CA in iOS\n\nClick Root CA in web ui, and follow the instruction to install\n\n\ntrust root CA in iOS after 10.3\n\nBesides installing root CA, you have to \"turn on\" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.\n\n\nconfig iOS/Android proxy server\n\nproxy settings are placed in wifi setting\n\niOS\n\n\n\n\nAndroid\n\n\nFAQ\nQ: can not deal https request in rule module.\n\nA: Any of these options could be used to change the way AnyProxy deall https requests\nconfig --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module\nplace a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.\n\n\n\nQ: get an error says function is not yieldable\n\nA: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.\n\nQ: The connection is not private\nAnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.\n\nIf you run AnyProxy by command line\nPass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.\nanyproxy -i --ignore-unauthorized-ssl\n\n\nIf you run AnyProxy by Nodejs\nPass in the option dangerouslyIgnoreUnauthorized:true, like this:\nconst options = {\n ...,\n dangerouslyIgnoreUnauthorized: true\n};\n\nconst anyproxyIns = new AnyProxy.ProxyCore(options);\nanyproxyIns.start();\n\nThis is also a global option, all certification errors will be ignored\n\nWith the help of AnyProxy Rule\nYou can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n // set rejectUnauthorized as false\n newRequestOptions.rejectUnauthorized = false;\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nAnd we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!\n\n\n"}}} \ No newline at end of file diff --git a/docs/en/src_doc.md b/docs/en/src_doc.md new file mode 100644 index 0000000..43572bb --- /dev/null +++ b/docs/en/src_doc.md @@ -0,0 +1,682 @@ +AnyProxy +=================== + +AnyProxy is a fully configurable http/https proxy in NodeJS. + +Ref: [中文文档](../cn) + +Github: + +* https://github.com/alibaba/anyproxy/tree/4.x + +Features: + +* Offer you the ablity to handle http traffic by invoking a js module +* Intercept https +* GUI webinterface + +Change Logs since 3.x: + +* Support Promise and Generator in rule module +* Simplified interface in rule module +* A newly designed web interface + + + +# Getting Start +### install + +To Debian and Ubuntu users, you may need to install `nodejs-legacy` at the same time + +```bash +sudo apg-get install nodejs-legacy +``` + +Then install the AnyProxy + +```bash +npm install -g anyproxy +``` + +### launch + +* start AnyProxy in command line, with default port 8001 + +```bash +anyproxy +``` + +* now you can use http proxy server by 127.0.0.1:8001 +* visit http://127.0.0.1:8002 to see the http requests + +### options + +* specify the port of http proxy + +```bash +anyproxy --port 1080 +``` + +### Use AnyProxy as an npm module + +AnyProxy can be used as an npm module + +> To enable https feature, please guide users to use `anyproxy-ca` in cli. Or use methods under `AnyProxy.utils.certMgr` to generate certificates. + +* install + +```bash +npm i anyproxy --save +``` + +* sample + +```js +const AnyProxy = require('anyproxy'); +const options = { + port: 8001, + rule: require('myRuleModule'), + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { /* */ }); +proxyServer.on('error', (e) => { /* */ }); +proxyServer.start(); + +//when finished +proxyServer.close(); +``` + +* Class: AnyProxy.proxyServer + * create a proxy server + + ```js + const proxy = new AnyProxy.proxyServer(options) + ``` + + * `options` + * `port` {number} required, port number of proxy server + * `rule` {object} your rule module + * `throttle` {number} throttle in kb/s, unlimited for default + * `forceProxyHttps` {boolean} in force intercept all https request, false for default + * `silent` {boolean} if keep silent in console, false for default`false` + * `dangerouslyIgnoreUnauthorized` {boolean} if ignore certificate error in request, false for default + * `webInterface` {object} config for web interface + * `enable` {boolean} if enable web interface, false for default + * `webPort` {number} port number for web interface + * Event: `ready` + * emit when proxy server is ready + * sample + + ```js + proxy.on('ready', function() { }) + ``` + + * Event: `error` + * emit when error happened inside proxy server + * sample + + ```js + proxy.on('error', function() { }) + ``` + * Method: `start` + * start proxy server + * sample + + ```js + proxy.start(); + ``` + * Method: `close` + * close proxy server + * sample + + ```js + proxy.close(); + ``` +* AnyProxy.utils.systemProxyMgr + * manage the system proxy config. sudo password may be required + * sample + + ```js + // set 127.0.0.1:8001 as system http server + AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); + + // disable global proxy server + AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + ``` + +* AnyProxy.utils.certMgr + * Manage certificates of AnyProxy + * `AnyProxy.utils.certMgr.ifRootCAFileExists()` + * detect if AnyProx rootCA exists + * `AnyProxy.utils.certMgr.generateRootCA(callback)` + * generate a rootCA + * Sample + + ```js + const AnyProxy = require('AnyProxy'); + const exec = require('child_process').exec; + + if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); + } + ``` + +# Proxy Https + +* AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate. + +> Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network. + +* generate certifycates and intercept + +```bash +anyproxy-ca #generate root CA. manually trust it after that. +anyproxy --intercept #launch anyproxy and intercept all https traffic +``` + +* [Appendix:how to trust CA](#config-certification) + +# Rule Introduction + +AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests. + +> Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk. + +Rule module could do the following stuff: +* intercept and modify the request which is being sent + * editable fields include request header, body, target address +* intercept and modify the response from server + * editable fields include response status code, header, body +* intercept https requests, modify request and response + +### sample + +* Target + * write a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds + +* Step 1,Write the rule file, save as sample.js + + ```js + // file: sample.js + module.exports = { + summary: 'a rule to modify response', + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + + newResponse.body += '
          -- AnyProxy Hacked! --'; + + newResponse.body += ``; + newResponse.header['Content-Type'] = 'text/html'; + + return new Promise((resolve, reject) => { + setTimeout(() => { // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }; + ``` + +* Step 2, start AnyProxy and load the rule file + * run `anyproxy --rule sample.js` + +* Step 3, test + + * use curl + ```bash + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + ``` + + * use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent + + * the expected response from proxy is + + ``` + { + "user-agent": "curl/7.43.0" + } + - AnyProxy Hacked! + ``` + +* Step 4, view the request log + + * visit http://127.0.0.1:8002, the request just sent should be listed here + +### how does it work + +* The flow chart is as follows + + + +* When got an http request, the entire process of proxy server is + * AnyProxy collects all the quest info, include method, header, body + * AnyProxy calls `beforeSendRequest` of the rule module. Rule module deal the request, return new request param or response content + * If `beforeSendRequest` returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here. + * Send request to target server, collect response + * Call `beforeSendResponse` of the rule module. Rule module deal the response data + * Send response to client + +* When AnyProxy get https request, it could replace the certificate and decrypt the request data + * AnyProxy calls `beforeDealHttpsRequest` of the rule module + * If the function returns `true`, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed. + +### how to load rule module + +* use local file +```bash +anyproxy --rule ./rule.js +``` + +* use an online rule file +```bash +anyproxy --rule https://sample.com/rule.js +``` + +* use an npm module + * AnyProxy uses `require()` to load rule module. You could either load a local npm module or a global-installed one. + + ```bash +anyproxy --rule ./myRulePkg/ #local module +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module + ``` + +# Rule module interface + +A typical rule module is as follows. All the functions are optional, just write the part you are interested in. + +```js +module.exports = { + // introduction + summary: 'my customized rule for AnyProxy', + // intercept before send request to server + *beforeSendRequest(requestDetail) { /* ... */ }, + // deal response before send to client + *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, + // if deal https request + *beforeDealHttpsRequest(requestDetail) { /* ... */ }, + // error happened when dealing requests + *onError(requestDetail, error) { /* ... */ }, + // error happened when connect to https server + *onConnectError(requestDetail, error) { /* ... */ } +}; +``` + +> All functions in your rule file, except summary, are all driven by [co](https://www.npmjs.com/package/co) . They should be yieldable, i.e. return a promise or be a generator function. + +### summary + +#### summary + +* Introduction of this rule file. AnyProxy will read this field and give some tip to user. + +### beforeSendRequest +#### beforeSendRequest(requestDetail) + +* Before sending request to server, AnyProxy will call `beforeSendRequest` with param `requestDetail` +* `requestDetail` + * `protocol` {string} the protocol to use, http or https + * `requestOptions` {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback + * `requestData` {object} request body + * `url` {string} request url + * `_req` {object} the native node.js request object +* e.g. When requesting *anyproxy.io*, `requestDetail` is something like the following + + ```js + { + protocol: 'http', + url: 'http://anyproxy.io/', + requestOptions: { + hostname: 'anyproxy.io', + port: 80, + path: '/', + method: 'GET', + headers: { + Host: 'anyproxy.io', + 'Proxy-Connection': 'keep-alive', + 'User-Agent': '...' + } + }, + requestData: '...', + _req: { /* ... */} + } + ``` + +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the request protocol,i.e. force use https + + ```js + return { + protocol: 'https' + }; + ``` + + * modify request param + + ```js + var newOption = Object.assign({}, requestDetail.requestOptions); + newOption.path = '/redirect/to/another/path'; + return { + requestOptions: newOption + }; + ``` + * modify request body + + ```js + return { + requestData: 'my new request data' + // requestOptions can also be used here + }; + ``` + * give response to the client, not sending request any longer. `statusCode` `headers`are required is this situation. + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### beforeSendResponse + +#### beforeSendResponse(requestDetail, responseDetail) + +* Before sending response to client, AnyProxy will call `beforeSendResponse` with param `requestDetail` `responseDetail` +* `requestDetail` is the same param as in `beforeSendRequest` +* `responseDetail` + * `response` {object} the response from server, includes `statusCode` `header` `body` + * `_res` {object} the native node.js response object +* e.g. When requesting *anyproxy.io*, `responseDetail` is something like the following + + ```js + { + response: { + statusCode: 200, + header: { + 'Content-Type': 'image/gif', + Connection: 'close', + 'Cache-Control': '...' + }, + body: '...' + }, + _res: { /* ... */ } + } + ``` +* Any of these return values are valid + * do nothing, and return null + + ```js + return null; + ``` + + * modify the response status code + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.statusCode = 404; + return { + response: newResponse + }; + ``` + + * modify the response content + + ```js + var newResponse = Object.assign({}, responseDetail.response); + newResponse.body += '--from anyproxy--'; + return { + response: newResponse + }; + ``` + +### beforeDealHttpsRequest + +#### beforeDealHttpsRequest(requestDetail) + +* When receiving https request, AnyProxy will call `beforeDealHttpsRequest` with param `requestDetail` +* If configed with `forceProxyHttps` in launching, AnyProxy will skip calling this method +* Only by returning true, AnyProxy will try to replace the certificate and intercept the https request. +* `requestDetail` + * `host` {string} the target host to request. Due to the request protocol, full url couldn't be got here + * `_req` {object} the native node.js request object. The `_req` here refers to the CONNECT request. +* return value + * `true` or `false`, whether AnyProxy should intercept the https request + +### onError + +#### onError(requestDetail, error) + +* AnyProxy will call this method when an error happened in request handling. +* Errors usually are issued during requesting, e.g. DNS failure, request timeout +* `requestDetail` is the same one as in `beforeSendRequest` +* Any of these return values are valid + * do nothing, and AnyProxy will response a default error page + + ```js + return null; + ``` + + * return a customized error page + + ```js + return { + response: { + statusCode: 200, + header: { 'content-type': 'text/html' }, + body: 'this could be a or ' + } + }; + ``` + +### onConnectError + +#### onConnectError(requestDetail, error) + +* AnyProxy will call this method when failed to connect target server in https request +* `requestDetail` is the same one as in `beforeDealHttpsRequest` +* no return value is required + +# Rule Samples + +* here are some samples about frequently used rule file +* try these samples by `anyproxy --rule http://....js` +* how to test with curl: + * request the server directly `curl http://httpbin.org/` + * request the server via proxy `curl http://httpbin.org/ --proxy http://127.0.0.1:8001` + +### use local response + * intercept the request towards http://httpbin.org , return the local-defined response + +{{sample-rule:rule_sample/sample_use_local_response.js}} + +### modify request header + * modify the user-agent sent to httpbin.org + +{{sample-rule:rule_sample/sample_modify_request_header.js}} + +### modify request body + * modify the post body of http://httpbin.org/post + +{{sample-rule:rule_sample/sample_modify_request_data.js}} + +### modify the request target + * send all the request towards http://httpbin.org/ to http://httpbin.org/user-agent + +{{sample-rule:rule_sample/sample_modify_request_path.js}} + +### modify request protocol + * modify the http request towards http://httpbin.org to https + +{{sample-rule:rule_sample/sample_modify_request_protocol.js}} + +### modify response status code + * modify all status code from http://httpbin.org to 404 + +{{sample-rule:rule_sample/sample_modify_response_statuscode.js}} + +### modify the response header + * add X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent + +{{sample-rule:rule_sample/sample_modify_response_header.js}} + +### modify response data and delay + * append some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds. + +{{sample-rule:rule_sample/sample_modify_response_data.js}} + +# Config Certification + +### Config root CA in OSX + +* this kind of errors is usually caused by untrusted root CA + + + +> Warning: please keep your root CA safe since it may influence your system security. + +install : + +* double click *rootCA.crt* + +* add cert into login or system + + + +* find the newly imported AnyProxy certificates, configured as **Always Trust** + + + +### Config root CA in windows + + + + +### Config OSX system proxy + +* the config is in wifi - advanced + + + +### config http proxy server + +* take Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例 + + + +### trust root CA in iOS + +* Click *Root CA* in web ui, and follow the instruction to install + + + +### trust root CA in iOS after 10.3 + +* Besides installing root CA, you have to "turn on" the certificate for web manually in *settings - general - about - Certificate Trust Settings*. Otherwire, safari will not trust the root CA generated by AnyProxy. + + + +### config iOS/Android proxy server + +* proxy settings are placed in wifi setting + +* iOS + + + +* Android + + + + +# FAQ + + +### Q: can not deal https request in rule module. + * A: Any of these options could be used to change the way AnyProxy deall https requests + 1. config `--intercept` when luanching AnyProxy via cli, or use `forceProxyHttps` when using as an npm module + 2. place a `beforeDealHttpsRequest` function in your rule file and determine which request to intercept by your own. + +### Q: get an error says *function is not yieldable* + * A: Rule module is driven by [co](https://www.npmjs.com/package/co). The functions inside should be yieldable, i.e. return a promise or be a generator function. + +### Q: The connection is not private +AnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below. + +- If you run AnyProxy by command line + Pass in the option `--ignore-unauthorized-ssl` to ignore the certification errors, please mind that the option will be active for all connections. + + ```bash + anyproxy -i --ignore-unauthorized-ssl + ``` +- If you run AnyProxy by Nodejs + Pass in the option `dangerouslyIgnoreUnauthorized:true`, like this: + + ```js + const options = { + ..., + dangerouslyIgnoreUnauthorized: true + }; + + const anyproxyIns = new AnyProxy.ProxyCore(options); + anyproxyIns.start(); + ``` + + *This is also a global option, all certification errors will be ignored* + +- With the help of AnyProxy Rule + You can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs `Http.rquest`, as we do in AnyProxy. A simple demo below: + + ```js + module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) { + const newRequestOptions = requestDetail.requestOptions; + // set rejectUnauthorized as false + newRequestOptions.rejectUnauthorized = false; + return { + requestOptions: newRequestOptions + }; + } + }, + }; + ``` + + And we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to! diff --git a/docs/gitbook/fonts/fontawesome/FontAwesome.otf b/docs/gitbook/fonts/fontawesome/FontAwesome.otf new file mode 100644 index 0000000..d4de13e Binary files /dev/null and b/docs/gitbook/fonts/fontawesome/FontAwesome.otf differ diff --git a/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot new file mode 100644 index 0000000..c7b00d2 Binary files /dev/null and b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot differ diff --git a/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg new file mode 100644 index 0000000..8b66187 --- /dev/null +++ b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg @@ -0,0 +1,685 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/fonts/fontawesome-webfont.ttf b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf old mode 100755 new mode 100644 similarity index 63% rename from web/fonts/fontawesome-webfont.ttf rename to docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf index 96a3639..f221e50 Binary files a/web/fonts/fontawesome-webfont.ttf and b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf differ diff --git a/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff new file mode 100644 index 0000000..6e7483c Binary files /dev/null and b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff differ diff --git a/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 new file mode 100644 index 0000000..7eb74fd Binary files /dev/null and b/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 differ diff --git a/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js b/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js new file mode 100644 index 0000000..ff7be71 --- /dev/null +++ b/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js @@ -0,0 +1,240 @@ +require(['gitbook', 'jquery'], function(gitbook, $) { + // Configuration + var MAX_SIZE = 4, + MIN_SIZE = 0, + BUTTON_ID; + + // Current fontsettings state + var fontState; + + // Default themes + var THEMES = [ + { + config: 'white', + text: 'White', + id: 0 + }, + { + config: 'sepia', + text: 'Sepia', + id: 1 + }, + { + config: 'night', + text: 'Night', + id: 2 + } + ]; + + // Default font families + var FAMILIES = [ + { + config: 'serif', + text: 'Serif', + id: 0 + }, + { + config: 'sans', + text: 'Sans', + id: 1 + } + ]; + + // Return configured themes + function getThemes() { + return THEMES; + } + + // Modify configured themes + function setThemes(themes) { + THEMES = themes; + updateButtons(); + } + + // Return configured font families + function getFamilies() { + return FAMILIES; + } + + // Modify configured font families + function setFamilies(families) { + FAMILIES = families; + updateButtons(); + } + + // Save current font settings + function saveFontSettings() { + gitbook.storage.set('fontState', fontState); + update(); + } + + // Increase font size + function enlargeFontSize(e) { + e.preventDefault(); + if (fontState.size >= MAX_SIZE) return; + + fontState.size++; + saveFontSettings(); + } + + // Decrease font size + function reduceFontSize(e) { + e.preventDefault(); + if (fontState.size <= MIN_SIZE) return; + + fontState.size--; + saveFontSettings(); + } + + // Change font family + function changeFontFamily(configName, e) { + if (e && e instanceof Event) { + e.preventDefault(); + } + + var familyId = getFontFamilyId(configName); + fontState.family = familyId; + saveFontSettings(); + } + + // Change type of color theme + function changeColorTheme(configName, e) { + if (e && e instanceof Event) { + e.preventDefault(); + } + + var $book = gitbook.state.$book; + + // Remove currently applied color theme + if (fontState.theme !== 0) + $book.removeClass('color-theme-'+fontState.theme); + + // Set new color theme + var themeId = getThemeId(configName); + fontState.theme = themeId; + if (fontState.theme !== 0) + $book.addClass('color-theme-'+fontState.theme); + + saveFontSettings(); + } + + // Return the correct id for a font-family config key + // Default to first font-family + function getFontFamilyId(configName) { + // Search for plugin configured font family + var configFamily = $.grep(FAMILIES, function(family) { + return family.config == configName; + })[0]; + // Fallback to default font family + return (!!configFamily)? configFamily.id : 0; + } + + // Return the correct id for a theme config key + // Default to first theme + function getThemeId(configName) { + // Search for plugin configured theme + var configTheme = $.grep(THEMES, function(theme) { + return theme.config == configName; + })[0]; + // Fallback to default theme + return (!!configTheme)? configTheme.id : 0; + } + + function update() { + var $book = gitbook.state.$book; + + $('.font-settings .font-family-list li').removeClass('active'); + $('.font-settings .font-family-list li:nth-child('+(fontState.family+1)+')').addClass('active'); + + $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); + $book.addClass('font-size-'+fontState.size); + $book.addClass('font-family-'+fontState.family); + + if(fontState.theme !== 0) { + $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); + $book.addClass('color-theme-'+fontState.theme); + } + } + + function init(config) { + // Search for plugin configured font family + var configFamily = getFontFamilyId(config.family), + configTheme = getThemeId(config.theme); + + // Instantiate font state object + fontState = gitbook.storage.get('fontState', { + size: config.size || 2, + family: configFamily, + theme: configTheme + }); + + update(); + } + + function updateButtons() { + // Remove existing fontsettings buttons + if (!!BUTTON_ID) { + gitbook.toolbar.removeButton(BUTTON_ID); + } + + // Create buttons in toolbar + BUTTON_ID = gitbook.toolbar.createButton({ + icon: 'fa fa-font', + label: 'Font Settings', + className: 'font-settings', + dropdown: [ + [ + { + text: 'A', + className: 'font-reduce', + onClick: reduceFontSize + }, + { + text: 'A', + className: 'font-enlarge', + onClick: enlargeFontSize + } + ], + $.map(FAMILIES, function(family) { + family.onClick = function(e) { + return changeFontFamily(family.config, e); + }; + + return family; + }), + $.map(THEMES, function(theme) { + theme.onClick = function(e) { + return changeColorTheme(theme.config, e); + }; + + return theme; + }) + ] + }); + } + + // Init configuration at start + gitbook.events.bind('start', function(e, config) { + var opts = config.fontsettings; + + // Generate buttons at start + updateButtons(); + + // Init current settings + init(opts); + }); + + // Expose API + gitbook.fontsettings = { + enlargeFontSize: enlargeFontSize, + reduceFontSize: reduceFontSize, + setTheme: changeColorTheme, + setFamily: changeFontFamily, + getThemes: getThemes, + setThemes: setThemes, + getFamilies: getFamilies, + setFamilies: setFamilies + }; +}); + + diff --git a/docs/gitbook/gitbook-plugin-fontsettings/website.css b/docs/gitbook/gitbook-plugin-fontsettings/website.css new file mode 100644 index 0000000..26591fe --- /dev/null +++ b/docs/gitbook/gitbook-plugin-fontsettings/website.css @@ -0,0 +1,291 @@ +/* + * Theme 1 + */ +.color-theme-1 .dropdown-menu { + background-color: #111111; + border-color: #7e888b; +} +.color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { + border-bottom: 9px solid #111111; +} +.color-theme-1 .dropdown-menu .buttons { + border-color: #7e888b; +} +.color-theme-1 .dropdown-menu .button { + color: #afa790; +} +.color-theme-1 .dropdown-menu .button:hover { + color: #73553c; +} +/* + * Theme 2 + */ +.color-theme-2 .dropdown-menu { + background-color: #2d3143; + border-color: #272a3a; +} +.color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { + border-bottom: 9px solid #2d3143; +} +.color-theme-2 .dropdown-menu .buttons { + border-color: #272a3a; +} +.color-theme-2 .dropdown-menu .button { + color: #62677f; +} +.color-theme-2 .dropdown-menu .button:hover { + color: #f4f4f5; +} +.book .book-header .font-settings .font-enlarge { + line-height: 30px; + font-size: 1.4em; +} +.book .book-header .font-settings .font-reduce { + line-height: 30px; + font-size: 1em; +} +.book.color-theme-1 .book-body { + color: #704214; + background: #f3eacb; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section { + background: #f3eacb; +} +.book.color-theme-2 .book-body { + color: #bdcadb; + background: #1c1f2b; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section { + background: #1c1f2b; +} +.book.font-size-0 .book-body .page-inner section { + font-size: 1.2rem; +} +.book.font-size-1 .book-body .page-inner section { + font-size: 1.4rem; +} +.book.font-size-2 .book-body .page-inner section { + font-size: 1.6rem; +} +.book.font-size-3 .book-body .page-inner section { + font-size: 2.2rem; +} +.book.font-size-4 .book-body .page-inner section { + font-size: 4rem; +} +.book.font-family-0 { + font-family: Georgia, serif; +} +.book.font-family-1 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { + color: #704214; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { + border-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { + color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { + background-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { + border-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { + background: #fdf6e3; + color: #657b83; + border-color: #f8df9c; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { + background-color: inherit; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { + border-color: #f5d06c; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { + color: inherit; + background-color: #fdf6e3; + border-color: #444444; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { + background-color: #fbeecb; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { + color: #bdcadb; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { + color: #3eb1d0; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { + color: #fffffa; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { + border-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { + color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { + background-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { + border-color: #373b4e; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { + color: #9dbed8; + background: #2d3143; + border-color: #2d3143; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { + background-color: #282a39; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { + border-color: #3b3f54; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { + color: #b6c2d2; + background-color: #2d3143; + border-color: #3b3f54; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { + background-color: #35394b; +} +.book.color-theme-1 .book-header { + color: #afa790; + background: transparent; +} +.book.color-theme-1 .book-header .btn { + color: #afa790; +} +.book.color-theme-1 .book-header .btn:hover { + color: #73553c; + background: none; +} +.book.color-theme-1 .book-header h1 { + color: #704214; +} +.book.color-theme-2 .book-header { + color: #7e888b; + background: transparent; +} +.book.color-theme-2 .book-header .btn { + color: #3b3f54; +} +.book.color-theme-2 .book-header .btn:hover { + color: #fffff5; + background: none; +} +.book.color-theme-2 .book-header h1 { + color: #bdcadb; +} +.book.color-theme-1 .book-body .navigation { + color: #afa790; +} +.book.color-theme-1 .book-body .navigation:hover { + color: #73553c; +} +.book.color-theme-2 .book-body .navigation { + color: #383f52; +} +.book.color-theme-2 .book-body .navigation:hover { + color: #fffff5; +} +/* + * Theme 1 + */ +.book.color-theme-1 .book-summary { + color: #afa790; + background: #111111; + border-right: 1px solid rgba(0, 0, 0, 0.07); +} +.book.color-theme-1 .book-summary .book-search { + background: transparent; +} +.book.color-theme-1 .book-summary .book-search input, +.book.color-theme-1 .book-summary .book-search input:focus { + border: 1px solid transparent; +} +.book.color-theme-1 .book-summary ul.summary li.divider { + background: #7e888b; + box-shadow: none; +} +.book.color-theme-1 .book-summary ul.summary li i.fa-check { + color: #33cc33; +} +.book.color-theme-1 .book-summary ul.summary li.done > a { + color: #877f6a; +} +.book.color-theme-1 .book-summary ul.summary li a, +.book.color-theme-1 .book-summary ul.summary li span { + color: #877f6a; + background: transparent; + font-weight: normal; +} +.book.color-theme-1 .book-summary ul.summary li.active > a, +.book.color-theme-1 .book-summary ul.summary li a:hover { + color: #704214; + background: transparent; + font-weight: normal; +} +/* + * Theme 2 + */ +.book.color-theme-2 .book-summary { + color: #bcc1d2; + background: #2d3143; + border-right: none; +} +.book.color-theme-2 .book-summary .book-search { + background: transparent; +} +.book.color-theme-2 .book-summary .book-search input, +.book.color-theme-2 .book-summary .book-search input:focus { + border: 1px solid transparent; +} +.book.color-theme-2 .book-summary ul.summary li.divider { + background: #272a3a; + box-shadow: none; +} +.book.color-theme-2 .book-summary ul.summary li i.fa-check { + color: #33cc33; +} +.book.color-theme-2 .book-summary ul.summary li.done > a { + color: #62687f; +} +.book.color-theme-2 .book-summary ul.summary li a, +.book.color-theme-2 .book-summary ul.summary li span { + color: #c1c6d7; + background: transparent; + font-weight: 600; +} +.book.color-theme-2 .book-summary ul.summary li.active > a, +.book.color-theme-2 .book-summary ul.summary li a:hover { + color: #f4f4f5; + background: #252737; + font-weight: 600; +} diff --git a/docs/gitbook/gitbook-plugin-highlight/ebook.css b/docs/gitbook/gitbook-plugin-highlight/ebook.css new file mode 100644 index 0000000..cecaaab --- /dev/null +++ b/docs/gitbook/gitbook-plugin-highlight/ebook.css @@ -0,0 +1,135 @@ +pre, +code { + /* http://jmblog.github.io/color-themes-for-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +pre .hljs-comment, +code .hljs-comment, +pre .hljs-title, +code .hljs-title { + color: #8e908c; +} +pre .hljs-variable, +code .hljs-variable, +pre .hljs-attribute, +code .hljs-attribute, +pre .hljs-tag, +code .hljs-tag, +pre .hljs-regexp, +code .hljs-regexp, +pre .hljs-deletion, +code .hljs-deletion, +pre .ruby .hljs-constant, +code .ruby .hljs-constant, +pre .xml .hljs-tag .hljs-title, +code .xml .hljs-tag .hljs-title, +pre .xml .hljs-pi, +code .xml .hljs-pi, +pre .xml .hljs-doctype, +code .xml .hljs-doctype, +pre .html .hljs-doctype, +code .html .hljs-doctype, +pre .css .hljs-id, +code .css .hljs-id, +pre .css .hljs-class, +code .css .hljs-class, +pre .css .hljs-pseudo, +code .css .hljs-pseudo { + color: #c82829; +} +pre .hljs-number, +code .hljs-number, +pre .hljs-preprocessor, +code .hljs-preprocessor, +pre .hljs-pragma, +code .hljs-pragma, +pre .hljs-built_in, +code .hljs-built_in, +pre .hljs-literal, +code .hljs-literal, +pre .hljs-params, +code .hljs-params, +pre .hljs-constant, +code .hljs-constant { + color: #f5871f; +} +pre .ruby .hljs-class .hljs-title, +code .ruby .hljs-class .hljs-title, +pre .css .hljs-rules .hljs-attribute, +code .css .hljs-rules .hljs-attribute { + color: #eab700; +} +pre .hljs-string, +code .hljs-string, +pre .hljs-value, +code .hljs-value, +pre .hljs-inheritance, +code .hljs-inheritance, +pre .hljs-header, +code .hljs-header, +pre .hljs-addition, +code .hljs-addition, +pre .ruby .hljs-symbol, +code .ruby .hljs-symbol, +pre .xml .hljs-cdata, +code .xml .hljs-cdata { + color: #718c00; +} +pre .css .hljs-hexcolor, +code .css .hljs-hexcolor { + color: #3e999f; +} +pre .hljs-function, +code .hljs-function, +pre .python .hljs-decorator, +code .python .hljs-decorator, +pre .python .hljs-title, +code .python .hljs-title, +pre .ruby .hljs-function .hljs-title, +code .ruby .hljs-function .hljs-title, +pre .ruby .hljs-title .hljs-keyword, +code .ruby .hljs-title .hljs-keyword, +pre .perl .hljs-sub, +code .perl .hljs-sub, +pre .javascript .hljs-title, +code .javascript .hljs-title, +pre .coffeescript .hljs-title, +code .coffeescript .hljs-title { + color: #4271ae; +} +pre .hljs-keyword, +code .hljs-keyword, +pre .javascript .hljs-function, +code .javascript .hljs-function { + color: #8959a8; +} +pre .hljs, +code .hljs { + display: block; + background: white; + color: #4d4d4c; + padding: 0.5em; +} +pre .coffeescript .javascript, +code .coffeescript .javascript, +pre .javascript .xml, +code .javascript .xml, +pre .tex .hljs-formula, +code .tex .hljs-formula, +pre .xml .javascript, +code .xml .javascript, +pre .xml .vbscript, +code .xml .vbscript, +pre .xml .css, +code .xml .css, +pre .xml .hljs-cdata, +code .xml .hljs-cdata { + opacity: 0.5; +} diff --git a/docs/gitbook/gitbook-plugin-highlight/website.css b/docs/gitbook/gitbook-plugin-highlight/website.css new file mode 100644 index 0000000..6674448 --- /dev/null +++ b/docs/gitbook/gitbook-plugin-highlight/website.css @@ -0,0 +1,434 @@ +.book .book-body .page-wrapper .page-inner section.normal pre, +.book .book-body .page-wrapper .page-inner section.normal code { + /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-title { + color: #8e908c; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-tag, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-deletion, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { + color: #c82829; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-literal, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-params, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-params, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-constant { + color: #f5871f; +} +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { + color: #eab700; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-value, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-value, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-header, +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-addition, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-addition, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + color: #718c00; +} +.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, +.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { + color: #3e999f; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, +.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, +.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, +.book .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, +.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { + color: #4271ae; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { + color: #8959a8; +} +.book .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + background: white; + color: #4d4d4c; + padding: 0.5em; +} +.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, +.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, +.book .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, +.book .book-body .page-wrapper .page-inner section.normal code .javascript .xml, +.book .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, +.book .book-body .page-wrapper .page-inner section.normal code .xml .javascript, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, +.book .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .css, +.book .book-body .page-wrapper .page-inner section.normal code .xml .css, +.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + opacity: 0.5; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { + /* + +Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull + +*/ + /* Solarized Green */ + /* Solarized Cyan */ + /* Solarized Blue */ + /* Solarized Yellow */ + /* Solarized Orange */ + /* Solarized Red */ + /* Solarized Violet */ +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + padding: 0.5em; + background: #fdf6e3; + color: #657b83; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-template_comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-template_comment, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-doctype, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-doctype, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pi, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pi, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-javadoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-javadoc { + color: #93a1a1; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-winutils, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-winutils, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .method, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .method, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-addition, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-addition, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-tag, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-tag, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-request, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-request, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-status, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-status, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .nginx .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .nginx .hljs-title { + color: #859900; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-command, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-command, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-tag .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-rules .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-rules .hljs-value, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-phpdoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-phpdoc, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-hexcolor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-hexcolor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_url, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_url { + color: #2aa198; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-localvars, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-localvars, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-chunk, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-chunk, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-decorator, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-decorator, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-identifier, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-identifier, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .vhdl .hljs-literal, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .vhdl .hljs-literal, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-id, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-id, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-function, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-function { + color: #268bd2; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-body, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-body, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .smalltalk .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .smalltalk .hljs-number, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-constant, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-class .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-class .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-parent, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-parent, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .haskell .hljs-type, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .haskell .hljs-type, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_reference, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_reference { + color: #b58900; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor .hljs-keyword, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-shebang, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-shebang, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol .hljs-string, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-change, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-change, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-special, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-special, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attr_selector, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attr_selector, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-subst, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-subst, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-cdata, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-cdata, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .clojure .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .clojure .hljs-title, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-header { + color: #cb4b16; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-deletion, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-important, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-important { + color: #dc322f; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_label, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_label { + color: #6c71c4; +} +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula { + background: #eee8d5; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { + /* Tomorrow Night Bright Theme */ + /* Original theme - https://github.com/chriskempson/tomorrow-theme */ + /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + /* Tomorrow Comment */ + /* Tomorrow Red */ + /* Tomorrow Orange */ + /* Tomorrow Yellow */ + /* Tomorrow Green */ + /* Tomorrow Aqua */ + /* Tomorrow Blue */ + /* Tomorrow Purple */ +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-title { + color: #969896; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-tag, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-deletion, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { + color: #d54e53; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-number, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-literal, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-params, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-params, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-constant { + color: #e78c45; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { + color: #e7c547; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-string, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-value, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-value, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-header, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-addition, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-addition, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + color: #b9ca4a; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { + color: #70c0b1; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { + color: #7aa6da; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { + color: #c397d8; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs { + display: block; + background: black; + color: #eaeaea; + padding: 0.5em; +} +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .xml, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .javascript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .css, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .css, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, +.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { + opacity: 0.5; +} diff --git a/docs/gitbook/gitbook-plugin-livereload/plugin.js b/docs/gitbook/gitbook-plugin-livereload/plugin.js new file mode 100644 index 0000000..923b3ae --- /dev/null +++ b/docs/gitbook/gitbook-plugin-livereload/plugin.js @@ -0,0 +1,11 @@ +(function() { + var newEl = document.createElement('script'), + firstScriptTag = document.getElementsByTagName('script')[0]; + + if (firstScriptTag) { + newEl.async = 1; + newEl.src = '//' + window.location.hostname + ':35729/livereload.js'; + firstScriptTag.parentNode.insertBefore(newEl, firstScriptTag); + } + +})(); diff --git a/docs/gitbook/gitbook-plugin-lunr/lunr.min.js b/docs/gitbook/gitbook-plugin-lunr/lunr.min.js new file mode 100644 index 0000000..6aa6bc7 --- /dev/null +++ b/docs/gitbook/gitbook-plugin-lunr/lunr.min.js @@ -0,0 +1,7 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.12 + * Copyright (C) 2015 Oliver Nightingale + * MIT Licensed + * @license + */ +!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.5.12",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(t){return arguments.length&&null!=t&&void 0!=t?Array.isArray(t)?t.map(function(t){return t.toLowerCase()}):t.toString().trim().toLowerCase().split(/[\s\-]+/):[]},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,o=0;n>o;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;no;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n element for each result + res.results.forEach(function(res) { + var $li = $('
        • ', { + 'class': 'search-results-item' + }); + + var $title = $('

          '); + + var $link = $('', { + 'href': gitbook.state.basePath + '/' + res.url, + 'text': res.title + }); + + var content = res.body.trim(); + if (content.length > MAX_DESCRIPTION_SIZE) { + content = content.slice(0, MAX_DESCRIPTION_SIZE).trim()+'...'; + } + var $content = $('

          ').html(content); + + $link.appendTo($title); + $title.appendTo($li); + $content.appendTo($li); + $li.appendTo($searchList); + }); + } + + function launchSearch(q) { + // Add class for loading + $body.addClass('with-search'); + $body.addClass('search-loading'); + + // Launch search query + throttle(gitbook.search.query(q, 0, MAX_RESULTS) + .then(function(results) { + displayResults(results); + }) + .always(function() { + $body.removeClass('search-loading'); + }), 1000); + } + + function closeSearch() { + $body.removeClass('with-search'); + $bookSearchResults.removeClass('open'); + } + + function launchSearchFromQueryString() { + var q = getParameterByName('q'); + if (q && q.length > 0) { + // Update search input + $searchInput.val(q); + + // Launch search + launchSearch(q); + } + } + + function bindSearch() { + // Bind DOM + $searchInput = $('#book-search-input input'); + $bookSearchResults = $('#book-search-results'); + $searchList = $bookSearchResults.find('.search-results-list'); + $searchTitle = $bookSearchResults.find('.search-results-title'); + $searchResultsCount = $searchTitle.find('.search-results-count'); + $searchQuery = $searchTitle.find('.search-query'); + + // Launch query based on input content + function handleUpdate() { + var q = $searchInput.val(); + + if (q.length == 0) { + closeSearch(); + } + else { + launchSearch(q); + } + } + + // Detect true content change in search input + // Workaround for IE < 9 + var propertyChangeUnbound = false; + $searchInput.on('propertychange', function(e) { + if (e.originalEvent.propertyName == 'value') { + handleUpdate(); + } + }); + + // HTML5 (IE9 & others) + $searchInput.on('input', function(e) { + // Unbind propertychange event for IE9+ + if (!propertyChangeUnbound) { + $(this).unbind('propertychange'); + propertyChangeUnbound = true; + } + + handleUpdate(); + }); + + // Push to history on blur + $searchInput.on('blur', function(e) { + // Update history state + if (usePushState) { + var uri = updateQueryString('q', $(this).val()); + history.pushState({ path: uri }, null, uri); + } + }); + } + + gitbook.events.on('page.change', function() { + bindSearch(); + closeSearch(); + + // Launch search based on query parameter + if (gitbook.search.isInitialized()) { + launchSearchFromQueryString(); + } + }); + + gitbook.events.on('search.ready', function() { + bindSearch(); + + // Launch search from query param at start + launchSearchFromQueryString(); + }); + + function getParameterByName(name) { + var url = window.location.href; + name = name.replace(/[\[\]]/g, '\\$&'); + var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i'), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, ' ')); + } + + function updateQueryString(key, value) { + value = encodeURIComponent(value); + + var url = window.location.href; + var re = new RegExp('([?&])' + key + '=.*?(&|#|$)(.*)', 'gi'), + hash; + + if (re.test(url)) { + if (typeof value !== 'undefined' && value !== null) + return url.replace(re, '$1' + key + '=' + value + '$2$3'); + else { + hash = url.split('#'); + url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); + if (typeof hash[1] !== 'undefined' && hash[1] !== null) + url += '#' + hash[1]; + return url; + } + } + else { + if (typeof value !== 'undefined' && value !== null) { + var separator = url.indexOf('?') !== -1 ? '&' : '?'; + hash = url.split('#'); + url = hash[0] + separator + key + '=' + value; + if (typeof hash[1] !== 'undefined' && hash[1] !== null) + url += '#' + hash[1]; + return url; + } + else + return url; + } + } +}); diff --git a/docs/gitbook/gitbook-plugin-sharing/buttons.js b/docs/gitbook/gitbook-plugin-sharing/buttons.js new file mode 100644 index 0000000..709a4e4 --- /dev/null +++ b/docs/gitbook/gitbook-plugin-sharing/buttons.js @@ -0,0 +1,90 @@ +require(['gitbook', 'jquery'], function(gitbook, $) { + var SITES = { + 'facebook': { + 'label': 'Facebook', + 'icon': 'fa fa-facebook', + 'onClick': function(e) { + e.preventDefault(); + window.open('http://www.facebook.com/sharer/sharer.php?s=100&p[url]='+encodeURIComponent(location.href)); + } + }, + 'twitter': { + 'label': 'Twitter', + 'icon': 'fa fa-twitter', + 'onClick': function(e) { + e.preventDefault(); + window.open('http://twitter.com/home?status='+encodeURIComponent(document.title+' '+location.href)); + } + }, + 'google': { + 'label': 'Google+', + 'icon': 'fa fa-google-plus', + 'onClick': function(e) { + e.preventDefault(); + window.open('https://plus.google.com/share?url='+encodeURIComponent(location.href)); + } + }, + 'weibo': { + 'label': 'Weibo', + 'icon': 'fa fa-weibo', + 'onClick': function(e) { + e.preventDefault(); + window.open('http://service.weibo.com/share/share.php?content=utf-8&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)); + } + }, + 'instapaper': { + 'label': 'Instapaper', + 'icon': 'fa fa-instapaper', + 'onClick': function(e) { + e.preventDefault(); + window.open('http://www.instapaper.com/text?u='+encodeURIComponent(location.href)); + } + }, + 'vk': { + 'label': 'VK', + 'icon': 'fa fa-vk', + 'onClick': function(e) { + e.preventDefault(); + window.open('http://vkontakte.ru/share.php?url='+encodeURIComponent(location.href)); + } + } + }; + + + + gitbook.events.bind('start', function(e, config) { + var opts = config.sharing; + + // Create dropdown menu + var menu = $.map(opts.all, function(id) { + var site = SITES[id]; + + return { + text: site.label, + onClick: site.onClick + }; + }); + + // Create main button with dropdown + if (menu.length > 0) { + gitbook.toolbar.createButton({ + icon: 'fa fa-share-alt', + label: 'Share', + position: 'right', + dropdown: [menu] + }); + } + + // Direct actions to share + $.each(SITES, function(sideId, site) { + if (!opts[sideId]) return; + + gitbook.toolbar.createButton({ + icon: site.icon, + label: site.text, + position: 'right', + onClick: site.onClick + }); + }); + }); +}); diff --git a/docs/gitbook/gitbook.js b/docs/gitbook/gitbook.js new file mode 100644 index 0000000..10000c7 --- /dev/null +++ b/docs/gitbook/gitbook.js @@ -0,0 +1,4 @@ +!function e(t,n,r){function o(s,a){if(!n[s]){if(!t[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[s]={exports:{}};t[s][0].call(l.exports,function(e){var n=t[s][1][e];return o(n?n:e)},l,l.exports,e,t,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s0&&t-1 in e)}function o(e,t,n){return de.isFunction(t)?de.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?de.grep(e,function(e){return e===t!==n}):"string"!=typeof t?de.grep(e,function(e){return se.call(t,e)>-1!==n}):je.test(t)?de.filter(t,e,n):(t=de.filter(t,e),de.grep(e,function(e){return se.call(t,e)>-1!==n&&1===e.nodeType}))}function i(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function s(e){var t={};return de.each(e.match(qe)||[],function(e,n){t[n]=!0}),t}function a(e){return e}function u(e){throw e}function c(e,t,n){var r;try{e&&de.isFunction(r=e.promise)?r.call(e).done(t).fail(n):e&&de.isFunction(r=e.then)?r.call(e,t,n):t.call(void 0,e)}catch(e){n.call(void 0,e)}}function l(){te.removeEventListener("DOMContentLoaded",l),e.removeEventListener("load",l),de.ready()}function f(){this.expando=de.expando+f.uid++}function p(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Ie.test(e)?JSON.parse(e):e)}function h(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Pe,"-$&").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n=p(n)}catch(e){}Re.set(e,t,n)}else n=void 0;return n}function d(e,t,n,r){var o,i=1,s=20,a=r?function(){return r.cur()}:function(){return de.css(e,t,"")},u=a(),c=n&&n[3]||(de.cssNumber[t]?"":"px"),l=(de.cssNumber[t]||"px"!==c&&+u)&&$e.exec(de.css(e,t));if(l&&l[3]!==c){c=c||l[3],n=n||[],l=+u||1;do i=i||".5",l/=i,de.style(e,t,l+c);while(i!==(i=a()/u)&&1!==i&&--s)}return n&&(l=+l||+u||0,o=n[1]?l+(n[1]+1)*n[2]:+n[2],r&&(r.unit=c,r.start=l,r.end=o)),o}function g(e){var t,n=e.ownerDocument,r=e.nodeName,o=Ue[r];return o?o:(t=n.body.appendChild(n.createElement(r)),o=de.css(t,"display"),t.parentNode.removeChild(t),"none"===o&&(o="block"),Ue[r]=o,o)}function m(e,t){for(var n,r,o=[],i=0,s=e.length;i-1)o&&o.push(i);else if(c=de.contains(i.ownerDocument,i),s=v(f.appendChild(i),"script"),c&&y(s),n)for(l=0;i=s[l++];)Ve.test(i.type||"")&&n.push(i);return f}function b(){return!0}function w(){return!1}function T(){try{return te.activeElement}catch(e){}}function C(e,t,n,r,o,i){var s,a;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(a in t)C(e,a,n,r,t[a],i);return e}if(null==r&&null==o?(o=n,r=n=void 0):null==o&&("string"==typeof n?(o=r,r=void 0):(o=r,r=n,n=void 0)),o===!1)o=w;else if(!o)return e;return 1===i&&(s=o,o=function(e){return de().off(e),s.apply(this,arguments)},o.guid=s.guid||(s.guid=de.guid++)),e.each(function(){de.event.add(this,t,o,r,n)})}function j(e,t){return de.nodeName(e,"table")&&de.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e:e}function k(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function E(e){var t=rt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function S(e,t){var n,r,o,i,s,a,u,c;if(1===t.nodeType){if(Fe.hasData(e)&&(i=Fe.access(e),s=Fe.set(t,i),c=i.events)){delete s.handle,s.events={};for(o in c)for(n=0,r=c[o].length;n1&&"string"==typeof d&&!pe.checkClone&&nt.test(d))return e.each(function(n){var i=e.eq(n);g&&(t[0]=d.call(this,n,i.html())),A(i,t,r,o)});if(p&&(i=x(t,e[0].ownerDocument,!1,e,o),s=i.firstChild,1===i.childNodes.length&&(i=s),s||o)){for(a=de.map(v(i,"script"),k),u=a.length;f=0&&nC.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[$]=!0,e}function o(e){var t=L.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function i(e,t){for(var n=e.split("|"),r=n.length;r--;)C.attrHandle[n[r]]=t}function s(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function a(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return function(t){return"form"in t?t.parentNode&&t.disabled===!1?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&je(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var o,i=e([],n.length,t),s=i.length;s--;)n[o=i[s]]&&(n[o]=!(r[o]=n[o]))})})}function f(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function p(){}function h(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function m(e,n,r){for(var o=0,i=n.length;o-1&&(r[c]=!(s[c]=f))}}else x=v(x===s?x.splice(d,x.length):x),i?i(null,s,x,u):K.apply(s,x)})}function x(e){for(var t,n,r,o=e.length,i=C.relative[e[0].type],s=i||C.relative[" "],a=i?1:0,u=d(function(e){return e===t},s,!0),c=d(function(e){return ee(t,e)>-1},s,!0),l=[function(e,n,r){var o=!i&&(r||n!==A)||((t=n).nodeType?u(e,n,r):c(e,n,r));return t=null,o}];a1&&g(l),a>1&&h(e.slice(0,a-1).concat({value:" "===e[a-2].type?"*":""})).replace(ae,"$1"),n,a0,i=e.length>0,s=function(r,s,a,u,c){var l,f,p,h=0,d="0",g=r&&[],m=[],y=A,x=r||i&&C.find.TAG("*",c),b=B+=null==y?1:Math.random()||.1,w=x.length;for(c&&(A=s===L||s||c);d!==w&&null!=(l=x[d]);d++){if(i&&l){for(f=0,s||l.ownerDocument===L||(O(l),a=!F);p=e[f++];)if(p(l,s||L,a)){u.push(l);break}c&&(B=b)}o&&((l=!p&&l)&&h--,r&&g.push(l))}if(h+=d,o&&d!==h){for(f=0;p=n[f++];)p(g,m,s,a);if(r){if(h>0)for(;d--;)g[d]||m[d]||(m[d]=Q.call(u));m=v(m)}K.apply(u,m),c&&!r&&m.length>0&&h+n.length>1&&t.uniqueSort(u)}return c&&(B=b,A=y),g};return o?r(s):s}var w,T,C,j,k,E,S,N,A,q,D,O,L,H,F,R,I,P,M,$="sizzle"+1*new Date,W=e.document,B=0,_=0,U=n(),z=n(),X=n(),V=function(e,t){return e===t&&(D=!0),0},G={}.hasOwnProperty,Y=[],Q=Y.pop,J=Y.push,K=Y.push,Z=Y.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),le=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(ie),pe=new RegExp("^"+re+"$"),he={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+oe),PSEUDO:new RegExp("^"+ie),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},de=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ve=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ye=/[+~]/,xe=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),be=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},we=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,Te=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},Ce=function(){O()},je=d(function(e){return e.disabled===!0&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{K.apply(Y=Z.call(W.childNodes),W.childNodes),Y[W.childNodes.length].nodeType}catch(e){K={apply:Y.length?function(e,t){J.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}T=t.support={},k=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},O=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:W;return r!==L&&9===r.nodeType&&r.documentElement?(L=r,H=L.documentElement,F=!k(L),W!==L&&(n=L.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Ce,!1):n.attachEvent&&n.attachEvent("onunload",Ce)),T.attributes=o(function(e){return e.className="i",!e.getAttribute("className")}),T.getElementsByTagName=o(function(e){return e.appendChild(L.createComment("")),!e.getElementsByTagName("*").length}),T.getElementsByClassName=me.test(L.getElementsByClassName),T.getById=o(function(e){return H.appendChild(e).id=$,!L.getElementsByName||!L.getElementsByName($).length}),T.getById?(C.filter.ID=function(e){var t=e.replace(xe,be);return function(e){return e.getAttribute("id")===t}},C.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&F){var n=t.getElementById(e);return n?[n]:[]}}):(C.filter.ID=function(e){var t=e.replace(xe,be);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},C.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&F){var n,r,o,i=t.getElementById(e);if(i){if(n=i.getAttributeNode("id"),n&&n.value===e)return[i];for(o=t.getElementsByName(e),r=0;i=o[r++];)if(n=i.getAttributeNode("id"),n&&n.value===e)return[i]}return[]}}),C.find.TAG=T.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):T.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},C.find.CLASS=T.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&F)return t.getElementsByClassName(e)},I=[],R=[],(T.qsa=me.test(L.querySelectorAll))&&(o(function(e){H.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&R.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||R.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+$+"-]").length||R.push("~="),e.querySelectorAll(":checked").length||R.push(":checked"),e.querySelectorAll("a#"+$+"+*").length||R.push(".#.+[+~]")}),o(function(e){e.innerHTML="";var t=L.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&R.push("name"+ne+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&R.push(":enabled",":disabled"),H.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&R.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),R.push(",.*:")})),(T.matchesSelector=me.test(P=H.matches||H.webkitMatchesSelector||H.mozMatchesSelector||H.oMatchesSelector||H.msMatchesSelector))&&o(function(e){T.disconnectedMatch=P.call(e,"*"),P.call(e,"[s!='']:x"),I.push("!=",ie)}),R=R.length&&new RegExp(R.join("|")),I=I.length&&new RegExp(I.join("|")),t=me.test(H.compareDocumentPosition),M=t||me.test(H.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},V=t?function(e,t){if(e===t)return D=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!T.sortDetached&&t.compareDocumentPosition(e)===n?e===L||e.ownerDocument===W&&M(W,e)?-1:t===L||t.ownerDocument===W&&M(W,t)?1:q?ee(q,e)-ee(q,t):0:4&n?-1:1)}:function(e,t){if(e===t)return D=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,a=[e],u=[t];if(!o||!i)return e===L?-1:t===L?1:o?-1:i?1:q?ee(q,e)-ee(q,t):0;if(o===i)return s(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;a[r]===u[r];)r++;return r?s(a[r],u[r]):a[r]===W?-1:u[r]===W?1:0},L):L},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==L&&O(e),n=n.replace(le,"='$1']"),T.matchesSelector&&F&&!X[n+" "]&&(!I||!I.test(n))&&(!R||!R.test(n)))try{var r=P.call(e,n);if(r||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,L,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==L&&O(e),M(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==L&&O(e);var n=C.attrHandle[t.toLowerCase()],r=n&&G.call(C.attrHandle,t.toLowerCase())?n(e,t,!F):void 0;return void 0!==r?r:T.attributes||!F?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(we,Te)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,o=0;if(D=!T.detectDuplicates,q=!T.sortStable&&e.slice(0),e.sort(V),D){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)e.splice(n[r],1)}return q=null,e},j=t.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=j(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=j(t);return n},C=t.selectors={cacheLength:50,createPseudo:r,match:he,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xe,be),e[3]=(e[3]||e[4]||e[5]||"").replace(xe,be),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return he.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(xe,be).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=U[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&U(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(o){var i=t.attr(o,e);return null==i?"!="===n:!n||(i+="","="===n?i===r:"!="===n?i!==r:"^="===n?r&&0===i.indexOf(r):"*="===n?r&&i.indexOf(r)>-1:"$="===n?r&&i.slice(-r.length)===r:"~="===n?(" "+i.replace(se," ")+" ").indexOf(r)>-1:"|="===n&&(i===r||i.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,o){var i="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,u){var c,l,f,p,h,d,g=i!==s?"nextSibling":"previousSibling",m=t.parentNode,v=a&&t.nodeName.toLowerCase(),y=!u&&!a,x=!1;if(m){if(i){for(;g;){for(p=t;p=p[g];)if(a?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&y){for(p=m,f=p[$]||(p[$]={}),l=f[p.uniqueID]||(f[p.uniqueID]={}),c=l[e]||[],h=c[0]===B&&c[1],x=h&&c[2],p=h&&m.childNodes[h];p=++h&&p&&p[g]||(x=h=0)||d.pop();)if(1===p.nodeType&&++x&&p===t){l[e]=[B,h,x];break}}else if(y&&(p=t,f=p[$]||(p[$]={}),l=f[p.uniqueID]||(f[p.uniqueID]={}),c=l[e]||[],h=c[0]===B&&c[1],x=h),x===!1)for(;(p=++h&&p&&p[g]||(x=h=0)||d.pop())&&((a?p.nodeName.toLowerCase()!==v:1!==p.nodeType)||!++x||(y&&(f=p[$]||(p[$]={}),l=f[p.uniqueID]||(f[p.uniqueID]={}),l[e]=[B,x]),p!==t)););return x-=o,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var o,i=C.pseudos[e]||C.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return i[$]?i(n):i.length>1?(o=[e,e,"",n],C.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,o=i(e,n),s=o.length;s--;)r=ee(e,o[s]),e[r]=!(t[r]=o[s])}):function(e){return i(e,0,o)}):i}},pseudos:{not:r(function(e){var t=[],n=[],o=S(e.replace(ae,"$1"));return o[$]?r(function(e,t,n,r){for(var i,s=o(e,null,r,[]),a=e.length;a--;)(i=s[a])&&(e[a]=!(t[a]=i))}):function(e,r,i){return t[0]=e,o(t,null,i,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){ +return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(xe,be),function(t){return(t.textContent||t.innerText||j(t)).indexOf(e)>-1}}),lang:r(function(e){return pe.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(xe,be).toLowerCase(),function(t){var n;do if(n=F?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===H},focus:function(e){return e===L.activeElement&&(!L.hasFocus||L.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:c(!1),disabled:c(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!C.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return de.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(s=i[0]).type&&9===t.nodeType&&F&&C.relative[i[1].type]){if(t=(C.find.ID(s.matches[0].replace(xe,be),t)||[])[0],!t)return n;c&&(t=t.parentNode),e=e.slice(i.shift().value.length)}for(o=he.needsContext.test(e)?0:i.length;o--&&(s=i[o],!C.relative[a=s.type]);)if((u=C.find[a])&&(r=u(s.matches[0].replace(xe,be),ye.test(i[0].type)&&f(t.parentNode)||t))){if(i.splice(o,1),e=r.length&&h(i),!e)return K.apply(n,r),n;break}}return(c||S(e,l))(r,t,!F,n,!t||ye.test(e)&&f(t.parentNode)||t),n},T.sortStable=$.split("").sort(V).join("")===$,T.detectDuplicates=!!D,O(),T.sortDetached=o(function(e){return 1&e.compareDocumentPosition(L.createElement("fieldset"))}),o(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||i("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),T.attributes&&o(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||i("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),o(function(e){return null==e.getAttribute("disabled")})||i(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);de.find=xe,de.expr=xe.selectors,de.expr[":"]=de.expr.pseudos,de.uniqueSort=de.unique=xe.uniqueSort,de.text=xe.getText,de.isXMLDoc=xe.isXML,de.contains=xe.contains,de.escapeSelector=xe.escape;var be=function(e,t,n){for(var r=[],o=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(o&&de(e).is(n))break;r.push(e)}return r},we=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Te=de.expr.match.needsContext,Ce=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,je=/^.[^:#\[\.,]*$/;de.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?de.find.matchesSelector(r,e)?[r]:[]:de.find.matches(e,de.grep(t,function(e){return 1===e.nodeType}))},de.fn.extend({find:function(e){var t,n,r=this.length,o=this;if("string"!=typeof e)return this.pushStack(de(e).filter(function(){for(t=0;t1?de.uniqueSort(n):n},filter:function(e){return this.pushStack(o(this,e||[],!1))},not:function(e){return this.pushStack(o(this,e||[],!0))},is:function(e){return!!o(this,"string"==typeof e&&Te.test(e)?de(e):e||[],!1).length}});var ke,Ee=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,Se=de.fn.init=function(e,t,n){var r,o;if(!e)return this;if(n=n||ke,"string"==typeof e){if(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:Ee.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof de?t[0]:t,de.merge(this,de.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:te,!0)),Ce.test(r[1])&&de.isPlainObject(t))for(r in t)de.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return o=te.getElementById(r[2]),o&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):de.isFunction(e)?void 0!==n.ready?n.ready(e):e(de):de.makeArray(e,this)};Se.prototype=de.fn,ke=de(te);var Ne=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};de.fn.extend({has:function(e){var t=de(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&de.find.matchesSelector(n,e))){i.push(n);break}return this.pushStack(i.length>1?de.uniqueSort(i):i)},index:function(e){return e?"string"==typeof e?se.call(de(e),this[0]):se.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(de.uniqueSort(de.merge(this.get(),de(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),de.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return be(e,"parentNode")},parentsUntil:function(e,t,n){return be(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return be(e,"nextSibling")},prevAll:function(e){return be(e,"previousSibling")},nextUntil:function(e,t,n){return be(e,"nextSibling",n)},prevUntil:function(e,t,n){return be(e,"previousSibling",n)},siblings:function(e){return we((e.parentNode||{}).firstChild,e)},children:function(e){return we(e.firstChild)},contents:function(e){return e.contentDocument||de.merge([],e.childNodes)}},function(e,t){de.fn[e]=function(n,r){var o=de.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(o=de.filter(r,o)),this.length>1&&(Ae[e]||de.uniqueSort(o),Ne.test(e)&&o.reverse()),this.pushStack(o)}});var qe=/[^\x20\t\r\n\f]+/g;de.Callbacks=function(e){e="string"==typeof e?s(e):de.extend({},e);var t,n,r,o,i=[],a=[],u=-1,c=function(){for(o=e.once,r=t=!0;a.length;u=-1)for(n=a.shift();++u-1;)i.splice(n,1),n<=u&&u--}),this},has:function(e){return e?de.inArray(e,i)>-1:i.length>0},empty:function(){return i&&(i=[]),this},disable:function(){return o=a=[],i=n="",this},disabled:function(){return!i},lock:function(){return o=a=[],n||t||(i=n=""),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=n||[],n=[e,n.slice?n.slice():n],a.push(n),t||c()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},de.extend({Deferred:function(t){var n=[["notify","progress",de.Callbacks("memory"),de.Callbacks("memory"),2],["resolve","done",de.Callbacks("once memory"),de.Callbacks("once memory"),0,"resolved"],["reject","fail",de.Callbacks("once memory"),de.Callbacks("once memory"),1,"rejected"]],r="pending",o={state:function(){return r},always:function(){return i.done(arguments).fail(arguments),this},catch:function(e){return o.then(null,e)},pipe:function(){var e=arguments;return de.Deferred(function(t){de.each(n,function(n,r){var o=de.isFunction(e[r[4]])&&e[r[4]];i[r[1]](function(){var e=o&&o.apply(this,arguments);e&&de.isFunction(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,o?[e]:arguments)})}),e=null}).promise()},then:function(t,r,o){function i(t,n,r,o){return function(){var c=this,l=arguments,f=function(){var e,f;if(!(t=s&&(r!==u&&(c=void 0,l=[e]),n.rejectWith(c,l))}};t?p():(de.Deferred.getStackHook&&(p.stackTrace=de.Deferred.getStackHook()),e.setTimeout(p))}}var s=0;return de.Deferred(function(e){n[0][3].add(i(0,e,de.isFunction(o)?o:a,e.notifyWith)),n[1][3].add(i(0,e,de.isFunction(t)?t:a)),n[2][3].add(i(0,e,de.isFunction(r)?r:u))}).promise()},promise:function(e){return null!=e?de.extend(e,o):o}},i={};return de.each(n,function(e,t){var s=t[2],a=t[5];o[t[1]]=s.add,a&&s.add(function(){r=a},n[3-e][2].disable,n[0][2].lock),s.add(t[3].fire),i[t[0]]=function(){return i[t[0]+"With"](this===i?void 0:this,arguments),this},i[t[0]+"With"]=s.fireWith}),o.promise(i),t&&t.call(i,i),i},when:function(e){var t=arguments.length,n=t,r=Array(n),o=re.call(arguments),i=de.Deferred(),s=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?re.call(arguments):n,--t||i.resolveWith(r,o)}};if(t<=1&&(c(e,i.done(s(n)).resolve,i.reject),"pending"===i.state()||de.isFunction(o[n]&&o[n].then)))return i.then();for(;n--;)c(o[n],s(n),i.reject);return i.promise()}});var De=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;de.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&De.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},de.readyException=function(t){e.setTimeout(function(){throw t})};var Oe=de.Deferred();de.fn.ready=function(e){return Oe.then(e).catch(function(e){de.readyException(e)}),this},de.extend({isReady:!1,readyWait:1,holdReady:function(e){e?de.readyWait++:de.ready(!0)},ready:function(e){(e===!0?--de.readyWait:de.isReady)||(de.isReady=!0,e!==!0&&--de.readyWait>0||Oe.resolveWith(te,[de]))}}),de.ready.then=Oe.then,"complete"===te.readyState||"loading"!==te.readyState&&!te.documentElement.doScroll?e.setTimeout(de.ready):(te.addEventListener("DOMContentLoaded",l),e.addEventListener("load",l));var Le=function(e,t,n,r,o,i,s){var a=0,u=e.length,c=null==n;if("object"===de.type(n)){o=!0;for(a in n)Le(e,t,a,n[a],!0,i,s)}else if(void 0!==r&&(o=!0,de.isFunction(r)||(s=!0),c&&(s?(t.call(e,r),t=null):(c=t,t=function(e,t,n){return c.call(de(e),n)})),t))for(;a1,null,!0)},removeData:function(e){return this.each(function(){Re.remove(this,e)})}}),de.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Fe.get(e,t),n&&(!r||de.isArray(n)?r=Fe.access(e,t,de.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=de.queue(e,t),r=n.length,o=n.shift(),i=de._queueHooks(e,t),s=function(){de.dequeue(e,t)};"inprogress"===o&&(o=n.shift(),r--),o&&("fx"===t&&n.unshift("inprogress"),delete i.stop,o.call(e,s,i)),!r&&i&&i.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Fe.get(e,n)||Fe.access(e,n,{empty:de.Callbacks("once memory").add(function(){Fe.remove(e,[t+"queue",n])})})}}),de.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,Ve=/^$|\/(?:java|ecma)script/i,Ge={option:[1,""],thead:[1,"","
          "],col:[2,"","
          "],tr:[2,"","
          "],td:[3,"","
          "],_default:[0,"",""]};Ge.optgroup=Ge.option,Ge.tbody=Ge.tfoot=Ge.colgroup=Ge.caption=Ge.thead,Ge.th=Ge.td;var Ye=/<|&#?\w+;/;!function(){var e=te.createDocumentFragment(),t=e.appendChild(te.createElement("div")),n=te.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),pe.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="",pe.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var Qe=te.documentElement,Je=/^key/,Ke=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ze=/^([^.]*)(?:\.(.+)|)/;de.event={global:{},add:function(e,t,n,r,o){var i,s,a,u,c,l,f,p,h,d,g,m=Fe.get(e);if(m)for(n.handler&&(i=n,n=i.handler,o=i.selector),o&&de.find.matchesSelector(Qe,o),n.guid||(n.guid=de.guid++),(u=m.events)||(u=m.events={}),(s=m.handle)||(s=m.handle=function(t){return"undefined"!=typeof de&&de.event.triggered!==t.type?de.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(qe)||[""],c=t.length;c--;)a=Ze.exec(t[c])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h&&(f=de.event.special[h]||{},h=(o?f.delegateType:f.bindType)||h,f=de.event.special[h]||{},l=de.extend({type:h,origType:g,data:r,handler:n,guid:n.guid,selector:o,needsContext:o&&de.expr.match.needsContext.test(o),namespace:d.join(".")},i),(p=u[h])||(p=u[h]=[],p.delegateCount=0,f.setup&&f.setup.call(e,r,d,s)!==!1||e.addEventListener&&e.addEventListener(h,s)),f.add&&(f.add.call(e,l),l.handler.guid||(l.handler.guid=n.guid)),o?p.splice(p.delegateCount++,0,l):p.push(l),de.event.global[h]=!0)},remove:function(e,t,n,r,o){var i,s,a,u,c,l,f,p,h,d,g,m=Fe.hasData(e)&&Fe.get(e);if(m&&(u=m.events)){for(t=(t||"").match(qe)||[""],c=t.length;c--;)if(a=Ze.exec(t[c])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){for(f=de.event.special[h]||{},h=(r?f.delegateType:f.bindType)||h,p=u[h]||[],a=a[2]&&new RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=i=p.length;i--;)l=p[i],!o&&g!==l.origType||n&&n.guid!==l.guid||a&&!a.test(l.namespace)||r&&r!==l.selector&&("**"!==r||!l.selector)||(p.splice(i,1),l.selector&&p.delegateCount--,f.remove&&f.remove.call(e,l));s&&!p.length&&(f.teardown&&f.teardown.call(e,d,m.handle)!==!1||de.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)de.event.remove(e,h+t[c],n,r,!0);de.isEmptyObject(u)&&Fe.remove(e,"handle events")}},dispatch:function(e){var t,n,r,o,i,s,a=de.event.fix(e),u=new Array(arguments.length),c=(Fe.get(this,"events")||{})[a.type]||[],l=de.event.special[a.type]||{};for(u[0]=a,t=1;t=1))for(;c!==this;c=c.parentNode||this)if(1===c.nodeType&&("click"!==e.type||c.disabled!==!0)){for(i=[],s={},n=0;n-1:de.find(o,this,null,[c]).length),s[o]&&i.push(r);i.length&&a.push({elem:c,handlers:i})}return c=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,tt=/\s*$/g;de.extend({htmlPrefilter:function(e){return e.replace(et,"<$1>")},clone:function(e,t,n){var r,o,i,s,a=e.cloneNode(!0),u=de.contains(e.ownerDocument,e);if(!(pe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||de.isXMLDoc(e)))for(s=v(a),i=v(e),r=0,o=i.length;r0&&y(s,!u&&v(e,"script")),a},cleanData:function(e){for(var t,n,r,o=de.event.special,i=0;void 0!==(n=e[i]);i++)if(He(n)){if(t=n[Fe.expando]){if(t.events)for(r in t.events)o[r]?de.event.remove(n,r):de.removeEvent(n,r,t.handle);n[Fe.expando]=void 0}n[Re.expando]&&(n[Re.expando]=void 0)}}}),de.fn.extend({detach:function(e){return q(this,e,!0)},remove:function(e){return q(this,e)},text:function(e){return Le(this,function(e){return void 0===e?de.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=j(this,e);t.appendChild(e)}})},prepend:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=j(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(de.cleanData(v(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return de.clone(this,e,t)})},html:function(e){return Le(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!tt.test(e)&&!Ge[(Xe.exec(e)||["",""])[1].toLowerCase()]){e=de.htmlPrefilter(e);try{for(;n1)}}),de.Tween=I,I.prototype={constructor:I,init:function(e,t,n,r,o,i){this.elem=e,this.prop=n,this.easing=o||de.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=i||(de.cssNumber[n]?"":"px")},cur:function(){var e=I.propHooks[this.prop];return e&&e.get?e.get(this):I.propHooks._default.get(this)},run:function(e){var t,n=I.propHooks[this.prop];return this.options.duration?this.pos=t=de.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):I.propHooks._default.set(this),this}},I.prototype.init.prototype=I.prototype,I.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=de.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){de.fx.step[e.prop]?de.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[de.cssProps[e.prop]]&&!de.cssHooks[e.prop]?e.elem[e.prop]=e.now:de.style(e.elem,e.prop,e.now+e.unit)}}},I.propHooks.scrollTop=I.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},de.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},de.fx=I.prototype.init,de.fx.step={};var ht,dt,gt=/^(?:toggle|show|hide)$/,mt=/queueHooks$/;de.Animation=de.extend(U,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,$e.exec(t),n),n}]},tweener:function(e,t){de.isFunction(e)?(t=e,e=["*"]):e=e.match(qe);for(var n,r=0,o=e.length;r1)},removeAttr:function(e){return this.each(function(){de.removeAttr(this,e)})}}),de.extend({attr:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return"undefined"==typeof e.getAttribute?de.prop(e,t,n):(1===i&&de.isXMLDoc(e)||(o=de.attrHooks[t.toLowerCase()]||(de.expr.match.bool.test(t)?vt:void 0)),void 0!==n?null===n?void de.removeAttr(e,t):o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:(e.setAttribute(t,n+""),n):o&&"get"in o&&null!==(r=o.get(e,t))?r:(r=de.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!pe.radioValue&&"radio"===t&&de.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,o=t&&t.match(qe);if(o&&1===e.nodeType)for(;n=o[r++];)e.removeAttribute(n)}}),vt={set:function(e,t,n){return t===!1?de.removeAttr(e,n):e.setAttribute(n,n),n}},de.each(de.expr.match.bool.source.match(/\w+/g),function(e,t){var n=yt[t]||de.find.attr;yt[t]=function(e,t,r){var o,i,s=t.toLowerCase();return r||(i=yt[s],yt[s]=o,o=null!=n(e,t,r)?s:null,yt[s]=i),o}});var xt=/^(?:input|select|textarea|button)$/i,bt=/^(?:a|area)$/i;de.fn.extend({prop:function(e,t){return Le(this,de.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[de.propFix[e]||e]})}}),de.extend({prop:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return 1===i&&de.isXMLDoc(e)||(t=de.propFix[t]||t,o=de.propHooks[t]),void 0!==n?o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:e[t]=n:o&&"get"in o&&null!==(r=o.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=de.find.attr(e,"tabindex");return t?parseInt(t,10):xt.test(e.nodeName)||bt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),pe.optSelected||(de.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),de.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){de.propFix[this.toLowerCase()]=this}),de.fn.extend({addClass:function(e){var t,n,r,o,i,s,a,u=0;if(de.isFunction(e))return this.each(function(t){de(this).addClass(e.call(this,t,X(this)))});if("string"==typeof e&&e)for(t=e.match(qe)||[];n=this[u++];)if(o=X(n),r=1===n.nodeType&&" "+z(o)+" "){for(s=0;i=t[s++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");a=z(r),o!==a&&n.setAttribute("class",a)}return this},removeClass:function(e){var t,n,r,o,i,s,a,u=0;if(de.isFunction(e))return this.each(function(t){de(this).removeClass(e.call(this,t,X(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(qe)||[];n=this[u++];)if(o=X(n),r=1===n.nodeType&&" "+z(o)+" "){for(s=0;i=t[s++];)for(;r.indexOf(" "+i+" ")>-1;)r=r.replace(" "+i+" "," ");a=z(r),o!==a&&n.setAttribute("class",a)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):de.isFunction(e)?this.each(function(n){de(this).toggleClass(e.call(this,n,X(this),t),t)}):this.each(function(){var t,r,o,i;if("string"===n)for(r=0,o=de(this),i=e.match(qe)||[];t=i[r++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||(t=X(this),t&&Fe.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||e===!1?"":Fe.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(X(n))+" ").indexOf(t)>-1)return!0;return!1}});var wt=/\r/g;de.fn.extend({val:function(e){var t,n,r,o=this[0];{if(arguments.length)return r=de.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=r?e.call(this,n,de(this).val()):e,null==o?o="":"number"==typeof o?o+="":de.isArray(o)&&(o=de.map(o,function(e){return null==e?"":e+""})),t=de.valHooks[this.type]||de.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,o,"value")||(this.value=o))});if(o)return t=de.valHooks[o.type]||de.valHooks[o.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(o,"value"))?n:(n=o.value,"string"==typeof n?n.replace(wt,""):null==n?"":n)}}}),de.extend({valHooks:{option:{get:function(e){var t=de.find.attr(e,"value");return null!=t?t:z(de.text(e))}},select:{get:function(e){var t,n,r,o=e.options,i=e.selectedIndex,s="select-one"===e.type,a=s?null:[],u=s?i+1:o.length;for(r=i<0?u:s?i:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),i}}}}),de.each(["radio","checkbox"],function(){de.valHooks[this]={set:function(e,t){if(de.isArray(t))return e.checked=de.inArray(de(e).val(),t)>-1}},pe.checkOn||(de.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tt=/^(?:focusinfocus|focusoutblur)$/;de.extend(de.event,{trigger:function(t,n,r,o){var i,s,a,u,c,l,f,p=[r||te],h=ce.call(t,"type")?t.type:t,d=ce.call(t,"namespace")?t.namespace.split("."):[];if(s=a=r=r||te,3!==r.nodeType&&8!==r.nodeType&&!Tt.test(h+de.event.triggered)&&(h.indexOf(".")>-1&&(d=h.split("."),h=d.shift(),d.sort()),c=h.indexOf(":")<0&&"on"+h,t=t[de.expando]?t:new de.Event(h,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=d.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:de.makeArray(n,[t]),f=de.event.special[h]||{},o||!f.trigger||f.trigger.apply(r,n)!==!1)){if(!o&&!f.noBubble&&!de.isWindow(r)){for(u=f.delegateType||h,Tt.test(u+h)||(s=s.parentNode);s;s=s.parentNode)p.push(s),a=s;a===(r.ownerDocument||te)&&p.push(a.defaultView||a.parentWindow||e)}for(i=0;(s=p[i++])&&!t.isPropagationStopped();)t.type=i>1?u:f.bindType||h,l=(Fe.get(s,"events")||{})[t.type]&&Fe.get(s,"handle"),l&&l.apply(s,n),l=c&&s[c],l&&l.apply&&He(s)&&(t.result=l.apply(s,n),t.result===!1&&t.preventDefault());return t.type=h,o||t.isDefaultPrevented()||f._default&&f._default.apply(p.pop(),n)!==!1||!He(r)||c&&de.isFunction(r[h])&&!de.isWindow(r)&&(a=r[c],a&&(r[c]=null),de.event.triggered=h,r[h](),de.event.triggered=void 0,a&&(r[c]=a)),t.result}},simulate:function(e,t,n){var r=de.extend(new de.Event,n,{type:e,isSimulated:!0});de.event.trigger(r,null,t)}}),de.fn.extend({trigger:function(e,t){return this.each(function(){de.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return de.event.trigger(e,t,n,!0)}}),de.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){de.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),de.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),pe.focusin="onfocusin"in e,pe.focusin||de.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){de.event.simulate(t,e.target,de.event.fix(e))};de.event.special[t]={setup:function(){var r=this.ownerDocument||this,o=Fe.access(r,t);o||r.addEventListener(e,n,!0),Fe.access(r,t,(o||0)+1)},teardown:function(){var r=this.ownerDocument||this,o=Fe.access(r,t)-1;o?Fe.access(r,t,o):(r.removeEventListener(e,n,!0),Fe.remove(r,t))}}});var Ct=e.location,jt=de.now(),kt=/\?/;de.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||de.error("Invalid XML: "+t),n};var Et=/\[\]$/,St=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;de.param=function(e,t){var n,r=[],o=function(e,t){var n=de.isFunction(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(de.isArray(e)||e.jquery&&!de.isPlainObject(e))de.each(e,function(){o(this.name,this.value)});else for(n in e)V(n,e[n],t,o);return r.join("&")},de.fn.extend({serialize:function(){return de.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=de.prop(this,"elements");return e?de.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!de(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!ze.test(e))}).map(function(e,t){var n=de(this).val();return null==n?null:de.isArray(n)?de.map(n,function(e){return{name:t.name,value:e.replace(St,"\r\n")}}):{name:t.name,value:n.replace(St,"\r\n")}}).get()}});var qt=/%20/g,Dt=/#.*$/,Ot=/([?&])_=[^&]*/,Lt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ht=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ft=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Pt={},Mt="*/".concat("*"),$t=te.createElement("a");$t.href=Ct.href,de.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Ht.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Mt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":de.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Q(Q(e,de.ajaxSettings),t):Q(de.ajaxSettings,e)},ajaxPrefilter:G(It),ajaxTransport:G(Pt),ajax:function(t,n){function r(t,n,r,a){var c,p,h,b,w,T=n;l||(l=!0,u&&e.clearTimeout(u),o=void 0,s=a||"",C.readyState=t>0?4:0,c=t>=200&&t<300||304===t,r&&(b=J(d,C,r)),b=K(d,b,C,c),c?(d.ifModified&&(w=C.getResponseHeader("Last-Modified"),w&&(de.lastModified[i]=w),w=C.getResponseHeader("etag"),w&&(de.etag[i]=w)),204===t||"HEAD"===d.type?T="nocontent":304===t?T="notmodified":(T=b.state,p=b.data,h=b.error,c=!h)):(h=T,!t&&T||(T="error",t<0&&(t=0))),C.status=t,C.statusText=(n||T)+"",c?v.resolveWith(g,[p,T,C]):v.rejectWith(g,[C,T,h]),C.statusCode(x),x=void 0,f&&m.trigger(c?"ajaxSuccess":"ajaxError",[C,d,c?p:h]),y.fireWith(g,[C,T]),f&&(m.trigger("ajaxComplete",[C,d]),--de.active||de.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var o,i,s,a,u,c,l,f,p,h,d=de.ajaxSetup({},n),g=d.context||d,m=d.context&&(g.nodeType||g.jquery)?de(g):de.event,v=de.Deferred(),y=de.Callbacks("once memory"),x=d.statusCode||{},b={},w={},T="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(l){if(!a)for(a={};t=Lt.exec(s);)a[t[1].toLowerCase()]=t[2];t=a[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return l?s:null},setRequestHeader:function(e,t){return null==l&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==l&&(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)C.always(e[C.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return o&&o.abort(t),r(0,t),this}};if(v.promise(C),d.url=((t||d.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=(d.dataType||"*").toLowerCase().match(qe)||[""],null==d.crossDomain){c=te.createElement("a");try{c.href=d.url,c.href=c.href,d.crossDomain=$t.protocol+"//"+$t.host!=c.protocol+"//"+c.host}catch(e){d.crossDomain=!0}}if(d.data&&d.processData&&"string"!=typeof d.data&&(d.data=de.param(d.data,d.traditional)),Y(It,d,n,C),l)return C;f=de.event&&d.global,f&&0===de.active++&&de.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Ft.test(d.type),i=d.url.replace(Dt,""),d.hasContent?d.data&&d.processData&&0===(d.contentType||"").indexOf("application/x-www-form-urlencoded")&&(d.data=d.data.replace(qt,"+")):(h=d.url.slice(i.length),d.data&&(i+=(kt.test(i)?"&":"?")+d.data,delete d.data),d.cache===!1&&(i=i.replace(Ot,"$1"),h=(kt.test(i)?"&":"?")+"_="+jt++ +h),d.url=i+h),d.ifModified&&(de.lastModified[i]&&C.setRequestHeader("If-Modified-Since",de.lastModified[i]),de.etag[i]&&C.setRequestHeader("If-None-Match",de.etag[i])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",d.contentType),C.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Mt+"; q=0.01":""):d.accepts["*"]);for(p in d.headers)C.setRequestHeader(p,d.headers[p]);if(d.beforeSend&&(d.beforeSend.call(g,C,d)===!1||l))return C.abort();if(T="abort",y.add(d.complete),C.done(d.success),C.fail(d.error),o=Y(Pt,d,n,C)){if(C.readyState=1,f&&m.trigger("ajaxSend",[C,d]),l)return C;d.async&&d.timeout>0&&(u=e.setTimeout(function(){C.abort("timeout")},d.timeout));try{l=!1,o.send(b,r)}catch(e){if(l)throw e;r(-1,e)}}else r(-1,"No Transport");return C},getJSON:function(e,t,n){return de.get(e,t,n,"json")},getScript:function(e,t){return de.get(e,void 0,t,"script")}}),de.each(["get","post"],function(e,t){de[t]=function(e,n,r,o){return de.isFunction(n)&&(o=o||r,r=n,n=void 0),de.ajax(de.extend({url:e,type:t,dataType:o,data:n,success:r},de.isPlainObject(e)&&e))}}),de._evalUrl=function(e){return de.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},de.fn.extend({wrapAll:function(e){var t;return this[0]&&(de.isFunction(e)&&(e=e.call(this[0])),t=de(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return de.isFunction(e)?this.each(function(t){de(this).wrapInner(e.call(this,t))}):this.each(function(){var t=de(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=de.isFunction(e);return this.each(function(n){de(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){de(this).replaceWith(this.childNodes)}),this}}),de.expr.pseudos.hidden=function(e){return!de.expr.pseudos.visible(e)},de.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},de.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Wt={0:200,1223:204},Bt=de.ajaxSettings.xhr();pe.cors=!!Bt&&"withCredentials"in Bt,pe.ajax=Bt=!!Bt,de.ajaxTransport(function(t){var n,r;if(pe.cors||Bt&&!t.crossDomain)return{send:function(o,i){var s,a=t.xhr();if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(s in t.xhrFields)a[s]=t.xhrFields[s];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||o["X-Requested-With"]||(o["X-Requested-With"]="XMLHttpRequest");for(s in o)a.setRequestHeader(s,o[s]);n=function(e){return function(){n&&(n=r=a.onload=a.onerror=a.onabort=a.onreadystatechange=null,"abort"===e?a.abort():"error"===e?"number"!=typeof a.status?i(0,"error"):i(a.status,a.statusText):i(Wt[a.status]||a.status,a.statusText,"text"!==(a.responseType||"text")||"string"!=typeof a.responseText?{binary:a.response}:{text:a.responseText},a.getAllResponseHeaders()))}},a.onload=n(),r=a.onerror=n("error"),void 0!==a.onabort?a.onabort=r:a.onreadystatechange=function(){4===a.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{a.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),de.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),de.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return de.globalEval(e),e}}}),de.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),de.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,o){t=de(" + + + + +

          + + + + + diff --git a/docs/search_index.json b/docs/search_index.json new file mode 100644 index 0000000..0370d2f --- /dev/null +++ b/docs/search_index.json @@ -0,0 +1 @@ +{"index":{"version":"0.5.12","fields":[{"name":"title","boost":10},{"name":"keywords","boost":15},{"name":"body","boost":1}],"ref":"url","documentStore":{"store":{"./":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#gener","#global","#launch","#local","&&","'","'...'","'...',","'/',","'/index.html';","'/redirect/to/another/path';","'127.0.0.1'","'8001');","'8008';","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'hello","'http',","'http';","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'text/html';","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(){","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","/^win/.test(process.platform);","0)","10.3","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","127.0.0.1:8001/index.html","1,write","2,","200,","3,","3.x:","300);","4,","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>","[switchyomega]","_re","_req","_req:","_res:","`","`;","a:","abil","abliti","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxyin","anyproxyins.start();","apg","append","appendix:how","at',","attack","attack.","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","bodi","body,","body:","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","design","detect","determin","directli","disabl","dn","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","features:","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","fulli","function","function()","function.","g","gener","get","github:","give","global","go,","gui","guid","h","hacked!","hacked...\")","handl","handling.","happen","header","header,","header:","headers:","headersar","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http/http","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/post","http://httpbin.org/us","http://localhost:8008/index.html","httpbin.org","httpbin.org,","https://github.com/alibaba/anyproxy/tree/4.x","https://httpbin.org/us","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","interest","interfac","interface,","introduct","invok","io","ios/android","issu","iswin","it,","it.","javascript.","js","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.hostnam","newrequestoptions.method","newrequestoptions.path","newrequestoptions.port","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['cont","newresponse.header['x","newresponse.statuscod","node.j","nodej","nodejs.","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","offer","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","otherwire,","otherwise,","own.","page","param","part","pass","password","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.protocol","requestdetail.requestoptions);","requestdetail.requestoptions;","requestdetail;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(","settimeout(()","settings.","side","signed.","silent","silent:","simpl","simplifi","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","stuff:","sudo","summari","summary,","summary:","support","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","true;","trust","type':","type']","type:","typic","ubuntu","ui,","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","whether","wifi","window","window.alert(\"sorry,","without","work","world'","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","}","})","});","},","};","中文文档",":"],"doc.html":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#","#gener","#global","#launch","#local","&&","'","'...'","'...',","'/',","'/redirect/to/another/path';","'/user","'8001');","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'http',","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","/^win/.test(process.platform);","0)","10.3","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","1,write","2,","200,","3,","4,","4.0","4.x","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>","[switchyomega]","_re","_req","_req:","_res:","a:","abil","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent';","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxy@beta","anyproxyin","anyproxyins.start();","apg","append","appendix:how","at',","attack","attack.","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","beta","bodi","body,","body:","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","detect","determin","directli","disabl","dn","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","function","function()","function.","g","gener","get","give","global","go,","guid","h","hacked!","handl","handling.","happen","header","header,","header:","headers:","headersar","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/ani","http://httpbin.org/post","http://httpbin.org/us","httpbin.org","httpbin.org,","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","interest","interfac","interface,","introduct","io","ios/android","issu","iswin","it,","it.","javascript.","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.method","newrequestoptions.path","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['x","newresponse.statuscod","node.j","nodej","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","otherwire,","otherwise,","own.","page","param","part","pass","password","path","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.requestoptions);","requestdetail.requestoptions;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(()","settings.","side","signed.","silent","silent:","simpl","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","stuff:","sudo","summari","summary,","summary:","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","trust","type':","type:","typic","ubuntu","ui,","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","whether","wifi","window","without","work","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","}","})","});","},","};",":"]},"length":2},"tokenStore":{"root":{"0":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}},"1":{"0":{"0":{"0":{"0":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"8":{"0":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"docs":{}},"docs":{},".":{"3":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"docs":{}}},"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"/":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}},",":{"docs":{},"访":{"docs":{},"问":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},",":{"docs":{},"编":{"docs":{},"写":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"w":{"docs":{},"r":{"docs":{},"i":{"docs":{},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"2":{"0":{"0":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"3":{"0":{"0":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{},"x":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"4":{"0":{"4":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"docs":{}},"4":{"3":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"0":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"的":{"docs":{},"主":{"docs":{},"要":{"docs":{},"变":{"docs":{},"化":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},",":{"docs":{},"欢":{"docs":{},"迎":{"docs":{},"提":{"docs":{},"供":{"docs":{},"反":{"docs":{},"馈":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"docs":{},"x":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"5":{"0":{"0":{"0":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"docs":{}},"docs":{}},"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"2":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"3":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{},"\"":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{},"/":{"7":{"docs":{},".":{"4":{"3":{"docs":{},".":{"0":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"docs":{}}},"docs":{}},"docs":{}}},"docs":{}}}}}},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"t":{"docs":{},"h":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"r":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"#":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"全":{"docs":{},"局":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"启":{"docs":{},"动":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"本":{"docs":{},"地":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"生":{"docs":{},"成":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"生":{"docs":{},"成":{"docs":{},"后":{"docs":{},"需":{"docs":{},"要":{"docs":{},"手":{"docs":{},"动":{"docs":{},"信":{"docs":{},"任":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"l":{"docs":{},"a":{"docs":{},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"&":{"docs":{},"&":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"'":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"8":{"0":{"0":{"1":{"docs":{},"'":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"8":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},".":{"docs":{},".":{"docs":{},".":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"/":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"/":{"docs":{},"t":{"docs":{},"o":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"o":{"docs":{},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{},"/":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},";":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"a":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{},"i":{"docs":{},"o":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"/":{"0":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},"'":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"docs":{}}},"docs":{}}},"docs":{}}}}}}}}},"p":{"docs":{},"p":{"docs":{},"l":{"docs":{},"i":{"docs":{},"c":{"docs":{},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"/":{"docs":{},"j":{"docs":{},"s":{"docs":{},"o":{"docs":{},"n":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"a":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"h":{"docs":{},"e":{"docs":{},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"i":{"docs":{},"o":{"docs":{},"/":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{},"?":{"docs":{},"s":{"docs":{},"h":{"docs":{},"o":{"docs":{},"w":{"docs":{},"_":{"docs":{},"e":{"docs":{},"n":{"docs":{},"v":{"docs":{},"=":{"1":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"m":{"docs":{},"a":{"docs":{},"g":{"docs":{},"e":{"docs":{},"/":{"docs":{},"g":{"docs":{},"i":{"docs":{},"f":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"k":{"docs":{},"e":{"docs":{},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"m":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"o":{"docs":{},"r":{"docs":{},"i":{"docs":{},"g":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{},"e":{"docs":{},"x":{"docs":{},"t":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}},"h":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"{":{"docs":{},"\"":{"docs":{},"h":{"docs":{},"e":{"docs":{},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"(":{"docs":{},"!":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{},".":{"docs":{},"i":{"docs":{},"f":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"f":{"docs":{},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"e":{"docs":{},"x":{"docs":{},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{},"s":{"docs":{},"(":{"docs":{},")":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"{":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},"e":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},".":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},"o":{"docs":{},"f":{"docs":{},"(":{"docs":{},"'":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}},"/":{"docs":{},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}}}},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"*":{"docs":{},"/":{"docs":{"./":{"ref":"./","tf":0.007590132827324478},"doc.html":{"ref":"doc.html","tf":0.007797270955165692}},"}":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},"*":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"+":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},".":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"/":{"docs":{},"m":{"docs":{},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"p":{"docs":{},"k":{"docs":{},"g":{"docs":{},"/":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"/":{"docs":{},"*":{"docs":{"./":{"ref":"./","tf":0.008064516129032258},"doc.html":{"ref":"doc.html","tf":0.008284600389863547}}},"/":{"docs":{"./":{"ref":"./","tf":0.006641366223908918},"doc.html":{"ref":"doc.html","tf":0.00682261208576998}},"w":{"docs":{},"h":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"这":{"docs":{},"里":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"同":{"docs":{},"时":{"docs":{},"加":{"docs":{},"上":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"^":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{},"/":{"docs":{},".":{"docs":{},"t":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"(":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{},".":{"docs":{},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"t":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"m":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"=":{"docs":{"./":{"ref":"./","tf":0.021821631878557873},"doc.html":{"ref":"doc.html","tf":0.02046783625730994}},"=":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}}},">":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},">":{"docs":{},"=":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"关":{"docs":{},"于":{"docs":{},"本":{"docs":{},"机":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"证":{"docs":{},"书":{"docs":{},"信":{"docs":{},"任":{"docs":{},"设":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"通":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"q":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"`":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},"a":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0056925996204933585},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"'":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},";":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"l":{"docs":{},"i":{"docs":{},"v":{"docs":{},"e":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"w":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.03273244781783681},"doc.html":{"ref":"doc.html","tf":0.03167641325536062}}},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"c":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},"r":{"docs":{},"(":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"(":{"docs":{},"(":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"f":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},"f":{"docs":{},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"e":{"docs":{},"x":{"docs":{},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{},"s":{"docs":{},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"d":{"docs":{},"i":{"docs":{},"s":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"(":{"docs":{},"'":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"s":{"docs":{},".":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"使":{"docs":{},"用":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},"加":{"docs":{},"载":{"docs":{},"本":{"docs":{},"地":{"docs":{},"规":{"docs":{},"则":{"docs":{},",":{"docs":{},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"在":{"docs":{},"参":{"docs":{},"数":{"docs":{},"里":{"docs":{},"传":{"docs":{},"入":{"docs":{},"一":{"docs":{},"个":{"docs":{},"本":{"docs":{},"地":{"docs":{},"的":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{},"路":{"docs":{},"径":{"docs":{},",":{"docs":{},"或":{"docs":{},"是":{"docs":{},"某":{"docs":{},"个":{"docs":{},"全":{"docs":{},"局":{"docs":{},"安":{"docs":{},"装":{"docs":{},"的":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"可":{"docs":{},"以":{"docs":{},"作":{"docs":{},"为":{"docs":{},"一":{"docs":{},"个":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"使":{"docs":{},"用":{"docs":{},",":{"docs":{},"整":{"docs":{},"合":{"docs":{},"进":{"docs":{},"其":{"docs":{},"他":{"docs":{},"工":{"docs":{},"具":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"向":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"发":{"docs":{},"送":{"docs":{},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"发":{"docs":{},"送":{"docs":{},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"在":{"docs":{},"与":{"docs":{},"目":{"docs":{},"标":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"建":{"docs":{},"立":{"docs":{},"连":{"docs":{},"接":{"docs":{},"的":{"docs":{},"过":{"docs":{},"程":{"docs":{},"中":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"这":{"docs":{},"个":{"docs":{},"方":{"docs":{},"法":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"提":{"docs":{},"供":{"docs":{},"了":{"docs":{},"二":{"docs":{},"次":{"docs":{},"开":{"docs":{},"发":{"docs":{},"的":{"docs":{},"能":{"docs":{},"力":{"docs":{},",":{"docs":{},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"j":{"docs":{},"s":{"docs":{},"编":{"docs":{},"写":{"docs":{},"自":{"docs":{},"己":{"docs":{},"的":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"(":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},")":{"docs":{},",":{"docs":{},"来":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"网":{"docs":{},"络":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"处":{"docs":{},"理":{"docs":{},"逻":{"docs":{},"辑":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"收":{"docs":{},"到":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"时":{"docs":{},",":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"并":{"docs":{},"带":{"docs":{},"上":{"docs":{},"参":{"docs":{},"数":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"开":{"docs":{},"放":{"docs":{},"式":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}},"调":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"由":{"docs":{},"模":{"docs":{},"块":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},",":{"docs":{},"返":{"docs":{},"回":{"docs":{},"新":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"或":{"docs":{},"返":{"docs":{},"回":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"默":{"docs":{},"认":{"docs":{},"不":{"docs":{},"对":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},",":{"docs":{},"如":{"docs":{},"需":{"docs":{},"看":{"docs":{},"到":{"docs":{},"明":{"docs":{},"文":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"需":{"docs":{},"要":{"docs":{},"配":{"docs":{},"置":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"@":{"docs":{},"b":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}},"p":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"x":{"docs":{},":":{"docs":{},"h":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"t":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"t":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},")":{"docs":{},",":{"docs":{},"需":{"docs":{},"要":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"提":{"docs":{},"前":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"b":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"l":{"docs":{},"i":{"docs":{},"t":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"c":{"docs":{},"t":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"d":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"v":{"docs":{},"a":{"docs":{},"n":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"b":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"t":{"docs":{},"a":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"o":{"docs":{},"d":{"docs":{},"y":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"三":{"docs":{},"个":{"docs":{},"字":{"docs":{},"段":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},")":{"docs":{},",":{"docs":{},"甚":{"docs":{},"至":{"docs":{},"是":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"目":{"docs":{},"标":{"docs":{},"地":{"docs":{},"址":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},"n":{"docs":{},"o":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"y":{"docs":{},"'":{"docs":{},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"r":{"docs":{},"o":{"docs":{},"w":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.006641366223908918},"doc.html":{"ref":"doc.html","tf":0.00682261208576998}},"方":{"docs":{},"法":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},",":{"docs":{},"按":{"docs":{},"提":{"docs":{},"示":{"docs":{},"扫":{"docs":{},"描":{"docs":{},"二":{"docs":{},"维":{"docs":{},"码":{"docs":{},"即":{"docs":{},"可":{"docs":{},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"f":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"i":{"docs":{},"c":{"docs":{},"a":{"docs":{},"t":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"y":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"l":{"docs":{},"a":{"docs":{},"s":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"i":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"d":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},")":{"docs":{},"、":{"docs":{},"响":{"docs":{},"应":{"docs":{},"头":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"g":{"docs":{},"(":{"docs":{},"'":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{"./":{"ref":"./","tf":0.009013282732447819},"doc.html":{"ref":"doc.html","tf":0.009259259259259259}}}},"t":{"docs":{},"r":{"docs":{},"o":{"docs":{},"l":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"f":{"docs":{},"i":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"l":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"l":{"docs":{},"d":{"docs":{},"n":{"docs":{},"'":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"s":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0056925996204933585},"doc.html":{"ref":"doc.html","tf":0.005847953216374269}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"w":{"docs":{},"d":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"h":{"docs":{},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"u":{"docs":{},"s":{"docs":{},"l":{"docs":{},"y":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"z":{"docs":{},"e":{"docs":{},"d":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"'":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"e":{"docs":{},"l":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"e":{"docs":{},"d":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"b":{"docs":{},"i":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"c":{"docs":{},"r":{"docs":{},"y":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"f":{"docs":{},"a":{"docs":{},"u":{"docs":{},"l":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"m":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"m":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"u":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"l":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"s":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"r":{"docs":{},"i":{"docs":{},"v":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"u":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"g":{"docs":{},"l":{"docs":{},"i":{"docs":{},"s":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"t":{"docs":{},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.007115749525616698},"doc.html":{"ref":"doc.html","tf":0.007309941520467836}},")":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"v":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"x":{"docs":{},"e":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"'":{"docs":{},"o":{"docs":{},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"p":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"(":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"c":{"docs":{},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},".":{"docs":{},"g":{"docs":{},"o":{"docs":{},"o":{"docs":{},"g":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"/":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"/":{"docs":{},"p":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"k":{"docs":{},"g":{"docs":{},"c":{"docs":{},"e":{"docs":{},"m":{"docs":{},"l":{"docs":{},"o":{"docs":{},"k":{"docs":{},"b":{"docs":{},"a":{"docs":{},"d":{"docs":{},"o":{"docs":{},"h":{"docs":{},"g":{"docs":{},"k":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"j":{"docs":{},"o":{"docs":{},"m":{"docs":{},"c":{"docs":{},"l":{"docs":{},"g":{"docs":{},"j":{"docs":{},"g":{"docs":{},"i":{"docs":{},"f":{"docs":{},")":{"docs":{},"为":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},".":{"docs":{},"g":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},"d":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"m":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"q":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"u":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"i":{"docs":{},"s":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"o":{"docs":{},"r":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}},"w":{"docs":{},"a":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"l":{"docs":{},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"e":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}}},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":5.000487329434698}}}},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},"主":{"docs":{},"页":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"g":{"docs":{},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"a":{"docs":{},"l":{"docs":{},"i":{"docs":{},"b":{"docs":{},"a":{"docs":{},"b":{"docs":{},"a":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"/":{"docs":{},"t":{"docs":{},"r":{"docs":{},"e":{"docs":{},"e":{"docs":{},"/":{"4":{"docs":{},".":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"v":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"l":{"docs":{},"o":{"docs":{},"b":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"o":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{},"e":{"docs":{},"d":{"docs":{},"!":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},".":{"docs":{},".":{"docs":{},".":{"docs":{},"\"":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}},"n":{"docs":{},"d":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"p":{"docs":{},"p":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},")":{"docs":{},",":{"docs":{},"请":{"docs":{},"求":{"docs":{},"体":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"a":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},")":{"docs":{},"、":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"l":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.015180265654648957},"doc.html":{"ref":"doc.html","tf":0.014619883040935672}},"/":{"1":{"docs":{},".":{"1":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"docs":{}}},"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}},"2":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{},".":{"docs":{},".":{"docs":{},".":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"/":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}},"a":{"docs":{},"n":{"docs":{},"i":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},"l":{"docs":{},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},":":{"8":{"0":{"0":{"8":{"docs":{},"/":{"docs":{},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"e":{"docs":{},"x":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598}}}}}}}}}}}}}}}},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"i":{"docs":{},"t":{"docs":{},"h":{"docs":{},"u":{"docs":{},"b":{"docs":{},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"/":{"docs":{},"a":{"docs":{},"l":{"docs":{},"i":{"docs":{},"b":{"docs":{},"a":{"docs":{},"b":{"docs":{},"a":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"/":{"docs":{},"t":{"docs":{},"r":{"docs":{},"e":{"docs":{},"e":{"docs":{},"/":{"4":{"docs":{},".":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"a":{"docs":{},"p":{"docs":{},"i":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"#":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"_":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"连":{"docs":{},"接":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"出":{"docs":{},"错":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}},"的":{"docs":{},"代":{"docs":{},"理":{"docs":{},"模":{"docs":{},"式":{"docs":{},"中":{"docs":{},",":{"docs":{},"这":{"docs":{},"里":{"docs":{},"的":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"是":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}},".":{"docs":{},"r":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}},"n":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}},"r":{"docs":{},"u":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.007590132827324478},"doc.html":{"ref":"doc.html","tf":0.007309941520467836}},"`":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"按":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"启":{"docs":{},"动":{"docs":{},"时":{"docs":{},"配":{"docs":{},"置":{"docs":{},"`":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"`":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"都":{"docs":{},"会":{"docs":{},"被":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"f":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"o":{"docs":{},"d":{"docs":{},"u":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":10.001423149905124},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"c":{"docs":{},"l":{"docs":{},"u":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}},"f":{"docs":{},"l":{"docs":{},"u":{"docs":{},"e":{"docs":{},"n":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"v":{"docs":{},"o":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"o":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},".":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"m":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"k":{"docs":{},"e":{"docs":{},"y":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"n":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"c":{"docs":{},"o":{"docs":{},"m":{"docs":{},"'":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"b":{"docs":{},"/":{"docs":{},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"l":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"c":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}},"a":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"a":{"docs":{},"u":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"i":{"docs":{},"n":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"u":{"docs":{},"a":{"docs":{},"n":{"docs":{},"c":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"s":{"docs":{},"a":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"d":{"docs":{},"d":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},")":{"docs":{},",":{"docs":{},"用":{"docs":{},"户":{"docs":{},"必":{"docs":{},"须":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"才":{"docs":{},"能":{"docs":{},"进":{"docs":{},"行":{"docs":{},"后":{"docs":{},"续":{"docs":{},"流":{"docs":{},"程":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.013282732447817837},"doc.html":{"ref":"doc.html","tf":0.01364522417153996}}}}},"u":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.009487666034155597},"doc.html":{"ref":"doc.html","tf":0.008284600389863547}},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"e":{"docs":{},"x":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"p":{"docs":{},"k":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}},"a":{"docs":{},"k":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"a":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"u":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"n":{"docs":{},"e":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},".":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},".":{"docs":{},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},"s":{"docs":{},"[":{"docs":{},"'":{"docs":{},"u":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"r":{"docs":{},"e":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0056925996204933585},"doc.html":{"ref":"doc.html","tf":0.005847953216374269}},"e":{"docs":{},".":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},"[":{"docs":{},"'":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"w":{"docs":{},"o":{"docs":{},"r":{"docs":{},"k":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"t":{"docs":{},"h":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"p":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}},"u":{"docs":{},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}},"m":{"docs":{},"b":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"o":{"docs":{},"b":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"a":{"docs":{},"s":{"docs":{},"s":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"(":{"docs":{},"{":{"docs":{},"}":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}}}}}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"n":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"i":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"a":{"docs":{},"l":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},"x":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"v":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"f":{"docs":{},"f":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{},"w":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}}},"w":{"docs":{},"n":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"g":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"r":{"docs":{},"a":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"w":{"docs":{},"o":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}},"i":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"r":{"docs":{},"i":{"docs":{},"v":{"docs":{},"a":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},"(":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"v":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}}}}},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"\"":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},",":{"docs":{},"i":{"docs":{},".":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.016603415559772294},"doc.html":{"ref":"doc.html","tf":0.016569200779727095}}},"y":{"docs":{},".":{"docs":{},"c":{"docs":{},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"c":{"docs":{},"l":{"docs":{},"o":{"docs":{},"s":{"docs":{},"e":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"o":{"docs":{},"n":{"docs":{},"(":{"docs":{},"'":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"y":{"docs":{},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"p":{"docs":{},"m":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"v":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"l":{"docs":{},"a":{"docs":{},"c":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"e":{"docs":{},"a":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}},"q":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"f":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.024193548387096774},"doc.html":{"ref":"doc.html","tf":0.024853801169590642}},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}},".":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},";":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}}}}}}}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}},".":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"e":{"docs":{},"(":{"docs":{},"'":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"'":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}}}},"c":{"docs":{},"h":{"docs":{},"i":{"docs":{},"l":{"docs":{},"d":{"docs":{},"_":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"c":{"docs":{},"e":{"docs":{},"s":{"docs":{},"s":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"e":{"docs":{},"x":{"docs":{},"e":{"docs":{},"c":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"y":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"m":{"docs":{},"o":{"docs":{},"d":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"'":{"docs":{},")":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"d":{"docs":{},"i":{"docs":{},"r":{"docs":{},"n":{"docs":{},"a":{"docs":{},"m":{"docs":{},"e":{"docs":{},"(":{"docs":{},"k":{"docs":{},"e":{"docs":{},"y":{"docs":{},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"v":{"docs":{},"e":{"docs":{},"(":{"docs":{},"{":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.015654648956356737},"doc.html":{"ref":"doc.html","tf":0.01608187134502924}},"e":{"docs":{},"\"":{"docs":{},"}":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},":":{"docs":{"./":{"ref":"./","tf":0.007590132827324478},"doc.html":{"ref":"doc.html","tf":0.007797270955165692}}},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},")":{"docs":{},";":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},";":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"u":{"docs":{},"r":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.017077798861480076},"doc.html":{"ref":"doc.html","tf":0.01705653021442495}}}}}},"c":{"docs":{},"e":{"docs":{},"i":{"docs":{},"v":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}},"c":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"'":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.022296015180265655},"doc.html":{"ref":"doc.html","tf":0.021929824561403508}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"_":{"docs":{},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"/":{"docs":{},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{},"e":{"docs":{},"_":{"docs":{},"m":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{},"f":{"docs":{},"y":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"p":{"docs":{},"a":{"docs":{},"t":{"docs":{},"h":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"o":{"docs":{},"c":{"docs":{},"o":{"docs":{},"l":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"_":{"docs":{},"d":{"docs":{},"a":{"docs":{},"t":{"docs":{},"a":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"h":{"docs":{},"e":{"docs":{},"a":{"docs":{},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"_":{"docs":{},"l":{"docs":{},"o":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"接":{"docs":{},"口":{"docs":{},"文":{"docs":{},"档":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"样":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"n":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"i":{"docs":{},"s":{"docs":{},"k":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"a":{"docs":{},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},"e":{"docs":{},".":{"docs":{},"j":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},":":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"v":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"f":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"e":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"t":{"docs":{},"i":{"docs":{},"m":{"docs":{},"e":{"docs":{},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{},"(":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}},"(":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}},"n":{"docs":{},"g":{"docs":{},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"s":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"t":{"docs":{},"y":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"l":{"docs":{},"f":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.004743833017077799},"doc.html":{"ref":"doc.html","tf":0.004873294346978557}}},"t":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"r":{"docs":{},"v":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.008538899430740038},"doc.html":{"ref":"doc.html","tf":0.008771929824561403}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"i":{"docs":{},"l":{"docs":{},"e":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"(":{"docs":{},"s":{"docs":{},")":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"u":{"docs":{},"a":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"d":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"g":{"docs":{},"n":{"docs":{},"e":{"docs":{},"d":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"m":{"docs":{},"p":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}},"s":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"a":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":5.002436647173489}}}},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}}},"e":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}},"r":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"u":{"docs":{},"f":{"docs":{},"f":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"u":{"docs":{},"d":{"docs":{},"o":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"y":{"docs":{},"(":{"docs":{},")":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},"s":{"docs":{},"t":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}},"r":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"c":{"docs":{},"e":{"docs":{},"n":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{},"o":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}},"k":{"docs":{},"i":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"u":{"docs":{},"r":{"docs":{},"c":{"docs":{},"e":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"p":{"docs":{},"e":{"docs":{},"c":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}}}}},"t":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},":":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}},"x":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"/":{"docs":{},"p":{"docs":{},"l":{"docs":{},"a":{"docs":{},"i":{"docs":{},"n":{"docs":{},"\"":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"t":{"docs":{},"t":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"a":{"docs":{},"t":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"i":{"docs":{},"s":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"o":{"docs":{},"w":{"docs":{},"a":{"docs":{},"r":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0029239766081871343}}}}}},"!":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},",":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},";":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}},"或":{"docs":{},"者":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{},"e":{"docs":{},",":{"docs":{},"表":{"docs":{},"示":{"docs":{},"是":{"docs":{},"否":{"docs":{},"需":{"docs":{},"要":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.005218216318785579},"doc.html":{"ref":"doc.html","tf":0.005360623781676413}},")":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"a":{"docs":{},"f":{"docs":{},"f":{"docs":{},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"y":{"docs":{},"p":{"docs":{},"e":{"docs":{},"'":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"i":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}},"a":{"docs":{},"k":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"g":{"docs":{},"e":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.003320683111954459},"doc.html":{"ref":"doc.html","tf":0.00341130604288499}}}}}}},"i":{"docs":{},"m":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"p":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"u":{"docs":{},"i":{"docs":{},"中":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}},"d":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}},"k":{"docs":{},"n":{"docs":{},"o":{"docs":{},"w":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"l":{"docs":{},"i":{"docs":{},"m":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"s":{"docs":{},"e":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{"./":{"ref":"./","tf":0.009487666034155597},"doc.html":{"ref":"doc.html","tf":0.009746588693957114}},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"s":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"u":{"docs":{},"a":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}},"b":{"docs":{},"u":{"docs":{},"n":{"docs":{},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"v":{"docs":{},"a":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"l":{"docs":{},"i":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}},"u":{"docs":{"./":{"ref":"./","tf":0.0023719165085388993},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}}}}},"i":{"docs":{},"a":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"e":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"s":{"docs":{},"i":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{"./":{"ref":"./","tf":0.0028462998102466793},"doc.html":{"ref":"doc.html","tf":0.0024366471734892786}},"i":{"docs":{},"n":{"docs":{},"t":{"docs":{},"e":{"docs":{},"r":{"docs":{},"f":{"docs":{},"a":{"docs":{},"c":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"版":{"docs":{},"界":{"docs":{},"面":{"docs":{},"端":{"docs":{},"口":{"docs":{},"号":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"重":{"docs":{},"构":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"i":{"docs":{},"n":{"docs":{},"d":{"docs":{},"o":{"docs":{},"w":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},".":{"docs":{},"a":{"docs":{},"l":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"(":{"docs":{},"\"":{"docs":{},"s":{"docs":{},"o":{"docs":{},"r":{"docs":{},"r":{"docs":{},"y":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}}}}}}}}}}}},"s":{"docs":{},"系":{"docs":{},"统":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"f":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"u":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"o":{"docs":{},"r":{"docs":{},"l":{"docs":{},"d":{"docs":{},"'":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}},"k":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"r":{"docs":{},"t":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"a":{"docs":{},"n":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"r":{"docs":{},"n":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"y":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"h":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}},"r":{"docs":{},"i":{"docs":{},"t":{"docs":{},"e":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}},"t":{"docs":{},"e":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}},"x":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}},"y":{"docs":{},"i":{"docs":{},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}}}}}}}}}}},"o":{"docs":{},"u":{"docs":{},"'":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}},"{":{"docs":{"./":{"ref":"./","tf":0.04174573055028463},"doc.html":{"ref":"doc.html","tf":0.04337231968810916}},"b":{"docs":{},"o":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},"a":{"docs":{},"n":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0018975332068311196},"doc.html":{"ref":"doc.html","tf":0.001949317738791423}}}}}}}}}},"n":{"docs":{},"u":{"docs":{},"m":{"docs":{},"b":{"docs":{},"e":{"docs":{},"r":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}},"o":{"docs":{},"b":{"docs":{},"j":{"docs":{},"e":{"docs":{},"c":{"docs":{},"t":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.003795066413662239},"doc.html":{"ref":"doc.html","tf":0.003898635477582846}}}}}}}}},"s":{"docs":{},"t":{"docs":{},"r":{"docs":{},"i":{"docs":{},"n":{"docs":{},"g":{"docs":{},"}":{"docs":{"./":{"ref":"./","tf":0.0014231499051233396},"doc.html":{"ref":"doc.html","tf":0.0014619883040935672}}}}}}}}}},"|":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"}":{"docs":{"./":{"ref":"./","tf":0.014231499051233396},"doc.html":{"ref":"doc.html","tf":0.014619883040935672}},")":{"docs":{"./":{"ref":"./","tf":0.0009487666034155598},"doc.html":{"ref":"doc.html","tf":0.0009746588693957114}},";":{"docs":{"./":{"ref":"./","tf":0.004269449715370019},"doc.html":{"ref":"doc.html","tf":0.0043859649122807015}}}},",":{"docs":{"./":{"ref":"./","tf":0.009962049335863378},"doc.html":{"ref":"doc.html","tf":0.009746588693957114}}},";":{"docs":{"./":{"ref":"./","tf":0.013282732447817837},"doc.html":{"ref":"doc.html","tf":0.014132553606237816}}}},"不":{"docs":{},"做":{"docs":{},"任":{"docs":{},"何":{"docs":{},"处":{"docs":{},"理":{"docs":{},"。":{"docs":{},"此":{"docs":{},"时":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"返":{"docs":{},"回":{"docs":{},"一":{"docs":{},"个":{"docs":{},"默":{"docs":{},"认":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"页":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},",":{"docs":{},"返":{"docs":{},"回":{"docs":{},"n":{"docs":{},"u":{"docs":{},"l":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}},"中":{"docs":{},"把":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"证":{"docs":{},"书":{"docs":{},"的":{"docs":{},"开":{"docs":{},"关":{"docs":{},"打":{"docs":{},"开":{"docs":{},",":{"docs":{},"否":{"docs":{},"则":{"docs":{},"s":{"docs":{},"a":{"docs":{},"f":{"docs":{},"a":{"docs":{},"r":{"docs":{},"i":{"docs":{},"将":{"docs":{},"报":{"docs":{},"错":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"文":{"docs":{},"文":{"docs":{},"档":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}}}},"为":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"什":{"docs":{},"么":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"能":{"docs":{},"进":{"docs":{},"入":{"docs":{},"处":{"docs":{},"理":{"docs":{},"函":{"docs":{},"数":{"docs":{},"?":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"主":{"docs":{},"要":{"docs":{},"特":{"docs":{},"性":{"docs":{},"包":{"docs":{},"括":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"举":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},",":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},":":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"代":{"docs":{},"理":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"启":{"docs":{},"动":{"docs":{},"完":{"docs":{},"成":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"都":{"docs":{},"在":{"docs":{},"w":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"设":{"docs":{},"置":{"docs":{},"中":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"以":{"docs":{},"c":{"docs":{},"h":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"的":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"t":{"docs":{},"c":{"docs":{},"h":{"docs":{},"y":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"插":{"docs":{},"件":{"docs":{},"为":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"下":{"docs":{},"任":{"docs":{},"意":{"docs":{},"一":{"docs":{},"项":{"docs":{},"都":{"docs":{},"能":{"docs":{},"用":{"docs":{},"来":{"docs":{},"改":{"docs":{},"变":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"处":{"docs":{},"理":{"docs":{},"特":{"docs":{},"性":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"几":{"docs":{},"种":{"docs":{},"返":{"docs":{},"回":{"docs":{},"都":{"docs":{},"是":{"docs":{},"合":{"docs":{},"法":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.002243829468960359}}}}}}}}}}}}},"作":{"docs":{},"为":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"模":{"docs":{},"块":{"docs":{},"使":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"全":{"docs":{},"局":{"docs":{},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"你":{"docs":{},"可":{"docs":{},"以":{"docs":{},"通":{"docs":{},"过":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"使":{"docs":{},"用":{"docs":{},"n":{"docs":{},"p":{"docs":{},"m":{"docs":{},"包":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"举":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"在":{"docs":{},"线":{"docs":{},"地":{"docs":{},"址":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"本":{"docs":{},"地":{"docs":{},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"路":{"docs":{},"径":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"修":{"docs":{},"改":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}},"请":{"docs":{},"求":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"协":{"docs":{},"议":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},",":{"docs":{},"如":{"docs":{},"强":{"docs":{},"制":{"docs":{},"改":{"docs":{},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"发":{"docs":{},"起":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"头":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"的":{"docs":{},"目":{"docs":{},"标":{"docs":{},"地":{"docs":{},"址":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"内":{"docs":{},"容":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"头":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"的":{"docs":{},"内":{"docs":{},"容":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"关":{"docs":{},"闭":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"全":{"docs":{},"局":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"其":{"docs":{},"他":{"docs":{},"命":{"docs":{},"令":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"创":{"docs":{},"建":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"即":{"docs":{},"将":{"docs":{},"发":{"docs":{},"送":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"配":{"docs":{},"置":{"docs":{},",":{"docs":{},"供":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"i":{"docs":{},"r":{"docs":{},"e":{"docs":{},"(":{"docs":{},"'":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"'":{"docs":{},")":{"docs":{},".":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"作":{"docs":{},"为":{"docs":{},"使":{"docs":{},"用":{"docs":{},"。":{"docs":{},"详":{"docs":{},"见":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{},"/":{"docs":{},"a":{"docs":{},"p":{"docs":{},"i":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},".":{"docs":{},"h":{"docs":{},"t":{"docs":{},"m":{"docs":{},"l":{"docs":{},"#":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"_":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"_":{"docs":{},"o":{"docs":{},"p":{"docs":{},"t":{"docs":{},"i":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"_":{"docs":{},"c":{"docs":{},"a":{"docs":{},"l":{"docs":{},"l":{"docs":{},"b":{"docs":{},"a":{"docs":{},"c":{"docs":{},"k":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"原":{"docs":{},"始":{"docs":{},"的":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"返":{"docs":{},"回":{"docs":{},"对":{"docs":{},"象":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"双":{"docs":{},"击":{"docs":{},"打":{"docs":{},"开":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"发":{"docs":{},"送":{"docs":{},"响":{"docs":{},"应":{"docs":{},"前":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"请":{"docs":{},"求":{"docs":{},"前":{"docs":{},"拦":{"docs":{},"截":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"只":{"docs":{},"有":{"docs":{},"返":{"docs":{},"回":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"才":{"docs":{},"会":{"docs":{},"尝":{"docs":{},"试":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},"、":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"。":{"docs":{},"否":{"docs":{},"则":{"docs":{},"只":{"docs":{},"做":{"docs":{},"数":{"docs":{},"据":{"docs":{},"流":{"docs":{},"转":{"docs":{},"发":{"docs":{},",":{"docs":{},"无":{"docs":{},"法":{"docs":{},"看":{"docs":{},"到":{"docs":{},"明":{"docs":{},"文":{"docs":{},"数":{"docs":{},"据":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"可":{"docs":{},"以":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"函":{"docs":{},"数":{"docs":{},",":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"普":{"docs":{},"通":{"docs":{},"的":{"docs":{},"字":{"docs":{},"符":{"docs":{},"串":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"修":{"docs":{},"改":{"docs":{},"内":{"docs":{},"容":{"docs":{},"包":{"docs":{},"括":{"docs":{},"请":{"docs":{},"求":{"docs":{},"头":{"docs":{},"(":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"的":{"docs":{},"内":{"docs":{},"容":{"docs":{},"包":{"docs":{},"括":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{},"(":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"同":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"中":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"中":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}}}}}}}}}}}}}},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"加":{"docs":{},"载":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"后":{"docs":{},"将":{"docs":{},"终":{"docs":{},"端":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"配":{"docs":{},"置":{"docs":{},"为":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{},"即":{"docs":{},"可":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}}}},"命":{"docs":{},"令":{"docs":{},"行":{"docs":{},"启":{"docs":{},"动":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"时":{"docs":{},"配":{"docs":{},"置":{"docs":{},"`":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"端":{"docs":{},"口":{"docs":{},"号":{"8":{"0":{"0":{"1":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}}}}}},"直":{"docs":{},"接":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"在":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},"j":{"docs":{},"s":{"docs":{},"代":{"docs":{},"码":{"docs":{},"中":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"w":{"docs":{},"i":{"docs":{},"f":{"docs":{},"i":{"docs":{},"高":{"docs":{},"级":{"docs":{},"设":{"docs":{},"置":{"docs":{},"中":{"docs":{},",":{"docs":{},"配":{"docs":{},"置":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"即":{"docs":{},"可":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"构":{"docs":{},"造":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"实":{"docs":{},"例":{"docs":{},"的":{"docs":{},"时":{"docs":{},"候":{"docs":{},",":{"docs":{},"传":{"docs":{},"入":{"docs":{},"参":{"docs":{},"数":{"docs":{},"d":{"docs":{},"a":{"docs":{},"n":{"docs":{},"g":{"docs":{},"e":{"docs":{},"r":{"docs":{},"o":{"docs":{},"u":{"docs":{},"s":{"docs":{},"l":{"docs":{},"y":{"docs":{},"i":{"docs":{},"g":{"docs":{},"n":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"u":{"docs":{},"n":{"docs":{},"a":{"docs":{},"u":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"r":{"docs":{},"i":{"docs":{},"z":{"docs":{},"e":{"docs":{},"d":{"docs":{},":":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"请":{"docs":{},"求":{"docs":{},"处":{"docs":{},"理":{"docs":{},"过":{"docs":{},"程":{"docs":{},"中":{"docs":{},"发":{"docs":{},"生":{"docs":{},"错":{"docs":{},"误":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"调":{"docs":{},"用":{"docs":{},"o":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"r":{"docs":{},"o":{"docs":{},"r":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"并":{"docs":{},"提":{"docs":{},"供":{"docs":{},"对":{"docs":{},"应":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"基":{"docs":{},"于":{"docs":{},"n":{"docs":{},"o":{"docs":{},"d":{"docs":{},"e":{"docs":{},".":{"docs":{},"j":{"docs":{},"s":{"docs":{},",":{"docs":{},"开":{"docs":{},"放":{"docs":{},"二":{"docs":{},"次":{"docs":{},"开":{"docs":{},"发":{"docs":{},"能":{"docs":{},"力":{"docs":{},",":{"docs":{},"允":{"docs":{},"许":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"请":{"docs":{},"求":{"docs":{},"处":{"docs":{},"理":{"docs":{},"逻":{"docs":{},"辑":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"处":{"docs":{},"理":{"docs":{},"流":{"docs":{},"程":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"图":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"多":{"docs":{},"数":{"docs":{},"场":{"docs":{},"景":{"docs":{},"下":{"docs":{},",":{"docs":{},"错":{"docs":{},"误":{"docs":{},"会":{"docs":{},"在":{"docs":{},"请":{"docs":{},"求":{"docs":{},"目":{"docs":{},"标":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"的":{"docs":{},"时":{"docs":{},"候":{"docs":{},"发":{"docs":{},"生":{"docs":{},",":{"docs":{},"比":{"docs":{},"如":{"docs":{},"d":{"docs":{},"n":{"docs":{},"s":{"docs":{},"解":{"docs":{},"析":{"docs":{},"失":{"docs":{},"败":{"docs":{},"、":{"docs":{},"请":{"docs":{},"求":{"docs":{},"超":{"docs":{},"时":{"docs":{},"等":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"如":{"docs":{},"下":{"docs":{},"几":{"docs":{},"种":{"docs":{},"方":{"docs":{},"案":{"docs":{},"都":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"来":{"docs":{},"引":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"何":{"docs":{},"引":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"果":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"返":{"docs":{},"回":{"docs":{},"了":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},",":{"docs":{},"则":{"docs":{},"立":{"docs":{},"即":{"docs":{},"把":{"docs":{},"此":{"docs":{},"响":{"docs":{},"应":{"docs":{},"返":{"docs":{},"回":{"docs":{},"到":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"(":{"docs":{},"而":{"docs":{},"不":{"docs":{},"再":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{},"真":{"docs":{},"正":{"docs":{},"的":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},")":{"docs":{},",":{"docs":{},"流":{"docs":{},"程":{"docs":{},"结":{"docs":{},"束":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"配":{"docs":{},"置":{"docs":{},"了":{"docs":{},"全":{"docs":{},"局":{"docs":{},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"则":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"略":{"docs":{},"过":{"docs":{},"这":{"docs":{},"个":{"docs":{},"调":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"要":{"docs":{},"启":{"docs":{},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"解":{"docs":{},"析":{"docs":{},",":{"docs":{},"请":{"docs":{},"在":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"启":{"docs":{},"动":{"docs":{},"前":{"docs":{},"自":{"docs":{},"行":{"docs":{},"调":{"docs":{},"用":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},".":{"docs":{},"u":{"docs":{},"t":{"docs":{},"i":{"docs":{},"l":{"docs":{},"s":{"docs":{},".":{"docs":{},"c":{"docs":{},"e":{"docs":{},"r":{"docs":{},"t":{"docs":{},"m":{"docs":{},"g":{"docs":{},"r":{"docs":{},"相":{"docs":{},"关":{"docs":{},"方":{"docs":{},"法":{"docs":{},"生":{"docs":{},"成":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"并":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"信":{"docs":{},"任":{"docs":{},"安":{"docs":{},"装":{"docs":{},"。":{"docs":{},"或":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"使":{"docs":{},"用":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"c":{"docs":{},"a":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"对":{"docs":{},"于":{"docs":{},"d":{"docs":{},"e":{"docs":{},"b":{"docs":{},"i":{"docs":{},"a":{"docs":{},"n":{"docs":{},"或":{"docs":{},"者":{"docs":{},"u":{"docs":{},"b":{"docs":{},"u":{"docs":{},"n":{"docs":{},"t":{"docs":{},"u":{"docs":{},"系":{"docs":{},"统":{"docs":{},",":{"docs":{},"在":{"docs":{},"安":{"docs":{},"装":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"之":{"docs":{},"前":{"docs":{},",":{"docs":{},"可":{"docs":{},"能":{"docs":{},"还":{"docs":{},"需":{"docs":{},"要":{"docs":{},"安":{"docs":{},"装":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"开":{"docs":{},"发":{"docs":{},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"引":{"docs":{},"入":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"当":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"请":{"docs":{},"求":{"docs":{},"经":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"时":{"docs":{},",":{"docs":{},"具":{"docs":{},"体":{"docs":{},"处":{"docs":{},"理":{"docs":{},"过":{"docs":{},"程":{"docs":{},"是":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"收":{"docs":{},"到":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"时":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"可":{"docs":{},"以":{"docs":{},"替":{"docs":{},"换":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"对":{"docs":{},"请":{"docs":{},"求":{"docs":{},"做":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"访":{"docs":{},"问":{"docs":{},"特":{"docs":{},"定":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"站":{"docs":{},"点":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"会":{"docs":{},"提":{"docs":{},"示":{"docs":{},"该":{"docs":{},"站":{"docs":{},"点":{"docs":{},"不":{"docs":{},"是":{"docs":{},"一":{"docs":{},"个":{"docs":{},"安":{"docs":{},"全":{"docs":{},"的":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"这":{"docs":{},"通":{"docs":{},"常":{"docs":{},"是":{"docs":{},"因":{"docs":{},"为":{"docs":{},"站":{"docs":{},"点":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"设":{"docs":{},"置":{"docs":{},"不":{"docs":{},"能":{"docs":{},"被":{"docs":{},"正":{"docs":{},"确":{"docs":{},"识":{"docs":{},"别":{"docs":{},"导":{"docs":{},"致":{"docs":{},"的":{"docs":{},"(":{"docs":{},"比":{"docs":{},"如":{"docs":{},",":{"docs":{},"站":{"docs":{},"点":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"是":{"docs":{},"自":{"docs":{},"签":{"docs":{},"发":{"docs":{},"的":{"docs":{},")":{"docs":{},"。":{"docs":{},"如":{"docs":{},"果":{"docs":{},"您":{"docs":{},"信":{"docs":{},"任":{"docs":{},"该":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"可":{"docs":{},"以":{"docs":{},"用":{"docs":{},"以":{"docs":{},"下":{"docs":{},"方":{"docs":{},"式":{"docs":{},"来":{"docs":{},"继":{"docs":{},"续":{"docs":{},"访":{"docs":{},"问":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"必":{"docs":{},"选":{"docs":{},",":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"端":{"docs":{},"口":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"快":{"docs":{},"速":{"docs":{},"开":{"docs":{},"始":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"我":{"docs":{},"们":{"docs":{},"自":{"docs":{},"然":{"docs":{},"也":{"docs":{},"可":{"docs":{},"以":{"docs":{},"借":{"docs":{},"助":{"docs":{},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"的":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"来":{"docs":{},"实":{"docs":{},"现":{"docs":{},"这":{"docs":{},"个":{"docs":{},"效":{"docs":{},"果":{"docs":{},",":{"docs":{},"而":{"docs":{},"且":{"docs":{},"我":{"docs":{},"们":{"docs":{},"还":{"docs":{},"可":{"docs":{},"以":{"docs":{},"控":{"docs":{},"制":{"docs":{},"到":{"docs":{},"只":{"docs":{},"允":{"docs":{},"许":{"docs":{},"指":{"docs":{},"定":{"docs":{},"网":{"docs":{},"址":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"对":{"docs":{},"不":{"docs":{},"在":{"docs":{},"列":{"docs":{},"表":{"docs":{},"的":{"docs":{},"网":{"docs":{},"址":{"docs":{},",":{"docs":{},"进":{"docs":{},"行":{"docs":{},"证":{"docs":{},"书":{"docs":{},"的":{"docs":{},"强":{"docs":{},"验":{"docs":{},"证":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"b":{"docs":{},"i":{"docs":{},"n":{"docs":{},".":{"docs":{},"o":{"docs":{},"r":{"docs":{},"g":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"找":{"docs":{},"到":{"docs":{},"刚":{"docs":{},"刚":{"docs":{},"导":{"docs":{},"入":{"docs":{},"的":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"证":{"docs":{},"书":{"docs":{},",":{"docs":{},"配":{"docs":{},"置":{"docs":{},"为":{"docs":{},"信":{"docs":{},"任":{"docs":{},"(":{"docs":{},"a":{"docs":{},"l":{"docs":{},"w":{"docs":{},"a":{"docs":{},"y":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"把":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"响":{"docs":{},"应":{"docs":{},"信":{"docs":{},"息":{"docs":{},"返":{"docs":{},"回":{"docs":{},"给":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"所":{"docs":{},"有":{"docs":{},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"用":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"协":{"docs":{},"议":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"拦":{"docs":{},"截":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"对":{"docs":{},"内":{"docs":{},"容":{"docs":{},"做":{"docs":{},"修":{"docs":{},"改":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"发":{"docs":{},"送":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"并":{"docs":{},"修":{"docs":{},"改":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"响":{"docs":{},"应":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"正":{"docs":{},"在":{"docs":{},"发":{"docs":{},"送":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"提":{"docs":{},"供":{"docs":{},"g":{"docs":{},"u":{"docs":{},"i":{"docs":{},"界":{"docs":{},"面":{"docs":{},",":{"docs":{},"用":{"docs":{},"以":{"docs":{},"观":{"docs":{},"察":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"示":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"支":{"docs":{},"持":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"的":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"收":{"docs":{},"集":{"docs":{},"请":{"docs":{},"求":{"docs":{},"所":{"docs":{},"有":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"包":{"docs":{},"括":{"docs":{},"m":{"docs":{},"e":{"docs":{},"t":{"docs":{},"h":{"docs":{},"o":{"docs":{},"d":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"改":{"docs":{},"成":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"并":{"docs":{},"发":{"docs":{},"送":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"时":{"docs":{},",":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"参":{"docs":{},"数":{"docs":{},"内":{"docs":{},"容":{"docs":{},"大":{"docs":{},"致":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"t":{"docs":{},"a":{"docs":{},"i":{"docs":{},"l":{"docs":{},"参":{"docs":{},"数":{"docs":{},"内":{"docs":{},"容":{"docs":{},"大":{"docs":{},"致":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"否":{"docs":{},"启":{"docs":{},"用":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"版":{"docs":{},"界":{"docs":{},"面":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"处":{"docs":{},"理":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}},"屏":{"docs":{},"蔽":{"docs":{},"所":{"docs":{},"有":{"docs":{},"c":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"o":{"docs":{},"l":{"docs":{},"e":{"docs":{},"输":{"docs":{},"出":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"强":{"docs":{},"制":{"docs":{},"拦":{"docs":{},"截":{"docs":{},"所":{"docs":{},"有":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},",":{"docs":{},"忽":{"docs":{},"略":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"忽":{"docs":{},"略":{"docs":{},"请":{"docs":{},"求":{"docs":{},"中":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"错":{"docs":{},"误":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"f":{"docs":{},"a":{"docs":{},"l":{"docs":{},"s":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}},"必":{"docs":{},"选":{"docs":{},"字":{"docs":{},"段":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"包":{"docs":{},"括":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"本":{"docs":{},"文":{"docs":{},"档":{"docs":{},"的":{"docs":{},"适":{"docs":{},"用":{"docs":{},"范":{"docs":{},"围":{"docs":{},"是":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"质":{"docs":{},"是":{"docs":{},"中":{"docs":{},"间":{"docs":{},"人":{"docs":{},"攻":{"docs":{},"击":{"docs":{},"(":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"来":{"docs":{},"加":{"docs":{},"载":{"docs":{},"模":{"docs":{},"块":{"docs":{},"并":{"docs":{},"体":{"docs":{},"验":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"忽":{"docs":{},"略":{"docs":{},"证":{"docs":{},"书":{"docs":{},"认":{"docs":{},"证":{"docs":{},"的":{"docs":{},"错":{"docs":{},"误":{"docs":{},"。":{"docs":{},"需":{"docs":{},"要":{"docs":{},"注":{"docs":{},"意":{"docs":{},"的":{"docs":{},"是":{"docs":{},",":{"docs":{},"该":{"docs":{},"参":{"docs":{},"数":{"docs":{},"是":{"docs":{},"全":{"docs":{},"局":{"docs":{},"生":{"docs":{},"效":{"docs":{},"的":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"你":{"docs":{},"在":{"docs":{},"此":{"docs":{},"期":{"docs":{},"间":{"docs":{},"访":{"docs":{},"问":{"docs":{},"了":{"docs":{},"其":{"docs":{},"他":{"docs":{},"未":{"docs":{},"知":{"docs":{},"的":{"docs":{},"网":{"docs":{},"站":{"docs":{},",":{"docs":{},"他":{"docs":{},"们":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"问":{"docs":{},"题":{"docs":{},"也":{"docs":{},"会":{"docs":{},"被":{"docs":{},"忽":{"docs":{},"略":{"docs":{},",":{"docs":{},"这":{"docs":{},"可":{"docs":{},"能":{"docs":{},"会":{"docs":{},"带":{"docs":{},"来":{"docs":{},"安":{"docs":{},"全":{"docs":{},"隐":{"docs":{},"患":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"查":{"docs":{},"看":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"校":{"docs":{},"验":{"docs":{},"系":{"docs":{},"统":{"docs":{},"内":{"docs":{},"是":{"docs":{},"否":{"docs":{},"存":{"docs":{},"在":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"根":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"样":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"根":{"docs":{},"据":{"docs":{},"请":{"docs":{},"求":{"docs":{},"参":{"docs":{},"数":{"docs":{},",":{"docs":{},"向":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"发":{"docs":{},"出":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"接":{"docs":{},"收":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"响":{"docs":{},"应":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}},"模":{"docs":{},"块":{"docs":{},"介":{"docs":{},"绍":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"此":{"docs":{},"处":{"docs":{},"无":{"docs":{},"法":{"docs":{},"控":{"docs":{},"制":{"docs":{},"向":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"无":{"docs":{},"需":{"docs":{},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"注":{"docs":{},"意":{"docs":{},":":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"引":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"前":{"docs":{},",":{"docs":{},"请":{"docs":{},"务":{"docs":{},"必":{"docs":{},"确":{"docs":{},"保":{"docs":{},"文":{"docs":{},"件":{"docs":{},"来":{"docs":{},"源":{"docs":{},"可":{"docs":{},"靠":{"docs":{},",":{"docs":{},"以":{"docs":{},"免":{"docs":{},"发":{"docs":{},"生":{"docs":{},"安":{"docs":{},"全":{"docs":{},"问":{"docs":{},"题":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"测":{"docs":{},"试":{"docs":{},"规":{"docs":{},"则":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"访":{"docs":{},"问":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}}}}},"点":{"docs":{},"击":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"然":{"docs":{},"后":{"docs":{},",":{"docs":{},"安":{"docs":{},"装":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"生":{"docs":{},"成":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"r":{"docs":{},"o":{"docs":{},"o":{"docs":{},"t":{"docs":{},"c":{"docs":{},"a":{"docs":{},",":{"docs":{},"完":{"docs":{},"成":{"docs":{},"后":{"docs":{},"请":{"docs":{},"引":{"docs":{},"导":{"docs":{},"用":{"docs":{},"户":{"docs":{},"信":{"docs":{},"任":{"docs":{},".":{"docs":{},"c":{"docs":{},"r":{"docs":{},"t":{"docs":{},"文":{"docs":{},"件":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"证":{"docs":{},"书":{"docs":{},"并":{"docs":{},"解":{"docs":{},"析":{"docs":{},"所":{"docs":{},"有":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"用":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{},"发":{"docs":{},"请":{"docs":{},"求":{"docs":{},"测":{"docs":{},"试":{"docs":{},"的":{"docs":{},"方":{"docs":{},"法":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"测":{"docs":{},"试":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"测":{"docs":{},"试":{"docs":{},":":{"docs":{},"配":{"docs":{},"置":{"docs":{},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"为":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"会":{"docs":{},"被":{"docs":{},"解":{"docs":{},"析":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"p":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},"数":{"docs":{},"据":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}},"u":{"docs":{},"s":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"请":{"docs":{},"求":{"docs":{},"全":{"docs":{},"部":{"docs":{},"改":{"docs":{},"到":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},",":{"docs":{},"使":{"docs":{},"用":{"docs":{},"本":{"docs":{},"地":{"docs":{},"数":{"docs":{},"据":{"docs":{},"代":{"docs":{},"替":{"docs":{},"服":{"docs":{},"务":{"docs":{},"端":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{},"里":{"docs":{},"加":{"docs":{},"上":{"docs":{},"测":{"docs":{},"试":{"docs":{},"信":{"docs":{},"息":{"docs":{},",":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"5":{"docs":{},"秒":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"docs":{}}}}}}}}}}}}},"头":{"docs":{},"里":{"docs":{},"加":{"docs":{},"上":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"最":{"docs":{},"后":{"docs":{},"追":{"docs":{},"加":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"签":{"docs":{},"名":{"docs":{},",":{"docs":{},"并":{"docs":{},"延":{"docs":{},"迟":{"5":{"docs":{},"秒":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"docs":{}}}}}}}}}}}}}}}}}}}},"状":{"docs":{},"态":{"docs":{},"码":{"docs":{},"都":{"docs":{},"改":{"docs":{},"成":{"4":{"0":{"4":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}},"直":{"docs":{},"接":{"docs":{},"请":{"docs":{},"求":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},":":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"客":{"docs":{},"户":{"docs":{},"端":{"docs":{},",":{"docs":{},"不":{"docs":{},"再":{"docs":{},"发":{"docs":{},"起":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"其":{"docs":{},"中":{"docs":{},"s":{"docs":{},"t":{"docs":{},"a":{"docs":{},"t":{"docs":{},"u":{"docs":{},"s":{"docs":{},"c":{"docs":{},"o":{"docs":{},"d":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}},"相":{"docs":{},"比":{"3":{"docs":{},".":{"docs":{},"x":{"docs":{},"版":{"docs":{},"本":{"docs":{},",":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},"docs":{}}},"确":{"docs":{},"认":{"docs":{},"将":{"docs":{},"证":{"docs":{},"书":{"docs":{},"添":{"docs":{},"加":{"docs":{},"到":{"docs":{},"l":{"docs":{},"o":{"docs":{},"g":{"docs":{},"i":{"docs":{},"n":{"docs":{},"或":{"docs":{},"s":{"docs":{},"y":{"docs":{},"s":{"docs":{},"t":{"docs":{},"e":{"docs":{},"m":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"示":{"docs":{},"例":{"docs":{"./":{"ref":"./","tf":0.0029917726252804786}}}},"简":{"docs":{},"介":{"docs":{"./":{"ref":"./","tf":10}}},"化":{"docs":{},"了":{"docs":{},"规":{"docs":{},"则":{"docs":{},"文":{"docs":{},"件":{"docs":{},"内":{"docs":{},"的":{"docs":{},"接":{"docs":{},"口":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"管":{"docs":{},"理":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"系":{"docs":{},"统":{"docs":{},"的":{"docs":{},"全":{"docs":{},"局":{"docs":{},"代":{"docs":{},"理":{"docs":{},"配":{"docs":{},"置":{"docs":{},",":{"docs":{},"方":{"docs":{},"法":{"docs":{},"调":{"docs":{},"用":{"docs":{},"时":{"docs":{},"可":{"docs":{},"能":{"docs":{},"会":{"docs":{},"弹":{"docs":{},"出":{"docs":{},"密":{"docs":{},"码":{"docs":{},"框":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"类":{"docs":{},"似":{"docs":{},"这":{"docs":{},"种":{"docs":{},"报":{"docs":{},"错":{"docs":{},"都":{"docs":{},"是":{"docs":{},"因":{"docs":{},"为":{"docs":{},"系":{"docs":{},"统":{"docs":{},"没":{"docs":{},"有":{"docs":{},"信":{"docs":{},"任":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"生":{"docs":{},"成":{"docs":{},"的":{"docs":{},"c":{"docs":{},"a":{"docs":{},"所":{"docs":{},"造":{"docs":{},"成":{"docs":{},"的":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"经":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"后":{"docs":{},",":{"docs":{},"期":{"docs":{},"望":{"docs":{},"的":{"docs":{},"返":{"docs":{},"回":{"docs":{},"如":{"docs":{},"下":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"被":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"后":{"docs":{},"的":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"处":{"docs":{},"理":{"docs":{},"流":{"docs":{},"程":{"docs":{},"同":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"一":{"docs":{},"致":{"docs":{},"。":{"docs":{},"未":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"会":{"docs":{},"再":{"docs":{},"进":{"docs":{},"入":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"做":{"docs":{},"处":{"docs":{},"理":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"规":{"docs":{},"则":{"docs":{},"文":{"docs":{},"件":{"docs":{},"中":{"docs":{},",":{"docs":{},"除":{"docs":{},"了":{"docs":{},"s":{"docs":{},"u":{"docs":{},"m":{"docs":{},"m":{"docs":{},"a":{"docs":{},"r":{"docs":{},"y":{"docs":{},",":{"docs":{},"都":{"docs":{},"是":{"docs":{},"由":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"内":{"docs":{},"提":{"docs":{},"供":{"docs":{},"`":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"`":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"返":{"docs":{},"回":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"(":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},")":{"docs":{},"全":{"docs":{},"面":{"docs":{},"支":{"docs":{},"持":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"和":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"模":{"docs":{},"块":{"docs":{},"应":{"docs":{},"该":{"docs":{},"符":{"docs":{},"合":{"docs":{},"c":{"docs":{},"m":{"docs":{},"d":{"docs":{},"规":{"docs":{},"范":{"docs":{},",":{"docs":{},"一":{"docs":{},"个":{"docs":{},"典":{"docs":{},"型":{"docs":{},"的":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"代":{"docs":{},"码":{"docs":{},"结":{"docs":{},"构":{"docs":{},"如":{"docs":{},"下":{"docs":{},"。":{"docs":{},"模":{"docs":{},"块":{"docs":{},"中":{"docs":{},"所":{"docs":{},"有":{"docs":{},"方":{"docs":{},"法":{"docs":{},"都":{"docs":{},"是":{"docs":{},"可":{"docs":{},"选":{"docs":{},"的":{"docs":{},",":{"docs":{},"只":{"docs":{},"需":{"docs":{},"实":{"docs":{},"现":{"docs":{},"业":{"docs":{},"务":{"docs":{},"感":{"docs":{},"兴":{"docs":{},"趣":{"docs":{},"的":{"docs":{},"部":{"docs":{},"分":{"docs":{},"即":{"docs":{},"可":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"是":{"docs":{},"用":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"的":{"docs":{},"介":{"docs":{},"绍":{"docs":{},"文":{"docs":{},"案":{"docs":{},",":{"docs":{},"用":{"docs":{},"于":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},"提":{"docs":{},"示":{"docs":{},"用":{"docs":{},"户":{"docs":{},",":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}},"能":{"docs":{},"力":{"docs":{},"范":{"docs":{},"围":{"docs":{},"包":{"docs":{},"括":{"docs":{},":":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"解":{"docs":{},"析":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"请":{"docs":{},"求":{"docs":{},"的":{"docs":{},"原":{"docs":{},"理":{"docs":{},"是":{"docs":{},"中":{"docs":{},"间":{"docs":{},"人":{"docs":{},"攻":{"docs":{},"击":{"docs":{},"(":{"docs":{},"m":{"docs":{},"a":{"docs":{},"n":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}},"警":{"docs":{},"告":{"docs":{},":":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{},"和":{"docs":{},"系":{"docs":{},"统":{"docs":{},"安":{"docs":{},"全":{"docs":{},"息":{"docs":{},"息":{"docs":{},"相":{"docs":{},"关":{"docs":{},",":{"docs":{},"建":{"docs":{},"议":{"docs":{},"亲":{"docs":{},"自":{"docs":{},"生":{"docs":{},"成":{"docs":{},",":{"docs":{},"并":{"docs":{},"妥":{"docs":{},"善":{"docs":{},"保":{"docs":{},"管":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"设":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}},"属":{"docs":{},"性":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"访":{"docs":{},"问":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},":":{"docs":{},"/":{"docs":{},"/":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"2":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{}}}}}}}}}},"证":{"docs":{},"书":{"docs":{},"配":{"docs":{},"置":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"请":{"docs":{},"求":{"docs":{},"b":{"docs":{},"o":{"docs":{},"d":{"docs":{},"i":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}},"使":{"docs":{},"用":{"docs":{},"的":{"docs":{},"协":{"docs":{},"议":{"docs":{},",":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"或":{"docs":{},"者":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"出":{"docs":{},"错":{"docs":{},"的":{"docs":{},"事":{"docs":{},"件":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"的":{"docs":{},"原":{"docs":{},"始":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{"./":{"ref":"./","tf":0.0014958863126402393}}}}}}}}}}}},"目":{"docs":{},"标":{"docs":{},"的":{"docs":{},"h":{"docs":{},"o":{"docs":{},"s":{"docs":{},"t":{"docs":{},",":{"docs":{},"受":{"docs":{},"制":{"docs":{},"于":{"docs":{},"协":{"docs":{},"议":{"docs":{},",":{"docs":{},"这":{"docs":{},"里":{"docs":{},"无":{"docs":{},"法":{"docs":{},"获":{"docs":{},"取":{"docs":{},"完":{"docs":{},"整":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"调":{"docs":{},"用":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"b":{"docs":{},"e":{"docs":{},"f":{"docs":{},"o":{"docs":{},"r":{"docs":{},"e":{"docs":{},"d":{"docs":{},"e":{"docs":{},"a":{"docs":{},"l":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"s":{"docs":{},"r":{"docs":{},"e":{"docs":{},"q":{"docs":{},"u":{"docs":{},"e":{"docs":{},"s":{"docs":{},"t":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"如":{"docs":{},"果":{"docs":{},"返":{"docs":{},"回":{"docs":{},"t":{"docs":{},"r":{"docs":{},"u":{"docs":{},"e":{"docs":{},",":{"docs":{},"会":{"docs":{},"明":{"docs":{},"文":{"docs":{},"解":{"docs":{},"析":{"docs":{},"这":{"docs":{},"个":{"docs":{},"请":{"docs":{},"求":{"docs":{},",":{"docs":{},"其":{"docs":{},"他":{"docs":{},"请":{"docs":{},"求":{"docs":{},"不":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"e":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"e":{"docs":{},"s":{"docs":{},"p":{"docs":{},"o":{"docs":{},"n":{"docs":{},"s":{"docs":{},"e":{"docs":{},"方":{"docs":{},"法":{"docs":{},",":{"docs":{},"由":{"docs":{},"模":{"docs":{},"块":{"docs":{},"对":{"docs":{},"响":{"docs":{},"应":{"docs":{},"内":{"docs":{},"容":{"docs":{},"进":{"docs":{},"行":{"docs":{},"处":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"运":{"docs":{},"行":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}},"返":{"docs":{},"回":{"docs":{},"值":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"错":{"docs":{},"误":{"docs":{},"页":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}},"这":{"docs":{},"里":{"docs":{},"提":{"docs":{},"供":{"docs":{},"一":{"docs":{},"些":{"docs":{},"样":{"docs":{},"例":{"docs":{},",":{"docs":{},"来":{"docs":{},"讲":{"docs":{},"解":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},"的":{"docs":{},"常":{"docs":{},"见":{"docs":{},"用":{"docs":{},"法":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}},"通":{"docs":{},"过":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{},"请":{"docs":{},"求":{"docs":{},":":{"docs":{},"c":{"docs":{},"u":{"docs":{},"r":{"docs":{},"l":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"启":{"docs":{},"动":{"docs":{},"参":{"docs":{},"数":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}},"自":{"docs":{},"定":{"docs":{},"义":{"docs":{},"的":{"docs":{},"r":{"docs":{},"u":{"docs":{},"l":{"docs":{},"e":{"docs":{},"来":{"docs":{},"修":{"docs":{},"改":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"这":{"docs":{},"种":{"docs":{},"方":{"docs":{},"式":{"docs":{},"初":{"docs":{},"始":{"docs":{},"化":{"docs":{},"的":{"docs":{},"a":{"docs":{},"n":{"docs":{},"y":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"x":{"docs":{},"y":{"docs":{},",":{"docs":{},"其":{"docs":{},"配":{"docs":{},"置":{"docs":{},"也":{"docs":{},"是":{"docs":{},"全":{"docs":{},"局":{"docs":{},"性":{"docs":{},"的":{"docs":{},",":{"docs":{},"所":{"docs":{},"有":{"docs":{},"网":{"docs":{},"站":{"docs":{},"的":{"docs":{},"证":{"docs":{},"书":{"docs":{},"问":{"docs":{},"题":{"docs":{},"都":{"docs":{},"会":{"docs":{},"被":{"docs":{},"忽":{"docs":{},"略":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"配":{"docs":{},"置":{"1":{"2":{"7":{"docs":{},".":{"0":{"docs":{},".":{"0":{"docs":{},".":{"1":{"docs":{},":":{"8":{"0":{"0":{"1":{"docs":{},"为":{"docs":{},"全":{"docs":{},"局":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{},"服":{"docs":{},"务":{"docs":{},"器":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}}},"docs":{}},"docs":{}},"docs":{},"i":{"docs":{},"o":{"docs":{},"s":{"docs":{},"/":{"docs":{},"a":{"docs":{},"n":{"docs":{},"d":{"docs":{},"r":{"docs":{},"o":{"docs":{},"i":{"docs":{},"d":{"docs":{},"系":{"docs":{},"统":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"o":{"docs":{},"s":{"docs":{},"x":{"docs":{},"系":{"docs":{},"统":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}},"启":{"docs":{},"动":{"docs":{},"端":{"docs":{},"口":{"docs":{},",":{"docs":{},"如":{"1":{"0":{"8":{"0":{"docs":{},"端":{"docs":{},"口":{"docs":{},"启":{"docs":{},"动":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}},"docs":{}},"docs":{}},"docs":{}},"docs":{}}}}}}},"浏":{"docs":{},"览":{"docs":{},"器":{"docs":{},"h":{"docs":{},"t":{"docs":{},"t":{"docs":{},"p":{"docs":{},"代":{"docs":{},"理":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"附":{"docs":{},"录":{"docs":{},":":{"docs":{},"如":{"docs":{},"何":{"docs":{},"信":{"docs":{},"任":{"docs":{},"c":{"docs":{},"a":{"docs":{},"证":{"docs":{},"书":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}},"限":{"docs":{},"速":{"docs":{},"值":{"docs":{},",":{"docs":{},"单":{"docs":{},"位":{"docs":{},"k":{"docs":{},"b":{"docs":{},"/":{"docs":{},"s":{"docs":{},",":{"docs":{},"默":{"docs":{},"认":{"docs":{},"不":{"docs":{},"限":{"docs":{},"速":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"除":{"docs":{},"了":{"docs":{},"上":{"docs":{},"述":{"docs":{},"证":{"docs":{},"书":{"docs":{},"安":{"docs":{},"装":{"docs":{},"过":{"docs":{},"程":{"docs":{},",":{"docs":{},"还":{"docs":{},"需":{"docs":{},"要":{"docs":{},"在":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}},"需":{"docs":{},"要":{"docs":{},"编":{"docs":{},"写":{"docs":{},"一":{"docs":{},"个":{"docs":{},"规":{"docs":{},"则":{"docs":{},"模":{"docs":{},"块":{"docs":{},",":{"docs":{},"在":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}},"驱":{"docs":{},"动":{"docs":{},"的":{"docs":{},",":{"docs":{},"函":{"docs":{},"数":{"docs":{},"需":{"docs":{},"要":{"docs":{},"满":{"docs":{},"足":{"docs":{},"y":{"docs":{},"i":{"docs":{},"e":{"docs":{},"l":{"docs":{},"d":{"docs":{},"a":{"docs":{},"b":{"docs":{},"l":{"docs":{},"e":{"docs":{},"。":{"docs":{},"可":{"docs":{},"以":{"docs":{},"使":{"docs":{},"用":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"方":{"docs":{},"法":{"docs":{},"或":{"docs":{},"是":{"docs":{},"返":{"docs":{},"回":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}},"返":{"docs":{},"回":{"docs":{},"p":{"docs":{},"r":{"docs":{},"o":{"docs":{},"m":{"docs":{},"i":{"docs":{},"s":{"docs":{},"e":{"docs":{},"或":{"docs":{},"使":{"docs":{},"用":{"docs":{},"g":{"docs":{},"e":{"docs":{},"n":{"docs":{},"e":{"docs":{},"r":{"docs":{},"a":{"docs":{},"t":{"docs":{},"o":{"docs":{},"r":{"docs":{},"函":{"docs":{},"数":{"docs":{},"。":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},",":{"docs":{},"w":{"docs":{},"e":{"docs":{},"b":{"docs":{},"界":{"docs":{},"面":{"docs":{},"上":{"docs":{},"能":{"docs":{},"看":{"docs":{},"到":{"docs":{},"所":{"docs":{},"有":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}}}},"界":{"docs":{},"面":{"docs":{},"上":{"docs":{},"能":{"docs":{},"看":{"docs":{},"到":{"docs":{},"刚":{"docs":{},"才":{"docs":{},"的":{"docs":{},"请":{"docs":{},"求":{"docs":{},"信":{"docs":{},"息":{"docs":{"./":{"ref":"./","tf":0.0007479431563201197}}}}}}}}}}}}}}}},",":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}},"[":{"docs":{},"s":{"docs":{},"w":{"docs":{},"i":{"docs":{},"t":{"docs":{},"c":{"docs":{},"h":{"docs":{},"y":{"docs":{},"o":{"docs":{},"m":{"docs":{},"e":{"docs":{},"g":{"docs":{},"a":{"docs":{},"]":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}}}}}},"j":{"docs":{},"a":{"docs":{},"v":{"docs":{},"a":{"docs":{},"s":{"docs":{},"c":{"docs":{},"r":{"docs":{},"i":{"docs":{},"p":{"docs":{},"t":{"docs":{},".":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}}}}}}}}}},"s":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799}}}},":":{"docs":{"./":{"ref":"./","tf":0.0004743833017077799},"doc.html":{"ref":"doc.html","tf":0.0004873294346978557}}}},"length":3960},"corpusTokens":["\"content","\"curl/7.43.0\"","\"data\":","\"https\"","\"i","\"thi","\"turn","\"user","\"x","#","#gener","#global","#launch","#local","#全局包","#启动anyproxy,并解析所有https请求","#本地包","#生成rootca证书,生成后需要手动信任","&&","'","'...'","'...',","'/',","'/index.html';","'/redirect/to/another/path';","'/user","'127.0.0.1'","'8001');","'8008';","';","'a","'anyproxy';","'anyproxy.io',","'anyproxy/0.0.0';","'application/json'","'cach","'close',","'content","'get',","'get';","'hello","'http',","'http';","'http://anyproxy.io/',","'http://httpbin.org/get?show_env=1'","'http://httpbin.org/us","'https'","'https',","'i","'image/gif',","'keep","'mi","'origin","'proxi","'rule","'text/html'","'text/html';","'thi","'user","'{\"hello\":","(!anyproxy.utils.certmgr.ifrootcafileexists())","(!error)","()","(){","(e)","(iswin)","(requestdetail.url","(requestdetail.url.indexof('http://httpbin.org')","(requestdetail.url.indexof('http://httpbin.org/post')","(requestdetail.url.indexof('http://httpbin.org/us","(requestdetail.url.indexof('https://httpbin.org/us","(requestdetail.url.indexof('https://th","*/","*/}","*beforedealhttpsrequest(requestdetail)","*beforesendrequest(requestdetail)","*beforesendresponse(requestdetail,","*onconnecterror(requestdetail,","*onerror(requestdetail,","*true*","+=",",",".",".',","...","...,","./myrulepkg/","./rule.j","/*","//","//when","//这里也可以同时加上requestopt","/^win/.test(process.platform);","0)","1.","10.3","10.3信任ca证书","10000,","1080","127.0.0.1:8001","127.0.0.1:8001,","127.0.0.1:8001/index.html","127.0.0.1:8001,访问","1,write","1,编写规则","2,","2.","200,","3,","3.x:","300);","4,","4.0","4.0的主要变化:","4.0,欢迎提供反馈","4.x","404","404;","443;","5","5000);","5s","80,","8001","8001,","8002,","8003,","=","===","=>",">=",">关于本机",">证书信任设置",">通用","[switchyomega]","_re","_req","_req:","_res:","`","`;","a:","abil","abliti","activ","add","address","advanc","advance.","agent","agent\":","agent'","agent')","agent':","agent';","agent']","agent,","alive',","alway","android","anyprox","anyproxi","anyproxy',","anyproxy.","anyproxy.io","anyproxy.io,","anyproxy.proxycore(options);","anyproxy.proxyserv","anyproxy.proxyserver(options)","anyproxy.proxyserver(options);","anyproxy.utils.certmgr","anyproxy.utils.certmgr.generaterootca((error,","anyproxy.utils.certmgr.generaterootca(callback)","anyproxy.utils.certmgr.ifrootcafileexists()","anyproxy.utils.systemproxymgr","anyproxy.utils.systemproxymgr.disableglobalproxy();","anyproxy.utils.systemproxymgr.enableglobalproxy('127.0.0.1',","anyproxy@beta","anyproxyin","anyproxyins.start();","anyproxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包","anyproxy可以作为一个npm模块使用,整合进其他工具。","anyproxy向客户端发送请求前,会调用beforesendresponse,并带上参数requestdetail","anyproxy向服务端发送请求前,会调用beforesendrequest,并带上参数requestdetail","anyproxy在与目标https服务器建立连接的过程中,如果发生错误,anyproxy会调用这个方法","anyproxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。","anyproxy收到https请求时,会调用beforedealhttpsrequest,并带上参数requestdetail","anyproxy是一个开放式的http代理服务器。","anyproxy调用规则模块beforesendrequest方法,由模块做处理,返回新的请求参数,或返回响应内容","anyproxy默认不对https请求做处理,如需看到明文信息,需要配置ca证书","apg","append","appendix:how","at',","attack","attack.","attack),需要客户端提前信任anyproxy生成的ca","be","befor","beforedealhttpsrequest","beforedealhttpsrequest(requestdetail)","beforesendrequest","beforesendrequest(requestdetail)","beforesendrespons","beforesendresponse(requestdetail,","below.","below:","besid","beta","bodi","body,","body:","body三个字段","body等","body),甚至是请求的目标地址等","bonou","browser","browser.","by']","by:","by:anyproxi","ca","ca,","ca.","call","caus","ca方法。","ca,按提示扫描二维码即可安装","cert","certdir","certdir);","certif","certificate.","certificates,","certificates.","certifyc","chang","chart","chrome","class:","cli,","cli.","click","client","client,","close","co","co.","code","code)、响应头(respons","code,","collect","command","common","config","config.","configur","connect","connection':","connection:","connections.","console,","console.error('error","console.log('th","const","content","content,","control':","couldn't","course.","creat","curl","curl:","custom","cwd:","d","dangerouslyignoreunauthor","dangerouslyignoreunauthorized:","dangerouslyignoreunauthorized:true,","data","data\"","data'","data',","deal","dealed.","deall","debian","decrypt","default","default.","defaultfals","defin","delay","demo","design","detect","determin","directli","disabl","dn","doc","doubl","driven","due","dure","e.g.","edit","emit","enabl","enable:","end","english","entir","error","error)","error);","errors,","event:","example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例","except","exec","exec('open","exec('start","exist","expect","extent","face","fail","failure,","fals","false,","false;","faq","feature,","features:","field","file","file,","file.","file:","find","finish","flow","follow","follows.","forc","forceproxyhttp","forceproxyhttps:","forward","found","frequent","full","fulli","function","function()","function.","g","gener","get","github:","github主页:https://github.com/alibaba/anyproxy/tree/4.x","give","global","go,","gui","guid","h","hacked!","hacked...\")","handl","handling.","happen","header","header),请求体(request","header,","header:","headers:","headersar","header)、响应内容等","help","here","here,","here.","hood,","host","host:","hostname:","http","http.rquest,","http/1.1","http/http","http://....j","http://127.0.0.1:8001","http://127.0.0.1:8002","http://127.0.0.1:8002,","http://httpbin.org","http://httpbin.org/","http://httpbin.org/ani","http://httpbin.org/post","http://httpbin.org/us","http://localhost:8008/index.html","httpbin.org","httpbin.org,","https://github.com/alibaba/anyproxy/tree/4.x","https://httpbin.org/us","https://nodejs.org/api/http.html#http_http_request_options_callback","https://sample.com/rule.j","https连接服务器出错","http代理配置","http的代理模式中,这里的request是connect请求","i.e.","ignor","import","in.","includ","influenc","info","info,","insid","instal","instruct","intercept","intercept`参数,按npm模块启动时配置`forceproxyhttps`参数,所有https请求都会被替换证书并解析","interest","interfac","interface,","introduct","invok","io","ios/android","ios系统信任ca证书","issu","iswin","it,","it.","javascript.","js","kb/s,","keep","keypath)","kind","know","know.com')","known","launch","launching,","legaci","line","line,","list","load","local","localrespons","log","logic","login","longer.","luanch","make","man","manag","manual","messag","method","method,","method:","middl","middle),用户必须信任anyproxy生成的ca证书,才能进行后续流程","mind","modifi","modul","module,","module.","module.export","myrulepkg","nativ","need","network.","new","newli","newopt","newoption.path","newoption.port","newrequestopt","newrequestoptions.headers['us","newrequestoptions.hostnam","newrequestoptions.method","newrequestoptions.path","newrequestoptions.port","newrequestoptions.rejectunauthor","newrespons","newresponse.bodi","newresponse.header['cont","newresponse.header['x","newresponse.statuscod","node.j","nodej","nodejs.","nothing,","now","npm","null","null;","number","object","object.","object.assign({},","offer","on","on\"","onconnecterror","onconnecterror(requestdetail,","one.","onerror","onerror(requestdetail,","onlin","option","option,","optional,","osx","osx系统信任ca证书","otherwire,","otherwise,","over","own.","page","param","part","pass","password","path","path:","place","pleas","point","port","port:","post","privat","process","promis","promise((resolve,","propmt","protocol","protocol\":","protocol,","protocol:","protocol,i.e.","provid","proxi","proxy.close();","proxy.on('error',","proxy.on('ready',","proxy.start();","proxyserv","proxyserver.close();","proxyserver.on('error',","proxyserver.on('ready',","proxyserver.start();","q:","quest","read","readi","receiv","redirect","ref:","refer","reject)","rejectunauthor","replac","request","request,","request.","requestdata","requestdata:","requestdetail","requestdetail.protocol","requestdetail.requestoptions);","requestdetail.requestoptions;","requestdetail;","requesting,","requestopt","requestoptions:","requests,","requests.","requir","require('anyproxy');","require('child_process').exec;","require('http').request","require('myrulemodule'),","require('path').dirname(keypath);","require()","required,","resolve({","respons","response\"}'","response',","response:","responsedetail","responsedetail)","responsedetail.response);","responsedetail.response;","return","risk.","root","rootca","rootca',","rootca.crt","rule","rule:","rule_sample/sample_modify_request_data.j","rule_sample/sample_modify_request_header.j","rule_sample/sample_modify_request_path.j","rule_sample/sample_modify_request_protocol.j","rule_sample/sample_modify_response_data.j","rule_sample/sample_modify_response_header.j","rule_sample/sample_modify_response_statuscode.j","rule_sample/sample_use_local_response.j","rule接口文档","rule样例","rule模块","run","safari","safe","same","sampl","sample.j","sample:","save","scenario,","second","seconds.","secur","security.","see","self","send","sent","server","server,","server.","set","settimeout(","settimeout(()","settings.","side","signed.","silent","silent:","simpl","simplifi","site","site(s)","situation.","skip","someth","source.","specifi","ssl","start","statu","statuscod","statuscode:","step","string","stuff:","sudo","summari","summary():","summary,","summary:","summary:str","support","sure","system","take","target","test","test:","text","text/plain\"","that.","this:","throttl","throttle:","time","timeout","tip","to!","toward","traffic","tri","true","true,","true;","true或者false,表示是否需要anyproxy替换证书并解析http","trust","trust)","type':","type']","type:","typic","ubuntu","ui,","ui中的","unauthor","under","unknown","unlimit","unsecur","untrust","url","url:","us","use,","user","user.","users,","usual","valid","valu","var","via","view","visit","want","warning:","way","web","webinterfac","webinterface:","webport","webport:","web版界面端口号,默认8002","web版界面配置","web版界面重构","whether","wifi","window","window.alert(\"sorry,","windows系统信任ca证书","without","work","world'","write","written","wsport:","x","yieldabl","yieldable,","you'r","{","{boolean}","{number}","{object}","{string}","|","}","})","});","},","};","不做任何处理。此时anyproxy会返回一个默认的错误页。","不做任何处理,返回null","中把anyproxy证书的开关打开,否则safari将报错。","中文文档","为","为什么https请求不能进入处理函数?","主要特性包括:","举例","举例,请求","举例:请求","代理http","代理服务器发生错误","代理服务器启动完成","代理服务器都在wifi设置中配置","以chrome的switchyomega插件为例","以下任意一项都能用来改变https的处理特性:","以下几种返回都是合法的","作为npm模块使用","作为全局模块","你可以通过","使用npm包","使用举例","使用在线地址","使用本地数据","使用本地路径anyproxi","使用示例","修改发送到","修改请求bodi","修改请求协议","修改请求协议,如强制改用https发起请求","修改请求参数","修改请求头","修改请求数据","修改请求的目标地址","修改返回内容并延迟","修改返回头","修改返回状态码","修改返回的内容","修改返回的状态码","关闭代理服务器","关闭全局代理服务器","其他命令","创建代理服务器","即将发送的请求配置,供require('http').request作为使用。详见:https://nodejs.org/api/http.html#http_http_request_options_callback","原始的服务端返回对象","双击打开rootca.crt","发送响应前处理","发送请求前拦截处理","只有返回true时,anyproxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。","可以是一个函数,也可以是一个普通的字符串","可修改内容包括请求头(request","可修改的内容包括http状态码(statu","同beforedealhttpsrequest中的参数","同beforesendrequest中的参数","启动","启动anyproxy,加载规则","启动代理服务器","启动后将终端http代理服务器配置为127.0.0.1:8001即可","命令行启动anyproxy时配置`","命令行启动anyproxy,默认端口号8001","命令行直接启动","在","在nodejs代码中启动","在wifi高级设置中,配置http代理即可","在构造anyproxy实例的时候,传入参数dangerouslyignoreunauthorized:true,","在请求处理过程中发生错误时,anyproxy会调用onerror方法,并提供对应的错误信息","基于node.js,开放二次开发能力,允许自定义请求处理逻辑","处理流程","处理流程图如下","多数场景下,错误会在请求目标服务器的时候发生,比如dns解析失败、请求超时等","如下几种方案都可以用来引用规则模块:","如下:","如何引用","如果beforesendrequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。","如果配置了全局解析https的参数,则anyproxy会略过这个调用","如要启用https解析,请在代理服务器启动前自行调用anyproxy.utils.certmgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxi","安装","安装ca:","对于debian或者ubuntu系统,在安装anyproxy之前,可能还需要安装","开发示例","引入","当http请求经过代理服务器时,具体处理过程是:","当代理服务器收到https请求时,anyproxy可以替换证书,对请求做明文解析。","当访问特定的https站点,anyproxy会提示该站点不是一个安全的网站,这通常是因为站点的证书设置不能被正确识别导致的(比如,站点的证书是自签发的)。如果您信任该网站,可以用以下方式来继续访问:","必选,代理服务器端口","快速开始","我们自然也可以借助自定义的rule来实现这个效果,而且我们还可以控制到只允许指定网址的证书错误,对不在列表的网址,进行证书的强验证。","所有http://httpbin.org","找到刚刚导入的anyproxy证书,配置为信任(alway","把","把响应信息返回给客户端","把所有发送到","把用http协议请求的","拦截https请求,对内容做修改","拦截发送到","拦截并修改服务端响应","拦截并修改正在发送的请求","提供gui界面,用以观察请求","提示","支持https的解析","收集请求所有请求参数,包括method,","改成https并发送","时,requestdetail参数内容大致如下","时,responsedetail参数内容大致如下","是否启用web版界面,默认fals","是否处理https请求","是否屏蔽所有console输出,默认fals","是否强制拦截所有的https,忽略规则模块的返回,默认fals","是否忽略请求中的证书错误,默认fals","是必选字段","服务端的返回信息,包括statuscod","本文档的适用范围是anyproxi","本质是中间人攻击(man","来加载模块并体验","来忽略证书认证的错误。需要注意的是,该参数是全局生效的,如果你在此期间访问了其他未知的网站,他们的证书问题也会被忽略,这可能会带来安全隐患。","查看请求信息","校验系统内是否存在anyproxy的根证书","样例","根据请求参数,向服务端发出请求,接收服务端响应。","模块介绍","此处无法控制向客户端的返回信息,无需返回值。","注意:http","注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题","测试规则","浏览器访问http://127.0.0.1:8002","点击web","然后,安装anyproxi","生成anyproxy的rootca,完成后请引导用户信任.crt文件","生成证书并解析所有https请求","用curl发请求测试的方法如下","用curl测试","用浏览器测试:配置浏览器http代理为","的https请求会被解析","的post数据","的user","的请求全部改到","的请求,使用本地数据代替服务端返回","的返回值里加上测试信息,并延迟5秒返回","的返回头里加上","的返回最后追加anyproxy的签名,并延迟5秒","的返回状态码都改成404","直接请求服务器:curl","直接返回客户端,不再发起请求,其中statuscod","相比3.x版本,anyproxi","确认将证书添加到login或system","示例","简介","简化了规则文件内的接口","管理anyproxy的证书","管理系统的全局代理配置,方法调用时可能会弹出密码框","类似这种报错都是因为系统没有信任anyproxy生成的ca所造成的","经过代理服务器后,期望的返回如下","自定义规则模块","被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。","规则文件中,除了summary,都是由","规则文件内提供`beforedealhttpsrequest`方法,返回","规则文件(rule)全面支持promise和gener","规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。","规则模块是用","规则模块的介绍文案,用于anyproxy提示用户,","规则模块的能力范围包括:","解析https请求的原理是中间人攻击(man","警告:ca证书和系统安全息息相关,建议亲自生成,并妥善保管","设置","设置属性","访问http://127.0.0.1:8002","证书配置","请求bodi","请求url","请求使用的协议,http或者http","请求出错的事件","请求的原始request","请求目标的host,受制于协议,这里无法获取完整url","调用规则模块beforedealhttpsrequest方法,如果返回true,会明文解析这个请求,其他请求不处理","调用规则模块beforesendresponse方法,由模块对响应内容进行处理","运行","返回值","返回自定义错误页","这里提供一些样例,来讲解规则模块的常见用法","通过代理服务器请求:curl","通过启动参数","通过自定义的rule来修改","通过这种方式初始化的anyproxy,其配置也是全局性的,所有网站的证书问题都会被忽略","配置127.0.0.1:8001为全局http代理服务器","配置ios/android系统代理","配置osx系统代理","配置启动端口,如1080端口启动","配置浏览器http代理","附录:如何信任ca证书","限速值,单位kb/s,默认不限速","除了上述证书安装过程,还需要在","需要编写一个规则模块,在","驱动的,函数需要满足yieldable。可以使用generator方法或是返回promise。","驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。",",web界面上能看到所有的请求信息",",界面上能看到刚才的请求信息",":"],"pipeline":["stopWordFilter","stemmer"]},"store":{"./":{"url":"./","title":"Introduction","keywords":"","body":"AnyProxy\nAnyProxy is a fully configurable http/https proxy in NodeJS.\nRef: 中文文档\nGithub:\n\nhttps://github.com/alibaba/anyproxy/tree/4.x\n\nFeatures:\n\nOffer you the ablity to handle http traffic by invoking a js module\nIntercept https\nGUI webinterface\n\nChange Logs since 3.x:\n\nSupport Promise and Generator in rule module\nSimplified interface in rule module\nA newly designed web interface\n\n\nGetting Start\ninstall\nTo Debian and Ubuntu users, you may need to install nodejs-legacy at the same time\nsudo apg-get install nodejs-legacy\n\nThen install the AnyProxy\nnpm install -g anyproxy\n\nlaunch\n\nstart AnyProxy in command line, with default port 8001\n\nanyproxy\n\n\nnow you can use http proxy server by 127.0.0.1:8001\nvisit http://127.0.0.1:8002 to see the http requests\n\noptions\n\nspecify the port of http proxy\n\nanyproxy --port 1080\n\nUse AnyProxy as an npm module\nAnyProxy can be used as an npm module\n\nTo enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.\n\n\ninstall\n\nnpm i anyproxy --save\n\n\nsample\n\nconst AnyProxy = require('anyproxy');\nconst options = {\n port: 8001,\n rule: require('myRuleModule'),\n webInterface: {\n enable: true,\n webPort: 8002,\n wsPort: 8003,\n },\n throttle: 10000,\n forceProxyHttps: false,\n silent: false\n};\nconst proxyServer = new AnyProxy.ProxyServer(options);\n\nproxyServer.on('ready', () => { /* */ });\nproxyServer.on('error', (e) => { /* */ });\nproxyServer.start();\n\n//when finished\nproxyServer.close();\n\n\nClass: AnyProxy.proxyServer\n\ncreate a proxy server\nconst proxy = new AnyProxy.proxyServer(options)\n\n\noptions\n\nport {number} required, port number of proxy server\nrule {object} your rule module\nthrottle {number} throttle in kb/s, unlimited for default\nforceProxyHttps {boolean} in force intercept all https request, false for default\nsilent {boolean} if keep silent in console, false for defaultfalse\ndangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default\nwebInterface {object} config for web interface\nenable {boolean} if enable web interface, false for default\nwebPort {number} port number for web interface\n\n\n\n\nEvent: ready\n\nemit when proxy server is ready\nsample\n\nproxy.on('ready', function() { })\n\n\nEvent: error\n\nemit when error happened inside proxy server\nsample\n\nproxy.on('error', function() { })\n\n\nMethod: start\n\nstart proxy server\nsample\n\nproxy.start();\n\n\nMethod: close\n\nclose proxy server\nsample\n\nproxy.close();\n\n\n\n\nAnyProxy.utils.systemProxyMgr\n\nmanage the system proxy config. sudo password may be required\nsample\n\n// set 127.0.0.1:8001 as system http server\nAnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');\n\n// disable global proxy server\nAnyProxy.utils.systemProxyMgr.disableGlobalProxy();\n\n\nAnyProxy.utils.certMgr\n\nManage certificates of AnyProxy\nAnyProxy.utils.certMgr.ifRootCAFileExists()\ndetect if AnyProx rootCA exists\n\n\nAnyProxy.utils.certMgr.generateRootCA(callback)\ngenerate a rootCA\n\n\nSample\n\n const AnyProxy = require('AnyProxy');\n const exec = require('child_process').exec;\n\n if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {\n AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {\n // let users to trust this CA before using proxy\n if (!error) {\n const certDir = require('path').dirname(keyPath);\n console.log('The cert is generated at', certDir);\n const isWin = /^win/.test(process.platform);\n if (isWin) {\n exec('start .', { cwd: certDir });\n } else {\n exec('open .', { cwd: certDir });\n }\n } else {\n console.error('error when generating rootCA', error);\n }\n });\n }\n\n\n\nProxy Https\n\nAnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.\n\n\nUnder the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.\n\n\ngenerate certifycates and intercept\n\nanyproxy-ca #generate root CA. manually trust it after that.\nanyproxy --intercept #launch anyproxy and intercept all https traffic\n\n\nAppendix:how to trust CA\n\nRule Introduction\nAnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.\n\nMake sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.\n\nRule module could do the following stuff:\n\nintercept and modify the request which is being sent\neditable fields include request header, body, target address\n\n\nintercept and modify the response from server\neditable fields include response status code, header, body\n\n\nintercept https requests, modify request and response\n\nsample\n\nTarget\n\nwrite a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds\n\n\nStep 1,Write the rule file, save as sample.js\n// file: sample.js\nmodule.exports = {\n summary: 'a rule to modify response',\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n\n newResponse.body += '-- AnyProxy Hacked! --';\n\n newResponse.body += `\n setTimeout(\n function (){\n window.alert(\"Sorry, You Are Hacked...\")\n }, 300);\n `;\n newResponse.header['Content-Type'] = 'text/html';\n\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n\nStep 2, start AnyProxy and load the rule file\n\nrun anyproxy --rule sample.js\n\n\nStep 3, test\n\nuse curl\ncurl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n\n\nuse browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent\n\nthe expected response from proxy is\n\n\n{\n \"user-agent\": \"curl/7.43.0\"\n}\n- AnyProxy Hacked!\n\nStep 4, view the request log\n\nvisit http://127.0.0.1:8002, the request just sent should be listed here\n\n\n\nhow does it work\n\nThe flow chart is as follows\n\n\n\nWhen got an http request, the entire process of proxy server is\n\nAnyProxy collects all the quest info, include method, header, body\nAnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content\nIf beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.\nSend request to target server, collect response\nCall beforeSendResponse of the rule module. Rule module deal the response data\nSend response to client\n\n\nWhen AnyProxy get https request, it could replace the certificate and decrypt the request data\n\nAnyProxy calls beforeDealHttpsRequest of the rule module\nIf the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.\n\n\n\nhow to load rule module\n\nuse local file\nanyproxy --rule ./rule.js\n\n\nuse an online rule file\nanyproxy --rule https://sample.com/rule.js\n\n\nuse an npm module\n\nAnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.\n\nanyproxy --rule ./myRulePkg/ #local module\nnpm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module\n\n\n\nRule module interface\nA typical rule module is as follows. All the functions are optional, just write the part you are interested in.\nmodule.exports = {\n // introduction\n summary: 'my customized rule for AnyProxy',\n // intercept before send request to server\n *beforeSendRequest(requestDetail) { /* ... */ },\n // deal response before send to client\n *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },\n // if deal https request\n *beforeDealHttpsRequest(requestDetail) { /* ... */ },\n // error happened when dealing requests\n *onError(requestDetail, error) { /* ... */ },\n // error happened when connect to https server\n *onConnectError(requestDetail, error) { /* ... */ }\n};\n\n\nAll functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.\n\nsummary\nsummary\n\nIntroduction of this rule file. AnyProxy will read this field and give some tip to user.\n\nbeforeSendRequest\nbeforeSendRequest(requestDetail)\n\nBefore sending request to server, AnyProxy will call beforeSendRequest with param requestDetail\nrequestDetail\nprotocol {string} the protocol to use, http or https\nrequestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback\nrequestData {object} request body\nurl {string} request url\n_req {object} the native node.js request object\n\n\ne.g. When requesting anyproxy.io, requestDetail is something like the following\n{\n protocol: 'http',\n url: 'http://anyproxy.io/',\n requestOptions: {\n hostname: 'anyproxy.io',\n port: 80,\n path: '/',\n method: 'GET',\n headers: {\n Host: 'anyproxy.io',\n 'Proxy-Connection': 'keep-alive',\n 'User-Agent': '...'\n }\n },\n requestData: '...',\n _req: { /* ... */}\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the request protocol,i.e. force use https\n\nreturn {\n protocol: 'https'\n};\n\n\nmodify request param\n\nvar newOption = Object.assign({}, requestDetail.requestOptions);\nnewOption.path = '/redirect/to/another/path';\nreturn {\n requestOptions: newOption\n};\n\n\nmodify request body\n\nreturn {\n requestData: 'my new request data'\n // requestOptions can also be used here\n};\n\n\ngive response to the client, not sending request any longer. statusCode headersare required is this situation.\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nbeforeSendResponse\nbeforeSendResponse(requestDetail, responseDetail)\n\nBefore sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail\nrequestDetail is the same param as in beforeSendRequest\nresponseDetail\nresponse {object} the response from server, includes statusCode header body\n_res {object} the native node.js response object\n\n\ne.g. When requesting anyproxy.io, responseDetail is something like the following\n{\n response: {\n statusCode: 200,\n header: {\n 'Content-Type': 'image/gif',\n Connection: 'close',\n 'Cache-Control': '...'\n },\n body: '...'\n },\n _res: { /* ... */ }\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the response status code\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.statusCode = 404;\nreturn {\n response: newResponse\n};\n\n\nmodify the response content\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.body += '--from anyproxy--';\nreturn {\n response: newResponse\n};\n\n\n\nbeforeDealHttpsRequest\nbeforeDealHttpsRequest(requestDetail)\n\nWhen receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail\nIf configed with forceProxyHttps in launching, AnyProxy will skip calling this method\nOnly by returning true, AnyProxy will try to replace the certificate and intercept the https request.\nrequestDetail\nhost {string} the target host to request. Due to the request protocol, full url couldn't be got here\n_req {object} the native node.js request object. The _req here refers to the CONNECT request.\n\n\nreturn value\ntrue or false, whether AnyProxy should intercept the https request\n\n\n\nonError\nonError(requestDetail, error)\n\nAnyProxy will call this method when an error happened in request handling.\nErrors usually are issued during requesting, e.g. DNS failure, request timeout\nrequestDetail is the same one as in beforeSendRequest\nAny of these return values are valid\n\ndo nothing, and AnyProxy will response a default error page\n\nreturn null;\n\n\nreturn a customized error page\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nonConnectError\nonConnectError(requestDetail, error)\n\nAnyProxy will call this method when failed to connect target server in https request\nrequestDetail is the same one as in beforeDealHttpsRequest\nno return value is required\n\nRule Samples\n\nhere are some samples about frequently used rule file\ntry these samples by anyproxy --rule http://....js\nhow to test with curl:\nrequest the server directly curl http://httpbin.org/\nrequest the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001\n\n\n\nuse local response\n\nintercept the request towards http://httpbin.org , return the local-defined response\n\nanyproxy --rule rule_sample/sample_use_local_response.js\n\n/* \n sample: \n intercept all requests toward httpbin.org, use a local response\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n const localResponse = {\n statusCode: 200,\n header: { 'Content-Type': 'application/json' },\n body: '{\"hello\": \"this is local response\"}'\n };\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n return {\n response: localResponse\n };\n }\n },\n};\n\nmodify request header\n\nmodify the user-agent sent to httpbin.org\n\nanyproxy --rule rule_sample/sample_modify_request_header.js\n\n/* \n sample: \n modify the user-agent in requests toward httpbin.org\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request body\n\nmodify the post body of http://httpbin.org/post\n\nanyproxy --rule rule_sample/sample_modify_request_data.js\n\n/*\n sample:\n modify the post data towards http://httpbin.org/post\n test:\n curl -H \"Content-Type: text/plain\" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001\n expected response:\n { \"data\": \"i-am-anyproxy-modified-post-data\" }\n*/\nmodule.exports = {\n summary: 'Rule to modify request data',\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {\n return {\n requestData: 'i-am-anyproxy-modified-post-data'\n };\n }\n },\n};\n\nmodify the request target\n\nsend all the request towards http://httpbin.org/ to http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_path.js\n\n/*\n sample:\n redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html\n test:\n curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n expected response:\n 'hello world' from 127.0.0.1:8001/index.html\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n requestDetail.protocol = 'http';\n newRequestOptions.hostname = '127.0.0.1'\n newRequestOptions.port = '8008';\n newRequestOptions.path = '/index.html';\n newRequestOptions.method = 'GET';\n return requestDetail;\n }\n },\n *beforeDealHttpsRequest(requestDetail) {\n return true;\n }\n};\n\nmodify request protocol\n\nmodify the http request towards http://httpbin.org to https\n\nanyproxy --rule rule_sample/sample_modify_request_protocol.js\n\n/* \n sample: \n redirect all http requests of httpbin.org to https\n test:\n curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001\n expected response:\n { \"X-Forwarded-Protocol\": \"https\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newOption = requestDetail.requestOptions;\n newOption.port = 443;\n return {\n protocol: 'https',\n requestOptions: newOption\n };\n }\n }\n};\n\nmodify response status code\n\nmodify all status code from http://httpbin.org to 404\n\nanyproxy --rule rule_sample/sample_modify_response_statuscode.js\n\n/* \n sample: \n modify all status code of http://httpbin.org/ to 404\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n HTTP/1.1 404 Not Found\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newResponse = responseDetail.response;\n newResponse.statusCode = 404;\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify the response header\n\nadd X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_response_header.js\n\n/* \n sample: \n modify response header of http://httpbin.org/user-agent\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n X-Proxy-By: AnyProxy\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {\n const newResponse = responseDetail.response;\n newResponse.header['X-Proxy-By'] = 'AnyProxy';\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify response data and delay\n\nappend some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds.\n\nanyproxy --rule rule_sample/sample_modify_response_data.js\n\n/* \n sample: \n modify response data of http://httpbin.org/user-agent\n test:\n curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" } -- AnyProxy Hacked! --\n*/\n\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay the response for 5s\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\nConfig Certification\nConfig root CA in OSX\n\nthis kind of errors is usually caused by untrusted root CA\n\n\n\nWarning: please keep your root CA safe since it may influence your system security.\n\ninstall :\n\ndouble click rootCA.crt\n\nadd cert into login or system\n\n\n\n\nfind the newly imported AnyProxy certificates, configured as Always Trust\n\n\nConfig root CA in windows\n\nConfig OSX system proxy\n\nthe config is in wifi - advanced\n\n\nconfig http proxy server\n\ntake Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例\n\n\ntrust root CA in iOS\n\nClick Root CA in web ui, and follow the instruction to install\n\n\ntrust root CA in iOS after 10.3\n\nBesides installing root CA, you have to \"turn on\" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.\n\n\nconfig iOS/Android proxy server\n\nproxy settings are placed in wifi setting\n\niOS\n\n\n\n\nAndroid\n\n\nFAQ\nQ: can not deal https request in rule module.\n\nA: Any of these options could be used to change the way AnyProxy deall https requests\nconfig --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module\nplace a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.\n\n\n\nQ: get an error says function is not yieldable\n\nA: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.\n\nQ: The connection is not private\nAnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.\n\nIf you run AnyProxy by command line\nPass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.\nanyproxy -i --ignore-unauthorized-ssl\n\n\nIf you run AnyProxy by Nodejs\nPass in the option dangerouslyIgnoreUnauthorized:true, like this:\nconst options = {\n ...,\n dangerouslyIgnoreUnauthorized: true\n};\n\nconst anyproxyIns = new AnyProxy.ProxyCore(options);\nanyproxyIns.start();\n\nThis is also a global option, all certification errors will be ignored\n\nWith the help of AnyProxy Rule\nYou can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n // set rejectUnauthorized as false\n newRequestOptions.rejectUnauthorized = false;\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nAnd we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!\n\n\n"},"doc.html":{"url":"doc.html","title":"Getting-Start","keywords":"","body":"Getting Start\ninstall\nTo Debian and Ubuntu users, you may need to install nodejs-legacy at the same time\nsudo apg-get install nodejs-legacy\n\nThen install the AnyProxy\nnpm install -g anyproxy@beta # 4.x is in beta now\n\nlaunch\n\nstart AnyProxy in command line, with default port 8001\n\nanyproxy\n\n\nnow you can use http proxy server by 127.0.0.1:8001\nvisit http://127.0.0.1:8002 to see the http requests\n\noptions\n\nspecify the port of http proxy\n\nanyproxy --port 1080\n\nUse AnyProxy as an npm module\nAnyProxy can be used as an npm module\n\nTo enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.\n\n\ninstall\n\nnpm i anyproxy@beta --save # 4.0 is in beta now\n\n\nsample\n\nconst AnyProxy = require('anyproxy');\nconst options = {\n port: 8001,\n rule: require('myRuleModule'),\n webInterface: {\n enable: true,\n webPort: 8002,\n wsPort: 8003,\n },\n throttle: 10000,\n forceProxyHttps: false,\n silent: false\n};\nconst proxyServer = new AnyProxy.ProxyServer(options);\n\nproxyServer.on('ready', () => { /* */ });\nproxyServer.on('error', (e) => { /* */ });\nproxyServer.start();\n\n//when finished\nproxyServer.close();\n\n\nClass: AnyProxy.proxyServer\n\ncreate a proxy server\nconst proxy = new AnyProxy.proxyServer(options)\n\n\noptions\n\nport {number} required, port number of proxy server\nrule {object} your rule module\nthrottle {number} throttle in kb/s, unlimited for default\nforceProxyHttps {boolean} in force intercept all https request, false for default\nsilent {boolean} if keep silent in console, false for defaultfalse\ndangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default\nwebInterface {object} config for web interface\nenable {boolean} if enable web interface, false for default\nwebPort {number} port number for web interface\n\n\n\n\nEvent: ready\n\nemit when proxy server is ready\nsample\n\nproxy.on('ready', function() { })\n\n\nEvent: error\n\nemit when error happened inside proxy server\nsample\n\nproxy.on('error', function() { })\n\n\nMethod: start\n\nstart proxy server\nsample\n\nproxy.start();\n\n\nMethod: close\n\nclose proxy server\nsample\n\nproxy.close();\n\n\n\n\nAnyProxy.utils.systemProxyMgr\n\nmanage the system proxy config. sudo password may be required\nsample\n\n// set 127.0.0.1:8001 as system http server\nAnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');\n\n// disable global proxy server\nAnyProxy.utils.systemProxyMgr.disableGlobalProxy();\n\n\nAnyProxy.utils.certMgr\n\nManage certificates of AnyProxy\nAnyProxy.utils.certMgr.ifRootCAFileExists()\ndetect if AnyProx rootCA exists\n\n\nAnyProxy.utils.certMgr.generateRootCA(callback)\ngenerate a rootCA\n\n\nSample\n\n const AnyProxy = require('AnyProxy');\n const exec = require('child_process').exec;\n\n if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {\n AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {\n // let users to trust this CA before using proxy\n if (!error) {\n const certDir = require('path').dirname(keyPath);\n console.log('The cert is generated at', certDir);\n const isWin = /^win/.test(process.platform);\n if (isWin) {\n exec('start .', { cwd: certDir });\n } else {\n exec('open .', { cwd: certDir });\n }\n } else {\n console.error('error when generating rootCA', error);\n }\n });\n }\n\n\n\nProxy Https\n\nAnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.\n\n\nUnder the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.\n\n\ngenerate certifycates and intercept\n\nanyproxy-ca #generate root CA. manually trust it after that.\nanyproxy --intercept #launch anyproxy and intercept all https traffic\n\n\nAppendix:how to trust CA\n\nRule Introduction\nAnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.\n\nMake sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.\n\nRule module could do the following stuff:\n\nintercept and modify the request which is being sent\neditable fields include request header, body, target address\n\n\nintercept and modify the response from server\neditable fields include response status code, header, body\n\n\nintercept https requests, modify request and response\n\nsample\n\nTarget\n\nwrite a rule module to append some text to the response of GET http://httpbin.org/user-agent, and delay the response for 5 seconds\n\n\nStep 1,Write the rule file, save as sample.js\n// file: sample.js\nmodule.exports = {\n summary: 'a rule to modify response',\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\n\nStep 2, start AnyProxy and load the rule file\n\nrun anyproxy --rule sample.js\n\n\nStep 3, test\n\nuse curl\ncurl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n\n\nuse browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent\n\nthe expected response from proxy is\n\n\n{\n \"user-agent\": \"curl/7.43.0\"\n}\n- AnyProxy Hacked!\n\nStep 4, view the request log\n\nvisit http://127.0.0.1:8002, the request just sent should be listed here\n\n\n\nhow does it work\n\nThe flow chart is as follows\n\n\n\nWhen got an http request, the entire process of proxy server is\n\nAnyProxy collects all the quest info, include method, header, body\nAnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content\nIf beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.\nSend request to target server, collect response\nCall beforeSendResponse of the rule module. Rule module deal the response data\nSend response to client\n\n\nWhen AnyProxy get https request, it could replace the certificate and decrypt the request data\n\nAnyProxy calls beforeDealHttpsRequest of the rule module\nIf the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.\n\n\n\nhow to load rule module\n\nuse local file\nanyproxy --rule ./rule.js\n\n\nuse an online rule file\nanyproxy --rule https://sample.com/rule.js\n\n\nuse an npm module\n\nAnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.\n\nanyproxy --rule ./myRulePkg/ #local module\nnpm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module\n\n\n\nRule module interface\nA typical rule module is as follows. All the functions are optional, just write the part you are interested in.\nmodule.exports = {\n // introduction\n summary: 'my customized rule for AnyProxy',\n // intercept before send request to server\n *beforeSendRequest(requestDetail) { /* ... */ },\n // deal response before send to client\n *beforeSendResponse(requestDetail, responseDetail) { /* ... */ },\n // if deal https request\n *beforeDealHttpsRequest(requestDetail) { /* ... */ },\n // error happened when dealing requests\n *onError(requestDetail, error) { /* ... */ },\n // error happened when connect to https server\n *onConnectError(requestDetail, error) { /* ... */ }\n};\n\n\nAll functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.\n\nsummary\nsummary\n\nIntroduction of this rule file. AnyProxy will read this field and give some tip to user.\n\nbeforeSendRequest\nbeforeSendRequest(requestDetail)\n\nBefore sending request to server, AnyProxy will call beforeSendRequest with param requestDetail\nrequestDetail\nprotocol {string} the protocol to use, http or https\nrequestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback\nrequestData {object} request body\nurl {string} request url\n_req {object} the native node.js request object\n\n\ne.g. When requesting anyproxy.io, requestDetail is something like the following\n{\n protocol: 'http',\n url: 'http://anyproxy.io/',\n requestOptions: {\n hostname: 'anyproxy.io',\n port: 80,\n path: '/',\n method: 'GET',\n headers: {\n Host: 'anyproxy.io',\n 'Proxy-Connection': 'keep-alive',\n 'User-Agent': '...'\n }\n },\n requestData: '...',\n _req: { /* ... */}\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the request protocol,i.e. force use https\n\nreturn {\n protocol: 'https'\n};\n\n\nmodify request param\n\nvar newOption = Object.assign({}, requestDetail.requestOptions);\nnewOption.path = '/redirect/to/another/path';\nreturn {\n requestOptions: newOption\n};\n\n\nmodify request body\n\nreturn {\n requestData: 'my new request data'\n // requestOptions can also be used here\n};\n\n\ngive response to the client, not sending request any longer. statusCode headersare required is this situation.\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nbeforeSendResponse\nbeforeSendResponse(requestDetail, responseDetail)\n\nBefore sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail\nrequestDetail is the same param as in beforeSendRequest\nresponseDetail\nresponse {object} the response from server, includes statusCode header body\n_res {object} the native node.js response object\n\n\ne.g. When requesting anyproxy.io, responseDetail is something like the following\n{\n response: {\n statusCode: 200,\n header: {\n 'Content-Type': 'image/gif',\n Connection: 'close',\n 'Cache-Control': '...'\n },\n body: '...'\n },\n _res: { /* ... */ }\n}\n\n\nAny of these return values are valid\n\ndo nothing, and return null\n\nreturn null;\n\n\nmodify the response status code\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.statusCode = 404;\nreturn {\n response: newResponse\n};\n\n\nmodify the response content\n\nvar newResponse = Object.assign({}, responseDetail.response);\nnewResponse.body += '--from anyproxy--';\nreturn {\n response: newResponse\n};\n\n\n\nbeforeDealHttpsRequest\nbeforeDealHttpsRequest(requestDetail)\n\nWhen receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail\nIf configed with forceProxyHttps in launching, AnyProxy will skip calling this method\nOnly by returning true, AnyProxy will try to replace the certificate and intercept the https request.\nrequestDetail\nhost {string} the target host to request. Due to the request protocol, full url couldn't be got here\n_req {object} the native node.js request object. The _req here refers to the CONNECT request.\n\n\nreturn value\ntrue or false, whether AnyProxy should intercept the https request\n\n\n\nonError\nonError(requestDetail, error)\n\nAnyProxy will call this method when an error happened in request handling.\nErrors usually are issued during requesting, e.g. DNS failure, request timeout\nrequestDetail is the same one as in beforeSendRequest\nAny of these return values are valid\n\ndo nothing, and AnyProxy will response a default error page\n\nreturn null;\n\n\nreturn a customized error page\n\nreturn {\n response: {\n statusCode: 200,\n header: { 'content-type': 'text/html' },\n body: 'this could be a or '\n }\n};\n\n\n\nonConnectError\nonConnectError(requestDetail, error)\n\nAnyProxy will call this method when failed to connect target server in https request\nrequestDetail is the same one as in beforeDealHttpsRequest\nno return value is required\n\nRule Samples\n\nhere are some samples about frequently used rule file\ntry these samples by anyproxy --rule http://....js\nhow to test with curl:\nrequest the server directly curl http://httpbin.org/\nrequest the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001\n\n\n\nuse local response\n\nintercept the request towards http://httpbin.org , return the local-defined response\n\nanyproxy --rule rule_sample/sample_use_local_response.js\n\n/* \n sample: \n intercept all requests toward httpbin.org, use a local response\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n const localResponse = {\n statusCode: 200,\n header: { 'Content-Type': 'application/json' },\n body: '{\"hello\": \"this is local response\"}'\n };\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n return {\n response: localResponse\n };\n }\n },\n};\n\nmodify request header\n\nmodify the user-agent sent to httpbin.org\n\nanyproxy --rule rule_sample/sample_modify_request_header.js\n\n/* \n sample: \n modify the user-agent in requests toward httpbin.org\n test:\n curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request body\n\nmodify the post body of http://httpbin.org/post\n\nanyproxy --rule rule_sample/sample_modify_request_data.js\n\n/*\n sample:\n modify the post data towards http://httpbin.org/post\n test:\n curl -H \"Content-Type: text/plain\" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001\n expected response:\n { \"data\": \"i-am-anyproxy-modified-post-data\" }\n*/\nmodule.exports = {\n summary: 'Rule to modify request data',\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {\n return {\n requestData: 'i-am-anyproxy-modified-post-data'\n };\n }\n },\n};\n\nmodify the request target\n\nsend all the request towards http://httpbin.org/ to http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_request_path.js\n\n/* \n sample: \n redirect all httpbin.org requests to http://httpbin.org/user-agent\n test:\n curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n newRequestOptions.path = '/user-agent';\n newRequestOptions.method = 'GET';\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nmodify request protocol\n\nmodify the http request towards http://httpbin.org to https\n\nanyproxy --rule rule_sample/sample_modify_request_protocol.js\n\n/* \n sample: \n redirect all http requests of httpbin.org to https\n test:\n curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001\n expected response:\n { \"X-Forwarded-Protocol\": \"https\" }\n*/\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newOption = requestDetail.requestOptions;\n newOption.port = 443;\n return {\n protocol: 'https',\n requestOptions: newOption\n };\n }\n }\n};\n\nmodify response status code\n\nmodify all status code from http://httpbin.org to 404\n\nanyproxy --rule rule_sample/sample_modify_response_statuscode.js\n\n/* \n sample: \n modify all status code of http://httpbin.org/ to 404\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n HTTP/1.1 404 Not Found\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org') === 0) {\n const newResponse = responseDetail.response;\n newResponse.statusCode = 404;\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify the response header\n\nadd X-Proxy-By:AnyProxy to the response header from http://httpbin.org/user-agent\n\nanyproxy --rule rule_sample/sample_modify_response_header.js\n\n/* \n sample: \n modify response header of http://httpbin.org/user-agent\n test:\n curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n X-Proxy-By: AnyProxy\n*/\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {\n const newResponse = responseDetail.response;\n newResponse.header['X-Proxy-By'] = 'AnyProxy';\n return {\n response: newResponse\n };\n }\n }\n};\n\nmodify response data and delay\n\nappend some info to the response of http://httpbin.org/user-agent, then delay the response for 5 seconds.\n\nanyproxy --rule rule_sample/sample_modify_response_data.js\n\n/* \n sample: \n modify response data of http://httpbin.org/user-agent\n test:\n curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001\n expected response:\n { \"user-agent\": \"curl/7.43.0\" } -- AnyProxy Hacked! --\n*/\n\nmodule.exports = {\n *beforeSendResponse(requestDetail, responseDetail) {\n if (requestDetail.url === 'http://httpbin.org/user-agent') {\n const newResponse = responseDetail.response;\n newResponse.body += '-- AnyProxy Hacked! --';\n return new Promise((resolve, reject) => {\n setTimeout(() => { // delay the response for 5s\n resolve({ response: newResponse });\n }, 5000);\n });\n }\n },\n};\n\nConfig Certification\nConfig root CA in OSX\n\nthis kind of errors is usually caused by untrusted root CA\n\n\n\nWarning: please keep your root CA safe since it may influence your system security.\n\ninstall :\n\ndouble click rootCA.crt\n\nadd cert into login or system\n\n\n\n\nfind the newly imported AnyProxy certificates, configured as Always Trust\n\n\nConfig root CA in windows\n\nConfig OSX system proxy\n\nthe config is in wifi - advanced\n\n\nconfig http proxy server\n\ntake Chrome extent [SwitchyOmega] as an example(https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)为例\n\n\ntrust root CA in iOS\n\nClick Root CA in web ui, and follow the instruction to install\n\n\ntrust root CA in iOS after 10.3\n\nBesides installing root CA, you have to \"turn on\" the certificate for web manually in settings - general - about - Certificate Trust Settings. Otherwire, safari will not trust the root CA generated by AnyProxy.\n\n\nconfig iOS/Android proxy server\n\nproxy settings are placed in wifi setting\n\niOS\n\n\n\n\nAndroid\n\n\nFAQ\nQ: can not deal https request in rule module.\n\nA: Any of these options could be used to change the way AnyProxy deall https requests\nconfig --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module\nplace a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.\n\n\n\nQ: get an error says function is not yieldable\n\nA: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.\n\nQ: The connection is not private\nAnyProxy will propmt this message when the certification of the site you're visiting is not issued by a common known CA. This happens when the certification is self-signed. If you know and trust it, you can ignore the error as below.\n\nIf you run AnyProxy by command line\nPass in the option --ignore-unauthorized-ssl to ignore the certification errors, please mind that the option will be active for all connections.\nanyproxy -i --ignore-unauthorized-ssl\n\n\nIf you run AnyProxy by Nodejs\nPass in the option dangerouslyIgnoreUnauthorized:true, like this:\nconst options = {\n ...,\n dangerouslyIgnoreUnauthorized: true\n};\n\nconst anyproxyIns = new AnyProxy.ProxyCore(options);\nanyproxyIns.start();\n\nThis is also a global option, all certification errors will be ignored\n\nWith the help of AnyProxy Rule\nYou can change the request with rule of course. For this scenario, all you need is to pass in an option to Nodejs Http.rquest, as we do in AnyProxy. A simple demo below:\nmodule.exports = {\n *beforeSendRequest(requestDetail) {\n if (requestDetail.url.indexOf('https://the-site-you-know.com') === 0) {\n const newRequestOptions = requestDetail.requestOptions;\n // set rejectUnauthorized as false\n newRequestOptions.rejectUnauthorized = false;\n return {\n requestOptions: newRequestOptions\n };\n }\n },\n};\n\nAnd we get a bonous here, AnyProxy will only ignore the errors for the site(s) we want it to!\n\n\n"}}} \ No newline at end of file diff --git a/lib/asyncTaskMgr.js b/lib/asyncTaskMgr.js deleted file mode 100644 index a96987d..0000000 --- a/lib/asyncTaskMgr.js +++ /dev/null @@ -1,52 +0,0 @@ -function asyncTaskMgr(){ - var self = this; - - self.callbackList = { - sampleName:{ - status:0, /* 0,stopped,will not callback / 1,loading / 2,loaded */ - result:null, - callbackList:[] - } - } - - self.addTask = function(name,cb,action){ - if(self.callbackList[name]){ - var task = self.callbackList[name]; - - if(task.status == 2){ //done - cb && cb.apply(null,task.result); - - }else if(task.status == 1){ //pending - task.callbackList.push(cb); - - }else if(task.status == 0){ //stopped - return; //do nothing - } - }else{ - var task; - task = self.callbackList[name] = { - status : 1, - result : null, - callbackList : [cb] - }; - - action && action.call(null,function(){ //action应该带一个回调 - if(arguments && arguments[0] === -1){ //返回第一个参数为-1,为停止任务 - task.status = 0; - task.callbackList = []; - }else{ - task.result = arguments; - task.status = 2; - var tmpCb; - while(tmpCb = task.callbackList.shift()){ - tmpCb && tmpCb.apply(null,task.result); - } - } - }); - } - } - - -}; - -module.exports = asyncTaskMgr; diff --git a/lib/certGenerator.js b/lib/certGenerator.js deleted file mode 100644 index d23bab2..0000000 --- a/lib/certGenerator.js +++ /dev/null @@ -1,91 +0,0 @@ -var forge = require('node-forge'); - -var defaultAttrs = [ - { name: 'countryName', value: 'CN' }, - { name: 'organizationName', value: 'AnyProxy' }, - { shortName: 'ST', value: 'SH' }, - { shortName: 'OU', value: 'AnyProxy SSL Proxy'} -]; - -function getKeysAndCert(serialNumber){ - var keys = forge.pki.rsa.generateKeyPair(1024); - var cert = forge.pki.createCertificate(); - cert.publicKey = keys.publicKey; - cert.serialNumber = serialNumber || (Math.floor(Math.random() * 100000) + ''); - cert.validity.notBefore = new Date(); - cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 10); // 10 years - cert.validity.notAfter = new Date(); - cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 10); // 10 years - return { - keys: keys, - cert: cert - }; -} - -function generateRootCA(){ - var keysAndCert = getKeysAndCert(); - keys = keysAndCert.keys; - cert = keysAndCert.cert; - - var attrs = defaultAttrs.concat([ - { - name: 'commonName', - value: 'AnyProxy' - } - ]); - cert.setSubject(attrs); - cert.setIssuer(attrs); - cert.setExtensions([ - { name: 'basicConstraints', cA: true } - // { name: 'keyUsage', keyCertSign: true, digitalSignature: true, nonRepudiation: true, keyEncipherment: true, dataEncipherment: true }, - // { name: 'extKeyUsage', serverAuth: true, clientAuth: true, codeSigning: true, emailProtection: true, timeStamping: true }, - // { name: 'nsCertType', client: true, server: true, email: true, objsign: true, sslCA: true, emailCA: true, objCA: true }, - // { name: 'subjectAltName', altNames: [ { type: 6, /* URI */ value: 'http://example.org/webid#me' }, { type: 7, /* IP */ ip: '127.0.0.1' } ] }, - // { name: 'subjectKeyIdentifier' } - ]); - - cert.sign(keys.privateKey, forge.md.sha256.create()); - - return { - privateKey: forge.pki.privateKeyToPem(keys.privateKey), - publicKey: forge.pki.publicKeyToPem(keys.publicKey), - certificate: forge.pki.certificateToPem(cert) - }; - - return pem; -} - -function generateCertsForHostname(domain, rootCAConfig){ - - //generate a serialNumber for domain - var md = forge.md.md5.create(); - md.update(domain); - - var keysAndCert = getKeysAndCert(md.digest().toHex()); - keys = keysAndCert.keys; - cert = keysAndCert.cert; - - var caCert = forge.pki.certificateFromPem(rootCAConfig.cert); - var caKey = forge.pki.privateKeyFromPem(rootCAConfig.key); - - // issuer from CA - cert.setIssuer(caCert.subject.attributes); - - var attrs = defaultAttrs.concat([ - { - name: 'commonName', - value: domain - } - ]); - cert.setSubject(attrs); - cert.sign(caKey, forge.md.sha256.create()); - - return { - privateKey: forge.pki.privateKeyToPem(keys.privateKey), - publicKey: forge.pki.publicKeyToPem(keys.publicKey), - certificate: forge.pki.certificateToPem(cert) - }; -} - -module.exports.generateRootCA = generateRootCA; -module.exports.generateCertsForHostname = generateCertsForHostname; diff --git a/lib/certMgr.js b/lib/certMgr.js index 711c2ed..6947ca6 100644 --- a/lib/certMgr.js +++ b/lib/certMgr.js @@ -1,81 +1,57 @@ -var logUtil = require('./log'); -var util = require('./util'); -var color = require('colorful'); -var EasyCert = require('node-easy-cert'); -var exec = require('child_process').exec; -var path = require('path'); -var readline = require('readline'); +'use strict' -var isWin = /^win/.test(process.platform); -var options = { - rootDirPath: util.getUserHome() + '/.anyproxy_certs', - defaultCertAttrs: [ - { name: 'countryName', value: 'CN' }, - { name: 'organizationName', value: 'AnyProxy' }, - { shortName: 'ST', value: 'SH' }, - { shortName: 'OU', value: 'AnyProxy SSL Proxy' } - ] +const util = require('./util'); +const EasyCert = require('node-easy-cert'); +const co = require('co'); + +const options = { + rootDirPath: util.getAnyProxyPath('certificates'), + defaultCertAttrs: [ + { name: 'countryName', value: 'CN' }, + { name: 'organizationName', value: 'AnyProxy' }, + { shortName: 'ST', value: 'SH' }, + { shortName: 'OU', value: 'AnyProxy SSL Proxy' } + ] }; -var easyCert = new EasyCert(options); -var crtMgr = util.merge({}, easyCert); +const easyCert = new EasyCert(options); +const crtMgr = util.merge({}, easyCert); -// catch specified error, such as ROOT_CA_NOT_EXISTS -crtMgr.getCertificate = function (host, cb) { - easyCert.getCertificate(host, (error, keyContent, crtContent) => { - if (error === 'ROOT_CA_NOT_EXISTS') { - util.showRootInstallTip(); - process.exit(0); - return; - } +// rename function +crtMgr.ifRootCAFileExists = easyCert.isRootCAFileExists; - cb(error, keyContent, crtContent); - }); -}; - -// set default common name of the cert crtMgr.generateRootCA = function (cb) { - doGenerate(false); + doGenerate(false); - function doGenerate(overwrite) { - const rootOptions = { - commonName: 'AnyProxy', - overwrite: !!overwrite - }; - - easyCert.generateRootCA(rootOptions, (error, keyPath, crtPath) => { - if (!error) { - const certDir = path.dirname(keyPath); - logUtil.printLog(color.cyan('The cert is generated at "' + certDir + '"')); - if(isWin){ - exec("start .",{ cwd : certDir }); - }else{ - exec("open .",{ cwd : certDir }); - } - } - - if (error === 'ROOT_CA_EXISTED') { - var rl = readline.createInterface({ - input : process.stdin, - output: process.stdout - }); - - rl.question("do you really want to generate a new one ?)(yes/NO)", function(answer) { - if(/yes/i.test(answer)){ - doGenerate(true); - }else{ - console.log("will not generate a new one"); - - } - rl.close(); - }); - - return; - } - cb(error, keyPath, crtPath); - }); - } + // set default common name of the cert + function doGenerate(overwrite) { + const rootOptions = { + commonName: 'AnyProxy', + overwrite: !!overwrite + }; + easyCert.generateRootCA(rootOptions, (error, keyPath, crtPath) => { + cb(error, keyPath, crtPath); + }); + } }; -module.exports = crtMgr; \ No newline at end of file +crtMgr.getCAStatus = function *() { + return co(function *() { + const result = { + exist: false, + }; + const ifExist = easyCert.isRootCAFileExists(); + if (!ifExist) { + return result; + } else { + result.exist = true; + if (!/^win/.test(process.platform)) { + result.trusted = yield easyCert.ifRootCATrusted; + } + return result; + } + }); +} + +module.exports = crtMgr; diff --git a/lib/configUtil.js b/lib/configUtil.js new file mode 100644 index 0000000..2b12773 --- /dev/null +++ b/lib/configUtil.js @@ -0,0 +1,16 @@ +/** +* a util to set and get all configuable constant +* +*/ +const path = require('path'); + +const USER_HOME = process.env.HOME || process.env.USERPROFILE; +const DEFAULT_ANYPROXY_HOME = path.join(USER_HOME, '/.anyproxy/'); + +/** +* return AnyProxy's home path +*/ +module.exports.getAnyProxyHome = function () { + const ENV_ANYPROXY_HOME = process.env.ANYPROXY_HOME || ''; + return ENV_ANYPROXY_HOME || DEFAULT_ANYPROXY_HOME; +} diff --git a/lib/getPort.js b/lib/getPort.js deleted file mode 100644 index b91dab0..0000000 --- a/lib/getPort.js +++ /dev/null @@ -1,19 +0,0 @@ -var portrange = 40000; - -function getPort(cb) { - var port = portrange; - ++portrange; - - var server = require("net").createServer(); - server.listen(port, function (err) { - server.once('close', function () { - cb(port); - }); - server.close(); - }); - server.on('error', function (err) { - getPort(cb); - }); -}; - -module.exports = getPort; \ No newline at end of file diff --git a/lib/httpsServerMgr.js b/lib/httpsServerMgr.js index e016bcf..d57c14a 100644 --- a/lib/httpsServerMgr.js +++ b/lib/httpsServerMgr.js @@ -1,77 +1,194 @@ -//manage https servers -var getPort = require('./getPort'), - async = require("async"), - http = require('http'), - https = require('https'), - Buffer = require('buffer').Buffer, - fs = require('fs'), - net = require('net'), - tls = require('tls'), - crypto = require('crypto'), - color = require('colorful'), - certMgr = require("./certMgr"), - logUtil = require("./log"), - asyncTask = require("async-task-mgr"); +'use strict' -var createSecureContext = tls.createSecureContext || crypto.createSecureContext; +//manage https servers +const async = require('async'), + https = require('https'), + tls = require('tls'), + crypto = require('crypto'), + color = require('colorful'), + certMgr = require('./certMgr'), + logUtil = require('./log'), + util = require('./util'), + co = require('co'), + constants = require('constants'), + asyncTask = require('async-task-mgr'); + +const createSecureContext = tls.createSecureContext || crypto.createSecureContext; //using sni to avoid multiple ports -function SNIPrepareCert(serverName,SNICallback){ - var keyContent, crtContent,ctx; +function SNIPrepareCert(serverName, SNICallback) { + let keyContent, + crtContent, + ctx; - async.series([ - function(callback){ - certMgr.getCertificate(serverName,function(err,key,crt){ - if(err){ - callback(err); - }else{ - keyContent = key; - crtContent = crt; - callback(); - } - }); - }, - function(callback){ - try{ - ctx = createSecureContext({ - key :keyContent, - cert :crtContent - }); - callback(); - }catch(e){ - callback(e); - } + async.series([ + (callback) => { + certMgr.getCertificate(serverName, (err, key, crt) => { + if (err) { + callback(err); + } else { + keyContent = key; + crtContent = crt; + callback(); } - ],function(err,result){ - if(!err){ - var tipText = "proxy server for __NAME established".replace("__NAME",serverName); - logUtil.printLog(color.yellow(color.bold("[internal https]")) + color.yellow(tipText)); - SNICallback(null,ctx); - }else{ - logUtil.printLog("err occurred when prepare certs for SNI - " + err, logUtil.T_ERR); - logUtil.printLog("err occurred when prepare certs for SNI - " + err.stack, logUtil.T_ERR); - logUtil.printLog("you may upgrade your Node.js to >= v0.12", logUtil.T_ERR); - } - }); + }); + }, + (callback) => { + try { + ctx = createSecureContext({ + key: keyContent, + cert: crtContent + }); + callback(); + } catch (e) { + callback(e); + } + } + ], (err) => { + if (!err) { + const tipText = 'proxy server for __NAME established'.replace('__NAME', serverName); + logUtil.printLog(color.yellow(color.bold('[internal https]')) + color.yellow(tipText)); + SNICallback(null, ctx); + } else { + logUtil.printLog('err occurred when prepare certs for SNI - ' + err, logUtil.T_ERR); + logUtil.printLog('err occurred when prepare certs for SNI - ' + err.stack, logUtil.T_ERR); + } + }); } //config.port - port to start https server //config.handler - request handler -module.exports =function(config){ - var self = this; - if(!config || !config.port ){ - throw(new Error("please assign a port")); - } - certMgr.getCertificate("anyproxy_internal_https_server",function(err,keyContent,crtContent){ - https.createServer({ - SNICallback : SNIPrepareCert , - key : keyContent, - cert : crtContent - },config.handler).listen(config.port); +/** + * Create an https server + * + * @param {object} config + * @param {number} config.port + * @param {function} config.handler + */ +function createHttpsServer(config) { + if (!config || !config.port || !config.handler) { + throw (new Error('please assign a port')); + } + + return new Promise((resolve) => { + certMgr.getCertificate('anyproxy_internal_https_server', (err, keyContent, crtContent) => { + const server = https.createServer({ + secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1, + SNICallback: SNIPrepareCert, + key: keyContent, + cert: crtContent + }, config.handler).listen(config.port); + + resolve(server); }); - + }); } +/** +* create an https server that serving on IP address +* @param @required {object} config +* @param @required {string} config.ip the IP address of the server +* @param @required {number} config.port the port to listen on +* @param @required {function} handler the handler of each connect +*/ +function createIPHttpsServer(config) { + if (!config || !config.port || !config.handler) { + throw (new Error('please assign a port')); + } + if (!config.ip) { + throw (new Error('please assign an IP to create the https server')); + } + + return new Promise((resolve) => { + certMgr.getCertificate(config.ip, (err, keyContent, crtContent) => { + const server = https.createServer({ + secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1, + key: keyContent, + cert: crtContent + }, config.handler).listen(config.port); + + resolve(server); + }); + }); +} + +/** + * + * + * @class httpsServerMgr + * @param {object} config + * @param {function} config.handler handler to deal https request + * + */ +class httpsServerMgr { + constructor(config) { + if (!config || !config.handler) { + throw new Error('handler is required'); + } + this.instanceDefaultHost = '127.0.0.1'; + this.httpsAsyncTask = new asyncTask(); + this.handler = config.handler; + } + + getSharedHttpsServer(hostname) { + // ip address will have a unique name + const finalHost = util.isIpDomain(hostname) ? hostname : this.instanceDefaultHost; + + const self = this; + function prepareServer(callback) { + let instancePort; + co(util.getFreePort) + .then(co.wrap(function *(port) { + instancePort = port; + let httpsServer = null; + + // if ip address passed in, will create an IP http server + if (util.isIpDomain(hostname)) { + httpsServer = yield createIPHttpsServer({ + ip: hostname, + port, + handler: self.handler + }); + } else { + httpsServer = yield createHttpsServer({ + port, + handler: self.handler + }); + } + + + httpsServer.on('upgrade', (req, socket, head) => { + const reqHost = req.headers.host || 'unknown host'; + logUtil.printLog(`wss:// is not supported when intercepting https. This request will be closed by AnyProxy. You may either exclude this domain in your rule file, or stop all https intercepting. (${reqHost})`, logUtil.T_ERR); + socket.end(); + }); + const result = { + host: finalHost, + port: instancePort, + }; + callback(null, result); + return result; + })) + .catch(e => { + callback(e); + }); + } + + return new Promise((resolve, reject) => { + // each ip address will gain a unit task name, + // while the domain address will share a common task name + self.httpsAsyncTask.addTask(`createHttpsServer-${finalHost}`, prepareServer, (error, serverInfo) => { + if (error) { + reject(error); + } else { + resolve(serverInfo); + } + }); + }); + } +} + +module.exports = httpsServerMgr; diff --git a/lib/log.js b/lib/log.js index 4fc271d..470a494 100644 --- a/lib/log.js +++ b/lib/log.js @@ -1,17 +1,82 @@ -var ifPrint = true; +'use strict' -function setPrintStatus(status){ - ifPrint = !!status; +const color = require('colorful'); +const util = require('./util'); + +let ifPrint = true; +let logLevel = 0; +const LogLevelMap = { + tip: 0, + system_error: 1, + rule_error: 2, + warn: 3, + debug: 4, +}; + +function setPrintStatus(status) { + ifPrint = !!status; } -function printLog(content,type){ - if(!ifPrint) return; - - var tip = content; - console.log(tip); +function setLogLevel(level) { + logLevel = parseInt(level, 10); } -module.exports.printLog = printLog; +function printLog(content, type) { + if (!ifPrint) { + return; + } + const timeString = util.formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss'); + switch (type) { + case LogLevelMap.tip: { + if (logLevel > 0) { + return; + } + console.log(color.cyan(`[AnyProxy Log][${timeString}]: ` + content)); + break; + } + + case LogLevelMap.system_error: { + if (logLevel > 1) { + return; + } + console.error(color.red(`[AnyProxy ERROR][${timeString}]: ` + content)); + break; + } + + case LogLevelMap.rule_error: { + if (logLevel > 2) { + return; + } + + console.error(color.red(`[AnyProxy RULE_ERROR][${timeString}]: ` + content)); + break; + } + + case LogLevelMap.warn: { + if (logLevel > 3) { + return; + } + + console.error(color.magenta(`[AnyProxy WARN][${timeString}]: ` + content)); + break; + } + + case LogLevelMap.debug: { + return; + } + + default : { + console.log(color.cyan(`[AnyProxy Log][${timeString}]: ` + content)); + break; + } + } +} + +module.exports.printLog = printLog; module.exports.setPrintStatus = setPrintStatus; -module.exports.T_TIP = 0; -module.exports.T_ERR = 1; \ No newline at end of file +module.exports.setLogLevel = setLogLevel; +module.exports.T_TIP = LogLevelMap.tip; +module.exports.T_ERR = LogLevelMap.system_error; +module.exports.T_RULE_ERROR = LogLevelMap.rule_error; +module.exports.T_WARN = LogLevelMap.warn; +module.exports.T_DEBUG = LogLevelMap.debug; diff --git a/lib/recorder.js b/lib/recorder.js index 9917163..4bb49c0 100644 --- a/lib/recorder.js +++ b/lib/recorder.js @@ -1,233 +1,244 @@ +'use strict' + //start recording and share a list when required -var zlib = require('zlib'), - Datastore = require('nedb'), - util = require("util"), - path = require("path"), - fs = require("fs"), - events = require('events'), - iconv = require('iconv-lite'), - proxyUtil = require("./util"), - logUtil = require("./log"); +const Datastore = require('nedb'), + path = require('path'), + fs = require('fs'), + events = require('events'), + iconv = require('iconv-lite'), + proxyUtil = require('./util'); -//option.filename -function Recorder(option){ - var self = this, - id = 1, - cachePath = proxyUtil.generateCacheDir(), - db; +const BODY_FILE_PRFIX = 'res_body_'; +const CACHE_DIR_PREFIX = 'cache_r'; +function getCacheDir() { + const rand = Math.floor(Math.random() * 1000000), + cachePath = path.join(proxyUtil.getAnyProxyPath('cache'), './' + CACHE_DIR_PREFIX + rand); - option = option || {}; - if(option.filename && typeof option.filename == "string"){ - try{ - if(fs.existsSync(option.filename)){ - fs.writeFileSync(option.filename,""); //empty original file - } - - db = new Datastore({ - filename : option.filename, - autoload :true - }); - db.persistence.setAutocompactionInterval(5001); - logUtil.printLog("db file : " + option.filename); - - }catch(e){ - logUtil.printLog(e, logUtil.T_ERR); - logUtil.printLog("Failed to load on-disk db file. Will use in-meomory db instead.", logUtil.T_ERR); - db = new Datastore(); - } - - }else{ - //in-memory db - db = new Datastore(); - } - - self.recordBodyMap = []; // id - body - - self.emitUpdate = function(id,info){ - if(info){ - self.emit("update",info); - }else{ - self.getSingleRecord(id,function(err,doc){ - if(!err && !!doc && !!doc[0]){ - self.emit("update",doc[0]); - } - }); - } - }; - - self.updateRecord = function(id,info){ - if(id < 0 ) return; - - var finalInfo = normalizeInfo(id,info); - - db.update({_id:id},finalInfo); - self.updateRecordBody(id,info); - - self.emitUpdate(id,finalInfo); - }; - - self.updateExtInfo = function(id,extInfo){ - db.update({_id:id},{ $set: { ext: extInfo } },{},function(err,nums){ - if(!err){ - self.emitUpdate(id); - } - }); - - } - - self.appendRecord = function(info){ - if(info.req.headers.anyproxy_web_req){ //request from web interface - return -1; - } - - var thisId = id++, - finalInfo = normalizeInfo(thisId,info); - db.insert(finalInfo); - self.updateRecordBody(id,info); - - self.emitUpdate(id,finalInfo); - return thisId; - }; - - //update recordBody if exits - - //TODO : trigger update callback - var BODY_FILE_PRFIX = "res_body_"; - self.updateRecordBody =function(id,info){ - if(id == -1) return; - - if(!id || !info.resBody) return; - //add to body map - //ignore image data - var bodyFile = path.join(cachePath,BODY_FILE_PRFIX + id); - fs.writeFile(bodyFile, info.resBody); - }; - - self.getBody = function(id,cb){ - if(id < 0){ - cb && cb(""); - } - - var bodyFile = path.join(cachePath,BODY_FILE_PRFIX + id); - fs.access(bodyFile, fs.F_OK | fs.R_OK ,function(err){ - if(err){ - cb && cb(err); - }else{ - fs.readFile(bodyFile,cb); - } - }); - }; - - self.getDecodedBody = function(id,cb){ - var result = { - type : "unknown", - mime : "", - content : "" - }; - global.recorder.getSingleRecord(id,function(err,doc){ - //check whether this record exists - if(!doc || !doc[0]){ - cb(new Error("failed to find record for this id")); - return; - } - - self.getBody(id,function(err,bodyContent){ - if(err){ - cb(err); - }else if(!bodyContent){ - cb(null,result); - }else{ - var record = doc[0], - resHeader = record['resHeader'] || {}; - try{ - var headerStr = JSON.stringify(resHeader), - charsetMatch = headerStr.match(/charset="?([a-zA-Z0-9\-]+)"?/), - imageMatch = resHeader && resHeader["content-type"]; - - if(charsetMatch && charsetMatch.length){ - - var currentCharset = charsetMatch[1].toLowerCase(); - if(currentCharset != "utf-8" && iconv.encodingExists(currentCharset)){ - bodyContent = iconv.decode(bodyContent, currentCharset); - } - result.type = "text"; - result.content = bodyContent.toString(); - }else if(imageMatch && /image/i.test(imageMatch)){ - - result.type = "image"; - result.mime = imageMatch; - result.content = bodyContent; - }else{ - result.content = bodyContent.toString(); - } - }catch(e){} - - cb(null,result); - } - - }); - }); - }; - - self.getSingleRecord = function(id,cb){ - db.find({_id:parseInt(id)},cb); - }; - - self.getSummaryList = function(cb){ - db.find({},cb); - }; - - self.getRecords = function(idStart, limit, cb){ - limit = limit || 10; - idStart = typeof idStart == "number" ? idStart : (id - limit); - db.find({ _id: { $gte: parseInt(idStart) } }).limit(limit).exec(cb); - }; - - self.db = db; + fs.mkdirSync(cachePath); + return cachePath; } -util.inherits(Recorder, events.EventEmitter); +function normalizeInfo(id, info) { + const singleRecord = {}; -function normalizeInfo(id,info){ - var singleRecord = {}; + //general + singleRecord._id = id; + singleRecord.id = id; + singleRecord.url = info.url; + singleRecord.host = info.host; + singleRecord.path = info.path; + singleRecord.method = info.method; - //general - singleRecord._id = id; - singleRecord.id = id; - singleRecord.url = info.url; - singleRecord.host = info.host; - singleRecord.path = info.path; - singleRecord.method = info.method; + //req + singleRecord.reqHeader = info.req.headers; + singleRecord.startTime = info.startTime; + singleRecord.reqBody = info.reqBody || ''; + singleRecord.protocol = info.protocol || ''; - //req - singleRecord.reqHeader = info.req.headers; - singleRecord.startTime = info.startTime; - singleRecord.reqBody = info.reqBody || ""; - singleRecord.protocol = info.protocol || ""; - - //res - if(info.endTime){ - singleRecord.statusCode= info.statusCode; - singleRecord.endTime = info.endTime; - singleRecord.resHeader = info.resHeader; - singleRecord.length = info.length; - if(info.resHeader['content-type']){ - singleRecord.mime = info.resHeader['content-type'].split(";")[0]; - }else{ - singleRecord.mime = ""; - } - - singleRecord.duration = info.endTime - info.startTime; - }else{ - singleRecord.statusCode= ""; - singleRecord.endTime = ""; - singleRecord.resHeader = ""; - singleRecord.length = ""; - singleRecord.mime = ""; - singleRecord.duration = ""; + //res + if (info.endTime) { + singleRecord.statusCode = info.statusCode; + singleRecord.endTime = info.endTime; + singleRecord.resHeader = info.resHeader; + singleRecord.length = info.length; + const contentType = info.resHeader['content-type'] || info.resHeader['Content-Type']; + if (contentType) { + singleRecord.mime = contentType.split(';')[0]; + } else { + singleRecord.mime = ''; } - return singleRecord; + singleRecord.duration = info.endTime - info.startTime; + } else { + singleRecord.statusCode = ''; + singleRecord.endTime = ''; + singleRecord.resHeader = ''; + singleRecord.length = ''; + singleRecord.mime = ''; + singleRecord.duration = ''; + } + + return singleRecord; +} + +class Recorder extends events.EventEmitter { + constructor(config) { + super(config); + this.globalId = 1; + this.cachePath = getCacheDir(); + this.db = new Datastore(); + this.db.persistence.setAutocompactionInterval(5001); + + this.recordBodyMap = []; // id - body + } + + emitUpdate(id, info) { + const self = this; + if (info) { + self.emit('update', info); + } else { + self.getSingleRecord(id, (err, doc) => { + if (!err && !!doc && !!doc[0]) { + self.emit('update', doc[0]); + } + }); + } + } + + updateRecord(id, info) { + if (id < 0) return; + const self = this; + const db = self.db; + + const finalInfo = normalizeInfo(id, info); + + db.update({ _id: id }, finalInfo); + self.updateRecordBody(id, info); + + self.emitUpdate(id, finalInfo); + } + + updateExtInfo(id, extInfo) { + const self = this; + const db = self.db; + + db.update({ _id: id }, { $set: { ext: extInfo } }, {}, (err, nums) => { + if (!err) { + self.emitUpdate(id); + } + }); + } + + appendRecord(info) { + if (info.req.headers.anyproxy_web_req) { //TODO request from web interface + return -1; + } + const self = this; + const db = self.db; + + const thisId = self.globalId++; + const finalInfo = normalizeInfo(thisId, info); + db.insert(finalInfo); + self.updateRecordBody(thisId, info); + + self.emitUpdate(thisId, finalInfo); + return thisId; + } + + updateRecordBody(id, info) { + const self = this; + const cachePath = self.cachePath; + + if (id === -1) return; + + if (!id || typeof info.resBody === 'undefined') return; + //add to body map + //ignore image data + const bodyFile = path.join(cachePath, BODY_FILE_PRFIX + id); + fs.writeFile(bodyFile, info.resBody, () => {}); + } + + getBody(id, cb) { + const self = this; + const cachePath = self.cachePath; + + if (id < 0) { + cb && cb(''); + } + + const bodyFile = path.join(cachePath, BODY_FILE_PRFIX + id); + fs.access(bodyFile, fs.F_OK || fs.R_OK, (err) => { + if (err) { + cb && cb(err); + } else { + fs.readFile(bodyFile, cb); + } + }); + } + + getDecodedBody(id, cb) { + const self = this; + const result = { + type: 'unknown', + mime: '', + content: '' + }; + self.getSingleRecord(id, (err, doc) => { + //check whether this record exists + if (!doc || !doc[0]) { + cb(new Error('failed to find record for this id')); + return; + } + + self.getBody(id, (error, bodyContent) => { + if (error) { + cb(error); + } else if (!bodyContent) { + cb(null, result); + } else { + const record = doc[0], + resHeader = record.resHeader || {}; + try { + const headerStr = JSON.stringify(resHeader), + charsetMatch = headerStr.match(/charset='?([a-zA-Z0-9-]+)'?/), + contentType = resHeader && (resHeader['content-type'] || resHeader['Content-Type']); + + if (charsetMatch && charsetMatch.length) { + const currentCharset = charsetMatch[1].toLowerCase(); + if (currentCharset !== 'utf-8' && iconv.encodingExists(currentCharset)) { + bodyContent = iconv.decode(bodyContent, currentCharset); + } + + result.mime = contentType; + result.content = bodyContent.toString(); + result.type = contentType && /application\/json/i.test(contentType) ? 'json' : 'text'; + } else if (contentType && /image/i.test(contentType)) { + result.type = 'image'; + result.mime = contentType; + result.content = bodyContent; + } else { + result.type = contentType; + result.mime = contentType; + result.content = bodyContent.toString(); + } + result.fileName = path.basename(record.path); + result.statusCode = record.statusCode; + } catch (e) { + console.error(e); + } + cb(null, result); + } + }); + }); + } + + getSingleRecord(id, cb) { + const self = this; + const db = self.db; + db.find({ _id: parseInt(id, 10) }, cb); + } + + getSummaryList(cb) { + const self = this; + const db = self.db; + db.find({}, cb); + } + + getRecords(idStart, limit, cb) { + const self = this; + const db = self.db; + limit = limit || 10; + idStart = typeof idStart === 'number' ? idStart : (self.globalId - limit); + db.find({ _id: { $gte: parseInt(idStart, 10) } }) + .sort({ _id: 1 }) + .limit(limit) + .exec(cb); + } + + clear() { + const self = this; + proxyUtil.deleteFolderContentsRecursive(self.cachePath, true); + } } module.exports = Recorder; diff --git a/lib/requestErrorHandler.js b/lib/requestErrorHandler.js new file mode 100644 index 0000000..8fd9f9e --- /dev/null +++ b/lib/requestErrorHandler.js @@ -0,0 +1,84 @@ +'use strict'; + +/* +* handle all request error here, +* +*/ +const pug = require('pug'); +const path = require('path'); + +const error502PugFn = pug.compileFile(path.join(__dirname, '../resource/502.pug')); +const certPugFn = pug.compileFile(path.join(__dirname, '../resource/cert_error.pug')); + +/** +* get error content for certification issues +*/ +function getCertErrorContent(error, fullUrl) { + let content; + const title = 'The connection is not private. '; + let explain = 'There are error with the certfication of the site.'; + switch (error.code) { + case 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY': { + explain = 'The certfication of the site you are visiting is not issued by a known agency, ' + + 'It usually happenes when the cert is a self-signed one.
          ' + + 'If you know and trust the site, you can run AnyProxy with option -ignore-unauthorized-ssl to continue.' + + break; + } + default: { + explain = '' + break; + } + } + + try { + content = certPugFn({ + title: title, + explain: explain, + code: error.code + }); + } catch (parseErro) { + content = error.stack; + } + + return content; +} + +/* +* get the default error content +*/ +function getDefaultErrorCotent(error, fullUrl) { + let content; + + try { + content = error502PugFn({ + error, + url: fullUrl, + errorStack: error.stack.split(/\n/) + }); + } catch (parseErro) { + content = error.stack; + } + + return content; +} + +/* +* get mapped error content for each error +*/ +module.exports.getErrorContent = function (error, fullUrl) { + let content = ''; + error = error || {}; + switch (error.code) { + case 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY': { + content = getCertErrorContent(error, fullUrl); + break; + } + default: { + content = getDefaultErrorCotent(error, fullUrl); + break; + } + } + + return content; +} diff --git a/lib/requestHandler.js b/lib/requestHandler.js index 04b0cda..3c93e7b 100644 --- a/lib/requestHandler.js +++ b/lib/requestHandler.js @@ -1,439 +1,631 @@ -var http = require("http"), - https = require("https"), - net = require("net"), - fs = require("fs"), - url = require("url"), - pathUtil = require("path"), - zlib = require('zlib'), - async = require('async'), - color = require("colorful"), - Buffer = require('buffer').Buffer, - util = require("./util"), - getPort = require("./getPort"), - Stream = require("stream"), - logUtil = require("./log"), - httpsServerMgr = require("./httpsServerMgr"); +'use strict'; -var userRule = util.freshRequire('./rule_default'); +const http = require('http'), + https = require('https'), + net = require('net'), + url = require('url'), + zlib = require('zlib'), + color = require('colorful'), + Buffer = require('buffer').Buffer, + util = require('./util'), + Stream = require('stream'), + logUtil = require('./log'), + co = require('co'), + HttpsServerMgr = require('./httpsServerMgr'), + Readable = require('stream').Readable; + +const requestErrorHandler = require('./requestErrorHandler'); // to fix issue with TLS cache, refer to: https://github.com/nodejs/node/issues/8368 https.globalAgent.maxCachedSessions = 0; -function userRequestHandler(req,userRes){ - /* - note - req.url is wired - in http server : http://www.example.com/a/b/c - in https server : /a/b/c - */ +const DEFAULT_CHUNK_COLLECT_THRESHOLD = 20 * 1024 * 1024; // about 20 mb - var host = req.headers.host, - protocol = (!!req.connection.encrypted && !/^http:/.test(req.url)) ? "https" : "http", - fullUrl = protocol === "http" ? req.url : (protocol + '://' + host + req.url), - urlPattern = url.parse(fullUrl), - path = urlPattern.path, - resourceInfo, - resourceInfoId = -1, - reqData; - - // console.log(req.url); - // console.log(path); - - //record - resourceInfo = { - host : host, - method : req.method, - path : path, - protocol : protocol, - url : protocol + "://" + host + path, - req : req, - startTime : new Date().getTime() - }; - if(global.recorder){ - resourceInfoId = global.recorder.appendRecord(resourceInfo); - } - - logUtil.printLog(color.green("\nreceived request to : " + host + path)); - - //get request body and route to local or remote - async.series([ - fetchReqData, - routeReq - ],function(){ - //mark some ext info - if(req.anyproxy_map_local){ - global.recorder.updateExtInfo(resourceInfoId, {map : req.anyproxy_map_local}); - } +class CommonReadableStream extends Readable { + constructor(config) { + super({ + highWaterMark: DEFAULT_CHUNK_COLLECT_THRESHOLD * 5 }); + } + _read(size) { - //get request body - function fetchReqData(callback){ - var postData = []; - req.on("data",function(chunk){ - postData.push(chunk); - }); - req.on("end",function(){ - reqData = Buffer.concat(postData); - resourceInfo.reqBody = reqData.toString(); - global.recorder && global.recorder.updateRecord(resourceInfoId,resourceInfo); - - callback(); - }); - } - - //route to dealing function - function routeReq(callback){ - if(userRule.shouldUseLocalResponse(req,reqData)){ - logUtil.printLog("==>use local rules"); - dealWithLocalResponse(callback); - }else{ - logUtil.printLog("==>will forward to real server by proxy"); - dealWithRemoteResonse(callback); - } - } - - function dealWithLocalResponse(callback){ - userRule.dealLocalResponse(req,reqData,function(statusCode,resHeader,resBody){ - - //update record info - resourceInfo.endTime = new Date().getTime(); - resourceInfo.res = { //construct a self-defined res object - statusCode : statusCode || "", - headers : resHeader || {} - } - resourceInfo.resHeader = resHeader || {}; - resourceInfo.resBody = resBody; - resourceInfo.length = resBody ? resBody.length : 0; - resourceInfo.statusCode = statusCode; - - global.recorder && global.recorder.updateRecord(resourceInfoId,resourceInfo); - - userRes.writeHead(statusCode,resHeader); - userRes.end(resBody); - callback && callback(); - }); - - return; - } - - function dealWithRemoteResonse(callback){ - var options; - - //modify request protocol - protocol = userRule.replaceRequestProtocol(req,protocol) || protocol; - - //modify request options - options = { - hostname : urlPattern.hostname || req.headers.host, - port : urlPattern.port || req.port || (/https/.test(protocol) ? 443 : 80), - path : path, - method : req.method, - headers : req.headers - }; - - options = userRule.replaceRequestOption(req,options) || options; - options.rejectUnauthorized = false; - try{ - delete options.headers['accept-encoding']; //avoid gzipped response - }catch(e){} - - //update request data - reqData = userRule.replaceRequestData(req,reqData) || reqData; - options.headers = util.lower_keys(options.headers); - options.headers["content-length"] = reqData.length; //rewrite content length info - - options.headers = util.upper_keys(options.headers); - - //send request - var proxyReq = ( /https/.test(protocol) ? https : http).request(options, function(res) { - - //deal response header - var statusCode = res.statusCode; - statusCode = userRule.replaceResponseStatusCode(req,res,statusCode) || statusCode; - - var resHeader = userRule.replaceResponseHeader(req,res,res.headers) || res.headers; - resHeader = util.lower_keys(resHeader); - - // remove gzip related header, and ungzip the content - // note there are other compression types like deflate - var ifServerGzipped = /gzip/i.test(resHeader['content-encoding']); - if(ifServerGzipped){ - delete resHeader['content-encoding']; - } - delete resHeader['content-length']; - - userRes.writeHead(statusCode, resHeader); - - //deal response data - var length, - resData = []; - - res.on("data",function(chunk){ - resData.push(chunk); - }); - - res.on("end",function(){ - var serverResData; - - async.series([ - - //ungzip server res - function(callback){ - serverResData = Buffer.concat(resData); - if(ifServerGzipped ){ - zlib.gunzip(serverResData,function(err,buff){ - serverResData = buff; - callback(); - }); - }else{ - callback(); - } - - //get custom response - },function(callback){ - if(userRule.replaceServerResData){ - logUtil.printLog(color.red("replaceServerResData is deprecated, and will be unavilable soon. Use replaceServerResDataAsync instead."), logUtil.T_ERR); - serverResData = userRule.replaceServerResData(req,res,serverResData) || serverResData; - callback(); - }else if(userRule.replaceServerResDataAsync){ - userRule.replaceServerResDataAsync(req,res,serverResData,function(newRes){ - serverResData = newRes; - callback(); - }); - }else{ - callback(); - } - - //delay - },function(callback){ - var pauseTimeInMS = userRule.pauseBeforeSendingResponse(req,res); - if(pauseTimeInMS){ - setTimeout(callback,pauseTimeInMS); - }else{ - callback(); - } - - //send response - },function(callback){ - if(global._throttle){ - var thrStream = new Stream(); - - var readable = thrStream.pipe(global._throttle.throttle()); - readable.pipe(userRes); - - thrStream.emit("data",serverResData); - thrStream.emit("end"); - callback(); - }else{ - userRes.end(serverResData); - callback(); - } - - //udpate record info - },function(callback){ - resourceInfo.endTime = new Date().getTime(); - resourceInfo.statusCode = statusCode; - resourceInfo.resHeader = resHeader; - resourceInfo.resBody = serverResData; - resourceInfo.length = serverResData ? serverResData.length : 0; - - global.recorder && global.recorder.updateRecord(resourceInfoId,resourceInfo); - - callback(); - - //push trafic data to rule file - },function(callback){ - userRule.fetchTrafficData && userRule.fetchTrafficData(resourceInfoId,resourceInfo); - callback(); - - } - - ],function(err,result){ - callback && callback(); - }); - - }); - res.on('error',function(error){ - logUtil.printLog('error' + error, logUtil.T_ERR); - }); - - }); - - proxyReq.on("error",function(e){ - logUtil.printLog("err with request :" + e + " " + req.url, logUtil.T_ERR); - userRes.end(); - }); - - proxyReq.end(reqData); - } + } } -function connectReqHandler(req, socket, head){ - var host = req.url.split(":")[0], - targetPort= req.url.split(":")[1], - resourceInfo, - resourceInfoId; +/* +* get error response for exception scenarios +*/ +const getErrorResponse = (error, fullUrl) => { + // default error response + const errorResponse = { + statusCode: 500, + header: { + 'Content-Type': 'text/html; charset=utf-8', + 'Proxy-Error': true, + 'Proxy-Error-Message': error || 'null' + }, + body: requestErrorHandler.getErrorContent(error, fullUrl) + }; - var shouldIntercept = userRule.shouldInterceptHttpsReq(req); - - //bypass webSocket on webinterface - if(targetPort == 8003){ - shouldIntercept = false; // TODO : a more general solution? - } - - logUtil.printLog(color.green("\nreceived https CONNECT request " + host)); - if(shouldIntercept){ - logUtil.printLog("==>will forward to local https server"); - }else{ - logUtil.printLog("==>will bypass the man-in-the-middle proxy"); - } - - //record - resourceInfo = { - host : host, - method : req.method, - path : "", - url : "https://" + host, - req : req, - startTime : new Date().getTime() - }; - resourceInfoId = global.recorder.appendRecord(resourceInfo); - - var proxyPort, - proxyHost, - internalHttpsPort, - httpsServerMgrInstance; - - async.series([ - - //check if internal https server exists - function(callback){ - if(!shouldIntercept){ - callback(); - return; - }else{ - if(internalHttpsPort){ - callback(); - }else{ - getPort(function(port){ - internalHttpsPort = port; - httpsServerMgrInstance = new httpsServerMgr({ - port :port, - handler :userRequestHandler - }); - callback(); - }); - } - } - }, - - //determine the target server - function(callback){ - - if(shouldIntercept){ - proxyPort = internalHttpsPort; - proxyHost = "127.0.0.1"; - callback(); - - }else{ - proxyPort = (targetPort == 80)? 443 : targetPort; - proxyHost = host; - - callback(); - } - - //connect - },function(callback){ - try{ - var conn = net.connect(proxyPort, proxyHost, function(){ - socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8', function(){ - - //throttle for direct-foward https - if(global._throttle && !shouldIntercept ){ - var readable = conn.pipe(global._throttle.throttle()); - readable.pipe(socket); - socket.pipe(conn); - }else{ - conn.pipe(socket); - socket.pipe(conn); - } - - callback(); - }); - }); - - conn.on("error",function(e){ - logUtil.printLog("err when connect to + " + host + " , " + e, logUtil.T_ERR); - }); - }catch(e){ - logUtil.printLog("err when connect to remote https server (__host)".replace(/__host/,host), logUtil.T_ERR); - } - - //update record - },function(callback){ - resourceInfo.endTime = new Date().getTime(); - resourceInfo.statusCode = "200"; - resourceInfo.resHeader = {}; - resourceInfo.resBody = ""; - resourceInfo.length = 0; - - global.recorder && global.recorder.updateRecord(resourceInfoId,resourceInfo); - - callback(); - } - ],function(err,result){ - if(err){ - logUtil.printLog("err " + err, logUtil.T_ERR); - throw err; - } - }); + return errorResponse; } /** -* @return return the merged rule for reference -*/ -function setRules(newRule){ + * fetch remote response + * + * @param {string} protocol + * @param {object} options options of http.request + * @param {buffer} reqData request body + * @param {object} config + * @param {boolean} config.dangerouslyIgnoreUnauthorized + * @param {boolean} config.chunkSizeThreshold + * @returns + */ +function fetchRemoteResponse(protocol, options, reqData, config) { + reqData = reqData || ''; + return new Promise((resolve, reject) => { + delete options.headers['content-length']; // will reset the content-length after rule + delete options.headers['Content-Length']; + delete options.headers['Transfer-Encoding']; + delete options.headers['transfer-encoding']; - if(!newRule){ - return userRule; - }else{ + if (config.dangerouslyIgnoreUnauthorized) { + options.rejectUnauthorized = false; + } - if(!newRule.summary){ - newRule.summary = function(){ - return "this rule file does not have a summary"; - }; - } + if (!config.chunkSizeThreshold) { + throw new Error('chunkSizeThreshold is required'); + } - userRule = util.merge(userRule,newRule); + //send request + const proxyReq = (/https/i.test(protocol) ? https : http).request(options, (res) => { + res.headers = util.getHeaderFromRawHeaders(res.rawHeaders); + //deal response header + const statusCode = res.statusCode; + const resHeader = res.headers; + let resDataChunks = []; // array of data chunks or stream + const rawResChunks = []; // the original response chunks + let resDataStream = null; + let resSize = 0; + const finishCollecting = () => { + new Promise((fulfill, rejectParsing) => { + if (resDataStream) { + fulfill(resDataStream); + } else { + const serverResData = Buffer.concat(resDataChunks); - var functions = []; - if('function' == typeof(userRule.init)){ - functions.push(function(cb){ - userRule.init(cb); - }); - } - if('function' == typeof(userRule.summary)){ - functions.push(function(cb){ - logUtil.printLog(userRule.summary()); - cb(null); - }); - } - async.series(functions,function(errors,result){ - if(!errors){ - logUtil.printLog(color.green('Anyproxy rules initialize finished, have fun!')); + // put origin content length into header + resHeader['x-anyproxy-origin-content-length'] = util.getByteSize(serverResData); + + if (ifServerGzipped) { + zlib.gunzip(serverResData, (err, buff) => { // TODO test case to cover + if (err) { + rejectParsing(err); + } else { + fulfill(buff); + } + }); + } else if (isServerDeflated) { + zlib.inflateRaw(serverResData, (err, buff) => { // TODO test case to cover + if (err) { + rejectParsing(err); + } else { + fulfill(buff); + } + }); + } else { + fulfill(serverResData); } + } + }).then((serverResData) => { + resolve({ + statusCode, + header: resHeader, + body: serverResData, + rawBody: rawResChunks, + _res: res, + }); + }).catch((e) => { + reject(e); + }); + }; + + // remove gzip related header, and ungzip the content + // note there are other compression types like deflate + const contentEncoding = resHeader['content-encoding'] || resHeader['Content-Encoding']; + const ifServerGzipped = /gzip/i.test(contentEncoding); + const isServerDeflated = /deflate/i.test(contentEncoding); + + //deal response data + res.on('data', (chunk) => { + rawResChunks.push(chunk); + if (resDataStream) { // stream mode + resDataStream.push(chunk); + } else { // dataChunks + resSize += chunk.length; + resDataChunks.push(chunk); + + // stop collecting, convert to stream mode + if (resSize >= config.chunkSizeThreshold) { + resDataStream = new CommonReadableStream(); + while (resDataChunks.length) { + resDataStream.push(resDataChunks.shift()); + } + resDataChunks = null; + finishCollecting(); + } + } + }); + + res.on('end', () => { + if (resDataStream) { + resDataStream.emit('end'); // EOF + } else { + finishCollecting(); + } + }); + res.on('error', (error) => { + logUtil.printLog('error happend in response:' + error, logUtil.T_ERR); + reject(error); + }); + }); + + proxyReq.on('error', reject); + proxyReq.end(reqData); + }); +} + +/** + * get a request handler for http/https server + * + * @param {RequestHandler} reqHandlerCtx + * @param {object} userRule + * @param {Recorder} recorder + * @returns + */ +function getUserReqHandler(userRule, recorder) { + const reqHandlerCtx = this + + return function (req, userRes) { + /* + note + req.url is wired + in http server: http://www.example.com/a/b/c + in https server: /a/b/c + */ + + const host = req.headers.host; + const protocol = (!!req.connection.encrypted && !(/^http:/).test(req.url)) ? 'https' : 'http'; + const fullUrl = protocol === 'http' ? req.url : (protocol + '://' + host + req.url); + + const urlPattern = url.parse(fullUrl); + const path = urlPattern.path; + const chunkSizeThreshold = DEFAULT_CHUNK_COLLECT_THRESHOLD; + + let resourceInfo = null; + let resourceInfoId = -1; + let reqData; + let requestDetail; + + // refer to https://github.com/alibaba/anyproxy/issues/103 + // construct the original headers as the reqheaders + req.headers = util.getHeaderFromRawHeaders(req.rawHeaders); + + logUtil.printLog(color.green(`received request to: ${req.method} ${host}${path}`)); + + /** + * fetch complete req data + */ + const fetchReqData = () => new Promise((resolve) => { + const postData = []; + req.on('data', (chunk) => { + postData.push(chunk); + }); + req.on('end', () => { + reqData = Buffer.concat(postData); + resolve(); + }); + }); + + /** + * prepare detailed request info + */ + const prepareRequestDetail = () => { + const options = { + hostname: urlPattern.hostname || req.headers.host, + port: urlPattern.port || req.port || (/https/.test(protocol) ? 443 : 80), + path, + method: req.method, + headers: req.headers + }; + + requestDetail = { + requestOptions: options, + protocol, + url: fullUrl, + requestData: reqData, + _req: req + }; + + return Promise.resolve(); + }; + + /** + * send response to client + * + * @param {object} finalResponseData + * @param {number} finalResponseData.statusCode + * @param {object} finalResponseData.header + * @param {buffer|string} finalResponseData.body + */ + const sendFinalResponse = (finalResponseData) => { + const responseInfo = finalResponseData.response; + const resHeader = responseInfo.header; + const responseBody = responseInfo.body || ''; + + const transferEncoding = resHeader['transfer-encoding'] || resHeader['Transfer-Encoding'] || ''; + const contentEncoding = resHeader['content-encoding'] || resHeader['Content-Encoding']; + const contentLength = resHeader['content-length'] || resHeader['Content-Length']; + const connection = resHeader.Connection || resHeader.connection; + if (contentLength) { + delete resHeader['content-length']; + delete resHeader['Content-Length']; + } + + if (contentEncoding) { + resHeader['x-anyproxy-origin-content-encoding'] = contentEncoding; + delete resHeader['content-encoding']; + delete resHeader['Content-Encoding']; + } + + // set proxy-connection + if (connection) { + resHeader['x-anyproxy-origin-connection'] = connection; + delete resHeader.connection; + delete resHeader.Connection; + } + + if (!responseInfo) { + throw new Error('failed to get response info'); + } else if (!responseInfo.statusCode) { + throw new Error('failed to get response status code') + } else if (!responseInfo.header) { + throw new Error('filed to get response header'); + } + // if there is no transfer-encoding, set the content-length + if (!global._throttle + && transferEncoding !== 'chunked' + && !(responseBody instanceof CommonReadableStream) + ) { + resHeader['Content-Length'] = util.getByteSize(responseBody); + } + + userRes.writeHead(responseInfo.statusCode, resHeader); + + if (global._throttle) { + if (responseBody instanceof CommonReadableStream) { + responseBody.pipe(global._throttle.throttle()).pipe(userRes); + } else { + const thrStream = new Stream(); + thrStream.pipe(global._throttle.throttle()).pipe(userRes); + thrStream.emit('data', responseBody); + thrStream.emit('end'); + } + } else { + if (responseBody instanceof CommonReadableStream) { + responseBody.pipe(userRes); + } else { + userRes.end(responseBody); + } + } + + return responseInfo; + } + + // fetch complete request data + co(fetchReqData) + .then(prepareRequestDetail) + + .then(() => { + // record request info + if (recorder) { + resourceInfo = { + host, + method: req.method, + path, + protocol, + url: protocol + '://' + host + path, + req, + startTime: new Date().getTime() + }; + resourceInfoId = recorder.appendRecord(resourceInfo); + } + + try { + resourceInfo.reqBody = reqData.toString(); //TODO: deal reqBody in webInterface.js + recorder && recorder.updateRecord(resourceInfoId, resourceInfo); + } catch (e) { } + }) + + // invoke rule before sending request + .then(co.wrap(function *() { + const userModifiedInfo = (yield userRule.beforeSendRequest(Object.assign({}, requestDetail))) || {}; + const finalReqDetail = {}; + ['protocol', 'requestOptions', 'requestData', 'response'].map((key) => { + finalReqDetail[key] = userModifiedInfo[key] || requestDetail[key] + }); + return finalReqDetail; + })) + + // route user config + .then(co.wrap(function *(userConfig) { + if (userConfig.response) { + // user-assigned local response + userConfig._directlyPassToRespond = true; + return userConfig; + } else if (userConfig.requestOptions) { + const remoteResponse = yield fetchRemoteResponse(userConfig.protocol, userConfig.requestOptions, userConfig.requestData, { + dangerouslyIgnoreUnauthorized: reqHandlerCtx.dangerouslyIgnoreUnauthorized, + chunkSizeThreshold, + }); + return { + response: { + statusCode: remoteResponse.statusCode, + header: remoteResponse.header, + body: remoteResponse.body, + rawBody: remoteResponse.rawBody + }, + _res: remoteResponse._res, + }; + } else { + throw new Error('lost response or requestOptions, failed to continue'); + } + })) + + // invoke rule before responding to client + .then(co.wrap(function *(responseData) { + if (responseData._directlyPassToRespond) { + return responseData; + } else if (responseData.response.body && responseData.response.body instanceof CommonReadableStream) { // in stream mode + return responseData; + } else { + // TODO: err etimeout + return (yield userRule.beforeSendResponse(Object.assign({}, requestDetail), Object.assign({}, responseData))) || responseData; + } + })) + + .catch(co.wrap(function *(error) { + logUtil.printLog(util.collectErrorLog(error), logUtil.T_ERR); + + let errorResponse = getErrorResponse(error, fullUrl); + + // call user rule + try { + const userResponse = yield userRule.onError(Object.assign({}, requestDetail), error); + if (userResponse && userResponse.response && userResponse.response.header) { + errorResponse = userResponse.response; + } + } catch (e) {} + + return { + response: errorResponse + }; + })) + .then(sendFinalResponse) + + //update record info + .then((responseInfo) => { + resourceInfo.endTime = new Date().getTime(); + resourceInfo.res = { //construct a self-defined res object + statusCode: responseInfo.statusCode, + headers: responseInfo.header, + }; + + resourceInfo.statusCode = responseInfo.statusCode; + resourceInfo.resHeader = responseInfo.header; + resourceInfo.resBody = responseInfo.body instanceof CommonReadableStream ? '(big stream)' : (responseInfo.body || ''); + resourceInfo.length = resourceInfo.resBody.length; + + // console.info('===> resbody in record', resourceInfo); + + recorder && recorder.updateRecord(resourceInfoId, resourceInfo); + }) + .catch((e) => { + logUtil.printLog(color.green('Send final response failed:' + e.message), logUtil.T_ERR); + }); + } +} + +/** + * get a handler for CONNECT request + * + * @param {RequestHandler} reqHandlerCtx + * @param {object} userRule + * @param {Recorder} recorder + * @param {object} httpsServerMgr + * @returns + */ +function getConnectReqHandler(userRule, recorder, httpsServerMgr) { + const reqHandlerCtx = this; reqHandlerCtx.conns = new Map(); reqHandlerCtx.cltSockets = new Map() + + return function (req, cltSocket, head) { + const host = req.url.split(':')[0], + targetPort = req.url.split(':')[1]; + let shouldIntercept; + let requestDetail; + let resourceInfo = null; + let resourceInfoId = -1; + + const requestStream = new CommonReadableStream(); + + /* + 1. write HTTP/1.1 200 to client + 2. get request data + 3. tell if it is a websocket request + 4.1 if (websocket || do_not_intercept) --> pipe to target server + 4.2 else --> pipe to local server and do man-in-the-middle attack + */ + co(function *() { + // determine whether to use the man-in-the-middle server + logUtil.printLog(color.green('received https CONNECT request ' + host)); + if (reqHandlerCtx.forceProxyHttps) { + shouldIntercept = true; + } else { + requestDetail = { + host: req.url, + _req: req + }; + shouldIntercept = yield userRule.beforeDealHttpsRequest(requestDetail); + } + }) + .then(() => + new Promise((resolve) => { + // mark socket connection as established, to detect the request protocol + cltSocket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8', resolve); + }) + ) + .then(() => + new Promise((resolve, reject) => { + let resolved = false; + cltSocket.on('data', (chunk) => { + requestStream.push(chunk); + if (!resolved) { + resolved = true; + try { + const chunkString = chunk.toString(); + if (chunkString.indexOf('GET ') === 0) { + shouldIntercept = false; //websocket + } + } catch (e) { + console.error(e); + } + resolve(); + } + }); + cltSocket.on('end', () => { + requestStream.push(null); + }); + }) + ) + .then((result) => { + // log and recorder + if (shouldIntercept) { + logUtil.printLog('will forward to local https server'); + } else { + logUtil.printLog('will bypass the man-in-the-middle proxy'); + } + + //record + if (recorder) { + resourceInfo = { + host, + method: req.method, + path: '', + url: 'https://' + host, + req, + startTime: new Date().getTime() + }; + resourceInfoId = recorder.appendRecord(resourceInfo); + } + }) + .then(() => { + // determine the request target + if (!shouldIntercept) { + return { + host, + port: (targetPort === 80) ? 443 : targetPort, + } + } else { + return httpsServerMgr.getSharedHttpsServer(host).then(serverInfo => ({ host: serverInfo.host, port: serverInfo.port })); + } + }) + .then((serverInfo) => { + if (!serverInfo.port || !serverInfo.host) { + throw new Error('failed to get https server info'); + } + + return new Promise((resolve, reject) => { + const conn = net.connect(serverInfo.port, serverInfo.host, () => { + //throttle for direct-foward https + if (global._throttle && !shouldIntercept) { + requestStream.pipe(conn); + conn.pipe(global._throttle.throttle()).pipe(cltSocket); + } else { + requestStream.pipe(conn); + conn.pipe(cltSocket); + } + + resolve(); }); - return userRule; + conn.on('error', (e) => { + reject(e); + }); + + reqHandlerCtx.conns.set(serverInfo.host + ':' + serverInfo.port, conn) + reqHandlerCtx.cltSockets.set(serverInfo.host + ':' + serverInfo.port, cltSocket) + }); + }) + .then(() => { + if (recorder) { + resourceInfo.endTime = new Date().getTime(); + resourceInfo.statusCode = '200'; + resourceInfo.resHeader = {}; + resourceInfo.resBody = ''; + resourceInfo.length = 0; + + recorder && recorder.updateRecord(resourceInfoId, resourceInfo); + } + }) + .catch(co.wrap(function *(error) { + logUtil.printLog(util.collectErrorLog(error), logUtil.T_ERR); + + try { + yield userRule.onConnectError(requestDetail, error); + } catch (e) { } + + try { + let errorHeader = 'Proxy-Error: true\r\n'; + errorHeader += 'Proxy-Error-Message: ' + (error || 'null') + '\r\n'; + errorHeader += 'Content-Type: text/html\r\n'; + cltSocket.write('HTTP/1.1 502\r\n' + errorHeader + '\r\n\r\n'); + } catch (e) { } + })); + } +} + +class RequestHandler { + + /** + * Creates an instance of RequestHandler. + * + * @param {object} config + * @param {boolean} config.forceProxyHttps proxy all https requests + * @param {boolean} config.dangerouslyIgnoreUnauthorized + * @param {object} rule + * @param {Recorder} recorder + * + * @memberOf RequestHandler + */ + constructor(config, rule, recorder) { + const reqHandlerCtx = this; + if (config.forceProxyHttps) { + this.forceProxyHttps = true; } + if (config.dangerouslyIgnoreUnauthorized) { + this.dangerouslyIgnoreUnauthorized = true; + } + const default_rule = util.freshRequire('./rule_default'); + const userRule = util.merge(default_rule, rule); + + reqHandlerCtx.userRequestHandler = getUserReqHandler.apply(reqHandlerCtx, [userRule, recorder]); + + reqHandlerCtx.httpsServerMgr = new HttpsServerMgr({ + handler: reqHandlerCtx.userRequestHandler + }); + + this.connectReqHandler = getConnectReqHandler.apply(reqHandlerCtx, [userRule, recorder, reqHandlerCtx.httpsServerMgr]) + } } -function getRuleSummary(){ - return userRule.summary(); -} - -module.exports.userRequestHandler = userRequestHandler; -module.exports.connectReqHandler = connectReqHandler; -module.exports.setRules = setRules; -module.exports.getRuleSummary = getRuleSummary; -module.exports.token = Date.now(); +module.exports = RequestHandler; diff --git a/lib/ruleLoader.js b/lib/ruleLoader.js new file mode 100644 index 0000000..1d498ff --- /dev/null +++ b/lib/ruleLoader.js @@ -0,0 +1,74 @@ +'use strict'; + +const proxyUtil = require('./util'); +const path = require('path'); +const fs = require('fs'); +const request = require('request'); + +const cachePath = proxyUtil.getAnyProxyPath('cache'); + +/** + * download a file and cache + * + * @param {any} url + * @returns {string} cachePath + */ +function cacheRemoteFile(url) { + return new Promise((resolve, reject) => { + request(url, (error, response, body) => { + if (error) { + return reject(error); + } else if (response.statusCode !== 200) { + return reject(`failed to load with a status code ${response.statusCode}`); + } else { + const fileCreatedTime = proxyUtil.formatDate(new Date(), 'YYYY_MM_DD_hh_mm_ss'); + const random = Math.ceil(Math.random() * 500); + const fileName = `remote_rule_${fileCreatedTime}_r${random}.js`; + const filePath = path.join(cachePath, fileName); + fs.writeFileSync(filePath, body); + resolve(filePath); + } + }); + }); +} + + +/** + * load a local npm module + * + * @param {any} filePath + * @returns module + */ +function loadLocalPath(filePath) { + return new Promise((resolve, reject) => { + const ruleFilePath = path.resolve(process.cwd(), filePath); + if (fs.existsSync(ruleFilePath)) { + resolve(require(ruleFilePath)); + } else { + resolve(require(filePath)); + } + }); +} + + +/** + * load a module from url or local path + * + * @param {any} urlOrPath + * @returns module + */ +function requireModule(urlOrPath) { + return new Promise((resolve, reject) => { + if (/^http/i.test(urlOrPath)) { + resolve(cacheRemoteFile(urlOrPath)); + } else { + resolve(urlOrPath); + } + }).then(localPath => loadLocalPath(localPath)); +} + +module.exports = { + cacheRemoteFile, + loadLocalPath, + requireModule, +}; diff --git a/lib/rule_default.js b/lib/rule_default.js index 30f5a94..e5c441e 100644 --- a/lib/rule_default.js +++ b/lib/rule_default.js @@ -1,190 +1,68 @@ -var utils = require("./util"), - bodyParser = require("body-parser"), - path = require("path"), - fs = require("fs"), - Promise = require("promise"); - -var isRootCAFileExists = require("./certMgr.js").isRootCAFileExists(), - interceptFlag = false; - -//e.g. [ { keyword: 'aaa', local: '/Users/Stella/061739.pdf' } ] -var mapConfig = [], - configFile = "mapConfig.json"; -function saveMapConfig(content,cb){ - new Promise(function(resolve,reject){ - var anyproxyHome = utils.getAnyProxyHome(), - mapCfgPath = path.join(anyproxyHome,configFile); - - if(typeof content == "object"){ - content = JSON.stringify(content); - } - resolve({ - path :mapCfgPath, - content :content - }); - }) - .then(function(config){ - return new Promise(function(resolve,reject){ - fs.writeFile(config.path, config.content, function(e){ - if(e){ - reject(e); - }else{ - resolve(); - } - }); - }); - }) - .catch(function(e){ - cb && cb(e); - }) - .done(function(){ - cb && cb(); - }); -} -function getMapConfig(cb){ - var read = Promise.denodeify(fs.readFile); - - new Promise(function(resolve,reject){ - var anyproxyHome = utils.getAnyProxyHome(), - mapCfgPath = path.join(anyproxyHome,configFile); - - resolve(mapCfgPath); - }) - .then(read) - .then(function(content){ - return JSON.parse(content); - }) - .catch(function(e){ - cb && cb(e); - }) - .done(function(obj){ - cb && cb(null,obj); - }); -} - -setTimeout(function(){ - //load saved config file - getMapConfig(function(err,result){ - if(result){ - mapConfig = result; - } - }); -},1000); - +'use strict'; module.exports = { - token: Date.now(), - summary:function(){ - var tip = "the default rule for AnyProxy."; - if(!isRootCAFileExists){ - tip += "\nRoot CA does not exist, will not intercept any https requests."; - } - return tip; - }, - - shouldUseLocalResponse : function(req,reqBody){ - //intercept all options request - var simpleUrl = (req.headers.host || "") + (req.url || ""); - mapConfig.map(function(item){ - var key = item.keyword; - if(simpleUrl.indexOf(key) >= 0){ - req.anyproxy_map_local = item.local; - return false; - } - }); + + summary: 'the default rule for AnyProxy', + + /** + * + * + * @param {object} requestDetail + * @param {string} requestDetail.protocol + * @param {object} requestDetail.requestOptions + * @param {object} requestDetail.requestData + * @param {object} requestDetail.response + * @param {number} requestDetail.response.statusCode + * @param {object} requestDetail.response.header + * @param {buffer} requestDetail.response.body + * @returns + */ + *beforeSendRequest(requestDetail) { + return null; + }, - return !!req.anyproxy_map_local; - }, + /** + * + * + * @param {object} requestDetail + * @param {object} responseDetail + */ + *beforeSendResponse(requestDetail, responseDetail) { + return null; + }, - dealLocalResponse : function(req,reqBody,callback){ - if(req.anyproxy_map_local){ - fs.readFile(req.anyproxy_map_local,function(err,buffer){ - if(err){ - callback(200, {}, "[AnyProxy failed to load local file] " + err); - }else{ - var header = { - 'Content-Type': utils.contentType(req.anyproxy_map_local) - }; - callback(200, header, buffer); - } - }); - } - }, - replaceRequestProtocol:function(req,protocol){ - }, + /** + * + * + * @param {any} requestDetail + * @returns + */ + *beforeDealHttpsRequest(requestDetail) { + return false; + }, - replaceRequestOption : function(req,option){ - }, + /** + * + * + * @param {any} requestDetail + * @param {any} error + * @returns + */ + *onError(requestDetail, error) { + return null; + }, - replaceRequestData: function(req,data){ - }, - replaceResponseStatusCode: function(req,res,statusCode){ - }, - - replaceResponseHeader: function(req,res,header){ - }, - - // Deprecated - // replaceServerResData: function(req,res,serverResData){ - // return serverResData; - // }, - - replaceServerResDataAsync: function(req,res,serverResData,callback){ - callback(serverResData); - }, - - pauseBeforeSendingResponse: function(req,res){ - }, - - shouldInterceptHttpsReq:function(req){ - return interceptFlag; - }, - - //[beta] - //fetch entire traffic data - fetchTrafficData: function(id,info){}, - - setInterceptFlag: function(flag){ - interceptFlag = flag && isRootCAFileExists; - }, - - _plugIntoWebinterface: function(app,cb){ - - app.get("/filetree",function(req,res){ - try{ - var root = req.query.root || utils.getUserHome() || "/"; - utils.filewalker(root,function(err, info){ - res.json(info); - }); - }catch(e){ - res.end(e); - } - }); - - app.use(bodyParser.json()); - app.get("/getMapConfig",function(req,res){ - res.json(mapConfig); - }); - app.post("/setMapConfig",function(req,res){ - mapConfig = req.body; - res.json(mapConfig); - - saveMapConfig(mapConfig); - }); - - cb(); - }, - - _getCustomMenu : function(){ - return [ - // { - // name:"test", - // icon:"uk-icon-lemon-o", - // url :"http://anyproxy.io" - // } - ]; - } -}; \ No newline at end of file + /** + * + * + * @param {any} requestDetail + * @param {any} error + * @returns + */ + *onConnectError(requestDetail, error) { + return null; + }, +}; diff --git a/lib/systemProxyMgr.js b/lib/systemProxyMgr.js index f593680..353bd6b 100644 --- a/lib/systemProxyMgr.js +++ b/lib/systemProxyMgr.js @@ -1,21 +1,23 @@ -var child_process = require('child_process'); +'use strict' -var networkTypes = ['Ethernet', 'Thunderbolt Ethernet', 'Wi-Fi']; +const child_process = require('child_process'); + +const networkTypes = ['Ethernet', 'Thunderbolt Ethernet', 'Wi-Fi']; function execSync(cmd) { - var stdout, status = 0; + let stdout, + status = 0; + try { + stdout = child_process.execSync(cmd); + } catch (err) { + stdout = err.stdout; + status = err.status; + } - try { - stdout = child_process.execSync(cmd); - } catch (err) { - stdout = err.stdout; - status = err.status; - } - - return { - stdout: stdout.toString(), - status: status - }; + return { + stdout: stdout.toString(), + status + }; } /** @@ -49,79 +51,71 @@ function execSync(cmd) { * ------------------------------------------------------------------------ */ -var macProxyManager = {}; +const macProxyManager = {}; -macProxyManager.getNetworkType = function() { +macProxyManager.getNetworkType = () => { + for (let i = 0; i < networkTypes.length; i++) { + const type = networkTypes[i], + result = execSync('networksetup -getwebproxy ' + type); - for (var i = 0; i < networkTypes.length; i++) { + if (result.status === 0) { + macProxyManager.networkType = type; + return type; + } + } - var - type = networkTypes[i], - result = execSync('networksetup -getwebproxy ' + type); - - if (result.status === 0) { - macProxyManager.networkType = type; - return type; - } - } - - throw new Error('Unknown network type'); -}; - -macProxyManager.enableGlobalProxy = function(ip, port, proxyType) { - - if (!ip || !port) { - console.log('failed to set global proxy server.\n ip and port are required.'); - return; - }; - - proxyType = proxyType || 'http'; - - var networkType = macProxyManager.networkType || macProxyManager.getNetworkType(); - - return /^http$/i.test(proxyType) ? - - // set http proxy - execSync( - 'networksetup -setwebproxy ${networkType} ${ip} ${port}' - .replace("${networkType}", networkType) - .replace("${ip}", ip) - .replace("${port}", port)) : - - // set https proxy - execSync('networksetup -setsecurewebproxy ${networkType} ${ip} ${port}' - .replace("${networkType}", networkType) - .replace("${ip}", ip) - .replace("${port}", port)); - -}; - -macProxyManager.disableGlobalProxy = function(proxyType) { - proxyType = proxyType || 'http'; - - var networkType = macProxyManager.networkType || macProxyManager.getNetworkType(); - - return /^http$/i.test(proxyType) ? - - // set http proxy - execSync( - 'networksetup -setwebproxystate ${networkType} off' - .replace("${networkType}", networkType)) : - - // set https proxy - execSync( - 'networksetup -setsecurewebproxystate ${networkType} off' - .replace("${networkType}", networkType)); -}; - -macProxyManager.getProxyState = function() { - var networkType = macProxyManager.networkType || macProxyManager.getNetworkType(); - var result = execSync('networksetup -getwebproxy ${networkType}'.replace('${networkType}', networkType)); - - return result; + throw new Error('Unknown network type'); }; +macProxyManager.enableGlobalProxy = (ip, port, proxyType) => { + if (!ip || !port) { + console.log('failed to set global proxy server.\n ip and port are required.'); + return; + } + + proxyType = proxyType || 'http'; + + const networkType = macProxyManager.networkType || macProxyManager.getNetworkType(); + + return /^http$/i.test(proxyType) ? + + // set http proxy + execSync( + 'networksetup -setwebproxy ${networkType} ${ip} ${port} && networksetup -setproxybypassdomains ${networkType} 127.0.0.1 localhost' + .replace(/\${networkType}/g, networkType) + .replace('${ip}', ip) + .replace('${port}', port)) : + + // set https proxy + execSync('networksetup -setsecurewebproxy ${networkType} ${ip} ${port} && networksetup -setproxybypassdomains ${networkType} 127.0.0.1 localhost' + .replace(/\${networkType}/g, networkType) + .replace('${ip}', ip) + .replace('${port}', port)); +}; + +macProxyManager.disableGlobalProxy = (proxyType) => { + proxyType = proxyType || 'http'; + const networkType = macProxyManager.networkType || macProxyManager.getNetworkType(); + return /^http$/i.test(proxyType) ? + + // set http proxy + execSync( + 'networksetup -setwebproxystate ${networkType} off' + .replace('${networkType}', networkType)) : + + // set https proxy + execSync( + 'networksetup -setsecurewebproxystate ${networkType} off' + .replace('${networkType}', networkType)); +}; + +macProxyManager.getProxyState = () => { + const networkType = macProxyManager.networkType || macProxyManager.getNetworkType(); + const result = execSync('networksetup -getwebproxy ${networkType}'.replace('${networkType}', networkType)); + + return result; +}; /** * ------------------------------------------------------------------------ @@ -131,36 +125,28 @@ macProxyManager.getProxyState = function() { * ------------------------------------------------------------------------ */ -var winProxyManager = {}; +const winProxyManager = {}; -winProxyManager.enableGlobalProxy = function(ip, port) { +winProxyManager.enableGlobalProxy = (ip, port) => { + if (!ip && !port) { + console.log('failed to set global proxy server.\n ip and port are required.'); + return; + } - if (!ip && !port) { - console.log('failed to set global proxy server.\n ip and port are required.'); - return; - }; - - return execSync( - // set proxy - 'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer /t REG_SZ /d ${ip}:${port} /f & ' - .replace("${ip}", ip) - .replace("${port}", port) + - - // enable proxy - 'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 1 /f'); + return execSync( + // set proxy + 'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer /t REG_SZ /d ${ip}:${port} /f & ' + .replace('${ip}', ip) + .replace('${port}', port) + + // enable proxy + 'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 1 /f'); }; -winProxyManager.disableGlobalProxy = function() { - return execSync('reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f'); -}; +winProxyManager.disableGlobalProxy = () => execSync('reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f'); -winProxyManager.getProxyState = function() { - return ''; -}; +winProxyManager.getProxyState = () => '' -winProxyManager.getNetworkType = function() { - return ''; -}; +winProxyManager.getNetworkType = () => '' module.exports = /^win/.test(process.platform) ? winProxyManager : macProxyManager; diff --git a/lib/util.js b/lib/util.js index f3c2314..f101c39 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,110 +1,96 @@ -var fs = require("fs"), - path = require("path"), - mime = require('mime-types'), - color = require('colorful'), - logUtil = require("./log"), - exec = require('child_process').exec; +'use strict'; + +const fs = require('fs'), + path = require('path'), + mime = require('mime-types'), + color = require('colorful'), + Buffer = require('buffer').Buffer, + configUtil = require('./configUtil'), + logUtil = require('./log'); +const networkInterfaces = require('os').networkInterfaces(); -const changeCase = require('change-case'); // {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"} -module.exports.lower_keys = function(obj){ - for(var key in obj){ - var val = obj[key]; - delete obj[key]; +module.exports.lower_keys = (obj) => { + for (const key in obj) { + const val = obj[key]; + delete obj[key]; - obj[key.toLowerCase()] = val; - } + obj[key.toLowerCase()] = val; + } - return obj; -} + return obj; +}; -module.exports.merge = function(baseObj, extendObj){ - for(var key in extendObj){ - baseObj[key] = extendObj[key]; - } +module.exports.merge = function (baseObj, extendObj) { + for (const key in extendObj) { + baseObj[key] = extendObj[key]; + } - return baseObj; -} + return baseObj; +}; -function getUserHome(){ - return process.env.HOME || process.env.USERPROFILE; +function getUserHome() { + return process.env.HOME || process.env.USERPROFILE; } module.exports.getUserHome = getUserHome; +function getAnyProxyHome() { + const home = configUtil.getAnyProxyHome(); + if (!fs.existsSync(home)) { + fs.mkdirSync(home); + } + return home; +} +module.exports.getAnyProxyHome = getAnyProxyHome; -module.exports.getAnyProxyHome = function(){ - var home = path.join(util.getUserHome(),"/.anyproxy/"); +module.exports.getAnyProxyPath = function (pathName) { + const home = getAnyProxyHome(); + const targetPath = path.join(home, pathName); + if (!fs.existsSync(targetPath)) { + fs.mkdirSync(targetPath); + } + return targetPath; +} - if(!fs.existsSync(home)){ - try{ - fs.mkdirSync(home, '0777'); - }catch(e){ - return null; +module.exports.simpleRender = function (str, object, regexp) { + return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), (match, name) => { + if (match.charAt(0) === '\\') { + return match.slice(1); + } + return (object[name] != null) ? object[name] : ''; + }); +}; + +module.exports.filewalker = function (root, cb) { + root = root || process.cwd(); + + const ret = { + directory: [], + file: [] + }; + + fs.readdir(root, (err, list) => { + if (list && list.length) { + list.map((item) => { + const fullPath = path.join(root, item), + stat = fs.lstatSync(fullPath); + + if (stat.isFile()) { + ret.file.push({ + name: item, + fullPath + }); + } else if (stat.isDirectory()) { + ret.directory.push({ + name: item, + fullPath + }); } + }); } - return home; -} - -var CACHE_DIR_PREFIX = "cache_r"; -module.exports.generateCacheDir = function(){ - var rand = Math.floor(Math.random() * 1000000), - cachePath = path.join(util.getAnyProxyHome(),"./" + CACHE_DIR_PREFIX + rand); - - fs.mkdirSync(cachePath, '0777'); - return cachePath; -} - -module.exports.clearCacheDir = function(cb){ - var home = util.getAnyProxyHome(), - isWin = /^win/.test(process.platform); - - var dirNameWildCard = CACHE_DIR_PREFIX + "*"; - if(isWin){ - exec("for /D %f in (" + dirNameWildCard + ") do rmdir %f /s /q",{cwd : home},cb); - }else{ - exec("rm -rf " + dirNameWildCard + "",{cwd : home},cb); - } -} - -module.exports.simpleRender = function(str, object, regexp){ - return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), function(match, name){ - if (match.charAt(0) == '\\') return match.slice(1); - return (object[name] != null) ? object[name] : ''; - }); -} - -module.exports.filewalker = function(root,cb){ - root = root || process.cwd(); - - var ret = { - directory :[], - file :[] - }; - - fs.readdir(root,function(err, list){ - if(list && list.length){ - list.map(function(item){ - var fullPath = path.join(root,item), - stat = fs.lstatSync(fullPath); - - if(stat.isFile()){ - ret.file.push({ - name : item, - fullPath : fullPath - }); - - }else if(stat.isDirectory()){ - ret.directory.push({ - name : item, - fullPath : fullPath - }); - } - }); - } - - cb && cb.apply(null,[null,ret]); - }); + cb && cb.apply(null, [null, ret]); + }); }; /* @@ -112,46 +98,211 @@ module.exports.filewalker = function(root,cb){ * 比如在useLocalResponse的时候会使用到 */ module.exports.contentType = function (filepath) { - return mime.contentType(path.extname(filepath)); + return mime.contentType(path.extname(filepath)); }; /* * 读取file的大小,以byte为单位 */ module.exports.contentLength = function (filepath) { - try { - var stat = fs.statSync(filepath); - return stat.size; - } catch (e) { - logUtil.printLog(color.red("\nfailed to ready local file : " + filepath)); - logUtil.printLog(color.red(e)); - return 0; - } -}; - -module.exports.showRootInstallTip = function () { - logUtil.printLog(color.red("can not find rootCA.crt or rootCA.key"), logUtil.T_ERR); - logUtil.printLog(color.red("you may generate one by the following methods"), logUtil.T_ERR); - logUtil.printLog(color.red("\twhen using globally : anyproxy --root"), logUtil.T_ERR); - logUtil.printLog(color.red("\twhen using as a module : require(\"anyproxy\").generateRootCA();"), logUtil.T_ERR); - logUtil.printLog(color.red("\tmore info : https://github.com/alibaba/anyproxy/wiki/How-to-config-https-proxy"), logUtil.T_ERR); + try { + const stat = fs.statSync(filepath); + return stat.size; + } catch (e) { + logUtil.printLog(color.red('\nfailed to ready local file : ' + filepath)); + logUtil.printLog(color.red(e)); + return 0; + } }; /* -* remove the cache before requering, the path SHOULD BE RELATIVE TO UTIL.JS +* remove the cache before requiring, the path SHOULD BE RELATIVE TO UTIL.JS */ -module.exports.freshRequire = function (path) { - delete require.cache[require.resolve(path)]; - return require(path); +module.exports.freshRequire = function (modulePath) { + delete require.cache[require.resolve(modulePath)]; + return require(modulePath); }; -module.exports.upper_keys = function (obj) { - var upperObject = {}; - for(var key in obj) { - var upperKey = changeCase.headerCase(key); - upperObject[upperKey] = obj[key]; +/* +* format the date string +* @param date Date or timestamp +* @param formatter YYYYMMDDHHmmss +*/ +module.exports.formatDate = function (date, formatter) { + if (typeof date !== 'object') { + date = new Date(date); + } + const transform = function (value) { + return value < 10 ? '0' + value : value; + }; + return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => { + switch (match) { + case 'YYYY': + return transform(date.getFullYear()); + case 'MM': + return transform(date.getMonth() + 1); + case 'mm': + return transform(date.getMinutes()); + case 'DD': + return transform(date.getDate()); + case 'hh': + return transform(date.getHours()); + case 'ss': + return transform(date.getSeconds()); + default: + return '' } - - return upperObject; + }); }; + +/** +* get headers(Object) from rawHeaders(Array) +* @param rawHeaders [key, value, key2, value2, ...] + +*/ + +module.exports.getHeaderFromRawHeaders = function (rawHeaders) { + const headerObj = {}; + const _handleSetCookieHeader = function (key, value) { + if (headerObj[key].constructor === Array) { + headerObj[key].push(value); + } else { + headerObj[key] = [headerObj[key], value]; + } + }; + + if (!!rawHeaders) { + for (let i = 0; i < rawHeaders.length; i += 2) { + const key = rawHeaders[i]; + let value = rawHeaders[i + 1]; + + if (typeof value === 'string') { + value = value.replace(/\0+$/g, ''); // 去除 \u0000的null字符串 + } + + if (!headerObj[key]) { + headerObj[key] = value; + } else { + // headers with same fields could be combined with comma. Ref: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + // set-cookie should NOT be combined. Ref: https://tools.ietf.org/html/rfc6265 + if (key.toLowerCase() === 'set-cookie') { + _handleSetCookieHeader(key, value); + } else { + headerObj[key] = headerObj[key] + ',' + value; + } + } + } + } + return headerObj; +}; + +module.exports.getAllIpAddress = function getAllIpAddress() { + const allIp = []; + + Object.keys(networkInterfaces).map((nic) => { + networkInterfaces[nic].filter((detail) => { + if (detail.family.toLowerCase() === 'ipv4') { + allIp.push(detail.address); + } + }); + }); + + return allIp.length ? allIp : ['127.0.0.1']; +}; + +function deleteFolderContentsRecursive(dirPath, ifClearFolderItself) { + if (!dirPath.trim() || dirPath === '/') { + throw new Error('can_not_delete_this_dir'); + } + + if (fs.existsSync(dirPath)) { + fs.readdirSync(dirPath).forEach((file) => { + const curPath = path.join(dirPath, file); + if (fs.lstatSync(curPath).isDirectory()) { + deleteFolderContentsRecursive(curPath, true); + } else { // delete all files + fs.unlinkSync(curPath); + } + }); + + if (ifClearFolderItself) { + try { + // ref: https://github.com/shelljs/shelljs/issues/49 + const start = Date.now(); + while (true) { + try { + fs.rmdirSync(dirPath); + break; + } catch (er) { + if (process.platform === 'win32' && (er.code === 'ENOTEMPTY' || er.code === 'EBUSY' || er.code === 'EPERM')) { + // Retry on windows, sometimes it takes a little time before all the files in the directory are gone + if (Date.now() - start > 1000) throw er; + } else if (er.code === 'ENOENT') { + break; + } else { + throw er; + } + } + } + } catch (e) { + throw new Error('could not remove directory (code ' + e.code + '): ' + dirPath); + } + } + } +} + +module.exports.deleteFolderContentsRecursive = deleteFolderContentsRecursive; + +module.exports.getFreePort = function () { + return new Promise((resolve, reject) => { + const server = require('net').createServer(); + server.unref(); + server.on('error', reject); + server.listen(0, () => { + const port = server.address().port; + server.close(() => { + resolve(port); + }); + }); + }); +} + +module.exports.collectErrorLog = function (error) { + if (error && error.code && error.toString()) { + return error.toString(); + } else { + let result = [error, error.stack].join('\n'); + try { + const errorString = error.toString(); + if (errorString.indexOf('You may only yield a function') >= 0) { + result = 'Function is not yieldable. Did you forget to provide a generator or promise in rule file ? \nFAQ http://anyproxy.io/4.x/#faq'; + } + } catch (e) {} + return result + } +} + +module.exports.isFunc = function (source) { + return source && Object.tostring.call(source) === '[object Function]'; +}; + +/** +* @param {object} content +* @returns the size of the content +*/ +module.exports.getByteSize = function (content) { + return Buffer.byteLength(content); +}; + +/* +* identify whether the +*/ +module.exports.isIpDomain = function (domain) { + if (!domain) { + return false; + } + const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/; + + return ipReg.test(domain); +}; diff --git a/lib/webInterface.js b/lib/webInterface.js index cc616ea..4ee90a3 100644 --- a/lib/webInterface.js +++ b/lib/webInterface.js @@ -1,157 +1,361 @@ -var express = require("express"), - url = require('url'), - fs = require("fs"), - path = require("path"), - events = require("events"), - inherits = require("util").inherits, - qrCode = require('qrcode-npm'), - util = require("./util"), - certMgr = require("./certMgr"), - logUtil = require("./log"), - juicer = require("juicer"), - compress = require('compression'); +'use strict'; +const DEFAULT_WEB_PORT = 8002; // port for web interface -function webInterface(config){ - var port = config.port, - wsPort = config.wsPort, - ipAddress = config.ip, - userRule = config.userRule, - ruleSummary = "", - customMenu = [], - server; +const express = require('express'), + url = require('url'), + bodyParser = require('body-parser'), + fs = require('fs'), + path = require('path'), + events = require('events'), + qrCode = require('qrcode-npm'), + util = require('./util'), + certMgr = require('./certMgr'), + wsServer = require('./wsServer'), + juicer = require('juicer'), + ip = require('ip'), + co = require('co'), + compress = require('compression'); - try{ - ruleSummary = userRule.summary(); - customMenu = userRule._getCustomMenu(); - }catch(e){} +const packageJson = require('../package.json'); - var self = this, - myAbsAddress = "http://" + ipAddress + ":" + port +"/", - crtFilePath = certMgr.getRootCAFilePath(), - staticDir = path.join(__dirname,'../web'); +const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb +/** + * + * + * @class webInterface + * @extends {events.EventEmitter} + */ +class webInterface extends events.EventEmitter { - var app = express(); - app.use(compress()); //invoke gzip - app.use(function(req, res, next) { - res.setHeader("note", "THIS IS A REQUEST FROM ANYPROXY WEB INTERFACE"); + /** + * Creates an instance of webInterface. + * + * @param {object} config + * @param {number} config.webPort + * @param {number} config.wsPort + * @param {object} recorder + * + * @memberOf webInterface + */ + constructor(config, recorder) { + if (!recorder) { + throw new Error('recorder is required for web interface'); + } + super(); + const self = this; + self.webPort = config.webPort || DEFAULT_WEB_PORT; + self.recorder = recorder; + self.config = config || {}; + + self.app = null; + self.server = null; + self.wsServer = null; + } + + start() { + const self = this; + const recorder = self.recorder; + let wsPort; + + return co(function *() { + // determine ws port + wsPort = self.config.wsPort ? self.config.wsPort : yield util.getFreePort(); + }).then(() => { + const ipAddress = ip.address(), + // userRule = proxyInstance.proxyRule, + webBasePath = 'web'; + let ruleSummary = ''; + let customMenu = []; + + try { + ruleSummary = ''; //userRule.summary(); + customMenu = ''; // userRule._getCustomMenu(); + } catch (e) { } + + const myAbsAddress = 'http://' + ipAddress + ':' + self.webPort + '/', + staticDir = path.join(__dirname, '../', webBasePath); + + const app = express(); + app.use(compress()); //invoke gzip + app.use((req, res, next) => { + res.setHeader('note', 'THIS IS A REQUEST FROM ANYPROXY WEB INTERFACE'); return next(); - }); + }); + app.use(bodyParser.json()); - app.get("/lastestLog",function(req,res){ - recorder.getRecords(null,120,function(err,docs){ - if(err){ - res.end(err.toString()); - }else{ - res.json(docs); - } + app.get('/latestLog', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + recorder.getRecords(null, 10000, (err, docs) => { + if (err) { + res.end(err.toString()); + } else { + res.json(docs); + } }); - }); + }); + + app.get('/downloadBody', (req, res) => { + const query = req.query; + recorder.getDecodedBody(query.id, (err, result) => { + if (err || !result || !result.content) { + res.json({}); + } else if (result.mime) { + if (query.raw === 'true') { + //TODO : cache query result + res.type(result.mime).end(result.content); + } else if (query.download === 'true') { + res.setHeader('Content-disposition', `attachment; filename=${result.fileName}`); + res.setHeader('Content-type', result.mime); + res.end(result.content); + } + } else { + res.json({ - app.get("/fetchBody",function(req,res){ - var query = req.query; - if(query && query.id){ - global.recorder.getDecodedBody(query.id, function(err, result){ - if(err || !result || !result.content){ - res.json({}); - }else if(result.type && result.type == "image" && result.mime){ - if(query.raw){ - //TODO : cache query result - res.type(result.mime).end(result.content); - }else{ - res.json({ - id : query.id, - type : result.type, - ref : "/fetchBody?id=" + query.id + "&raw=true" - }); - } - }else{ - res.json({ - id : query.id, - type : result.type, - content : result.content - }); - } }); - }else{ - res.end({}); - } - }); + } + }); + }); - app.get("/fetchCrtFile",function(req,res){ - if(crtFilePath){ - res.setHeader("Content-Type","application/x-x509-ca-cert"); - res.setHeader("Content-Disposition",'attachment; filename="rootCA.crt"'); - res.end(fs.readFileSync(crtFilePath,{encoding:null})); - }else{ - res.setHeader("Content-Type","text/html"); - res.end("can not file rootCA ,plase use anyproxy --root to generate one"); - } - }); - - //make qr code - app.get("/qr",function(req,res){ - var qr = qrCode.qrcode(4, 'M'), - targetUrl = myAbsAddress, - qrImageTag, - resDom; - - qr.addData(targetUrl); - qr.make(); - qrImageTag = qr.createImgTag(4); - - resDom = ' __img
          click or scan qr code to start client
          '.replace(/__url/,targetUrl).replace(/__img/,qrImageTag); - res.setHeader("Content-Type", "text/html"); - res.end(resDom); - }); - - app.get("/qr_root",function(req,res){ - var qr = qrCode.qrcode(4, 'M'), - targetUrl = myAbsAddress + "fetchCrtFile", - qrImageTag, - resDom; - - qr.addData(targetUrl); - qr.make(); - qrImageTag = qr.createImgTag(4); - - resDom = ' __img
          click or scan qr code to download rootCA.crt
          '.replace(/__url/,targetUrl).replace(/__img/,qrImageTag); - res.setHeader("Content-Type", "text/html"); - res.end(resDom); - }); - - app.use(function(req,res,next){ - var indexTpl = fs.readFileSync(path.join(staticDir,"/index.html"),{encoding:"utf8"}), - opt = { - rule : ruleSummary || "", - customMenu : customMenu || [], - wsPort : wsPort, - ipAddress : ipAddress || "127.0.0.1" + app.get('/fetchBody', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const query = req.query; + if (query && query.id) { + recorder.getDecodedBody(query.id, (err, result) => { + // 返回下载信息 + const _resDownload = function (isDownload) { + isDownload = typeof isDownload === 'boolean' ? isDownload : true; + res.json({ + id: query.id, + type: result.type, + fileName: result.fileName, + ref: `/downloadBody?id=${query.id}&download=${isDownload}&raw=${!isDownload}` + }); }; - if( url.parse(req.url).pathname == "/"){ - res.setHeader("Content-Type", "text/html"); - res.end(juicer(indexTpl, opt)); - }else{ - next(); + // 返回内容 + const _resContent = () => { + if (util.getByteSize(result.content || '') > MAX_CONTENT_SIZE) { + _resDownload(true); + return; + } + + res.json({ + id: query.id, + type: result.type, + resBody: result.content + }); + }; + + if (err || !result) { + res.json({}); + } else if (result.statusCode === 200 && result.mime) { + if (result.type === 'json' || + result.mime.indexOf('text') === 0 || + // deal with 'application/x-javascript' and 'application/javascript' + result.mime.indexOf('javascript') > -1) { + _resContent(); + } else if (result.type === 'image') { + _resDownload(false); + } else { + _resDownload(true); + } + } else { + _resContent(); + } + }); + } else { + res.end({}); } - }); + }); - app.use(express.static(staticDir)); + app.get('/fetchReqBody', (req, res) => { + const query = req.query; + if (query && query.id) { + recorder.getSingleRecord(query.id, (err, doc) => { + if (err || !doc[0]) { + console.error(err); + res.end(''); + return; + } - //plugin from rule file - if(typeof userRule._plugIntoWebinterface == "function"){ - userRule._plugIntoWebinterface(app,function(){ - server = app.listen(port); + res.setHeader('Content-disposition', `attachment; filename=request_${query.id}_body.txt`); + res.setHeader('Content-type', 'text/plain'); + res.end(doc[0].reqBody); + }); + } else { + res.end(''); + } + }); + + app.get('/fetchCrtFile', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const _crtFilePath = certMgr.getRootCAFilePath(); + if (_crtFilePath) { + res.setHeader('Content-Type', 'application/x-x509-ca-cert'); + res.setHeader('Content-Disposition', 'attachment; filename="rootCA.crt"'); + res.end(fs.readFileSync(_crtFilePath, { encoding: null })); + } else { + res.setHeader('Content-Type', 'text/html'); + res.end('can not file rootCA ,plase use anyproxy --root to generate one'); + } + }); + + //make qr code + app.get('/qr', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const qr = qrCode.qrcode(4, 'M'), + targetUrl = myAbsAddress; + qr.addData(targetUrl); + qr.make(); + const qrImageTag = qr.createImgTag(4); + const resDom = ' __img
          click or scan qr code to start client
          '.replace(/__url/, targetUrl).replace(/__img/, qrImageTag); + res.setHeader('Content-Type', 'text/html'); + res.end(resDom); + }); + + app.get('/api/getQrCode', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const qr = qrCode.qrcode(4, 'M'), + targetUrl = myAbsAddress + 'fetchCrtFile'; + + qr.addData(targetUrl); + qr.make(); + const qrImageTag = qr.createImgTag(4); + + // resDom = ' __img
          click or scan qr code to download rootCA.crt
          '.replace(/__url/,targetUrl).replace(/__img/,qrImageTag); + // res.setHeader("Content-Type", "text/html"); + // res.end(resDom); + + const isRootCAFileExists = certMgr.isRootCAFileExists(); + res.json({ + status: 'success', + url: targetUrl, + isRootCAFileExists, + qrImgDom: qrImageTag }); - }else{ - server = app.listen(port); - } + }); - self.app = app; - self.server = server; + // response init data + app.get('/api/getInitData', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const rootCAExists = certMgr.isRootCAFileExists(); + const rootDirPath = certMgr.getRootDirPath(); + const interceptFlag = false; //proxyInstance.getInterceptFlag(); TODO + const globalProxyFlag = false; // TODO: proxyInstance.getGlobalProxyFlag(); + res.json({ + status: 'success', + rootCAExists, + rootCADirPath: rootDirPath, + currentInterceptFlag: interceptFlag, + currentGlobalProxyFlag: globalProxyFlag, + ruleSummary: ruleSummary || '', + ipAddress: util.getAllIpAddress(), + port: '', //proxyInstance.proxyPort, // TODO + wsPort, + appVersion: packageJson.version + }); + }); + + app.post('/api/generateRootCA', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const rootExists = certMgr.isRootCAFileExists(); + if (!rootExists) { + certMgr.generateRootCA(() => { + res.json({ + status: 'success', + code: 'done' + }); + }); + } else { + res.json({ + status: 'success', + code: 'root_ca_exists' + }); + } + }); + + // should not be available in in-build version + // app.post('/api/toggleInterceptHttps', (req, res) => { + // const rootExists = certMgr.isRootCAFileExists(); + // if (!rootExists) { + // certMgr.generateRootCA(() => { + // proxyInstance.setIntercept(req.body.flag); + // // Also inform the web if RootCa exists + // res.json({ + // status: 'success', + // rootExists + // }); + // }); + // } else { + // proxyInstance.setIntercept(req.body.flag); + // res.json({ + // status: 'success', + // rootExists + // }); + // } + // }); + + // app.post('/api/toggleGlobalProxy', (req, res) => { + // const flag = req.body.flag; + // let result = {}; + // result = flag ? proxyInstance.enableGlobalProxy() : proxyInstance.disableGlobalProxy(); + + // if (result.status) { + // res.json({ + // status: 'failed', + // errorMsg: result.stdout + // }); + // } else { + // res.json({ + // status: 'success', + // isWindows: /^win/.test(process.platform) + // }); + // } + // }); + + app.use((req, res, next) => { + const indexTpl = fs.readFileSync(path.join(staticDir, '/index.html'), { encoding: 'utf8' }), + opt = { + rule: ruleSummary || '', + customMenu: customMenu || [], + wsPort, + ipAddress: ipAddress || '127.0.0.1' + }; + + if (url.parse(req.url).pathname === '/') { + res.setHeader('Content-Type', 'text/html'); + res.end(juicer(indexTpl, opt)); + } else { + next(); + } + }); + + app.use(express.static(staticDir)); + + //plugin from rule file + const server = app.listen(self.webPort); + + self.app = app; + self.server = server; + }).then(() => { + // start ws server + self.wsServer = new wsServer({ + port: wsPort + }, recorder); + + return self.wsServer.start(); + }); + } + + close() { + this.server && this.server.close(); + this.wsServer && this.wsServer.closeAll(); + + this.server = null; + this.wsServer = null; + this.proxyInstance = null; + } } -inherits(webInterface, events.EventEmitter); - module.exports = webInterface; diff --git a/lib/wsServer.js b/lib/wsServer.js index 81da63a..e1fa39c 100644 --- a/lib/wsServer.js +++ b/lib/wsServer.js @@ -1,98 +1,158 @@ +'use strict'; + //websocket server manager -var WebSocketServer = require('ws').Server, - logUtil = require("./log"); +const WebSocketServer = require('ws').Server; +const logUtil = require('./log'); -function resToMsg(msg,cb){ - var result = {}, - jsonData; +function resToMsg(msg, recorder, cb) { + let result = {}, + jsonData; - try{ - jsonData = JSON.parse(msg); - }catch(e){ - result = { - type : "error", - error: "failed to parse your request : " + e.toString() + try { + jsonData = JSON.parse(msg); + } catch (e) { + result = { + type: 'error', + error: 'failed to parse your request : ' + e.toString() + }; + cb && cb(result); + return; + } + + if (jsonData.reqRef) { + result.reqRef = jsonData.reqRef; + } + + if (jsonData.type === 'reqBody' && jsonData.id) { + result.type = 'body'; + recorder.getBody(jsonData.id, (err, data) => { + if (err) { + result.content = { + id: null, + body: null, + error: err.toString() }; - cb && cb(result); - return; - } - - if(jsonData.reqRef){ - result.reqRef = jsonData.reqRef; - } - - if(jsonData.type == "reqBody" && jsonData.id){ - result.type = "body"; - global.recorder.getBody(jsonData.id, function(err, data){ - if(err){ - result.content = { - id : null, - body : null, - error : err.toString() - }; - }else{ - result.content = { - id : jsonData.id, - body : data.toString() - }; - } - cb && cb(result); - }); - }else{ // more req handler here - return null; - } + } else { + result.content = { + id: jsonData.id, + body: data.toString() + }; + } + cb && cb(result); + }); + } else { // more req handler here + return null; + } } //config.port -function wsServer(config){ - //web socket interface - var self = this, - wss = new WebSocketServer({port: config.port}); - wss.broadcast = function(data) { - var key = data.id; - if(typeof data == "object"){ - data = JSON.stringify(data); + +class wsServer { + constructor(config, recorder) { + if (!recorder) { + throw new Error('proxy recorder is required'); + } else if (!config || !config.port) { + throw new Error('config.port is required'); + } + + const self = this; + self.config = config; + self.recorder = recorder; + } + + start() { + const self = this; + const config = self.config; + const recorder = self.recorder; + return new Promise((resolve, reject) => { + //web socket interface + const wss = new WebSocketServer({ + port: config.port, + clientTracking: true, + }, resolve); + + logUtil.printLog(`WebSoket setup at port ${config.port} `, logUtil.T_DEBUG); + // the queue of the messages to be delivered + let messageQueue = []; + // the flat to indicate wheter to broadcast the record + let broadcastFlag = true; + + setInterval(() => { + broadcastFlag = true; + sendMultipleMessage(); + }, 50); + + function sendMultipleMessage(data) { + // if the flag goes to be true, and there are records to send + if (broadcastFlag && messageQueue.length > 0) { + wss && wss.broadcast({ + type: 'updateMultiple', + content: messageQueue + }); + messageQueue = []; + broadcastFlag = false; + } else { + data && messageQueue.push(data); } + } - for(var i in this.clients){ - try{ - this.clients[i].send(data); - }catch(e){ - logUtil.printLog("websocket failed to send data, " + e, logUtil.T_ERR); - } + wss.broadcast = function (data) { + if (typeof data === 'object') { + data = JSON.stringify(data); } - }; - - wss.on("connection",function(ws){ - ws.on("message",function(msg){ - resToMsg(msg,function(res){ - res && ws.send(JSON.stringify(res)); - }); - }); - }); - - wss.on("close",function(){}); - - global.recorder.on("update",function(data){ - try{ - wss && wss.broadcast({ - type : "update", - content: data - }); - - }catch(e){ - console.log("ws error"); - console.log(e); + for (const client of wss.clients) { + try { + client.send(data); + } catch (e) { + logUtil.printLog('websocket failed to send data, ' + e, logUtil.T_ERR); + } } - }); + }; - self.wss = wss; -} + wss.on('connection', (ws) => { + ws.on('message', (msg) => { + resToMsg(msg, recorder, (res) => { + res && ws.send(JSON.stringify(res)); + }); + }); -wsServer.prototype.closeAll = function(){ - var self = this; - self.wss.close(); + ws.on('error', (e) => { + console.error('error in ws:', e); + }); + }); + + wss.on('error', (e) => { + logUtil.printLog('websocket error, ' + e, logUtil.T_ERR); + }); + + wss.on('close', () => { }); + + recorder.on('update', (data) => { + try { + sendMultipleMessage(data); + } catch (e) { + console.log('ws error'); + console.log(e); + } + }); + + self.wss = wss; + }); + } + + closeAll() { + const self = this; + return new Promise((resolve, reject) => { + self.wss.close((e) => { + if (e) { + reject(e); + } else { + resolve(); + } + }); + }); + } } module.exports = wsServer; diff --git a/module_sample/core_reload.js b/module_sample/core_reload.js new file mode 100644 index 0000000..d86a210 --- /dev/null +++ b/module_sample/core_reload.js @@ -0,0 +1,47 @@ +const AnyProxy = require('../proxy'); +const exec = require('child_process').exec; + +const AnyProxyRecorder = require('../lib/recorder'); +const WebInterfaceLite = require('../lib/webInterface'); + +/* +------------------------------- +| ProxyServerA | ProxyServerB | +------------------------------- ---------------------------- +| Common Recorder | -------(by events)------| WebInterfaceLite | +------------------------------- ---------------------------- +*/ + + +const commonRecorder = new AnyProxyRecorder(); + +// web interface依赖recorder +new WebInterfaceLite({ // common web interface + webPort: 8002 +}, commonRecorder); + +// proxy core只依赖recorder,与webServer无关 +const optionsA = { + port: 8001, + recorder: commonRecorder, // use common recorder +}; + +const optionsB = { + port: 8005, + recorder: commonRecorder, // use common recorder +}; + +const proxyServerA = new AnyProxy.ProxyCore(optionsA); +const proxyServerB = new AnyProxy.ProxyCore(optionsB); + +proxyServerA.start(); +proxyServerB.start(); + +// after both ready +setTimeout(() => { + exec('curl http://www.qq.com --proxy http://127.0.0.1:8001'); + exec('curl http://www.sina.com.cn --proxy http://127.0.0.1:8005'); +}, 1000); + +// visit http://127.0.0.1 , there should be two records + diff --git a/module_sample/https_config.js b/module_sample/https_config.js new file mode 100644 index 0000000..92ffc82 --- /dev/null +++ b/module_sample/https_config.js @@ -0,0 +1,23 @@ +const AnyProxy = require('../proxy'); +const exec = require('child_process').exec; + +if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = require('path').dirname(keyPath); + console.log('The cert is generated at', certDir); + const isWin = /^win/.test(process.platform); + if (isWin) { + exec('start .', { cwd: certDir }); + } else { + exec('open .', { cwd: certDir }); + } + } else { + console.error('error when generating rootCA', error); + } + }); +} else { + // clear all the certificates + // AnyProxy.utils.certMgr.clearCerts() +} diff --git a/module_sample/normal_use.js b/module_sample/normal_use.js new file mode 100644 index 0000000..5c813c2 --- /dev/null +++ b/module_sample/normal_use.js @@ -0,0 +1,70 @@ +const AnyProxy = require('../proxy'); + +const options = { + type: 'http', + port: 8001, + rule: null, + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: true, + silent: false +}; +const proxyServer = new AnyProxy.ProxyServer(options); + +proxyServer.on('ready', () => { + console.log('ready'); + // set as system proxy + proxyServer.close().then(() => { + const proxyServerB = new AnyProxy.ProxyServer(options); + proxyServerB.start(); + }); + + console.log('closed'); + // setTimeout(() => { + + // }, 2000); + + + // AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001'); +}); + +proxyServer.on('error', (e) => { + console.log('proxy error'); + console.log(e); +}); + +process.on('SIGINT', () => { + // AnyProxy.utils.systemProxyMgr.disableGlobalProxy(); + proxyServer.close(); + process.exit(); +}); + + +proxyServer.start(); + + +// const WebSocketServer = require('ws').Server; +// const wsServer = new WebSocketServer({ port: 8003 },function(){ +// console.log('ready'); + +// try { +// const serverB = new WebSocketServer({ port: 8003 }, function (e, result) { +// console.log('---in B---'); +// console.log(e); +// console.log(result); +// }); +// } catch(e) { +// console.log(e); +// console.log('e'); +// } + +// // wsServer.close(function (e, result) { +// // console.log('in close'); +// // console.log(e); +// // console.log(result); +// // }); +// }); diff --git a/module_sample/simple_use.js b/module_sample/simple_use.js new file mode 100644 index 0000000..aa81151 --- /dev/null +++ b/module_sample/simple_use.js @@ -0,0 +1,10 @@ +const AnyProxy = require('../proxy'); + +const options = { + port: 8001, + webInterface: { + enable: true + } +}; +const proxyServer = new AnyProxy.ProxyServer(options); +proxyServer.start(); diff --git a/package.json b/package.json index cad28d1..1c14a20 100644 --- a/package.json +++ b/package.json @@ -1,55 +1,110 @@ { "name": "anyproxy", - "version": "3.10.4", - "description": "A fully configurable proxy in NodeJS, which can handle HTTPS requests perfectly.", + "version": "4.0.0", + "description": "A fully configurable HTTP/HTTPS proxy in Node.js", "main": "proxy.js", "bin": { - "anyproxy": "bin.js" + "anyproxy-ca": "bin/anyproxy-ca", + "anyproxy": "bin/anyproxy" }, "dependencies": { "async": "~0.9.0", "async-task-mgr": ">=1.1.0", "body-parser": "^1.13.1", - "change-case": "^3.0.0", + "classnames": "^2.2.5", + "clipboard-js": "^0.3.3", + "co": "^4.6.0", "colorful": "^2.1.0", - "commander": "~2.3.0", + "commander": "~2.11.0", + "component-emitter": "^1.2.1", "compression": "^1.4.4", + "es6-promise": "^3.3.1", "express": "^4.8.5", "iconv-lite": "^0.4.6", + "inquirer": "^3.0.1", "ip": "^0.3.2", "juicer": "^0.6.6-stable", "mime-types": "2.1.11", - "nedb": "^0.11.0", + "moment": "^2.15.1", + "nedb": "^1.8.0", "node-easy-cert": "^1.0.0", "node-forge": "^0.6.39", "npm": "^2.7.0", - "promise": "^7.0.4", + "pug": "^2.0.0-beta6", + "q": "^1.4.1", "qrcode-npm": "0.0.3", + "request": "^2.74.0", "stream-throttle": "^0.1.3", - "ws": "^1.1.0" + "whatwg-fetch": "^1.0.0", + "ws": "^2.2.0" }, "devDependencies": { + "antd": "^2.5.0", + "autoprefixer": "^6.4.1", + "babel-core": "^6.14.0", + "babel-eslint": "^7.0.0", + "babel-loader": "^6.2.5", + "babel-plugin-import": "^1.0.0", + "babel-plugin-transform-runtime": "^6.15.0", "babel-polyfill": "^6.13.0", "babel-preset-es2015": "^6.13.2", + "babel-preset-react": "^6.11.1", "babel-preset-stage-0": "^6.5.0", "babel-register": "^6.11.6", + "babel-runtime": "^6.11.6", + "css-loader": "^0.23.1", + "eslint": "^3.5.0", + "eslint-config-airbnb": "^15.1.0", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-jsx-a11y": "^5.1.1", + "eslint-plugin-react": "^7.4.0", + "extract-text-webpack-plugin": "^1.0.1", + "file-loader": "^0.9.0", "https-proxy-agent": "^1.0.0", - "jasmine": "^2.4.1", + "jasmine": "^2.5.3", "koa": "^1.2.1", "koa-body": "^1.4.0", "koa-router": "^5.4.0", "koa-send": "^3.2.0", "koa-websocket": "^2.0.0", + "less": "^2.7.1", + "less-loader": "^2.2.3", + "memwatch-next": "^0.3.0", + "node-simhash": "^0.1.0", "nodeunit": "^0.9.1", - "request": "^2.74.0", - "stream-equal": "0.1.8" + "phantom": "^4.0.0", + "postcss-loader": "^0.13.0", + "pre-commit": "^1.2.2", + "react": "^15.3.1", + "react-addons-perf": "^15.4.0", + "react-dom": "^15.3.1", + "react-json-tree": "^0.10.0", + "react-redux": "^4.4.5", + "react-tap-event-plugin": "^1.0.0", + "redux": "^3.6.0", + "redux-saga": "^0.11.1", + "stream-equal": "0.1.8", + "style-loader": "^0.13.1", + "svg-inline-loader": "^0.7.1", + "svg-inline-react": "^1.0.2", + "url-loader": "^0.5.7", + "webpack": "^1.12.9", + "worker-loader": "^0.7.1" }, "scripts": { - "test": "sh test/test.sh", + "prepublish":"npm run buildweb", + "test": "node test.js", + "lint": "eslint .", "testserver": "node test/server/startServer.js", - "no-pre-commit": "exit 0" + "testOutWeb": "jasmine test/spec_outweb/test_realweb_spec.js", + "buildweb": "NODE_ENV=production webpack --config web/webpack.config.js --process --colors", + "webserver": "NODE_ENV=test webpack --config web/webpack.config.js --process --colors --watch", + "doc:serve": "node build_scripts/prebuild-doc.js && gitbook serve ./docs-src ./docs --log debug", + "doc:build": "node build_scripts/prebuild-doc.js && gitbook build ./docs-src ./docs" }, - "pre-commit": "no-pre-commit", + "pre-commit": [ + "lint" + ], "repository": { "type": "git", "url": "https://github.com/alibaba/anyproxy" diff --git a/proxy.js b/proxy.js index 8f566d7..bf495d5 100644 --- a/proxy.js +++ b/proxy.js @@ -1,262 +1,386 @@ -try{ - global.util = require('./lib/util'); -}catch(e){} +'use strict'; -var http = require('http'), - https = require('https'), - fs = require('fs'), - async = require("async"), - url = require('url'), - program = require('commander'), - color = require('colorful'), - certMgr = require("./lib/certMgr"), - getPort = require("./lib/getPort"), - Recorder = require("./lib/recorder"), - logUtil = require("./lib/log"), - wsServer = require("./lib/wsServer"), - webInterface = require("./lib/webInterface"), - SystemProxyMgr = require('./lib/systemProxyMgr'), - inherits = require("util").inherits, - util = require("./lib/util"), - path = require("path"), - juicer = require('juicer'), - events = require("events"), - express = require("express"), - ip = require("ip"), - ThrottleGroup = require("stream-throttle").ThrottleGroup, - iconv = require('iconv-lite'), - Buffer = require('buffer').Buffer; +const http = require('http'), + https = require('https'), + async = require('async'), + color = require('colorful'), + certMgr = require('./lib/certMgr'), + Recorder = require('./lib/recorder'), + logUtil = require('./lib/log'), + util = require('./lib/util'), + events = require('events'), + co = require('co'), + WebInterface = require('./lib/webInterface'), + ThrottleGroup = require('stream-throttle').ThrottleGroup; -var T_TYPE_HTTP = 0, - T_TYPE_HTTPS = 1, - DEFAULT_PORT = 8001, - DEFAULT_WEB_PORT = 8002, // port for web interface - DEFAULT_WEBSOCKET_PORT = 8003, // internal web socket for web interface, not for end users - DEFAULT_CONFIG_PORT = 8088, - DEFAULT_HOST = "localhost", - DEFAULT_TYPE = T_TYPE_HTTP; +// const memwatch = require('memwatch-next'); -var default_rule = util.freshRequire('./rule_default'); -var requestHandler = util.freshRequire('./requestHandler'); +// setInterval(() => { +// console.log(process.memoryUsage()); +// const rss = Math.ceil(process.memoryUsage().rss / 1000 / 1000); +// console.log('Program is using ' + rss + ' mb of Heap.'); +// }, 1000); -//option -//option.type : 'http'(default) or 'https' -//option.port : 8001(default) -//option.hostname : localhost(default) -//option.rule : ruleModule -//option.webPort : 8002(default) -//option.socketPort : 8003(default) -//option.webConfigPort : 8088(default) -//option.dbFile : null(default) -//option.throttle : null(default) -//option.disableWebInterface -//option.silent : false(default) -//option.interceptHttps ,internal param for https -function proxyServer(option){ - option = option || {}; +// memwatch.on('stats', (info) => { +// console.log('gc !!'); +// console.log(process.memoryUsage()); +// const rss = Math.ceil(process.memoryUsage().rss / 1000 / 1000); +// console.log('GC !! Program is using ' + rss + ' mb of Heap.'); - var self = this, - proxyType = /https/i.test(option.type || DEFAULT_TYPE) ? T_TYPE_HTTPS : T_TYPE_HTTP , - proxyPort = option.port || DEFAULT_PORT, - proxyHost = option.hostname || DEFAULT_HOST, - proxyRules = option.rule || default_rule, - proxyWebPort = option.webPort || DEFAULT_WEB_PORT, //port for web interface - socketPort = option.socketPort || DEFAULT_WEBSOCKET_PORT, //port for websocket - proxyConfigPort = option.webConfigPort || DEFAULT_CONFIG_PORT, //port to ui config server - disableWebInterface = !!option.disableWebInterface, - ifSilent = !!option.silent; +// // var heapUsed = Math.ceil(process.memoryUsage().heapUsed / 1000); +// // console.log("Program is using " + heapUsed + " kb of Heap."); +// // console.log(info); +// }); - if(ifSilent){ - logUtil.setPrintStatus(false); +const T_TYPE_HTTP = 'http', + T_TYPE_HTTPS = 'https', + DEFAULT_TYPE = T_TYPE_HTTP; + +const PROXY_STATUS_INIT = 'INIT'; +const PROXY_STATUS_READY = 'READY'; +const PROXY_STATUS_CLOSED = 'CLOSED'; + +/** + * + * @class ProxyCore + * @extends {events.EventEmitter} + */ +class ProxyCore extends events.EventEmitter { + + /** + * Creates an instance of ProxyCore. + * + * @param {object} config - configs + * @param {number} config.port - port of the proxy server + * @param {object} [config.rule=null] - rule module to use + * @param {string} [config.type=http] - type of the proxy server, could be 'http' or 'https' + * @param {strign} [config.hostname=localhost] - host name of the proxy server, required when this is an https proxy + * @param {number} [config.throttle] - speed limit in kb/s + * @param {boolean} [config.forceProxyHttps=false] - if proxy all https requests + * @param {boolean} [config.silent=false] - if keep the console silent + * @param {boolean} [config.dangerouslyIgnoreUnauthorized=false] - if ignore unauthorized server response + * @param {object} [config.recorder] - recorder to use + * + * @memberOf ProxyCore + */ + constructor(config) { + super(); + config = config || {}; + + this.status = PROXY_STATUS_INIT; + this.proxyPort = config.port; + this.proxyType = /https/i.test(config.type || DEFAULT_TYPE) ? T_TYPE_HTTPS : T_TYPE_HTTP; + this.proxyHostName = config.hostname || 'localhost'; + this.recorder = config.recorder; + + if (parseInt(process.versions.node.split('.')[0], 10) < 4) { + throw new Error('node.js >= v4.x is required for anyproxy'); + } else if (config.forceProxyHttps && !certMgr.ifRootCAFileExists()) { + logUtil.printLog('You can run `anyproxy-ca` to generate one root CA and then re-run this command'); + throw new Error('root CA not found. Please run `anyproxy-ca` to generate one first.'); + } else if (this.proxyType === T_TYPE_HTTPS && !config.hostname) { + throw new Error('hostname is required in https proxy'); + } else if (!this.proxyPort) { + throw new Error('proxy port is required'); + } else if (!this.recorder) { + throw new Error('recorder is required'); + } else if (config.forceProxyHttps && config.rule && config.rule.beforeDealHttpsRequest) { + logUtil.printLog('both "-i(--intercept)" and rule.beforeDealHttpsRequest are specified, the "-i" option will be ignored.', logUtil.T_WARN); + config.forceProxyHttps = false; } - // copy the rule to keep the original proxyRules independent - proxyRules = Object.assign({}, proxyRules); + this.httpProxyServer = null; + this.requestHandler = null; - var currentRule = requestHandler.setRules(proxyRules); //TODO : optimize calling for set rule + // copy the rule to keep the original proxyRule independent + this.proxyRule = config.rule || {}; - if(!!option.interceptHttps){ - if (!certMgr.isRootCAFileExists()) { - util.showRootInstallTip(); - process.exit(0); - return; - } - - currentRule.setInterceptFlag(true); - - //print a tip when using https features in Node < v0.12 - var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); - if(nodeVersion < 0.12){ - logUtil.printLog(color.red("node >= v0.12 is required when trying to intercept HTTPS requests :("), logUtil.T_ERR); - } - - logUtil.printLog(color.blue("The WebSocket will not work properly in the https intercept mode :("), logUtil.T_TIP); + if (config.silent) { + logUtil.setPrintStatus(false); } - if(option.throttle){ - logUtil.printLog("throttle :" + option.throttle + "kb/s"); - const rate = parseInt(option.throttle); - if (rate < 1) { - logUtil.printLog(color.red('Invalid throttle rate value, should be positive integer\n'), logUtil.T_ERR); - process.exit(0); - } - global._throttle = new ThrottleGroup({rate: 1024 * parseFloat(option.throttle) }); // rate - byte/sec + if (config.throttle) { + logUtil.printLog('throttle :' + config.throttle + 'kb/s'); + const rate = parseInt(config.throttle, 10); + if (rate < 1) { + throw new Error('Invalid throttle rate value, should be positive integer'); + } + global._throttle = new ThrottleGroup({ rate: 1024 * rate }); // rate - byte/sec } - self.httpProxyServer = null; + // init recorder + this.recorder = config.recorder; + // init request handler + const RequestHandler = util.freshRequire('./requestHandler'); + this.requestHandler = new RequestHandler({ + forceProxyHttps: !!config.forceProxyHttps, + dangerouslyIgnoreUnauthorized: !!config.dangerouslyIgnoreUnauthorized + }, this.proxyRule, this.recorder); + } + + /** + * manage all created socket + * for each new socket, we put them to a map; + * if the socket is closed itself, we remove it from the map + * when the `close` method is called, we'll close the sockes before the server closed + * + * @param {Socket} the http socket that is creating + * @returns undefined + * @memberOf ProxyCore + */ + handleExistConnections(socket) { + const self = this; + self.socketIndex ++; + const key = `socketIndex_${self.socketIndex}`; + self.socketPool[key] = socket; + + // if the socket is closed already, removed it from pool + socket.on('close', () => { + delete self.socketPool[key]; + }); + } + /** + * start the proxy server + * + * @returns ProxyCore + * + * @memberOf ProxyCore + */ + start() { + const self = this; + self.socketIndex = 0; + self.socketPool = {}; + + if (self.status !== PROXY_STATUS_INIT) { + throw new Error('server status is not PROXY_STATUS_INIT, can not run start()'); + } async.series( - [ - //clear cache dir, prepare recorder - function(callback){ - util.clearCacheDir(function(){ - if(option.dbFile){ - global.recorder = new Recorder({filename: option.dbFile}); - }else{ - global.recorder = new Recorder(); - } - callback(); - }); - }, - - //creat proxy server - function(callback){ - if(proxyType == T_TYPE_HTTPS){ - certMgr.getCertificate(proxyHost,function(err,keyContent,crtContent){ - if(err){ - callback(err); - }else{ - self.httpProxyServer = https.createServer({ - key : keyContent, - cert: crtContent - },requestHandler.userRequestHandler); - callback(null); - } - }); - }else{ - self.httpProxyServer = http.createServer(requestHandler.userRequestHandler); - callback(null); - } - }, - - //handle CONNECT request for https over http - function(callback){ - self.httpProxyServer.on('connect',requestHandler.connectReqHandler); + [ + //creat proxy server + function (callback) { + if (self.proxyType === T_TYPE_HTTPS) { + certMgr.getCertificate(self.proxyHostName, (err, keyContent, crtContent) => { + if (err) { + callback(err); + } else { + self.httpProxyServer = https.createServer({ + key: keyContent, + cert: crtContent + }, self.requestHandler.userRequestHandler); callback(null); - }, + } + }); + } else { + self.httpProxyServer = http.createServer(self.requestHandler.userRequestHandler); + callback(null); + } + }, - //start proxy server - function(callback){ - self.httpProxyServer.listen(proxyPort); - callback(null); - }, + //handle CONNECT request for https over http + function (callback) { + self.httpProxyServer.on('connect', self.requestHandler.connectReqHandler); - //start web socket service - function(callback){ - self.ws = new wsServer({port : socketPort}); - callback(null); - }, + callback(null); + }, - //start web interface - function(callback){ - if(disableWebInterface){ - logUtil.printLog('web interface is disabled'); - }else{ - var config = { - port : proxyWebPort, - wsPort : socketPort, - userRule : proxyRules, - ip : ip.address() - }; + function (callback) { + // remember all sockets, so we can destory them when call the method 'close'; + self.httpProxyServer.on('connection', (socket) => { + self.handleExistConnections.call(self, socket); + }); + callback(null); + }, - self.webServerInstance = new webInterface(config); - } - callback(null); - }, + //start proxy server + function (callback) { + self.httpProxyServer.listen(self.proxyPort); + callback(null); + }, + ], - //set global proxy - function(callback) { - if (option.setAsGlobalProxy) { - console.log('setting global proxy for you...'); - if(!/^win/.test(process.platform) && !process.env.SUDO_UID){ - console.log('sudo password may be required.'); - } - var result = SystemProxyMgr.enableGlobalProxy(ip.address(), proxyPort, proxyType == T_TYPE_HTTP ? "Http" : "Https"); - if (result.status) { - callback(result.stdout); - } else { - if(/^win/.test(process.platform)){ - console.log('AnyProxy is now the default proxy for your system. It may take up to 1min to take effect.'); - } else{ - console.log('AnyProxy is now the default proxy for your system.'); - } - callback(null); - } - } else { - callback(null); - } - }, + //final callback + (err, result) => { + if (!err) { + const tipText = (self.proxyType === T_TYPE_HTTP ? 'Http' : 'Https') + ' proxy started on port ' + self.proxyPort; + logUtil.printLog(color.green(tipText)); - //server status manager - function(callback){ - process.on("exit",function(code){ - logUtil.printLog('AnyProxy is about to exit with code: ' + code, logUtil.T_ERR); + if (self.webServerInstance) { + const webTip = 'web interface started on port ' + self.webServerInstance.webPort; + logUtil.printLog(color.green(webTip)); + } - if (option.setAsGlobalProxy) { - console.log('resigning global proxy...'); - var result = SystemProxyMgr.disableGlobalProxy(proxyType == T_TYPE_HTTP ? "Http" : "Https"); + let ruleSummaryString = ''; + const ruleSummary = this.proxyRule.summary; + if (ruleSummary) { + co(function *() { + if (typeof ruleSummary === 'string') { + ruleSummaryString = ruleSummary; + } else { + ruleSummaryString = yield ruleSummary(); + } - if (result.status) { - console.log(color.red(result.stdout)); - } else{ - console.log('global proxy resigned.'); - } - } + logUtil.printLog(color.green(`Active rule is: ${ruleSummaryString}`)); + }); + } - process.exit(); - }); - - //exit cause ctrl+c - process.on("SIGINT", function() { - process.exit(); - }); - - process.on("uncaughtException",function(err){ - logUtil.printLog('Caught exception: ' + (err.stack || err), logUtil.T_ERR); - process.exit(); - }); - - callback(null); - } - ], - - //final callback - function(err,result){ - if(!err){ - var webTip,webUrl; - webUrl = "http://" + ip.address() + ":" + proxyWebPort +"/"; - webTip = "GUI interface started at : " + webUrl; - logUtil.printLog(color.green(webTip)); - - var tipText = (proxyType == T_TYPE_HTTP ? "Http" : "Https") + " proxy started at " + color.bold(ip.address() + ":" + proxyPort); - logUtil.printLog(color.green(tipText)); - }else{ - var tipText = "err when start proxy server :("; - logUtil.printLog(color.red(tipText), logUtil.T_ERR); - logUtil.printLog(err, logUtil.T_ERR); - } + self.status = PROXY_STATUS_READY; + self.emit('ready'); + } else { + const tipText = 'err when start proxy server :('; + logUtil.printLog(color.red(tipText), logUtil.T_ERR); + logUtil.printLog(err, logUtil.T_ERR); + self.emit('error', { + error: err + }); } + } ); - self.close = function(){ - self.httpProxyServer && self.httpProxyServer.close(); - self.ws && self.ws.closeAll(); - self.webServerInstance && self.webServerInstance.server && self.webServerInstance.server.close(); - logUtil.printLog("server closed :" + proxyHost + ":" + proxyPort); - } + return self; + } + + + /** + * close the proxy server + * + * @returns ProxyCore + * + * @memberOf ProxyCore + */ + close() { + // clear recorder cache + return new Promise((resolve) => { + if (this.httpProxyServer) { + // destroy conns & cltSockets when closing proxy server + for (const connItem of this.requestHandler.conns) { + const key = connItem[0]; + const conn = connItem[1]; + logUtil.printLog(`destorying https connection : ${key}`); + conn.end(); + } + + for (const cltSocketItem of this.requestHandler.cltSockets) { + const key = cltSocketItem[0]; + const cltSocket = cltSocketItem[1]; + logUtil.printLog(`endding https cltSocket : ${key}`); + cltSocket.end(); + } + + if (this.socketPool) { + for (const key in this.socketPool) { + this.socketPool[key].destroy(); + } + } + + this.httpProxyServer.close((error) => { + if (error) { + console.error(error); + logUtil.printLog(`proxy server close FAILED : ${error.message}`, logUtil.T_ERR); + } else { + this.httpProxyServer = null; + + this.status = PROXY_STATUS_CLOSED; + logUtil.printLog(`proxy server closed at ${this.proxyHostName}:${this.proxyPort}`); + } + resolve(error); + }); + } else { + resolve(); + } + }) + } } -module.exports.proxyServer = proxyServer; -module.exports.generateRootCA = certMgr.generateRootCA; -module.exports.isRootCAFileExists = certMgr.isRootCAFileExists; -module.exports.setRules = requestHandler.setRules; +/** + * start proxy server as well as recorder and webInterface + */ +class ProxyServer extends ProxyCore { + /** + * + * @param {object} config - config + * @param {object} [config.webInterface] - config of the web interface + * @param {boolean} [config.webInterface.enable=false] - if web interface is enabled + * @param {number} [config.webInterface.webPort=8002] - http port of the web interface + * @param {number} [config.webInterface.wsPort] - web socket port of the web interface + */ + constructor(config) { + // prepare a recorder + const recorder = new Recorder(); + const configForCore = Object.assign({ + recorder, + }, config); + + super(configForCore); + + this.proxyWebinterfaceConfig = config.webInterface; + this.recorder = recorder; + this.webServerInstance = null; + } + + start() { + // start web interface if neeeded + if (this.proxyWebinterfaceConfig && this.proxyWebinterfaceConfig.enable) { + this.webServerInstance = new WebInterface(this.proxyWebinterfaceConfig, this.recorder); + } + + new Promise((resolve) => { + // start web server + if (this.webServerInstance) { + resolve(this.webServerInstance.start()); + } else { + resolve(null); + } + }) + .then(() => { + // start proxy core + super.start() + }) + .catch((e) => { + this.emit('error', e); + }); + } + + close() { + return new Promise((resolve, reject) => { + super.close() + .then((error) => { + if (error) { + resolve(error); + } + }); + + if (this.recorder) { + logUtil.printLog('clearing cache file...'); + this.recorder.clear(); + } + const tmpWebServer = this.webServerInstance; + this.recorder = null; + this.webServerInstance = null; + if (tmpWebServer) { + logUtil.printLog('closing webserver...'); + tmpWebServer.close((error) => { + if (error) { + console.error(error); + logUtil.printLog(`proxy web server close FAILED: ${error.message}`, logUtil.T_ERR); + } else { + logUtil.printLog(`proxy web server closed at ${this.proxyHostName} : ${this.webPort}`); + } + + resolve(error); + }) + } else { + resolve(null); + } + }); + } +} + +module.exports.ProxyCore = ProxyCore; +module.exports.ProxyServer = ProxyServer; +module.exports.ProxyRecorder = Recorder; +module.exports.ProxyWebServer = WebInterface; +module.exports.utils = { + systemProxyMgr: require('./lib/systemProxyMgr'), + certMgr, +}; diff --git a/resource/502.pug b/resource/502.pug new file mode 100644 index 0000000..639407c --- /dev/null +++ b/resource/502.pug @@ -0,0 +1,81 @@ +doctype html +html(lang="en") + head + title AnyProxy Inner Error + style. + body { + color: #666; + line-height: 1.5; + font-size: 13px; + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,SimSun,sans-serif; + } + + body * { + box-sizing: border-box; + } + + .stackError { + border-radius: 5px; + padding: 20px; + border: 1px solid #fdc; + background-color: #ffeee6; + color: #666; + } + .stackError li { + list-style-type: none; + } + .infoItem { + position: relative; + overflow: hidden; + border: 1px solid #d5f1fd; + background-color: #eaf8fe; + border-radius: 4px; + margin-bottom: 5px; + padding-left: 70px; + } + .infoItem .label { + position: absolute; + top: 0; + left: 0; + bottom: 0; + display: flex; + justify-content: flex-start; + align-items: center; + width: 70px; + font-weight: 300; + background-color: #76abc1; + color: #fff; + padding: 5px; + } + .infoItem .value { + overflow:hidden; + padding: 5px; + } + + .tipItem .label { + background-color: #ecf6fd; + } + .tip { + color: #808080; + } + body + h1 # AnyProxy Inner Error + h3 Oops! Error happend when AnyProxy handle the request. + p.tip This is an error occurred inside AnyProxy, not from your target website. + .infoItem + .label + | Error: + .value #{error} + .infoItem + .label + | URL: + .value #{url} + if tipMessage + .infoItem + .label + | TIP: + .value!= tipMessage + p + ul.stackError + each item in errorStack + li= item \ No newline at end of file diff --git a/resource/cert_error.pug b/resource/cert_error.pug new file mode 100644 index 0000000..65b8c1f --- /dev/null +++ b/resource/cert_error.pug @@ -0,0 +1,46 @@ +doctype html +html(lang="en") + head + title Security Vulnerable + style. + body { + color: #666; + line-height: 1.5; + font-size: 13px; + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,SimSun,sans-serif; + } + + body * { + box-sizing: border-box; + } + + .container { + max-width: 1200px; + padding: 20px; + padding-top: 150px; + margin: 0 auto; + } + + .title { + font-size: 20px; + margin-bottom: 20px; + } + + .explain { + font-size: 14px; + font-weight: 200; + color: #666; + } + + .explainCode { + color: #999; + margin-bottom: 10px; + } + body + .container + div.title + | #{title} + div.explainCode + | #{code} + div.explain + div!= explain diff --git a/rule_sample/README.md b/rule_sample/README.md deleted file mode 100644 index 7380b37..0000000 --- a/rule_sample/README.md +++ /dev/null @@ -1,32 +0,0 @@ -The following are sample rules. - -* rule__blank.js - * blank rule file with some comments. You may read this before writing your own rule file. - * 空白的规则文件模板,和一些注释 -* rule_adjust_response_time.js - * delay all the response for 1500ms - * 把所有的响应延迟1500毫秒 -* rule_allow_CORS.js - * add CORS headers to allow cross-domain ajax request - * 为ajax请求增加跨域头 -* rule_intercept_some_https_requests.js - * intercept https requests toward github.com and append some data - * 截获github.com的https请求,再在最后加点文字 -* rule_remove_cache_header.js - * remove all cache-related headers from server - * 去除响应头里缓存相关的头 -* rule_replace_request_option.js - * replace request parameters before sending to the server - * 在请求发送到服务端前对参数做一些调整 -* rule_replace_response_data.js - * modify response data - * 修改响应数据 -* rule_replace_response_status_code.js - * replace server's status code - * 改变服务端响应的http状态码 -* rule_reverse_proxy.js - * assign a specific ip address for request - * 为请求绑定目标ip -* rule_use_local_data.js - * map some requests to local file - * 把图片响应映射到本地 \ No newline at end of file diff --git a/rule_sample/rule__blank.js b/rule_sample/rule__blank.js deleted file mode 100644 index 30d3c54..0000000 --- a/rule_sample/rule__blank.js +++ /dev/null @@ -1,118 +0,0 @@ -/* -read the following wiki before using rule file -https://github.com/alibaba/anyproxy/wiki/What-is-rule-file-and-how-to-write-one -*/ -module.exports = { - /* - These functions will overwrite the default ones, write your own when necessary. - Comments in Chinese are nothing but a translation of key points. Be relax if you dont understand. - 致中文用户:中文注释都是只摘要,必要时请参阅英文文档。欢迎提出修改建议。 - */ - summary:function(){ - return "this is a blank rule for AnyProxy"; - }, - - - - - //======================= - //when getting a request from user - //收到用户请求之后 - //======================= - - //是否截获https请求 - //should intercept https request, or it will be forwarded to real server - shouldInterceptHttpsReq :function(req){ - return false; - }, - - //是否在本地直接发送响应(不再向服务器发出请求) - //whether to intercept this request by local logic - //if the return value is true, anyproxy will call dealLocalResponse to get response data and will not send request to remote server anymore - //req is the user's request sent to the proxy server - shouldUseLocalResponse : function(req,reqBody){ - return false; - }, - - //如果shouldUseLocalResponse返回true,会调用这个函数来获取本地响应内容 - //you may deal the response locally instead of sending it to server - //this function be called when shouldUseLocalResponse returns true - //callback(statusCode,resHeader,responseData) - //e.g. callback(200,{"content-type":"text/html"},"hello world") - dealLocalResponse : function(req,reqBody,callback){ - callback(statusCode,resHeader,responseData) - }, - - - - //======================= - //when ready to send a request to server - //向服务端发出请求之前 - //======================= - - //替换向服务器发出的请求协议(http和https的替换) - //replace the request protocol when sending to the real server - //protocol : "http" or "https" - replaceRequestProtocol:function(req,protocol){ - var newProtocol = protocol; - return newProtocol; - }, - - //替换向服务器发出的请求参数(option) - //option is the configuration of the http request sent to remote server. You may refers to http://nodejs.org/api/http.html#http_http_request_options_callback - //you may return a customized option to replace the original one - //you should not overwrite content-length header in options, since anyproxy will handle it for you - replaceRequestOption : function(req,option){ - var newOption = option; - return newOption; - }, - - //替换请求的body - //replace the request body - replaceRequestData: function(req,data){ - return data; - }, - - - - //======================= - //when ready to send the response to user after receiving response from server - //向用户返回服务端的响应之前 - //======================= - - //替换服务器响应的http状态码 - //replace the statusCode before it's sent to the user - replaceResponseStatusCode: function(req,res,statusCode){ - var newStatusCode = statusCode; - return newStatusCode; - }, - - //替换服务器响应的http头 - //replace the httpHeader before it's sent to the user - //Here header == res.headers - replaceResponseHeader: function(req,res,header){ - var newHeader = header; - return newHeader; - }, - - //替换服务器响应的数据 - //replace the response from the server before it's sent to the user - //you may return either a Buffer or a string - //serverResData is a Buffer. for those non-unicode reponse , serverResData.toString() should not be your first choice. - replaceServerResDataAsync: function(req,res,serverResData,callback){ - callback(serverResData); - }, - - //Deprecated - // replaceServerResData: function(req,res,serverResData){ - // return serverResData; - // }, - - //在请求返回给用户前的延迟时间 - //add a pause before sending response to user - pauseBeforeSendingResponse : function(req,res){ - var timeInMS = 1; //delay all requests for 1ms - return timeInMS; - } - -}; \ No newline at end of file diff --git a/rule_sample/rule_adjust_response_time.js b/rule_sample/rule_adjust_response_time.js deleted file mode 100644 index 32ec584..0000000 --- a/rule_sample/rule_adjust_response_time.js +++ /dev/null @@ -1,10 +0,0 @@ -//rule scheme : - -module.exports = { - - pauseBeforeSendingResponse : function(req,res){ - //delay all the response for 1500ms - return 1500; - } - -}; \ No newline at end of file diff --git a/rule_sample/rule_allow_CORS.js b/rule_sample/rule_allow_CORS.js deleted file mode 100644 index 182152c..0000000 --- a/rule_sample/rule_allow_CORS.js +++ /dev/null @@ -1,40 +0,0 @@ -//rule scheme : -// Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS - -module.exports = { - shouldUseLocalResponse : function(req,reqBody){ - //intercept all options request - if(req.method == "OPTIONS"){ - return true; - }else{ - return false; - } - }, - - dealLocalResponse : function(req,reqBody,callback){ - if(req.method == "OPTIONS"){ - callback(200,mergeCORSHeader(req.headers),""); - } - }, - - replaceResponseHeader: function(req,res,header){ - return mergeCORSHeader(req.headers, header); - } - -}; - -function mergeCORSHeader(reqHeader,originHeader){ - var targetObj = originHeader || {}; - - delete targetObj["Access-Control-Allow-Credentials"]; - delete targetObj["Access-Control-Allow-Origin"]; - delete targetObj["Access-Control-Allow-Methods"]; - delete targetObj["Access-Control-Allow-Headers"]; - - targetObj["access-control-allow-credentials"] = "true"; - targetObj["access-control-allow-origin"] = reqHeader['origin'] || "-___-||"; - targetObj["access-control-allow-methods"] = "GET, POST, PUT"; - targetObj["access-control-allow-headers"] = reqHeader['access-control-request-headers'] || "-___-||"; - - return targetObj; -} diff --git a/rule_sample/rule_intercept_some_https_requests.js b/rule_sample/rule_intercept_some_https_requests.js deleted file mode 100644 index 825e57c..0000000 --- a/rule_sample/rule_intercept_some_https_requests.js +++ /dev/null @@ -1,25 +0,0 @@ -//rule scheme : - -module.exports = { - - - replaceServerResDataAsync: function(req,res,serverResData,callback){ - //add "hello github" to all github pages - if(req.headers.host == "github.com"){ - serverResData += "hello github"; - } - callback(serverResData); - }, - - shouldInterceptHttpsReq :function(req){ - //intercept https://github.com/ - //otherwise, all the https traffic will not go through this proxy - - // return true; - if(req.headers.host == "github.com"){ - return true; - }else{ - return false; - } - } -}; \ No newline at end of file diff --git a/rule_sample/rule_remove_cache_header.js b/rule_sample/rule_remove_cache_header.js deleted file mode 100644 index 4018ddb..0000000 --- a/rule_sample/rule_remove_cache_header.js +++ /dev/null @@ -1,20 +0,0 @@ -//rule scheme : - -module.exports = { - replaceRequestOption : function(req,option){ - var newOption = option; - delete newOption.headers['if-none-match']; - delete newOption.headers['if-modified-since']; - - return newOption; - }, - - replaceResponseHeader: function(req,res,header){ - header = header || {}; - header["Cache-Control"] = "no-cache, no-store, must-revalidate"; - header["Pragma"] = "no-cache"; - header["Expires"] = 0; - - return header; - } -}; \ No newline at end of file diff --git a/rule_sample/rule_replace_request_option.js b/rule_sample/rule_replace_request_option.js deleted file mode 100644 index 6f3930c..0000000 --- a/rule_sample/rule_replace_request_option.js +++ /dev/null @@ -1,23 +0,0 @@ -//rule scheme : - -module.exports = { - - replaceRequestOption : function(req,option){ - //replace request towards http://www.taobao.com - // to http://www.taobao.com/about/ - - /* - option scheme: - { - hostname : "www.taobao.com" - port : 80 - path : "/" - method : "GET" - headers : {cookie:""} - } - */ - if(option.hostname == "www.taobao.com" && option.path == "/"){ - option.path = "/about/"; - } - } -}; \ No newline at end of file diff --git a/rule_sample/rule_replace_response_data.js b/rule_sample/rule_replace_response_data.js deleted file mode 100644 index a8ffbe2..0000000 --- a/rule_sample/rule_replace_response_data.js +++ /dev/null @@ -1,19 +0,0 @@ -//rule scheme : - -module.exports = { - - replaceServerResDataAsync: function(req,res,serverResData,callback){ - //append "hello world" to all web pages - - //for those non-unicode response , serverResData.toString() should not be your first choice. - //this issue may help you understanding how to modify an non-unicode response: https://github.com/alibaba/anyproxy/issues/20 - if(/html/i.test(res.headers['content-type'])){ - var newDataStr = serverResData.toString(); - newDataStr += "hello world!"; - callback(newDataStr); - }else{ - callback(serverResData); - } - - } -}; diff --git a/rule_sample/rule_replace_response_status_code.js b/rule_sample/rule_replace_response_status_code.js deleted file mode 100644 index 24b7777..0000000 --- a/rule_sample/rule_replace_response_status_code.js +++ /dev/null @@ -1,24 +0,0 @@ -//rule scheme : - -module.exports = { - - replaceResponseStatusCode: function(req,res,statusCode){ - //redirect requests toward http://www.taobao.com/* - // to http://www.etao.com - //using 302 - - if(req.headers.host == "www.taobao.com"){ - statusCode = 302; - } - - return statusCode; - }, - - replaceResponseHeader: function(req,res,header){ - if(req.headers.host == "www.taobao.com"){ - header.location = "http://www.etao.com"; - } - - return header; - } -}; \ No newline at end of file diff --git a/rule_sample/rule_reverse_proxy.js b/rule_sample/rule_reverse_proxy.js deleted file mode 100644 index 4aa456d..0000000 --- a/rule_sample/rule_reverse_proxy.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -read the following wiki before using rule file -https://github.com/alibaba/anyproxy/wiki/What-is-rule-file-and-how-to-write-one -*/ -module.exports = { - - summary:function(){ - return "reverse proxy - assign an IP adress for some request"; - }, - - replaceRequestOption : function(req,option){ - var newOption = option; - - //options : http://nodejs.org/api/http.html#http_http_request_options_callback - if(newOption.headers.host == "www.taobao.com"){ - newOption.hostname = "127.0.0.1"; - newOption.port = "80"; - } - - return newOption; - } - -}; \ No newline at end of file diff --git a/rule_sample/rule_use_local_data.js b/rule_sample/rule_use_local_data.js deleted file mode 100644 index f560f6d..0000000 --- a/rule_sample/rule_use_local_data.js +++ /dev/null @@ -1,28 +0,0 @@ -//replace all the images with local one -var fs = require("fs"); - -var LOCAL_IMAGE = "/Users/path/to/image.png"; - -module.exports = { - - summary:function(){ - return "replace all the images with local one"; - }, - - //mark if use local response - shouldUseLocalResponse : function(req,reqBody){ - if(/\.(png|gif|jpg|jpeg)$/.test(req.url)){ - req.replaceLocalFile = true; - return true; - }else{ - return false; - } - }, - - dealLocalResponse : function(req,reqBody,callback){ - if(req.replaceLocalFile){ - callback(200, {"content-type":"image/png"}, fs.readFileSync(LOCAL_IMAGE) ); - } - } -}; - diff --git a/rule_sample/sample_modify_request_data.js b/rule_sample/sample_modify_request_data.js new file mode 100644 index 0000000..dc548f9 --- /dev/null +++ b/rule_sample/sample_modify_request_data.js @@ -0,0 +1,18 @@ +/* + sample: + modify the post data towards http://httpbin.org/post + test: + curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001 + expected response: + { "data": "i-am-anyproxy-modified-post-data" } +*/ +module.exports = { + summary: 'Rule to modify request data', + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) { + return { + requestData: 'i-am-anyproxy-modified-post-data' + }; + } + }, +}; diff --git a/rule_sample/sample_modify_request_header.js b/rule_sample/sample_modify_request_header.js new file mode 100644 index 0000000..80b91df --- /dev/null +++ b/rule_sample/sample_modify_request_header.js @@ -0,0 +1,17 @@ +/* + sample: + modify the user-agent in requests toward httpbin.org + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newRequestOptions = requestDetail.requestOptions; + newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0'; + return { + requestOptions: newRequestOptions + }; + } + }, +}; diff --git a/rule_sample/sample_modify_request_path.js b/rule_sample/sample_modify_request_path.js new file mode 100644 index 0000000..1df4aca --- /dev/null +++ b/rule_sample/sample_modify_request_path.js @@ -0,0 +1,24 @@ +/* + sample: + redirect all https://httpbin.org/user-agent requests to http://localhost:8008/index.html + test: + curl https://httpbin.org/user-agent --proxy http://127.0.0.1:8001 + expected response: + 'hello world' from 127.0.0.1:8001/index.html +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('https://httpbin.org/user-agent') === 0) { + const newRequestOptions = requestDetail.requestOptions; + requestDetail.protocol = 'http'; + newRequestOptions.hostname = '127.0.0.1' + newRequestOptions.port = '8008'; + newRequestOptions.path = '/index.html'; + newRequestOptions.method = 'GET'; + return requestDetail; + } + }, + *beforeDealHttpsRequest(requestDetail) { + return true; + } +}; diff --git a/rule_sample/sample_modify_request_protocol.js b/rule_sample/sample_modify_request_protocol.js new file mode 100644 index 0000000..4d15f75 --- /dev/null +++ b/rule_sample/sample_modify_request_protocol.js @@ -0,0 +1,20 @@ +/* + sample: + redirect all http requests of httpbin.org to https + test: + curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001 + expected response: + { "X-Forwarded-Protocol": "https" } +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newOption = requestDetail.requestOptions; + newOption.port = 443; + return { + protocol: 'https', + requestOptions: newOption + }; + } + } +}; diff --git a/rule_sample/sample_modify_response_data.js b/rule_sample/sample_modify_response_data.js new file mode 100644 index 0000000..ee650be --- /dev/null +++ b/rule_sample/sample_modify_response_data.js @@ -0,0 +1,22 @@ +/* + sample: + modify response data of http://httpbin.org/user-agent + test: + curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! -- +*/ + +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '-- AnyProxy Hacked! --'; + return new Promise((resolve, reject) => { + setTimeout(() => { // delay the response for 5s + resolve({ response: newResponse }); + }, 5000); + }); + } + }, +}; diff --git a/rule_sample/sample_modify_response_header.js b/rule_sample/sample_modify_response_header.js new file mode 100644 index 0000000..1b92972 --- /dev/null +++ b/rule_sample/sample_modify_response_header.js @@ -0,0 +1,19 @@ +/* + sample: + modify response header of http://httpbin.org/user-agent + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + X-Proxy-By: AnyProxy +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) { + const newResponse = responseDetail.response; + newResponse.header['X-Proxy-By'] = 'AnyProxy'; + return { + response: newResponse + }; + } + } +}; diff --git a/rule_sample/sample_modify_response_statuscode.js b/rule_sample/sample_modify_response_statuscode.js new file mode 100644 index 0000000..34b25f1 --- /dev/null +++ b/rule_sample/sample_modify_response_statuscode.js @@ -0,0 +1,19 @@ +/* + sample: + modify all status code of http://httpbin.org/ to 404 + test: + curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001 + expected response: + HTTP/1.1 404 Not Found +*/ +module.exports = { + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 404; + return { + response: newResponse + }; + } + } +}; diff --git a/rule_sample/sample_use_local_response.js b/rule_sample/sample_use_local_response.js new file mode 100644 index 0000000..9a1d798 --- /dev/null +++ b/rule_sample/sample_use_local_response.js @@ -0,0 +1,20 @@ +/* + sample: + intercept all requests toward httpbin.org, use a local response + test: + curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001 +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + const localResponse = { + statusCode: 200, + header: { 'Content-Type': 'application/json' }, + body: '{"hello": "this is local response"}' + }; + if (requestDetail.url.indexOf('http://httpbin.org') === 0) { + return { + response: localResponse + }; + } + }, +}; diff --git a/test.js b/test.js new file mode 100644 index 0000000..abd6d8c --- /dev/null +++ b/test.js @@ -0,0 +1,16 @@ +const Jasmine = require('jasmine'); + +const jasmine = new Jasmine(); +const util = require('./lib/util'); +const path = require('path'); + +const testTmpPath = path.join(__dirname, './test/temp'); +const configFilePath = path.join(__dirname, './test/jasmine.json'); +// rm - rf./test / temp / +util.deleteFolderContentsRecursive(testTmpPath); + +jasmine.loadConfigFile(configFilePath); +jasmine.configureDefaultReporter({ + showColors: false +}); +jasmine.execute(); diff --git a/test/ab_local.sh b/test/ab_local.sh deleted file mode 100755 index ce232d8..0000000 --- a/test/ab_local.sh +++ /dev/null @@ -1 +0,0 @@ -ab -X 127.0.0.1:8001 -n $1 -c $2 -H "HOST:127.0.0.1" http://127.0.0.1:8080/ \ No newline at end of file diff --git a/test/ab_taobao.sh b/test/ab_taobao.sh deleted file mode 100755 index 4bab722..0000000 --- a/test/ab_taobao.sh +++ /dev/null @@ -1 +0,0 @@ -ab -X 127.0.0.1:8001 -n $1 -c $2 -H "HOST:www.taobao.com" http://www.taobao.com/ \ No newline at end of file diff --git a/test/data/headers.js b/test/data/headers.js index 2e47a4e..8062bf0 100644 --- a/test/data/headers.js +++ b/test/data/headers.js @@ -6,23 +6,24 @@ // Get 和 Post共有的header信息 /*eslint max-len: ["off"]*/ const CommonRequestHeader = { - Accept: 'application/json;charset=utf-8,text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', - 'Accept-Charset': 'utf-8', - 'Accept-Encoding': 'gzip, deflate', - 'Accept-Language': 'zh-CN', - 'Accept-Datetime': 'Thu, 31 May 2007 20:35:00 GMT', - 'Authorization': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', - 'Cookie': 'testCookie1=cookie1; testCookie2=cookie2', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Date': 'Tue, 15 Nov 1994 08:12:31 GMT', - 'Origin': 'http://localhost', - 'Pragma': 'no-cache', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36' + Accept: 'application/json;charset=utf-8,text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Charset': 'utf-8', + 'Accept-Encoding': 'gzip, deflate', + 'Accept-Language': 'zh-CN', + 'Accept-Datetime': 'Thu, 31 May 2007 20:35:00 GMT', + Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + Cookie: 'testCookie1=cookie1; testCookie2=cookie2', + 'Content-Type': 'application/x-www-form-urlencoded', + Date: 'Tue, 15 Nov 1994 08:12:31 GMT', + Origin: 'http://localhost', + Pragma: 'no-cache', + some_thing: 'only_to_test_letter_case', + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36' }; module.exports = { - CommonRequestHeader + CommonRequestHeader }; diff --git a/test/data/test.js b/test/data/test.js index 9883731..8021481 100644 --- a/test/data/test.js +++ b/test/data/test.js @@ -1,3 +1,4 @@ -function test () { - console.info('This is nothing but a js file, to test the js download'); -} \ No newline at end of file +function test() { + console.info('This is nothing but a js file, to test the js download'); +} +test(); diff --git a/jasmine.json b/test/jasmine.json similarity index 70% rename from jasmine.json rename to test/jasmine.json index 3216038..689230e 100644 --- a/jasmine.json +++ b/test/jasmine.json @@ -1,12 +1,13 @@ { "spec_dir": "test", "spec_files": [ - "**/*[sS]pec.js" + "spec_lib/*.js", + "spec_rule/*.js" ], "helpers": [ "../node_modules/babel-register/lib/node.js", "../node_modules/babel-polyfill/dist/polyfill.js" ], - "stopSpecOnExpectationFailure": false, + "stopSpecOnExpectationFailure": true, "random": false } diff --git a/test/large_post.js b/test/large_post.js index b92e968..45c5865 100644 --- a/test/large_post.js +++ b/test/large_post.js @@ -1,37 +1,35 @@ -var proxy = require("../proxy.js"), - proxyTester = require("proxy-eval"), - WebSocket = require("ws"), - Buffer = require("buffer").Buffer, - express = require("express"); +const proxyTester = require('proxy-eval'), + Buffer = require('buffer').Buffer, + express = require('express'); -var app = express() +const app = express(); -app.post('/', function (req, res) { - var bigBody = new Buffer(1024 * 1024 * 10); - res.send( bigBody ); //10 mb +app.post('/', (req, res) => { + const bigBody = new Buffer(1024 * 1024 * 10); + res.send(bigBody); //10 mb }); app.listen(3000); -function test(){ - //test the basic availibility of proxy server - setTimeout(function(){ - var testParam = { - proxy : 'http://127.0.0.1:8001/', - reqTimeout : 4500, - httpGetUrl : "", - httpPostUrl : "http://127.0.0.1:3000/", - httpPostBody : "123", - httpsGetUrl : "", - httpsPostUrl : "", - httpsPostBody : "" - }; - proxyTester.test(testParam ,function(results){ - process.exit(); - }); - },1000); -}; +function test() { + //test the basic availibility of proxy server + setTimeout(() => { + const testParam = { + proxy: 'http://127.0.0.1:8001/', + reqTimeout: 4500, + httpGetUrl: '', + httpPostUrl: 'http://127.0.0.1:3000/', + httpPostBody: '123', + httpsGetUrl: '', + httpsPostUrl: '', + httpsPostBody: '' + }; + proxyTester.test(testParam, (results) => { + process.exit(); + }); + }, 1000); +} -setTimeout(function(){ - test(); -},3000); +setTimeout(() => { + test(); +}, 3000); diff --git a/test/no_rule_spec.js b/test/no_rule_spec.js deleted file mode 100644 index f474cf1..0000000 --- a/test/no_rule_spec.js +++ /dev/null @@ -1,459 +0,0 @@ -const http = require('http'); -const querystring = require('querystring'); -const path = require('path'); -const fs = require('fs'); -const Buffer = require('buffer').Buffer; -const Server = require('./server/server.js'); -const - { - proxyGet, - proxyPost, - directGet, - directPost, - directUpload, - proxyUpload, - generateUrl, - proxyPut, - directPut, - proxyDelete, - directDelete, - directHead, - proxyHead, - directOptions, - proxyOptions, - proxyPutUpload, - directPutUpload - } = require('./util/HttpUtil.js'); -const { CommonRequestHeader } = require('./data/headers.js'); -const { isCommonResHeaderEqual, isCommonReqEqual, printLog } = require('./util/CommonUtil.js'); -const color = require('colorful'); -const streamEqual = require('stream-equal'); -const WebSocket = require('ws'); - -const ProxyServerUtil = require('./util/ProxyServerUtil.js'); - -const wsHost = 'ws://localhost:3000/test/socket'; - -testRequest('http'); -testRequest('https'); - -// Test suites for http and https request -function testRequest(protocol = 'http') { - - function constructUrl(urlPath) { - return generateUrl(protocol, urlPath); - } - - describe('Test request without proxy rules in protocol ' + protocol, () => { - let proxyServer ; - let serverInstance; - - beforeAll((done) => { - jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; - printLog('Start server for no_rule_spec'); - - serverInstance = new Server(); - proxyServer = ProxyServerUtil.defaultProxyServer(); - setTimeout(function() { - done(); - }, 2000); - }); - - afterAll(() => { - serverInstance && serverInstance.close(); - proxyServer && proxyServer.close(); - printLog('Closed server for no_rule_spec'); - }); - - - it('Get should work as direct without proxy rules', (done) => { - const url = constructUrl('/test'); - const getParam = { - param: 'nothing' - }; - - proxyGet(url, getParam, CommonRequestHeader).then((proxyRes) => { - directGet(url, getParam, CommonRequestHeader).then(directRes => { - - expect(proxyRes.statusCode).toEqual(200); - - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - expect(proxyRes.statusCode).toEqual(directRes.statusCode); - expect(directRes.body).toEqual(proxyRes.body); - - done(); - }, error => { - console.error('error happend in direct get:', error); - done.fail('error happend in direct get'); - }); - - }, error => { - console.log('error happened in proxy get:', error); - done.fail('error happend in proxy get'); - }); - }); - - it('Post should work as direct without proxy rules', (done) => { - const url = constructUrl('/test/getuser'); - const param = { - param: 'postnothing' - }; - - proxyPost(url, param, CommonRequestHeader).then(proxyRes => { - directPost(url, param, CommonRequestHeader).then(directRes => { - - expect(proxyRes.statusCode).toEqual(200); - - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(proxyRes.statusCode).toEqual(directRes.statusCode); - expect(directRes.body).toEqual(proxyRes.body); - - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - - done(); - }, error => { - console.error('error in direct post:', error); - done.fail('error happend in direct post'); - }); - - }, error => { - console.log('error happened in proxy post,', error); - done.fail('error happend in proxy post'); - }); - }); - - it('PUT should work as direct without proxy rules', done => { - const url = constructUrl('/test/put'); - const param = { - param: 'putsomething' - }; - proxyPut(url, param, CommonRequestHeader).then(proxyRes => { - directPut(url, param, CommonRequestHeader).then(directRes => { - expect(directRes.statusCode).toEqual(200); - - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(directRes.statusCode).toEqual(proxyRes.statusCode); - expect(directRes.body).toEqual(proxyRes.body); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - - done(); - }, error => { - console.error('error happened in direct put', error); - done.fail('error happened in direct put'); - }); - }, error => { - console.error('error happened in proxy put', error); - done.fail('error happened in proxy put'); - }); - - }); - - it('DELETE rquest should work as direct without proxy rules', (done) => { - const url = constructUrl('/test/delete/123456'); - - proxyDelete(url, {}, CommonRequestHeader).then(proxyRes => { - directDelete(url, {}, CommonRequestHeader).then(directRes => { - expect(directRes.statusCode).toEqual(200); - - expect(directRes.statusCode).toEqual(proxyRes.statusCode); - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(directRes.body).toEqual(proxyRes.body); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - - done(); - }, error => { - console.error('error happened in direct delete :', error); - done.fail('error happened in direct delete'); - }); - }, error => { - console.error('error happened in proxy delete :', error); - done.fail('error happened in proxy delete'); - }); - }); - - it('HEAD request should work as direct without proxy rules', (done) => { - const url = constructUrl('/test/head'); - - proxyHead(url, CommonRequestHeader) - .then(proxyRes => { - directHead(url, CommonRequestHeader) - .then(directRes => { - expect(directRes.statusCode).toEqual(200); - expect(directRes.body).toEqual(''); - - expect(directRes.statusCode).toEqual(proxyRes.statusCode); - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(directRes.body).toEqual(proxyRes.body); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - - done(); - }, error => { - console.error('error happened in direct head request:', error); - done.fail('error happened in direct head request'); - }); - }, error => { - console.error('error happened in proxy head request:', error); - done.fail('error happened in proxy head request'); - }); - - }); - - it('OPTIONS request should work as direct without proxy rules', (done) => { - const url = constructUrl('/test/options'); - - proxyOptions(url, CommonRequestHeader) - .then(proxyRes => { - directOptions(url, CommonRequestHeader) - .then(directRes => { - expect(directRes.statusCode).toEqual(200); - expect(directRes.body).toEqual('could_be_empty'); - - expect(directRes.statusCode).toEqual(proxyRes.statusCode); - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(directRes.body).toEqual(proxyRes.body); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - - done(); - }, error => { - console.error('error happened in direct options request:', error); - done.fail('error happened in direct options request'); - }); - }, error => { - console.error('error happened in proxy options request:', error); - done.fail('error happened in proxy options request'); - }); - - }); - - describe('Response code should be honored as direct without proxy rules', () => { - [301, 302, 303].forEach(code => { - testRedirect(code); - }); - - function testRedirect (redirectCode) { - it(`${redirectCode} response should work as direct without proxy rules`, (done) => { - const url = constructUrl(`/test/response/${redirectCode}`); - - proxyGet(url) - .then(proxyRes => { - directGet(url) - .then(directRes => { - const redirects = directRes.request._redirect.redirects || []; - const proxyRedirects = proxyRes.request._redirect.redirects || []; - expect(redirects.length).toEqual(1); - expect(proxyRedirects.length).toEqual(1); - - expect(redirects[0].statusCode).toEqual(redirectCode); - expect(redirects[0].redirectUri).toEqual(proxyRedirects[0].redirectUri); - expect(redirects[0].statusCode).toEqual(proxyRedirects[0].statusCode); - if (protocol === 'https') { - expect(redirects[0].redirectUri).toEqual('https://localhost:3001/test'); - } else { - expect(redirects[0].redirectUri).toEqual('http://localhost:3000/test'); - } - done(); - }, error => { - console.log(`error happened in direct ${redirectCode}:`, error); - done.fail(`error happened in direct ${redirectCode}`); - }); - - }, error => { - console.log(`error happened in proxy ${redirectCode}:`, error); - done.fail(`error happened in proxy ${redirectCode}`); - }); - - }); - } - }); - - - - describe('Test file download ', () => { - const testArray = [ - { - url: constructUrl('/test/download/png'), - type: 'png', - contentType: 'image/png' - }, - { - url: constructUrl('/test/download/webp'), - type: 'WEBP', - contentType: 'image/webp' - }, - { - url: constructUrl('/test/download/json'), - type: 'JSON', - contentType: 'application/json; charset=utf-8' - }, - { - url: constructUrl('/test/download/css'), - type: 'CSS', - contentType: 'text/css; charset=utf-8' - }, - { - url: constructUrl('/test/download/ttf'), - type: 'TTF', - contentType: 'application/x-font-ttf' - }, - { - url: constructUrl('/test/download/eot'), - type: 'EOT', - contentType: 'application/vnd.ms-fontobject' - }, - { - url: constructUrl('/test/download/svg'), - type: 'SVG', - contentType: 'image/svg+xml' - }, - { - url: constructUrl('/test/download/woff'), - type: 'WOFF', - contentType: 'application/font-woff' - }, - { - url: constructUrl('/test/download/woff2'), - type: 'WOFF2', - contentType: 'application/font-woff2' - } - ]; - - testArray.forEach(item => { - testFileDownload(item.url, item.type, item.contentType); - }); - - // 封装测试文件下载的测试工具类 - function testFileDownload (url, filetype, contentType) { - const describe = `${filetype} file download without rules should be work as direct download`; - const param = {}; - - it(describe, (done) => { - - proxyGet(url, param).then(proxyRes => { - directGet(url, param).then(directRes => { - expect(proxyRes.statusCode).toEqual(200); - - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(proxyRes.statusCode).toEqual(directRes.statusCode); - expect(proxyRes.body).toEqual(directRes.body); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - - done(); - }, error => { - console.error('error in direct get :', filetype, error); - done.fail(`error happend in direct get ${filetype}`); - }); - }, error => { - console.error('error in proxy get :', filetype, error); - done.fail(`error happend in proxy get ${filetype}`); - }); - }); - } - - }); - - describe('Test file upload', () => { - const formParams = { - param1: 'param_1', - param2: 'param2' - }; - it('POST upload should be working', (done) => { - const url = constructUrl('/test/upload/png'); - const filePath = path.resolve('./test/data/test.png'); - - proxyUpload(url, filePath, formParams) - .then(proxyRes => { - directUpload(url, filePath, formParams) - .then((directRes) => { - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - expect(isCommonReqEqual(url, serverInstance)).toBe(true); - assertReponse(proxyRes, directRes, filePath, done); - }, error => { - console.error('error in direct upload:', error); - done.fail('error in direct upload'); - }); - }, error => { - console.error('error in proxy upload:', error); - done.fail('error in proxy upload:'); - }); - - }); - - it('PUT upload should be working', (done) => { - const url = constructUrl('/test/upload/putpng'); - const filePath = path.resolve('./test/data/test.png'); - proxyPutUpload(url, filePath, formParams) - .then(proxyRes => { - directPutUpload(url, filePath, formParams) - .then((directRes) => { - expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); - - assertReponse(proxyRes, directRes, filePath, done); - }, error => { - console.error('error in direct upload:', error); - done.fail('error in direct upload'); - }); - }, error => { - console.error('error in proxy upload:', error); - done.fail('error in proxy upload:'); - }); - }); - - function assertReponse (proxyRes, directRes, originFilePath, done) { - expect(proxyRes.statusCode).toEqual(200); - - expect(proxyRes.statusCode).toEqual(directRes.statusCode); - // expect(proxyRes.headers.reqbody).toEqual(directRes.headers.reqbody); - - // the body will be the file path - const directUploadedStream = fs.createReadStream(directRes.body); - const proxyUploadedStream = fs.createReadStream(proxyRes.body); - const localFileStream = fs.createReadStream(originFilePath); - streamEqual(directUploadedStream, localFileStream) - .then(isLocalEqual => { - expect(isLocalEqual).toBe(true); - streamEqual(directUploadedStream, proxyUploadedStream) - .then(isUploadedEqual => { - expect(isUploadedEqual).toBe(true); - done(); - }, error => { - console.error('error in comparing directUpload with proxy:\n',error); - done.fail('error in comparing directUpload with proxy'); - }); - done(); - }, error => { - console.error('error in comparing directUpload with local:\n',error); - done.fail('error in comparing directUpload with local'); - }); - } - }); - - // describe('Test Big file download', () => { - // // const url = '/test/download/bigfile'; - // const url = 'http://yunpan.alibaba-inc.com/downloadService.do?token=pZWiXMXUguIUQDvR098qnUVqVAWhNVY6'; - // const contentType = 'application/octet-stream'; - // const param = {}; - // it('BIG file downlaod should be working', (done) => { - // directGet(url, param, CommonRequestHeader).then(proxyRes => { - // console.info('proxyRes body:', proxyRes.body); - - // directGet(url, param, CommonRequestHeader).then(directRes => { - // expect(proxyRes.statusCode).toEqual(200); - // expect(proxyRes.headers['content-type']).toEqual(contentType); - - // expect(proxyRes.statusCode).toEqual(directRes.statusCode); - // expect(proxyRes.headers['content-type']).toEqual(directRes.headers['content-type']); - // expect(proxyRes.body).toEqual(directRes.body); - // done(); - // }, error => { - // console.error('error in direct get bigfile :', error); - // done.fail(`error happend in direct get bigfile`); - // }); - // }, error => { - // console.error('error in proxy get bigfile :', error); - // done.fail(`error happend in proxy get bigfile`); - // }); - // }); - // }); - }); -} diff --git a/test/no_rule_websocket_spec.js b/test/no_rule_websocket_spec.js deleted file mode 100644 index bdfade2..0000000 --- a/test/no_rule_websocket_spec.js +++ /dev/null @@ -1,144 +0,0 @@ -/* -* Test suites for WebSocket. -* ONLY TO ENSURE THE REQUEST WILL BE BYPASSED SUCCESSFULLY, WE HAVEN'T SUPPORTTED WEBSOCKET YET. -* -*/ -const ProxyServerUtil = require('./util/ProxyServerUtil.js'); -const { generateWsUrl, directWs, proxyWs } = require('./util/HttpUtil.js'); -const Server = require('./server/server.js'); -const { printLog } = require('./util/CommonUtil.js'); - -const wsHost = 'ws://localhost:3000/test/socket'; - -testWebsocket('ws'); -testWebsocket('wss'); - -function testWebsocket(protocol) { - describe('Test WebSocket in protocol : ' + protocol , () =>{ - const url = generateWsUrl(protocol, '/test/socket'); - let serverInstance ; - let proxyServer ; - - beforeAll((done) => { - jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; - printLog('Start server for no_rule_websocket_spec'); - serverInstance = new Server(); - - proxyServer = ProxyServerUtil.proxyServerWithoutHttpsIntercept(); - - setTimeout(function() { - done(); - }, 2000); - }); - - afterAll(() => { - serverInstance && serverInstance.close(); - proxyServer && proxyServer.close(); - printLog('Closed server for no_rule_websocket_spec'); - }); - - it('Default websocket option', done => { - const sendMessage = 'Send the message with default option'; - let directMessage ; // set the flag for direct message, compare when both direct and proxy got message - let proxyMessage; - - const ws = directWs(url); - const porxyWsRef = proxyWs(url); - ws.on('open', () => { - ws.send(sendMessage); - }); - - porxyWsRef.on('open', () => { - porxyWsRef.send(sendMessage); - }); - - ws.on('message', (data, flag) => { - const message = JSON.parse(data); - if (message.type === 'onMessage') { - directMessage = message.content; - compareMessageIfReady(); - } - }); - - porxyWsRef.on('message', (data, flag) => { - const message = JSON.parse(data); - if (message.type === 'onMessage') { - proxyMessage = message.content; - compareMessageIfReady(); - } - }); - - ws.on('error', error => { - console.error('error happened in direct websocket:', error); - done.fail('Error happened in direct websocket'); - }); - - porxyWsRef.on('error', error => { - console.error('error happened in proxy websocket:', error); - done.fail('Error happened in proxy websocket'); - }); - - function compareMessageIfReady () { - if (directMessage && proxyMessage) { - expect(directMessage).toEqual(proxyMessage); - expect(directMessage).toEqual(sendMessage); - done(); - } - } - }); - - it('masked:true', done => { - const sendMessage = 'Send the message with option masked:true'; - let directMessage ; // set the flag for direct message, compare when both direct and proxy got message - let proxyMessage; - - const ws = directWs(url); - const porxyWsRef = proxyWs(url); - ws.on('open', () => { - ws.send(sendMessage, { masked: true }); - }); - - porxyWsRef.on('open', () => { - porxyWsRef.send(sendMessage, { masked: true }); - }); - - ws.on('message', (data, flag) => { - const message = JSON.parse(data); - if (message.type === 'onMessage') { - directMessage = message.content; - compareMessageIfReady(); - } - }); - - porxyWsRef.on('message', (data, flag) => { - const message = JSON.parse(data); - if (message.type === 'onMessage') { - proxyMessage = message.content; - compareMessageIfReady(); - } - }); - - ws.on('error', error => { - console.error('error happened in direct websocket:', error); - done.fail('Error happened in direct websocket'); - }); - - porxyWsRef.on('error', error => { - console.error('error happened in proxy websocket:', error); - - done.fail('Error happened in proxy websocket'); - }); - - function compareMessageIfReady () { - if (directMessage && proxyMessage) { - expect(directMessage).toEqual(proxyMessage); - expect(directMessage).toEqual(sendMessage); - done(); - } - } - - }); - }); - -} - diff --git a/test/report/README b/test/report/README new file mode 100644 index 0000000..bb7c511 --- /dev/null +++ b/test/report/README @@ -0,0 +1 @@ +* this is a folder to save test reports * \ No newline at end of file diff --git a/test/rule_shouldUseLocalResponse_spec.js b/test/rule_shouldUseLocalResponse_spec.js deleted file mode 100644 index e540f66..0000000 --- a/test/rule_shouldUseLocalResponse_spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -* test for rule shouldUseLocal -* -*/ - -const ProxyServerUtil = require('./util/ProxyServerUtil.js'); -const { proxyGet, generateUrl } = require('./util/HttpUtil.js'); -const Server = require('./server/server.js'); -const { printLog } = require('./util/CommonUtil.js'); - -const rule = require('./test_rules/shouldUseLocalResponseRule.js'); -const expectedLocalBody = 'handled_in_local_response'; - - -testWrapper('http'); -testWrapper('https'); - -function testWrapper(protocol, ) { - describe('Rule shouldUseLocalResponse should be working in :' + protocol, () => { - let proxyServer ; - let serverInstance ; - - beforeAll((done) => { - jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000000; - printLog('Start server for rule_shouldUseLocalResponse_spec'); - - serverInstance = new Server(); - - proxyServer = ProxyServerUtil.proxyServerWithRule(rule); - - setTimeout(function() { - done(); - }, 2000); - }); - - afterAll(() => { - serverInstance && serverInstance.close(); - proxyServer && proxyServer.close(); - printLog('Close server for rule_shouldUseLocalResponse_spec'); - - }); - - it('Should use local response if the assertion is true', done => { - const url = generateUrl(protocol, '/test/uselocal'); - proxyGet(url, {}) - .then(res => { - expect(res.body).toEqual(expectedLocalBody); - expect(res.headers['via-proxy-local']).toEqual('true'); - done(); - }, error => { - console.log('error happened in proxy get for shouldUseLocal: ',error); - done.fail('error happened when test shouldUseLocal rule'); - }); - - }); - }); -} diff --git a/test/server/server.js b/test/server/server.js index 74bf6d7..f37c43f 100644 --- a/test/server/server.js +++ b/test/server/server.js @@ -4,271 +4,339 @@ const koaBody = require('koa-body'); const send = require('koa-send'); const path = require('path'); const https = require('https'); -const certMgr = require("../../lib/certMgr"); +const certMgr = require('../../lib/certMgr'); const fs = require('fs'); +const nurl = require('url'); const websocket = require('koa-websocket'); -const wsRouter = require('koa-router')(); const color = require('colorful'); const WebSocketServer = require('ws').Server; +const tls = require('tls'); +const crypto = require('crypto'); + +const createSecureContext = tls.createSecureContext || crypto.createSecureContext; const DEFAULT_PORT = 3000; const HTTPS_PORT = 3001; -const UPLOAD_DIR = './test/temp'; +const HTTPS_PORT2 = 3002; // start multiple https server +const UPLOAD_DIR = path.resolve(__dirname, '../temp'); const PROXY_KEY_PREFIX = 'proxy-'; -function KoaServer() { - this.httpServer = null; - this.httpsServer = null; - this.requestRecordMap = {}; // store all request data to the map - const self = this; +function SNICertCallback(serverName, SNICallback) { + certMgr.getCertificate(serverName, (err, key, crt) => { + if (err) { + console.error('error happend in sni callback', err); + return; + } + const ctx = createSecureContext({ + key, + cert: crt + }); - /** - * log the request info, write as - */ - this.logRequest = function* (next) { - const headers = this.request.headers; - let key = this.request.host + this.request.url; - - // take proxy data with 'proxy-' + url - if (headers['via-proxy'] === 'true') { - key = PROXY_KEY_PREFIX + key; - } - - let body = this.request.body; - body = typeof body === 'object' ? JSON.stringify(body) : body; - self.requestRecordMap[key] = { - headers: headers, - body: body - }; - yield next; - }; - - this.start(); + SNICallback(null, ctx); + }); } -KoaServer.prototype.constructRouter = function() { - const router = KoaRouter(); - router.post('/test/getuser', this.logRequest, koaBody(), function*(next) { - printLog('requesting post /test/getuser'); - this.response.set('reqbody', JSON.stringify(this.request.body)); - this.response.body = 'something in post'; - }); +function KoaServer() { + this.httpServer = null; + this.httpsServer = null; + this.requestRecordMap = {}; // store all request data to the map + const self = this; - router.get('/test', this.logRequest, function*(next) { - printLog('request in get: ' + JSON.stringify(this.request)); - this.response.body = 'something'; - this.response.__req = this.request; - printLog('response in get:' + JSON.stringify(this.response)); - }); + /** + * log the request info, write as + */ + this.logRequest = function *(next) { + const headers = this.request.headers; + let key = this.request.protocol + '://' + this.request.host + nurl.parse(this.request.url).pathname; // remove param to get clean key - router.get('/test/uselocal', this.logRequest, function*(next) { - printLog('request in get local:' + JSON.stringify(this.request)); - this.response.body = 'something should be in local'; - this.response.__req = this.request; - printLog('response in get:' + JSON.stringify(this.response)); - }); + // take proxy data with 'proxy-' + url + if (headers['via-proxy'] === 'true') { + key = PROXY_KEY_PREFIX + key; + } - ['png', 'webp', 'json', 'js', 'css', 'ttf', 'eot', 'svg', 'woff', 'woff2'].forEach(item => { - router.get(`/test/download/${item}`, this.logRequest, function* (next) { - printLog(`now downloading the ${item}`); - yield send(this, `test/data/test.${item}`); - }); - }); - - router.get('/test/response/303', function*(next) { - printLog('now to redirect 303'); - this.redirect('/test'); - this.status = 303; - }); - - router.get('/test/response/302', function*(next) { - printLog('now to redirect 302'); - this.redirect('/test'); - }); - - router.get('/test/response/301', function*(next) { - printLog('now to redirect permanently'); - this.redirect('/test'); - this.status = 301; - }); - - const onFileBegin = function(name, file) { - if (!fs.existsSync('./test/temp')) { - try { - fs.mkdirSync('./test/temp', '0777'); - } catch (e) { - return null; - } - } - - file.name = 'test_upload_' + Date.now() + '.png'; - var folder = path.dirname(file.path); - file.path = path.join(folder, file.name); + printLog('log request with key :' + key); + let body = this.request.body; + body = typeof body === 'object' ? JSON.stringify(body) : body; + self.requestRecordMap[key] = { + headers, + body }; + yield next; + }; - router.post('/test/upload/png', - this.logRequest, - koaBody({ - multipart: true, - formidable: { - uploadDir: UPLOAD_DIR, - onFileBegin: onFileBegin - } - }), - function*(next) { - const file = this.request.body.files.file; - this.response.set('reqbody', JSON.stringify(this.request.body.fields)); - this.response.body = file.path; - } - ); + this.start(); +} - router.put('/test/upload/putpng', - this.logRequest, - koaBody({ - multipart: true, - formidable: { - uploadDir: UPLOAD_DIR, - onFileBegin: onFileBegin - } - }), - function*(next) { - const file = this.request.body.files.file; - this.response.body = file.path; - } - ); +KoaServer.prototype.constructRouter = function () { + const router = KoaRouter(); + router.post('/test/getuser', koaBody(), this.logRequest, function *(next) { + printLog('requesting post /test/getuser'); + this.response.set('reqbody', JSON.stringify(this.request.body)); + this.response.body = 'body_post_getuser'; + }); - router.put('/test/put', koaBody(), this.logRequest, function*(next) { - printLog('requesting put /test/put' + JSON.stringify(this.request)); - this.response.body = 'something in put'; + router.get('/test', this.logRequest, function *(next) { + printLog('request in get: ' + JSON.stringify(this.request)); + this.cookies.set('a1', 'a1value'); + this.cookies.set('a2', 'a2value'); + this.cookies.set('a3', 'a3value'); + this.response.set('header1', 'cookie2=headervalue2'); + + this.response.body = 'something'; + this.response.__req = this.request; + printLog('response in get:' + JSON.stringify(this.response)); + }); + + router.get('/test/uselocal', this.logRequest, function *(next) { + printLog('request in get local:' + JSON.stringify(this.request)); + this.response.body = 'something should be in local'; + // this.response.__req = this.request; + printLog('response in get:' + JSON.stringify(this.response)); + }); + + ['png', 'webp', 'json', 'js', 'css', 'ttf', 'eot', 'svg', 'woff', 'woff2'].forEach(item => { + router.get(`/test/download/${item}`, this.logRequest, function *(next) { + yield send(this, `./data/test.${item}`, { + root: path.resolve(__dirname, '../') + }); }); + }); - router.delete('/test/delete/:id', this.logRequest, function*(next) { - printLog('requesting delete /test/delete/:id'+ JSON.stringify(this.params)); - this.response.body = 'something in delete'; - }); + router.get('/test/response/303', function *(next) { + printLog('now to redirect 303'); + this.redirect('/test'); + this.status = 303; + }); - router.head('/test/head', this.logRequest, function*(next) { - printLog('requesting head /test/head'); - this.response.body = ''; // the body will not be passed to response, in HEAD request - this.response.set('reqBody', 'head_request_contains_no_resbody'); - }); + router.get('/test/response/302', function *(next) { + printLog('now to redirect 302'); + this.redirect('/test'); + }); - router.options('/test/options', this.logRequest, function*(next) { - printLog('requesting options /test/options'); - this.response.body = 'could_be_empty'; - this.response.set('Allow', 'GET, HEAD, POST, OPTIONS'); - }); + router.get('/test/response/301', function *(next) { + printLog('now to redirect permanently'); + this.redirect('/test'); + this.status = 301; + }); - // router.connect('/test/connect', function *(next) { - // printLog('requesting connect /test/connect'); - // this.response.body = 'connect_established_body'; - // }); + const onFileBegin = function (name, file) { + if (!fs.existsSync(UPLOAD_DIR)) { + try { + fs.mkdirSync(UPLOAD_DIR, '0777'); + } catch (e) { + console.log(e); + return null; + } + } - return router; + file.name = 'test_upload_' + Date.now() + '.png'; + const folder = path.dirname(file.path); + file.path = path.join(folder, file.name); + }; + + router.post('/test/upload/png', + this.logRequest, + koaBody({ + multipart: true, + formidable: { + uploadDir: UPLOAD_DIR, + onFileBegin + } + }), + function *(next) { + const file = this.request.body.files.file; + this.response.set('reqbody', JSON.stringify(this.request.body.fields)); + this.response.body = file.path; + } + ); + + router.put('/test/upload/putpng', + this.logRequest, + koaBody({ + multipart: true, + formidable: { + uploadDir: UPLOAD_DIR, + onFileBegin + } + }), + function *(next) { + const file = this.request.body.files.file; + this.response.body = file.path; + } + ); + + router.put('/test/put', koaBody(), this.logRequest, function *(next) { + printLog('requesting put /test/put' + JSON.stringify(this.request)); + this.response.body = 'something in put'; + }); + + router.delete('/test/delete/:id', this.logRequest, function *(next) { + printLog('requesting delete /test/delete/:id' + JSON.stringify(this.params)); + this.response.body = 'something in delete'; + }); + + router.head('/test/head', this.logRequest, function *(next) { + printLog('requesting head /test/head'); + this.response.body = ''; // the body will not be passed to response, in HEAD request + this.response.set('reqBody', 'head_request_contains_no_resbody'); + }); + + router.options('/test/options', this.logRequest, function *(next) { + printLog('requesting options /test/options'); + this.response.body = 'could_be_empty'; + this.response.set('Allow', 'GET, HEAD, POST, OPTIONS'); + }); + + // router.connect('/test/connect', function *(next) { + // printLog('requesting connect /test/connect'); + // this.response.body = 'connect_established_body'; + // }); + + router.get('/test/should_not_replace_option', this.logRequest, function *(next) { + this.response.body = 'the_option_that_not_be_replaced'; + }); + + router.get('/test/should_replace_option', this.logRequest, function *(next) { + this.response.body = 'the_request_that_has_not_be_replaced'; + }); + + router.get('/test/new_replace_option', this.logRequest, function *(next) { + this.response.body = 'the_new_replaced_option_page_content'; + }); + + router.get('/test/normal_request1', this.logRequest, koaBody(), function *(next) { + printLog('requesting get /test/normal_request1'); + this.response.body = 'body_normal_request1'; + }); + + router.get('/test/normal_request2', this.logRequest, koaBody(), function *(next) { + printLog('requesting get /test/normal_request2'); + this.response.body = 'body_normal_request2'; + }); + + router.post('/test/normal_post_request1', koaBody(), this.logRequest, function *(next) { + printLog('requesting post /test/normal_post_request1'); + this.response.body = 'body_normal_post_request1'; + }); + + router.get('/big_response', this.logRequest, function *(next) { + const buf = new Buffer(1 * 1024 * 1024 * 1024); // 1GB + buf.fill(1); + printLog('request in get big response of 1GB'); + this.response.type = 'application/octet-stream'; + this.response.body = buf; + }); + + return router; }; -KoaServer.prototype.constructWsRouter = function() { - const wsRouter = KoaRouter(); - const self = this; - wsRouter.get('/test/socket', function*(next) { - const ws = this.websocket; - const messageObj = { - type: 'initial', - content: 'default message' - }; +KoaServer.prototype.constructWsRouter = function () { + const wsRouter = KoaRouter(); + const self = this; + wsRouter.get('/test/socket', function *(next) { + const ws = this.websocket; + const messageObj = { + type: 'initial', + content: 'default message' + }; - ws.send(JSON.stringify(messageObj)); - ws.on('message', message => { - printLog('message from request socket: ' + message); - self.handleRecievedMessage(ws, message); - }); - yield next; + ws.send(JSON.stringify(messageObj)); + ws.on('message', message => { + printLog('message from request socket: ' + message); + self.handleRecievedMessage(ws, message); }); + yield next; + }); - return wsRouter; + return wsRouter; }; KoaServer.prototype.getRequestRecord = function (key) { - return this.requestRecordMap[key] || {}; + return this.requestRecordMap[key] || null; }; KoaServer.prototype.getProxyRequestRecord = function (key) { - key = PROXY_KEY_PREFIX + key; - return this.requestRecordMap[key] || {}; + key = PROXY_KEY_PREFIX + key; + return this.requestRecordMap[key] || null; }; -KoaServer.prototype.handleRecievedMessage = function(ws, message) { - const newMessage = { - type: 'onMessage', - content: message - }; - ws.send(JSON.stringify(newMessage)); +KoaServer.prototype.handleRecievedMessage = function (ws, message) { + const newMessage = { + type: 'onMessage', + content: message + }; + ws.send(JSON.stringify(newMessage)); }; -KoaServer.prototype.start = function() { - printLog('Starting the server...'); - const router = this.constructRouter(); - const wsRouter = this.constructWsRouter(); - const self = this; - const app = Koa(); - websocket(app); +KoaServer.prototype.start = function () { + printLog('Starting the server...'); + const router = this.constructRouter(); + const wsRouter = this.constructWsRouter(); + const self = this; + const app = Koa(); + websocket(app); - app.use(router.routes()); - app.ws.use(wsRouter.routes()); - this.httpServer = app.listen(DEFAULT_PORT); + app.use(router.routes()); + app.ws.use(wsRouter.routes()); + this.httpServer = app.listen(DEFAULT_PORT); - printLog('HTTP is now listening on port :' + DEFAULT_PORT); + printLog('HTTP is now listening on port :' + DEFAULT_PORT); - certMgr.getCertificate('localhost', function(error, keyContent, crtContent) { - if (error) { - console.error('failed to create https server:', error); - } else { - self.httpsServer = https.createServer({ - key: keyContent, - cert: crtContent - }, app.callback()); + certMgr.getCertificate('localhost', (error, keyContent, crtContent) => { + if (error) { + console.error('failed to create https server:', error); + } else { + self.httpsServer = https.createServer({ + SNICallback: SNICertCallback, + key: keyContent, + cert: crtContent + }, app.callback()); - // create wss server - const wss = new WebSocketServer({ - server: self.httpsServer - }); + // create wss server + const wss = new WebSocketServer({ + server: self.httpsServer + }); - wss.on('connection', function connection(ws) { - ws.on('message', function incoming(message) { - printLog('received in wss: ' + message); - self.handleRecievedMessage(ws, message); - }); + wss.on('connection', (ws) => { + ws.on('message', (message) => { + printLog('received in wss: ' + message); + self.handleRecievedMessage(ws, message); + }); + }); - }); + wss.on('error', e => console.error('erro happened in wss:%s', error)); - wss.on('error', error => { - console.error('erro happened in wss:%s', error); - }); + self.httpsServer.listen(HTTPS_PORT); - self.httpsServer.listen(HTTPS_PORT); + self.httpsServer2 = https.createServer({ + key: keyContent, + cert: crtContent + }, app.callback()); - printLog('HTTPS is now listening on port :' + HTTPS_PORT); + self.httpsServer2.listen(HTTPS_PORT2); - printLog('Server started successfully'); - } - }); + printLog('HTTPS is now listening on port :' + HTTPS_PORT); - return this; + printLog('Server started successfully'); + } + }); + + return this; }; -KoaServer.prototype.close = function() { - printLog('Closing server now...'); - this.httpServer && this.httpServer.close(); - this.httpsServer && this.httpsServer.close(); - this.requestRecordMap = {}; - printLog('Server closed successfully'); +KoaServer.prototype.close = function () { + printLog('Closing server now...'); + this.httpServer && this.httpServer.close(); + this.httpsServer && this.httpsServer.close(); + this.httpsServer2 && this.httpsServer2.close(); + this.requestRecordMap = {}; + printLog('Server closed successfully'); }; function printLog(content) { - console.log(color.cyan('===SERVER LOG===' + content)); + console.log(color.cyan('[SERVER LOG]: ' + content)); } - -module.exports = KoaServer; \ No newline at end of file +module.exports = KoaServer; diff --git a/test/server/startServer.js b/test/server/startServer.js index 15af246..7b861e3 100644 --- a/test/server/startServer.js +++ b/test/server/startServer.js @@ -1,3 +1,3 @@ const Server = require('./server.js'); -new Server(); \ No newline at end of file +new Server(); diff --git a/test/spec_lib/proxyServerModule.js b/test/spec_lib/proxyServerModule.js new file mode 100644 index 0000000..971c648 --- /dev/null +++ b/test/spec_lib/proxyServerModule.js @@ -0,0 +1,93 @@ +/* +* test for rule replaceOption rule +* +*/ +const ip = require('ip'); +const AnyProxy = require('../../proxy'); +const { proxyGet, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); + +const OUT_BOUND_IP = ip.address(); + +describe('AnyProxy.proxyServer basic test', () => { + it('should successfully start a proxy server', done => { + const options = { + port: 8001, + rule: null, + webInterface: { + enable: true, + webPort: 8002, + wsPort: 8003, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false + }; + const proxyServer = new AnyProxy.ProxyServer(options); + proxyServer.on('ready', () => { + proxyServer.close(); + done(); + }); + proxyServer.on('error', done.fail); + proxyServer.start(); + }); +}); + +describe('AnyProxy.proxyServer high order test', () => { + let proxyServer; + let serverInstance; + beforeAll(done => { + // jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + serverInstance = new Server(); + + const options = { + port: 8001, + rule: null, + webInterface: { + enable: true, + webPort: 8002, + }, + throttle: 10000, + forceProxyHttps: false, + silent: false + }; + proxyServer = new AnyProxy.ProxyServer(options); + proxyServer.on('ready', done); + proxyServer.start(); + }); + + afterAll(() => { + proxyServer && proxyServer.close(); + serverInstance && serverInstance.close(); + }); + + it('should work as expected for domain host', done => { + // test if proxy server works + proxyGet('https://www.tmall.com', {}, {}) + .then(res => { + expect(res && res.statusCode && res.statusCode === 200 && res.body.length > 300).toBe(true); + done(); + }) + .catch(done) + }); + + it('should work as expected for ip host', done => { + // test if proxy server works + proxyGet(`https://${OUT_BOUND_IP}:3001/test`, {}, {}) + .then(res => { + expect(res && res.statusCode && res.statusCode === 200).toBe(true); + done(); + }) + .catch(done) + }); + + it('should start webinterface correctly', done => { + // test web interface + directGet('http://127.0.0.1:8002', {}, {}) + .then(res => { + expect(res && res.statusCode && res.statusCode === 200 && res.body.length > 300).toBe(true); + done(); + }) + .catch(done) + }); +}); diff --git a/test/spec_lib/ruleLoader.js b/test/spec_lib/ruleLoader.js new file mode 100644 index 0000000..3bf4560 --- /dev/null +++ b/test/spec_lib/ruleLoader.js @@ -0,0 +1,51 @@ +/* +* test for rule replaceOption rule +* +*/ + +const ruleLoader = require('../../lib/ruleLoader'); +const fs = require('fs'); +const path = require('path'); + +const localModulePath = path.join(__dirname, '../util/CommonUtil.js'); +describe('rule loader', () => { + it('should successfully cache a remote file', done => { + ruleLoader.cacheRemoteFile('https://cdn.bootcss.com/lodash.js/4.16.4/lodash.min.js') + .then(filePath => { + let content; + if (filePath) { + content = fs.readFileSync(filePath, { encoding: 'utf8' }); + } + expect(content && content.length > 100).toBe(true); + done(); + }) + .catch(done.fail); + }); + + it('should load a local module ../util/CommonUtil', done => { + ruleLoader.loadLocalPath(localModulePath) + .then(module => { + expect(module.printLog).not.toBeUndefined(); + done(); + }) + .catch(done.fail); + }); + + it('should smart load a remote module', done => { + ruleLoader.requireModule('https://cdn.bootcss.com/lodash.js/4.16.4/lodash.min.js') + .then(module => { + expect(module.VERSION).toEqual('4.16.4'); + done(); + }) + .catch(done.fail); + }); + + it('should smart load a local module', done => { + ruleLoader.requireModule(localModulePath) + .then(module => { + expect(module.printLog).not.toBeUndefined(); + done(); + }) + .catch(done.fail); + }); +}); diff --git a/test/spec_lib/util.js b/test/spec_lib/util.js new file mode 100644 index 0000000..8881df3 --- /dev/null +++ b/test/spec_lib/util.js @@ -0,0 +1,26 @@ +/* +* test for rule replaceOption rule +* +*/ +const util = require('../../lib/util'); + +describe('utils', () => { + it('should get some free ports', done => { + const count = 100; + const tasks = []; + for (let i = 1; i <= count; i++) { + tasks.push(util.getFreePort()); + } + Promise.all(tasks) + .then((results) => { + // ensure ports are unique + const portMap = {}; + results.map((portNumber) => { + portMap[portNumber] = true; + }); + expect(Object.keys(portMap).length).toEqual(count); + done(); + }) + .catch(done.fail); + }); +}); diff --git a/test/spec_outweb/test_realweb_spec.js b/test/spec_outweb/test_realweb_spec.js new file mode 100644 index 0000000..f419155 --- /dev/null +++ b/test/spec_outweb/test_realweb_spec.js @@ -0,0 +1,147 @@ + +/** + * use phantomjs to capture requests in real websites, then compare the directly-connected response with those through AnyProxy + */ +const fs = require('fs'); +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const HttpUtil = require('../util/HttpUtil.js'); +const path = require('path'); +const { printLog, printError, printHilite, stringSimilarity } = require('../util/CommonUtil.js'); + +const reportPath = path.join(__dirname, '../report/'); + +const testUrls = ['https://www.taobao.com', 'https://www.baidu.com', 'https://www.tmall.com']; + +let direcrtResponseSampleA = []; +let direcrtResponseSampleB = []; +let proxyResponse = []; + +function test(url, requestHeaders = {}) { + fdescribe('Test requests in real broswer', () => { + let proxyServer; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + printLog('Start server for ' + url); + + proxyServer = ProxyServerUtil.defaultProxyServer(); + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + proxyServer && proxyServer.close(); + printLog('Closed server for ' + url); + }); + + it(`Request towards ${url}`, (done) => { + HttpUtil.getRequestListFromPage(url).then((arr) => { + const directPromisesA = []; + const directPromisesB = []; + const proxyPromises = []; + arr.forEach((data, i) => { + const requestPath = data.url; + const headers = data.headers; + const method = data.method; + const params = data.method === 'POST' ? JSON.parse(data.postData) : {}; + if (HttpUtil.isSupportedProtocol(requestPath)) { + directPromisesA.push(HttpUtil.directRequest(method, requestPath, params, headers)); + directPromisesB.push(HttpUtil.directRequest(method, requestPath, params, headers)); + proxyPromises.push(HttpUtil.proxyRequest(method, requestPath, params, headers)); + } + }); + Promise.all(directPromisesA).then(responseArr => { direcrtResponseSampleA = responseArr }).then(() => { + Promise.all(directPromisesB).then(responseArr => { direcrtResponseSampleB = responseArr }).then(() => { + Promise.all(proxyPromises).then(responseArr => { proxyResponse = responseArr }).then(() => { + showResponseResult(); + const { compareResult: TESTRESULT, errRecord } = compareResponses(url); + const reportFile = dealLogFile(errRecord, url, () => { + printHilite('====== COMPARE RESULT: ' + TESTRESULT.toString().toUpperCase() + ' ======'); + !TESTRESULT && printHilite(`Check more details in ${reportFile}`); + // expect(TESTRESULT).toBe(true); + done(); + }); + }) + .catch((err) => { + printError(err); + done(); + }); + }) + .catch((err) => { + printError(err); + done(); + }); + }) + .catch((err) => { + printError(err); + done(); + }); + }); + }) + }) +} + +function compareResponses(curUrl) { + const errRecord = []; + if (direcrtResponseSampleA.length !== direcrtResponseSampleB.length || direcrtResponseSampleA.length !== proxyResponse.length) { + printError('compare fail: length not match'); + return { + compareResult: false, + errRecord + } + } + for (let i = 0; i < proxyResponse.length; i++) { + const direcrtResponseInfoA = direcrtResponseSampleA[i]; + const direcrtResponseInfoB = direcrtResponseSampleB[i]; + const proxyResponseInfo = proxyResponse[i]; + const { similarity: similarity_direct } = stringSimilarity(stringify(direcrtResponseInfoA.body), stringify(direcrtResponseInfoB.body)); + const { similarity: similarity_proxy } = stringSimilarity(stringify(direcrtResponseInfoA.body), stringify(proxyResponseInfo.body)); + if (similarity_direct !== similarity_proxy) { + let LogText = `Similarity from ${proxyResponseInfo.request.href} between direct samples and proxy is not equal : ${similarity_direct} | ${similarity_proxy}\n`; + printError(LogText); + LogText += `\n${stringify(direcrtResponseInfoA.body)}`; + LogText += '\n=============================================\n'; + LogText += `${stringify(direcrtResponseInfoB.body)}`; + LogText += '\n=============================================\n'; + LogText += `${stringify(proxyResponseInfo.body)}\n\n`; + errRecord.push(LogText); + } + } + return { + compareResult: errRecord.length === 0, + errRecord + }; +} + +function stringify(data) { + return data ? data.replace(/\s+/g, '') : ''; +} + +function showResponseResult() { + if (direcrtResponseSampleA.length !== direcrtResponseSampleB.length || direcrtResponseSampleA.length !== proxyResponse.length) { + printError('compare fail: length not match'); + } + proxyResponse.forEach((dataObj, i) => { + const direcrtResponseInfoA = direcrtResponseSampleA[i]; + const direcrtResponseInfoB = direcrtResponseSampleB[i]; + printLog(`Direct Sample A ${direcrtResponseInfoA.request.method}: ${direcrtResponseInfoA.request.href} ${direcrtResponseInfoA.statusCode}${direcrtResponseInfoA.statusMessage}`); + printLog(`Direct Sample B ${direcrtResponseInfoB.request.method}: ${direcrtResponseInfoB.request.href} ${direcrtResponseInfoB.statusCode}${direcrtResponseInfoB.statusMessage}`); + printLog(`PROXY ${dataObj.request.method}: ${dataObj.request.href} ${dataObj.statusCode}${dataObj.statusMessage}`); + }) + console.log('\n'); +} + +function dealLogFile(dataObj = 'Log', url, cb) { + const filePath = reportPath + url.replace(/[^\u4E00-\u9FA5A-Za-z\s()()\d•·]/g, '_') + '.txt'; + fs.writeFile(filePath, dataObj, (err) => { + if (err) throw err; + console.log('Log is saved!'); + cb && cb(); + }); + return filePath; +} + +testUrls.forEach((link) => { + test(link); +}); diff --git a/test/spec_rule/no_rule_big_response_spec.js b/test/spec_rule/no_rule_big_response_spec.js new file mode 100644 index 0000000..a215ab8 --- /dev/null +++ b/test/spec_rule/no_rule_big_response_spec.js @@ -0,0 +1,42 @@ +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; +const { printLog } = require('../util/CommonUtil.js'); +const spawn = require('child_process').spawn; +const Server = require('../server/server.js'); + +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); + +describe('Test request with big body', () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + printLog('Start server for no_rule_big_response'); + + serverInstance = new Server(); + proxyServer = ProxyServerUtil.defaultProxyServer(); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Closed server for no_rule_spec'); + }); + + it('should successfully get file', (done) => { + const isWin = /^win/.test(process.platform); + if (isWin) { + done(); + } else { + const curl = spawn('curl', ['http://localhost:3000/big_response', '--proxy', 'http://127.0.0.1:8001', '-o', '/dev/null']); + curl.on('close', (code) => { + expect(code).toEqual(0); + done(); + }); + } + }); +}); diff --git a/test/spec_rule/no_rule_spec.js b/test/spec_rule/no_rule_spec.js new file mode 100644 index 0000000..6e9e123 --- /dev/null +++ b/test/spec_rule/no_rule_spec.js @@ -0,0 +1,400 @@ + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; +const path = require('path'); +const fs = require('fs'); +const Server = require('../server/server.js'); +const + { + proxyGet, + proxyPost, + directGet, + directPost, + directUpload, + proxyUpload, + generateUrl, + proxyPut, + directPut, + proxyDelete, + directDelete, + directHead, + proxyHead, + directOptions, + proxyOptions, + proxyPutUpload, + directPutUpload + } = require('../util/HttpUtil.js'); +const { CommonRequestHeader } = require('../data/headers.js'); +const { isCommonResHeaderEqual, isCommonReqEqual, printLog } = require('../util/CommonUtil.js'); +const streamEqual = require('stream-equal'); + +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); + +testRequest('http'); +testRequest('https'); + +// Test suites for http and https request +function testRequest(protocol = 'http') { + function constructUrl(urlPath) { + return generateUrl(protocol, urlPath); + } + + describe('Test request without proxy rules in protocol ' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + printLog('Start server for no_rule_spec'); + + serverInstance = new Server(); + proxyServer = ProxyServerUtil.defaultProxyServer(); + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Closed server for no_rule_spec'); + }); + + + it('Get should work as direct without proxy rules', (done) => { + const url = constructUrl('/test'); + const getParam = { + param: 'nothing' + }; + + proxyGet(url, getParam, CommonRequestHeader).then((proxyRes) => { + directGet(url, getParam, CommonRequestHeader).then(directRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + expect(proxyRes.statusCode).toEqual(directRes.statusCode); + expect(directRes.body).toEqual(proxyRes.body); + + done(); + }, error => { + console.error('error happend in direct get:', error); + done.fail('error happend in direct get'); + }); + }, error => { + console.log('error happened in proxy get:', error); + done.fail('error happend in proxy get'); + }); + }); + + it('Post should work as direct without proxy rules', (done) => { + const url = constructUrl('/test/getuser'); + const param = { + param: 'postnothing' + }; + + proxyPost(url, param, CommonRequestHeader).then(proxyRes => { + directPost(url, param, CommonRequestHeader).then(directRes => { + expect(proxyRes.statusCode).toEqual(200); + + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(proxyRes.statusCode).toEqual(directRes.statusCode); + expect(directRes.body).toEqual(proxyRes.body); + + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + + done(); + }, error => { + console.error('error in direct post:', error); + done.fail('error happend in direct post'); + }); + }, error => { + console.log('error happened in proxy post,', error); + done.fail('error happend in proxy post'); + }); + }); + + it('PUT should work as direct without proxy rules', done => { + const url = constructUrl('/test/put'); + const param = { + param: 'putsomething' + }; + proxyPut(url, param, CommonRequestHeader).then(proxyRes => { + directPut(url, param, CommonRequestHeader).then(directRes => { + expect(directRes.statusCode).toEqual(200); + + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(directRes.statusCode).toEqual(proxyRes.statusCode); + expect(directRes.body).toEqual(proxyRes.body); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + + done(); + }, error => { + console.error('error happened in direct put', error); + done.fail('error happened in direct put'); + }); + }, error => { + console.error('error happened in proxy put', error); + done.fail('error happened in proxy put'); + }); + }); + + it('DELETE rquest should work as direct without proxy rules', (done) => { + const url = constructUrl('/test/delete/123456'); + + proxyDelete(url, {}, CommonRequestHeader).then(proxyRes => { + directDelete(url, {}, CommonRequestHeader).then(directRes => { + expect(directRes.statusCode).toEqual(200); + + expect(directRes.statusCode).toEqual(proxyRes.statusCode); + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(directRes.body).toEqual(proxyRes.body); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + + done(); + }, error => { + console.error('error happened in direct delete :', error); + done.fail('error happened in direct delete'); + }); + }, error => { + console.error('error happened in proxy delete :', error); + done.fail('error happened in proxy delete'); + }); + }); + + it('HEAD request should work as direct without proxy rules', (done) => { + const url = constructUrl('/test/head'); + + proxyHead(url, CommonRequestHeader) + .then(proxyRes => { + directHead(url, CommonRequestHeader) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual(''); + + expect(directRes.statusCode).toEqual(proxyRes.statusCode); + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(directRes.body).toEqual(proxyRes.body); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + + done(); + }, error => { + console.error('error happened in direct head request:', error); + done.fail('error happened in direct head request'); + }); + }, error => { + console.error('error happened in proxy head request:', error); + done.fail('error happened in proxy head request'); + }); + }); + + it('OPTIONS request should work as direct without proxy rules', (done) => { + const url = constructUrl('/test/options'); + + proxyOptions(url, CommonRequestHeader) + .then(proxyRes => { + directOptions(url, CommonRequestHeader) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual('could_be_empty'); + + expect(directRes.statusCode).toEqual(proxyRes.statusCode); + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(directRes.body).toEqual(proxyRes.body); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + + done(); + }, error => { + console.error('error happened in direct options request:', error); + done.fail('error happened in direct options request'); + }); + }, error => { + console.error('error happened in proxy options request:', error); + done.fail('error happened in proxy options request'); + }); + }); + + describe('Response code should be honored as direct without proxy rules', () => { + [301, 302, 303].forEach(code => { + testRedirect(code); + }); + + function testRedirect(redirectCode) { + it(`${redirectCode} response should work as direct without proxy rules`, (done) => { + const url = constructUrl(`/test/response/${redirectCode}`); + + proxyGet(url) + .then(proxyRes => { + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(redirectCode); + expect(directRes.headers.location).toEqual(proxyRes.headers.location); + expect(directRes.statusCode).toEqual(proxyRes.statusCode); + expect(directRes.headers.location).toEqual('/test'); + done(); + }).catch(error => { + console.log(`error happened in direct ${redirectCode}:`, error); + done.fail(`error happened in direct ${redirectCode}`); + }); + }).catch(error => { + console.log(`error happened in proxy ${redirectCode}:`, error); + done.fail(`error happened in proxy ${redirectCode}`); + }); + }); + } + }); + + describe('Test file download ', () => { + const testArray = [ + { + url: constructUrl('/test/download/png'), + type: 'png', + contentType: 'image/png' + }, + { + url: constructUrl('/test/download/webp'), + type: 'WEBP', + contentType: 'image/webp' + }, + { + url: constructUrl('/test/download/json'), + type: 'JSON', + contentType: 'application/json; charset=utf-8' + }, + { + url: constructUrl('/test/download/css'), + type: 'CSS', + contentType: 'text/css; charset=utf-8' + }, + { + url: constructUrl('/test/download/ttf'), + type: 'TTF', + contentType: 'application/x-font-ttf' + }, + { + url: constructUrl('/test/download/eot'), + type: 'EOT', + contentType: 'application/vnd.ms-fontobject' + }, + { + url: constructUrl('/test/download/svg'), + type: 'SVG', + contentType: 'image/svg+xml' + }, + { + url: constructUrl('/test/download/woff'), + type: 'WOFF', + contentType: 'application/font-woff' + }, + { + url: constructUrl('/test/download/woff2'), + type: 'WOFF2', + contentType: 'application/font-woff2' + } + ]; + + testArray.forEach(item => { + testFileDownload(item.url, item.type, item.contentType); + }); + + // 封装测试文件下载的测试工具类 + function testFileDownload(url, filetype, contentType) { + const describe = `${filetype} file download without rules should be work as direct download`; + const param = {}; + + it(describe, (done) => { + proxyGet(url, param).then(proxyRes => { + directGet(url, param).then(directRes => { + expect(proxyRes.statusCode).toEqual(200); + + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(proxyRes.statusCode).toEqual(directRes.statusCode); + expect(proxyRes.body).toEqual(directRes.body); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + done(); + }, error => { + console.error('error in direct get :', filetype, error); + done.fail(`error happend in direct get ${filetype}`); + }); + }, error => { + console.error('error in proxy get :', filetype, error); + done.fail(`error happend in proxy get ${filetype}`); + }); + }); + } + }); + + describe('Test file upload', () => { + const formParams = { + param1: 'param_1', + param2: 'param2' + }; + it('POST upload should be working', (done) => { + const url = constructUrl('/test/upload/png'); + const filePath = path.resolve(__dirname, '../data/test.png'); + + proxyUpload(url, filePath, formParams) + .then(proxyRes => { + directUpload(url, filePath, formParams) + .then((directRes) => { + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + expect(isCommonReqEqual(url, serverInstance)).toBe(true); + assertReponse(proxyRes, directRes, filePath, done); + }, error => { + console.error('error in direct upload:', error); + done.fail('error in direct upload'); + }); + }, error => { + console.error('error in proxy upload:', error); + done.fail('error in proxy upload:'); + }); + }); + + it('PUT upload should be working', (done) => { + const url = constructUrl('/test/upload/putpng'); + const filePath = path.resolve(__dirname, '../data/test.png'); + proxyPutUpload(url, filePath, formParams) + .then(proxyRes => { + directPutUpload(url, filePath, formParams) + .then((directRes) => { + expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true); + + assertReponse(proxyRes, directRes, filePath, done); + }, error => { + console.error('error in direct upload:', error); + done.fail('error in direct upload'); + }); + }, error => { + console.error('error in proxy upload:', error); + done.fail('error in proxy upload:'); + }); + }); + + function assertReponse(proxyRes, directRes, originFilePath, done) { + expect(proxyRes.statusCode).toEqual(200); + + expect(proxyRes.statusCode).toEqual(directRes.statusCode); + // expect(proxyRes.headers.reqbody).toEqual(directRes.headers.reqbody); + + // the body will be the file path + const directUploadedStream = fs.createReadStream(directRes.body); + const proxyUploadedStream = fs.createReadStream(proxyRes.body); + const localFileStream = fs.createReadStream(originFilePath); + streamEqual(directUploadedStream, localFileStream) + .then(isLocalEqual => { + expect(isLocalEqual).toBe(true); + streamEqual(directUploadedStream, proxyUploadedStream) + .then(isUploadedEqual => { + expect(isUploadedEqual).toBe(true); + done(); + }, error => { + console.error('error in comparing directUpload with proxy:\n', error); + done.fail('error in comparing directUpload with proxy'); + }); + done(); + }, error => { + console.error('error in comparing directUpload with local:\n', error); + done.fail('error in comparing directUpload with local'); + }); + } + }); + }); +} diff --git a/test/spec_rule/no_rule_websocket_spec.js b/test/spec_rule/no_rule_websocket_spec.js new file mode 100644 index 0000000..94bc999 --- /dev/null +++ b/test/spec_rule/no_rule_websocket_spec.js @@ -0,0 +1,140 @@ +/* +* Test suites for WebSocket. +* ONLY TO ENSURE THE REQUEST WILL BE BYPASSED SUCCESSFULLY, WE HAVEN'T SUPPORTTED WEBSOCKET YET. +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { generateWsUrl, directWs, proxyWs } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +testWebsocket('ws'); +testWebsocket('wss'); + +function testWebsocket(protocol) { + describe('Test WebSocket in protocol : ' + protocol, () => { + const url = generateWsUrl(protocol, '/test/socket'); + let serverInstance; + let proxyServer; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + printLog('Start server for no_rule_websocket_spec'); + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithoutHttpsIntercept(); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Closed server for no_rule_websocket_spec'); + }); + + it('Default websocket option', done => { + const sendMessage = 'Send the message with default option'; + let directMessage; // set the flag for direct message, compare when both direct and proxy got message + let proxyMessage; + + const ws = directWs(url); + const porxyWsRef = proxyWs(url); + ws.on('open', () => { + ws.send(sendMessage); + }); + + porxyWsRef.on('open', () => { + porxyWsRef.send(sendMessage); + }); + + ws.on('message', (data, flag) => { + const message = JSON.parse(data); + if (message.type === 'onMessage') { + directMessage = message.content; + compareMessageIfReady(); + } + }); + + porxyWsRef.on('message', (data, flag) => { + const message = JSON.parse(data); + if (message.type === 'onMessage') { + proxyMessage = message.content; + compareMessageIfReady(); + } + }); + + ws.on('error', error => { + console.error('error happened in direct websocket:', error); + done.fail('Error happened in direct websocket'); + }); + + porxyWsRef.on('error', error => { + console.error('error happened in proxy websocket:', error); + done.fail('Error happened in proxy websocket'); + }); + + function compareMessageIfReady() { + if (directMessage && proxyMessage) { + expect(directMessage).toEqual(proxyMessage); + expect(directMessage).toEqual(sendMessage); + done(); + } + } + }); + + it('masked:true', done => { + const sendMessage = 'Send the message with option masked:true'; + let directMessage; // set the flag for direct message, compare when both direct and proxy got message + let proxyMessage; + + const ws = directWs(url); + const porxyWsRef = proxyWs(url); + ws.on('open', () => { + ws.send(sendMessage, { masked: true }); + }); + + porxyWsRef.on('open', () => { + porxyWsRef.send(sendMessage, { masked: true }); + }); + + ws.on('message', (data, flag) => { + const message = JSON.parse(data); + if (message.type === 'onMessage') { + directMessage = message.content; + compareMessageIfReady(); + } + }); + + porxyWsRef.on('message', (data, flag) => { + const message = JSON.parse(data); + if (message.type === 'onMessage') { + proxyMessage = message.content; + compareMessageIfReady(); + } + }); + + ws.on('error', error => { + console.error('error happened in direct websocket:', error); + done.fail('Error happened in direct websocket'); + }); + + porxyWsRef.on('error', error => { + console.error('error happened in proxy websocket:', error); + + done.fail('Error happened in proxy websocket'); + }); + + function compareMessageIfReady() { + if (directMessage && proxyMessage) { + expect(directMessage).toEqual(proxyMessage); + expect(directMessage).toEqual(sendMessage); + done(); + } + } + }); + }); +} + diff --git a/test/spec_rule/rule/rule_replace_request_data.js b/test/spec_rule/rule/rule_replace_request_data.js new file mode 100644 index 0000000..1f3bb3b --- /dev/null +++ b/test/spec_rule/rule/rule_replace_request_data.js @@ -0,0 +1,22 @@ +/* +* add authToken parameter to the request data +* +*/ +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('/getuser') >= 0) { + let requestStr = requestDetail.requestData.toString(); + try { + requestStr = JSON.stringify(Object.assign(JSON.parse(requestStr), { + authToken: 'auth_token_inrule' + })) + } catch (e) { + requestStr += '&authToken=auth_token_inrule'; + } + return { + requestOptions: requestDetail.requestOptions, + requestData: requestStr + }; + } + } +}; diff --git a/test/spec_rule/rule/rule_replace_request_option.js b/test/spec_rule/rule/rule_replace_request_option.js new file mode 100644 index 0000000..915d910 --- /dev/null +++ b/test/spec_rule/rule/rule_replace_request_option.js @@ -0,0 +1,13 @@ +//rule scheme : +module.exports = { + + *beforeSendRequest(requestDetail) { + const newOption = requestDetail.requestOptions; + if (newOption.hostname === 'localhost' && newOption.path === '/test/should_replace_option') { + newOption.path = '/test/new_replace_option'; + return { + requestOptions: newOption + } + } + } +}; diff --git a/test/spec_rule/rule/rule_replace_request_protocol.js b/test/spec_rule/rule/rule_replace_request_protocol.js new file mode 100644 index 0000000..bd5942c --- /dev/null +++ b/test/spec_rule/rule/rule_replace_request_protocol.js @@ -0,0 +1,15 @@ +//rule scheme : +module.exports = { + *summary() { + return 'The rule to replace request protocol'; + }, + + *beforeSendRequest(requestDetail) { + const newConfig = { + protocol: 'http', + requestOptions: requestDetail.requestOptions + }; + newConfig.requestOptions.port = 3000; + return newConfig; + } +}; diff --git a/test/spec_rule/rule/rule_replace_response_data.js b/test/spec_rule/rule/rule_replace_response_data.js new file mode 100644 index 0000000..fc79191 --- /dev/null +++ b/test/spec_rule/rule/rule_replace_response_data.js @@ -0,0 +1,17 @@ +//rule scheme : replace the reponse data + +module.exports = { + + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('/test/normal_request1') > -1) { + const newResponse = responseDetail.response; + + const newDataStr = newResponse.body.toString() + '_hello_world!'; + newResponse.body = newDataStr; + + return { + response: newResponse + }; + } + } +}; diff --git a/test/spec_rule/rule/rule_replace_response_header.js b/test/spec_rule/rule/rule_replace_response_header.js new file mode 100644 index 0000000..5a39c87 --- /dev/null +++ b/test/spec_rule/rule/rule_replace_response_header.js @@ -0,0 +1,30 @@ +//rule scheme : remove the cache headers in response headers +module.exports = { + *summary() { + return 'The rule to remove the cache headers in response'; + }, + + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('/test/normal_request1') >= 0) { + const newResponse = responseDetail.response; + newResponse.header.replacedheaderkey = 'replacedHeader_value_in_rule'; + + return { + response: newResponse + }; + } + } + + // replaceResponseHeader(req, res, header) { + // const d = Q.defer(); + + // header = Object.assign({}, header); + // if (req.url.indexOf('test/normal_request1') > -1) { + // header.replacedheaderkey = 'replacedHeader_value_in_rule'; + // } + + // d.resolve(header); + + // return d.promise; + // } +}; diff --git a/test/spec_rule/rule/rule_replace_response_status_code.js b/test/spec_rule/rule/rule_replace_response_status_code.js new file mode 100644 index 0000000..31eac12 --- /dev/null +++ b/test/spec_rule/rule/rule_replace_response_status_code.js @@ -0,0 +1,20 @@ +//replace all the images with local one +module.exports = { + + *summary() { + return 'replace the response status code.'; + }, + + *beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url.indexOf('/test/normal_request1') >= 0) { + const newResponse = responseDetail.response; + newResponse.statusCode = 302; + newResponse.header.location = 'www.taobao.com'; + + return { + response: newResponse + }; + } + }, +}; + diff --git a/test/spec_rule/rule/rule_should_intercept_https_req.js b/test/spec_rule/rule/rule_should_intercept_https_req.js new file mode 100644 index 0000000..a73ccda --- /dev/null +++ b/test/spec_rule/rule/rule_should_intercept_https_req.js @@ -0,0 +1,19 @@ +//rule scheme : + +module.exports = { + *summary() { + return 'Rule to intercept https request'; + }, + + *beforeSendResponse(requestDetail, responseDetail) { + const newResponse = responseDetail.response; + newResponse.body = newResponse.body.toString() + '_hello_world'; + return { + response: newResponse + }; + }, + + *beforeDealHttpsRequest(requestDetail) { + return requestDetail.host.indexOf('localhost:3001') > -1; + } +}; diff --git a/test/spec_rule/rule/rule_should_use_local_response.js b/test/spec_rule/rule/rule_should_use_local_response.js new file mode 100644 index 0000000..9056a69 --- /dev/null +++ b/test/spec_rule/rule/rule_should_use_local_response.js @@ -0,0 +1,21 @@ +/* +* Rule defination for shouldUseLocalResponse +* +*/ +const localData = 'handled_in_local_response'; + +module.exports = { + *beforeSendRequest(requestDetail) { + if (requestDetail.url.indexOf('uselocal') > -1) { + return { + response: { + statusCode: 200, + header: { + 'Via-Proxy-Local': 'true' + }, + body: localData + } + }; + } + } +}; diff --git a/test/spec_rule/rule_deal_error_spec.js b/test/spec_rule/rule_deal_error_spec.js new file mode 100644 index 0000000..d208b9f --- /dev/null +++ b/test/spec_rule/rule_deal_error_spec.js @@ -0,0 +1,154 @@ +/* +* test for rule replaceResponseStatus rule +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet } = require('../util/HttpUtil.js'); + +const { printLog } = require('../util/CommonUtil.js'); + +let errorInRule = null; +const ruleNotDealError = { + *onError(requestDetail, error) { + errorInRule = error; + } +}; + +let errorInConnect = null; +const ruleDealConnectError = { + *onConnectError(requestDetail, error) { + errorInConnect = error; + } +}; + +const ERROR_PAGE_IN_RULE = 'this is my error page'; +const ruleReturnAnErrorPage = { + *onError(requestDetail, error) { + return { + response: { + statusCode: '200', + header: {}, + body: ERROR_PAGE_IN_RULE, + } + }; + } +}; + +testWrapper('http'); +testWrapper('https'); +testHttpsConnect(); + +function testWrapper(protocol) { + describe('Rule should get an error in :' + protocol, () => { + let proxyServer; + // let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_deal_error_spec'); + errorInRule = null; + + proxyServer = ProxyServerUtil.proxyServerWithRule(ruleNotDealError); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + // serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_deal_error_spec'); + }); + + it('Should get a request error', done => { + const url = protocol + '://not_exist_url.anyproxy.io'; + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(500); + expect(proxyRes.headers['proxy-error']).toEqual('true'); + expect(errorInRule).not.toBe(null); + done(); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + }); + + describe('Rule should return a custom error page in :' + protocol, () => { + let proxyServer; + // let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_deal_error_custom_error_page'); + + proxyServer = ProxyServerUtil.proxyServerWithRule(ruleReturnAnErrorPage); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + // serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_deal_error_custom_error_page'); + }); + + it('Should get a request error', done => { + const url = protocol + '://not_exist_url.anyproxy.io'; + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.headers['proxy-error']).toBe(undefined); + expect(proxyRes.body).toEqual(ERROR_PAGE_IN_RULE); + done(); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + }); +} + +function testHttpsConnect() { + describe('Rule should get a connect error', () => { + let proxyServer; + // let serverInstance; + + beforeAll((done) => { + errorInConnect = null; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_deal_error_custom_error_page'); + + proxyServer = ProxyServerUtil.proxyServerWithRule(ruleDealConnectError, { + forceProxyHttps: false + }); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + proxyServer && proxyServer.close(); + printLog('Close server for rule_deal_error_custom_error_page'); + }); + + it('Should get a request error', done => { + const url = 'https://not_exist_url.anyproxy.io'; + proxyGet(url) + .then(proxyRes => { + done.fail('should throw an error when requesting'); + }) + .catch(error => { + expect(errorInConnect).not.toBe(null); + done(); + }); + }); + }); +} diff --git a/test/spec_rule/rule_replace_request_data_spec.js b/test/spec_rule/rule_replace_request_data_spec.js new file mode 100644 index 0000000..5edc2e6 --- /dev/null +++ b/test/spec_rule/rule_replace_request_data_spec.js @@ -0,0 +1,120 @@ +/* +* test for rule replaceRequestData rule +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyPost, generateUrl, directPost, isViaProxy } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog, isObjectEqual } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_replace_request_data.js'); + +testWrapper('http'); +testWrapper('https'); + +function testWrapper(protocol) { + describe('Rule replaceRequestData should be working in :' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_replace_request_data_spec'); + + serverInstance = new Server(); + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_replace_request_data_spec'); + }); + + it('Should replace the request data in proxy if the assertion is true', done => { + const url = generateUrl(protocol, '/test/getuser'); + const userName = 'username_test'; + const param = { + username: userName + }; + + proxyPost(url, param) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.body).toEqual('body_post_getuser'); + const proxyRequest = serverInstance.getProxyRequestRecord(url); + const proxyReqBodyObj = JSON.parse(proxyRequest.body.toString()); + + expect(isViaProxy(proxyRequest)).toBe(true); + + expect(proxyReqBodyObj.username).toEqual(userName); + expect(proxyReqBodyObj.authToken).toEqual('auth_token_inrule'); + + directPost(url, param) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual(proxyRes.body); + + + const directRequest = serverInstance.getRequestRecord(url); + const directReqBodyObj = JSON.parse(directRequest.body.toString()); + expect(isViaProxy(directRequest)).toBe(false); + + expect(directReqBodyObj.username).toEqual(userName); + expect(directReqBodyObj.authToken).toBeUndefined(); + done(); + }) + .catch(error => { + console.error('error happened in direct post: ', error); + done.fail('error happened in direct post'); + }); + }) + .catch(error => { + console.error('error happened in proxy post: ', error); + console.error(error); + console.error(error.stack); + done.fail('error happened in proxy post'); + }); + }); + + it('Should not replace the request data in proxy if the assertion is false', done => { + const url = generateUrl(protocol, '/test/normal_post_request1'); + const userName = 'normal_username_test'; + + const param = { + username: userName + }; + + proxyPost(url, param) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.body).toEqual('body_normal_post_request1'); + const proxyReqRecord = serverInstance.getProxyRequestRecord(url); + const proxyReqBody = JSON.parse(proxyReqRecord.body); + expect(isObjectEqual(proxyReqBody, param, url)).toBe(true); + + directPost(url, param) + .then(directRes => { + expect(directRes.statusCode).toEqual(proxyRes.statusCode); + expect(directRes.body).toEqual(proxyRes.body); + const directReqRecord = serverInstance.getRequestRecord(url); + const directReqBody = JSON.parse(directReqRecord.body); + expect(isObjectEqual(directReqBody, proxyReqBody, url)).toBe(true); + done(); + }) + .catch(error => { + console.error('error happened in direct post:', error); + done.fail('error happened in direct post'); + }); + }) + .catch(error => { + console.error('error happened in proxy post:', error); + done.fail('error happened in proxy post'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_replace_request_option_spec.js b/test/spec_rule/rule_replace_request_option_spec.js new file mode 100644 index 0000000..dbcaae0 --- /dev/null +++ b/test/spec_rule/rule_replace_request_option_spec.js @@ -0,0 +1,89 @@ +/* +* test for rule replaceOption rule +* +*/ + +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_replace_request_option.js'); + +testWrapper('http'); +testWrapper('https'); + +function testWrapper(protocol) { + describe('Rule replaceRequestOption should be working in :' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll(done => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_replace_request_option_spec'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_replace_request_option_spec'); + }); + + it('Should replace request option if the assertion is true', done => { + const url = generateUrl(protocol, '/test/should_replace_option'); + const replacedUrl = generateUrl(protocol, '/test/new_replace_option'); + + const token = 'replacedOption' + Date.now(); + const directToken = 'notRepacedOption' + Date.now(); + proxyGet(url, {}, { token }) + .then(proxyRes => { + directGet(url, {}, { token: directToken }) + .then(directRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.body).toEqual('the_new_replaced_option_page_content'); + + const proxyRequestObj = serverInstance.getProxyRequestRecord(replacedUrl); + expect(proxyRequestObj.headers.token).toEqual(token); + expect(proxyRequestObj.headers['via-proxy']).toEqual('true'); + + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual('the_request_that_has_not_be_replaced'); + + const directRequestObj = serverInstance.getRequestRecord(url); + expect(directRequestObj.headers.token).toEqual(directToken); + + done(); + }).catch(error => { + console.error('error happened in direct get for replaceOption rule: ', error); + done.fail('error happened when direct test replaceOption rule '); + }); + }).catch(error => { + console.error('error happened in proxy get for replaceOption rule: ', error); + done.fail('error happened when proxy test replaceOption rule '); + }); + }); + + it('Should not replace request option if the assertion is false', done => { + const url = generateUrl(protocol, '/test/should_not_replace_option'); + + proxyGet(url) + .then(proxyRes => { + done(); + }, error => { + console.error('error happened in proxy get:', error); + done.fail('error happened in proxy get'); + }).catch(error => { + console.error('error happend in syntax:', error); + done.fail('error happend in syntax'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_replace_request_protocol_spec.js b/test/spec_rule/rule_replace_request_protocol_spec.js new file mode 100644 index 0000000..9a41fec --- /dev/null +++ b/test/spec_rule/rule_replace_request_protocol_spec.js @@ -0,0 +1,112 @@ +/* +* test for rule replaceOption rule +* +*/ + +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_replace_request_protocol.js'); + +testWrapper(); + +function testWrapper() { + describe('Rule replaceRequestProtocol should be working', () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_replace_request_protocol'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_replace_request_protocol'); + }); + + it('Should replace request protocol in PROXY https request', done => { + const url = generateUrl('https', '/test/normal_request1'); + const httpUrl = url.replace('https', 'http'); + const token = 'proxy_request1_token_' + Date.now(); + proxyGet(url, {}, { token }) + .then(proxyRes => { + expect(proxyRes.body).toEqual('body_normal_request1'); + + // there should be no https url be requested in proxy, it should be http request + expect(serverInstance.getProxyRequestRecord(url)).toBe(null); + const httpRecord = serverInstance.getProxyRequestRecord(httpUrl); + expect(httpRecord.headers.token).toEqual(token); + expect(httpRecord.headers['via-proxy']).toEqual('true'); + done(); + }) + .catch(error => { + console.error('Error happened in proxy the https request: ', error); + done.fail('error happened in proxy the https request'); + }); + }); + + it('Should not replace protocol in PROXY http request', done => { + const url = generateUrl('http', '/test/normal_request2'); + const token = 'proxy_request2_token_' + Date.now(); + proxyGet(url, {}, { token }) + .then(proxyRes => { + expect(proxyRes.body).toEqual('body_normal_request2'); + const requestRecord = serverInstance.getProxyRequestRecord(url); + expect(requestRecord).not.toBe(null); + expect(requestRecord.headers.token).toEqual(token); + expect(requestRecord.headers['via-proxy']).toEqual('true'); + done(); + }) + .catch(error => { + console.error('error happened in proxy the http request: ', error); + done.fail('error happend in proxy the http request'); + }); + }); + + it('Should the direct request still be working with https', done => { + const url = generateUrl('https', '/test/normal_request1'); + const token = 'direct_request1_token_' + Date.now(); + directGet(url, {}, { token }) + .then(directRes => { + expect(directRes.body).toEqual('body_normal_request1'); + const requestRecord = serverInstance.getRequestRecord(url); + expect(requestRecord.headers.token).toEqual(token); + expect(requestRecord.headers['via-proxy']).toBeUndefined(); + done(); + }) + .catch(error => { + console.error('error happened in direct https get:', error); + done.fail('error happened in direct https get'); + }); + }); + + it('Should the direct request still be working with http', done => { + const url = generateUrl('http', '/test/normal_request2'); + const token = 'direct_request1_token_' + Date.now(); + directGet(url, {}, { token }) + .then(directRes => { + expect(directRes.body).toEqual('body_normal_request2'); + const requestRecord = serverInstance.getRequestRecord(url); + expect(requestRecord.headers.token).toEqual(token); + expect(requestRecord.headers['via-proxy']).toBeUndefined(); + done(); + }) + .catch(error => { + console.error('error happened in direct http get:', error); + done.fail('error happened in direct http get'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_replace_response_data_spec.js b/test/spec_rule/rule_replace_response_data_spec.js new file mode 100644 index 0000000..d3c507c --- /dev/null +++ b/test/spec_rule/rule_replace_response_data_spec.js @@ -0,0 +1,87 @@ +/* +* test for rule replaceResponseData rule +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_replace_response_data.js'); + +testWrapper('http'); +testWrapper('https'); + +function testWrapper(protocol) { + describe('Rule replaceResponseData should be working in :' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_replace_response_data'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_replace_response_data'); + }); + + it('Should replace the header in proxy if assertion is true', done => { + const url = generateUrl(protocol, '/test/normal_request1'); + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.body).toEqual('body_normal_request1_hello_world!'); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual('body_normal_request1'); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + + it('Should not replace the header in proxy if assertion is false', done => { + const url = generateUrl(protocol, '/test/normal_request2'); + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.body).toEqual('body_normal_request2'); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual(proxyRes.body); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_replace_response_header_spec.js b/test/spec_rule/rule_replace_response_header_spec.js new file mode 100644 index 0000000..6a21599 --- /dev/null +++ b/test/spec_rule/rule_replace_response_header_spec.js @@ -0,0 +1,91 @@ +/* +* test for rule replaceResponseHeader rule +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_replace_response_header.js'); + +testWrapper('http'); +testWrapper('https'); + +function testWrapper(protocol) { + describe('Rule replaceResponseHeader should be working in :' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_replace_response_header_spec'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_replace_response_header_spec'); + }); + + it('Should replace the header in proxy if assertion is true', done => { + const url = generateUrl(protocol, '/test/normal_request1'); + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.headers.replacedheaderkey).toEqual('replacedHeader_value_in_rule'); + expect(proxyRes.body).toEqual('body_normal_request1'); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.headers.replacedheaderkey).toBeUndefined(); + expect(directRes.body).toEqual(proxyRes.body); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + + it('Should not replace the header in proxy if the assertion is false', done => { + const url = generateUrl(protocol, '/test/normal_request2'); + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.headers.replacedheaderkey).toBeUndefined(); + expect(proxyRes.body).toEqual('body_normal_request2'); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.headers.replacedheaderkey).toBeUndefined(); + expect(directRes.body).toEqual(proxyRes.body); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_replace_response_status_code_spec.js b/test/spec_rule/rule_replace_response_status_code_spec.js new file mode 100644 index 0000000..3e7448d --- /dev/null +++ b/test/spec_rule/rule_replace_response_status_code_spec.js @@ -0,0 +1,87 @@ +/* +* test for rule replaceResponseStatus rule +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_replace_response_status_code.js'); + +testWrapper('http'); +testWrapper('https'); + +function testWrapper(protocol) { + describe('Rule replaceResponseStatus should be working in :' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_replace_response_status_code'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_replace_response_status_code'); + }); + + it('Should replace the status code in proxy if assertion is true', done => { + const url = generateUrl(protocol, '/test/normal_request1'); + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(302); + expect(proxyRes.headers.location).toEqual('www.taobao.com'); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual('body_normal_request1'); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + + it('Should not replace the status code in proxy if assertion is false', done => { + const url = generateUrl(protocol, '/test/normal_request2'); + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + expect(proxyRes.body).toEqual('body_normal_request2'); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body).toEqual('body_normal_request2'); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_should_intercept_https_req_spec.js b/test/spec_rule/rule_should_intercept_https_req_spec.js new file mode 100644 index 0000000..0f256c0 --- /dev/null +++ b/test/spec_rule/rule_should_intercept_https_req_spec.js @@ -0,0 +1,87 @@ +/* +* test for rule shouldInterceptHttpsReq rule +* +*/ +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, directGet } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_should_intercept_https_req.js'); + +testWrapper(); + +function testWrapper() { + describe('Rule shouldInterceptHttpsReq should be working', () => { + let proxyServer; + let serverInstance; + + beforeAll(done => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for test_rule_should_intercept_https_req_spec'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule, { + forceProxyHttps: false + }); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for test_rule_should_intercept_https_req_spec'); + }); + + it('Should replace the header in proxy if assertion is true', done => { + const url = 'https://localhost:3001/test'; + + proxyGet(url) + .then(proxyRes => { + directGet(url) + .then(directRes => { + expect(proxyRes.statusCode).toEqual(200); + + expect(directRes.statusCode).toEqual(200); + expect(directRes.body + '_hello_world').toEqual(proxyRes.body); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + + it('Should not replace the header in proxy if assertion is false', done => { + const url = 'https://localhost:3002/test'; + proxyGet(url) + .then(proxyRes => { + expect(proxyRes.statusCode).toEqual(200); + + directGet(url) + .then(directRes => { + expect(directRes.statusCode).toEqual(200); + expect(directRes.body.replace(/\s/g, '')).toEqual(proxyRes.body.replace(/\s/g, '')); + done(); + }) + .catch(error => { + console.error('error happened in direct get: ', error); + done.fail('error happened in direct get'); + }); + }) + .catch(error => { + console.error('error happened in proxy get: ', error); + done.fail('error happened in proxy get'); + }); + }); + }); +} diff --git a/test/spec_rule/rule_should_use_local_response_spec.js b/test/spec_rule/rule_should_use_local_response_spec.js new file mode 100644 index 0000000..74f4bc4 --- /dev/null +++ b/test/spec_rule/rule_should_use_local_response_spec.js @@ -0,0 +1,55 @@ +/* +* test for rule shouldUseLocal +* +*/ + +const ProxyServerUtil = require('../util/ProxyServerUtil.js'); +const { proxyGet, generateUrl } = require('../util/HttpUtil.js'); +const Server = require('../server/server.js'); +const { printLog } = require('../util/CommonUtil.js'); + +const rule = require('./rule/rule_should_use_local_response.js'); + +const expectedLocalBody = 'handled_in_local_response'; + +testWrapper('http'); +testWrapper('https'); + +function testWrapper(protocol) { + describe('Rule shouldUseLocalResponse should be working in :' + protocol, () => { + let proxyServer; + let serverInstance; + + beforeAll((done) => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000; + printLog('Start server for rule_shouldUseLocalResponse_spec'); + + serverInstance = new Server(); + + proxyServer = ProxyServerUtil.proxyServerWithRule(rule); + + setTimeout(() => { + done(); + }, 2000); + }); + + afterAll(() => { + serverInstance && serverInstance.close(); + proxyServer && proxyServer.close(); + printLog('Close server for rule_shouldUseLocalResponse_spec'); + }); + + it('Should use local response if the assertion is true', done => { + const url = generateUrl(protocol, '/test/uselocal'); + proxyGet(url, {}) + .then(res => { + expect(res.body).toEqual(expectedLocalBody); + expect(res.headers['via-proxy-local']).toEqual('true'); + done(); + }).catch((error) => { + console.log('error happened in proxy get for shouldUseLocal: ', error); + done.fail('error happened when test shouldUseLocal rule'); + }); + }); + }); +} diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 8e300e9..0000000 --- a/test/test.js +++ /dev/null @@ -1,60 +0,0 @@ -var proxy = require("../proxy.js"), - proxyTester = require("proxy-eval"), - WebSocket = require("ws"); - -//start a new proxy at port 8995, with websocket port 8996 -var SOCKET_PORT = 8996, - PROXY_PORT = 8995; - -new proxy.proxyServer({ - type :"http", - port :PROXY_PORT, - socketPort :SOCKET_PORT, - silent :true -}); - - -exports.avalibility = function(test){ - test.expect(2); - var updateCount = 0; - - //test web socket - setTimeout(function(){ - var ws = new WebSocket('ws://127.0.0.1:' + SOCKET_PORT , { - protocolVersion: 8 - }); - - ws.on('open', function open(){}); - ws.on('close', function close(){}); - ws.on('message', function message(data, flags) { - try{ - var jsonData = JSON.parse(data); - jsonData.type == "update" && ++updateCount; - }catch(e){} - }); - setTimeout(function(){ - test.ok(updateCount >= 4,"web socket message count of type 'update' "); - test.done(); - setTimeout(function(){ - process.exit(); - },1000); - },10*1000); - - },1000); - - //test the basic availibility of proxy server - setTimeout(function(){ - proxyTester.test({proxy : 'http://127.0.0.1:8995',reqTimeout:4500} ,function(results){ - var successCount = 0; - results.map(function(item){ - item.success && ++successCount; - }); - - var ifPassed = (true || results.length == successCount); - if(!ifPassed){ - proxyTester.printResult(results); - } - test.ok(ifPassed, "availibility test failed"); - }); - },1000); -}; diff --git a/test/test.sh b/test/test.sh deleted file mode 100755 index b752548..0000000 --- a/test/test.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -echo "Begin to run the test suites, JASMINE is required.\n" -echo "Removing test temp directory before running" -rm -rf ./test/temp/* -echo "Removing done, test cases now running" -node -v -jasmine JASMINE_CONFIG_PATH=./jasmine.json diff --git a/test/test_rules/shouldUseLocalResponseRule.js b/test/test_rules/shouldUseLocalResponseRule.js deleted file mode 100644 index ac32bb1..0000000 --- a/test/test_rules/shouldUseLocalResponseRule.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -* Rule defination for shouldUseLocalResponse -* -*/ - -const dealLocalBody = 'handled_in_local_response'; - -module.exports = { - shouldUseLocalResponse: function (req, reqBody) { - return req.url.indexOf('uselocal') > -1; - }, - shouldInterceptHttpsReq: function () { - return true; - }, - dealLocalResponse: function (req, reqBody, callback) { - const header = { - 'Via-Proxy-Local': 'true' - }; - - callback(200, header, dealLocalBody); - } -}; diff --git a/test/util/CommonUtil.js b/test/util/CommonUtil.js index 430f138..75a3751 100644 --- a/test/util/CommonUtil.js +++ b/test/util/CommonUtil.js @@ -2,87 +2,117 @@ * * The utility class for test */ -const zlib = require('zlib'); const color = require('colorful'); +function _isDeepEqual(source, target) { + // if the objects are Array + if (source.constructor === Array && target.constructor === Array) { + if (source.length !== target.length) { + return false; + } + + let _isEqual = true; + for (let i = 0; i < source.length; i++) { + if (!_isDeepEqual(source[i], target[i])) { + _isEqual = false; + break; + } + } + + return _isEqual; + } + + // if the source and target are just object + if (typeof source === 'object' && typeof target === 'object') { + let _isEqual = true; + for (const key in source) { + if (!_isDeepEqual(source[key], target[key])) { + _isEqual = false; + break; + } + } + + return _isEqual; + } + + return source === target; +} /* * Compare whether tow object are equal */ -function isObjectEqual (source = {} , target = {}, url = '') { - source = Object.assign({}, source); - target = Object.assign({}, target); - let isEqual = true; +function isObjectEqual(source = {}, target = {}, url = '') { + source = Object.assign({}, source); + target = Object.assign({}, target); + let isEqual = true; - for(const key in source) { - isEqual = isEqual && source[key] === target[key]; + for (const key in source) { + isEqual = isEqual && _isDeepEqual(source[key], target[key]); - if (!isEqual) { - console.info('source object :', source); - console.info('target object :', target); - printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}", - target is "${target[key]}" the url is ${url}`); - break; - } - - delete source[key]; - delete target[key]; + if (!isEqual) { + console.info('source object :', source); + console.info('target object :', target); + printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}", + target is "${target[key]}" the url is ${url}`); + break; } - for(const key in target) { - isEqual = isEqual && source[key] === target[key]; + delete source[key]; + delete target[key]; + } - if (!isEqual) { - console.info('source object :', source); - console.info('target object :', target); - printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}", - target is "${target[key]}" the url is ${url}`); - break; - } + for (const key in target) { + isEqual = isEqual && source[key] === target[key]; - delete source[key]; - delete target[key]; + if (!isEqual) { + console.info('source object :', source); + console.info('target object :', target); + printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}", + target is "${target[key]}" the url is ${url}`); + break; } - return isEqual; + delete source[key]; + delete target[key]; + } + + return isEqual; } /* * Compare the header between direct with proxy * Will exclude the header(s) which modified by proxy */ -function isCommonResHeaderEqual (directHeaders, proxyHeaders, requestUrl) { - directHeaders = Object.assign({}, directHeaders); - proxyHeaders = Object.assign({}, proxyHeaders); - let isEqual = true; - const mustEqualFileds = []; // the fileds that have to be equal, or the assert will be failed +function isCommonResHeaderEqual(directHeaders, proxyHeaders, requestUrl) { + directHeaders = Object.assign({}, directHeaders); + proxyHeaders = Object.assign({}, proxyHeaders); + let isEqual = true; + const mustEqualFileds = []; // the fileds that have to be equal, or the assert will be failed - if (!/gzip/i.test(directHeaders['content-encoding'])) { - // if the content is gzipped, proxy will unzip and remove the header - mustEqualFileds.push('content-encoding'); - mustEqualFileds.push('content-length'); + if (!/gzip/i.test(directHeaders['content-encoding'])) { + // if the content is gzipped, proxy will unzip and remove the header + mustEqualFileds.push('content-encoding'); + } + mustEqualFileds.push('content-type'); + mustEqualFileds.push('cache-control'); + mustEqualFileds.push('allow'); + + // ensure the required fileds are same + mustEqualFileds.forEach(filedName => { + isEqual = directHeaders[filedName] === proxyHeaders[filedName]; + delete directHeaders[filedName]; + delete proxyHeaders[filedName]; + }); + + // remained filed are good to be same, but are allowed to be different + // will warn out those different fileds + for (const key in directHeaders) { + if (!_isDeepEqual(directHeaders[key], proxyHeaders[key])) { + printWarn(`key "${key}" of two response headers are different in request "${requestUrl}" : + direct is: "${directHeaders[key]}", proxy is: "${proxyHeaders[key]}"`); } - mustEqualFileds.push('content-type'); - mustEqualFileds.push('cache-control'); - mustEqualFileds.push('allow'); + } - // ensure the required fileds are same - mustEqualFileds.forEach(filedName => { - isEqual = directHeaders[filedName] === proxyHeaders[filedName]; - delete directHeaders[filedName]; - delete proxyHeaders[filedName]; - }); - - // remained filed are good to be same, but are allowed to be different - // will warn out those different fileds - for (const key in directHeaders) { - if (directHeaders[key] !== proxyHeaders[key]) { - printWarn(`key "${key}" of two response headers are different in request "${requestUrl}" : - direct is: "${directHeaders[key]}", proxy is: "${proxyHeaders[key]}"`); - } - continue; - } - - return isEqual; + return isEqual; } /* @@ -90,64 +120,156 @@ function isCommonResHeaderEqual (directHeaders, proxyHeaders, requestUrl) { * */ function isCommonReqEqual(url, serverInstance) { - try{ - url = url.replace('https://', '').replace('http://', ''); // only the remained path is required - let isEqual = true; + try { + let isEqual = true; - const directReqObj = serverInstance.getRequestRecord(url); - const proxyReqObj = serverInstance.getProxyRequestRecord(url); + const directReqObj = serverInstance.getRequestRecord(url); + const proxyReqObj = serverInstance.getProxyRequestRecord(url); - // ensure the proxy header is correct - isEqual = isEqual && proxyReqObj.headers['via-proxy'] === 'true'; - delete proxyReqObj.headers['via-proxy']; + // ensure the proxy header is correct + isEqual = isEqual && proxyReqObj.headers['via-proxy'] === 'true'; + delete proxyReqObj.headers['via-proxy']; - // exclued accept-encoding from comparing, since the proxy will remove it before sending it out - delete directReqObj.headers['accept-encoding']; + // exclued accept-encoding from comparing, since the proxy will remove it before sending it out + delete directReqObj.headers['accept-encoding']; - // per undefined header, proxy will set it with 0, and an empty request body - if (typeof directReqObj.headers['content-length'] === 'undefined') { + // TODO: 我这里proxy出去的options里没有accept-encoding, 但node自己加上了。Why ? + // By 加里 2017.1.31 + delete proxyReqObj.headers['accept-encoding']; - directReqObj.headers['content-length'] = "0"; - } + directReqObj.headers['content-type'] = trimFormContentType(directReqObj.headers['content-type']); + proxyReqObj.headers['content-type'] = trimFormContentType(proxyReqObj.headers['content-type']); - directReqObj.headers['content-type'] = trimFormContentType(directReqObj.headers['content-type']); - proxyReqObj.headers['content-type'] = trimFormContentType(proxyReqObj.headers['content-type']); - - isEqual = isEqual && directReqObj.url === proxyReqObj.url; - isEqual = isEqual && isObjectEqual(directReqObj.headers, proxyReqObj.headers, url); - isEqual = isEqual && directReqObj.body === proxyReqObj.body; - return isEqual; - } catch (e) { - console.error(e); - } + // avoid compare content-length header via proxy + delete directReqObj.headers['content-length']; + delete proxyReqObj.headers['content-length']; + delete directReqObj.headers['transfer-encoding']; + delete proxyReqObj.headers['transfer-encoding']; + isEqual = isEqual && directReqObj.url === proxyReqObj.url; + isEqual = isEqual && isObjectEqual(directReqObj.headers, proxyReqObj.headers, url); + isEqual = isEqual && directReqObj.body === proxyReqObj.body; + return isEqual; + } catch (e) { + console.error(e); + } } /* * for multipart-form, the boundary will be different with each update, we trim it here */ -function trimFormContentType (contentType = '') { - return contentType.replace(/boundary.*/, ''); +function trimFormContentType(contentType = '') { + return contentType.replace(/boundary.*/, ''); } -function printLog (content) { - console.log(color.blue('==LOG==: ' + content)); +function printLog(content) { + console.log(color.blue('==LOG==: ' + content)); } function printWarn(content) { - console.log(color.magenta('==WARN==: ' + content)); + console.log(color.magenta('==WARN==: ' + content)); } function printError(content) { - console.log(color.red('==ERROR==: ' + content)); + console.log(color.red('==ERROR==: ' + content)); +} + +function printHilite(content) { + console.log(color.yellow('==LOG==: ' + content)); +} + +function parseUrlQuery(string = '') { + const parameterArray = string.split('&'); + const parsedObj = {}; + parameterArray.forEach((parameter) => { + // 获取等号的位置 + const indexOfEqual = parameter.indexOf('='); + const name = parameter.substr(0, indexOfEqual); + const value = parameter.substr(indexOfEqual + 1); + parsedObj[name] = value; + }); + return parsedObj; +} + +function stringSimilarity(a, b, precision = 2) { + let similarity = '0%'; + let isCongruent = false; + if (a && b) { + const targetLen = Math.max(a.length, b.length); + targetLen > 1000 ? + similarity = simHasH(a, b) : + similarity = LevenshteinSimilarity(a, b); + isCongruent = similarity === 100; + similarity = similarity.toFixed(precision) + '%'; + } + return { + isCongruent, + similarity + } +} + +/** +* simhash similarity +*/ +function simHasH(a, b) { + const simhash = require('node-simhash'); + return (simhash.compare(a, b) * 100); +} + +/** +* Levenshtein Distance +*/ +function LevenshteinSimilarity(a, b) { + let cost; + const maxLen = Math.max(a.length, b.length); + const minOfThree = (numa, numb, numc) => { + if (numa > numb) { + return numb > numc ? numc : numb; + } else { + return numa > numc ? numc : numa; + } + } + if (a.length === 0) cost = b.length; + if (b.length === 0) cost = a.length; + + if (a.length > b.length) { + const tmp = a; + a = b; + b = tmp; + } + + const row = []; + for (let i = 0; i <= a.length; i++) { + row[i] = i; + } + + for (let i = 1; i <= b.length; i++) { + let prev = i; + for (let j = 1; j <= a.length; j++) { + let val; + if (b.charAt(i - 1) === a.charAt(j - 1)) { + val = row[j - 1]; + } else { + val = minOfThree(row[j - 1] + 1, prev + 1, row[j] + 1); + } + row[j - 1] = prev; + prev = val; + } + row[a.length] = prev; + } + cost = row[a.length]; + return ((maxLen - cost) / maxLen * 100); } module.exports = { - isObjectEqual, - isCommonResHeaderEqual, - printLog, - printWarn, - printError, - isCommonReqEqual + isObjectEqual, + isCommonResHeaderEqual, + printLog, + printWarn, + printError, + printHilite, + isCommonReqEqual, + parseUrlQuery, + stringSimilarity }; diff --git a/test/util/HttpUtil.js b/test/util/HttpUtil.js index 4f308e6..fb3d570 100644 --- a/test/util/HttpUtil.js +++ b/test/util/HttpUtil.js @@ -1,17 +1,14 @@ +/* eslint prefer-arrow-callback: 0 */ /** * An util to make the request out * */ -const querystring = require('querystring'); -const http = require('http'); -const zlib = require('zlib'); -const Buffer = require('buffer').Buffer; const request = require('request'); const fs = require('fs'); const WebSocket = require('ws'); const HttpsProxyAgent = require('https-proxy-agent'); +const stream = require('stream'); -const DEFAULT_HOST = 'localhost'; const PROXY_HOST = 'http://localhost:8001'; const SOCKET_PROXY_HOST = 'http://localhost:8001'; @@ -20,243 +17,336 @@ const HTTP_SERVER_BASE = 'http://localhost:3000'; const HTTPS_SERVER_BASE = 'https://localhost:3001'; const WS_SERVER_BASE = 'ws://localhost:3000'; const WSS_SERVER_BASE = 'wss://localhost:3001'; +const DEFAULT_CHUNK_COLLECT_THRESHOLD = 20 * 1024 * 1024; // about 20 mb -const DEFAULT_PROXY_OPTIONS = { - port: 8001, // proxy的端口 - method: 'GET', - host: 'localhost' -}; - -const DEFAULT_OPTIONS = { - -}; - -function getHostFromUrl (url = '') { - const hostReg = /^(https{0,1}:\/\/)(\w+)/; - const match = url.match(hostReg); - - return match && match[2] ? match[2] : ''; +class commonStream extends stream.Readable { + constructor(config) { + super({ + highWaterMark: DEFAULT_CHUNK_COLLECT_THRESHOLD * 5 + }); + } + _read(size) {} } -function getPortFromUrl (url = '') { - const portReg = /^https{0,1}:\/\/\w+(:(\d+)){0,1}/; - const match = url.match(portReg); - let port = match && match[2] ? match[2] : ''; +function getHostFromUrl(url = '') { + const hostReg = /^(https{0,1}:\/\/)(\w+)/; + const match = url.match(hostReg); - if (!port) { - port = url.indexOf('https://') === 0 ? 443 : 80; - } - return port; + return match && match[2] ? match[2] : ''; +} + +function getPortFromUrl(url = '') { + const portReg = /^https{0,1}:\/\/\w+(:(\d+)){0,1}/; + const match = url.match(portReg); + let port = match && match[2] ? match[2] : ''; + + if (!port) { + port = url.indexOf('https://') === 0 ? 443 : 80; + } + return port; } /** * 获取url中的path */ -function getPathFromUrl (url = '') { - const pathReg = /^https{0,1}:\/\/\w+(:\d+){0,1}(.+)/; - const match = url.match(pathReg); - const path = match && match[3] ? match[2] : url; - return path; +function getPathFromUrl(url = '') { + const pathReg = /^https{0,1}:\/\/\w+(:\d+){0,1}(.+)/; + const match = url.match(pathReg); + const path = match && match[3] ? match[2] : url; + return path; } -function proxyRequest (method = 'GET', url, params, headers = {}) { - return doRequest(method, url, params, headers, true); +function proxyRequest(method = 'GET', url, params, headers = {}) { + return doRequest(method, url, params, headers, true); } /* * 直接请求到真实服务器,不经过代理服务器 * */ -function directRequest (method = 'GET', url, params, headers = {}) { - return doRequest(method, url, params, headers); +function directRequest(method = 'GET', url, params, headers = {}) { + return doRequest(method, url, params, headers); } -function directUpload (url, filepath, formParams = {}, headers = {}) { - return doUpload(url, 'POST', filepath, formParams, headers); +function directUpload(url, filepath, formParams = {}, headers = {}) { + return doUpload(url, 'POST', filepath, formParams, headers); } -function proxyUpload (url, filepath, formParams = {}, headers = {}) { - return doUpload(url, 'POST', filepath, formParams, headers, true); +function proxyUpload(url, filepath, formParams = {}, headers = {}) { + return doUpload(url, 'POST', filepath, formParams, headers, true); } -function directPutUpload (url, filepath, formParams = {}, headers = {}) { - return doUpload(url, 'PUT', filepath, formParams, headers); +function directPutUpload(url, filepath, formParams = {}, headers = {}) { + return doUpload(url, 'PUT', filepath, formParams, headers); } -function proxyPutUpload (url, filepath, headers = {}) { - return doUpload(url, 'PUT', filepath, headers, true); +function proxyPutUpload(url, filepath, headers = {}) { + return doUpload(url, 'PUT', filepath, headers, true); } -function doRequest (method = 'GET', url, params, headers = {}, isProxy) { - headers = Object.assign({}, headers); - const requestData = { - method: method, - form: params, - url: url, - headers: headers, - rejectUnauthorized: false - }; +/** + * @param params {String} json类型或file路径 + * {Object} key-value形式 + */ +function doRequest(method = 'GET', url, params, headers = {}, isProxy) { + headers = Object.assign({}, headers); - if (isProxy) { - requestData.proxy = PROXY_HOST; - requestData.headers['via-proxy'] = 'true'; + let reqStream = new commonStream(); + const requestData = { + headers, + followRedirect: false, + rejectUnauthorized: false + }; + + if (isProxy) { + requestData.proxy = PROXY_HOST; + requestData.headers['via-proxy'] = 'true'; + } + + const streamReq = (resolve, reject) => { + requestData.headers['content-type'] = 'text/plain'; //otherwise, koa-body could not recognize + if (typeof params === 'string') { + fs.existsSync(params) ? + reqStream = fs.createReadStream(params) : + reqStream.push(params); + } else if (typeof params === 'object') { + reqStream.push(JSON.stringify(params)); } - - const requestTask = new Promise((resolve, reject) => { - request( - requestData, - function (error, response, body) { - if (error) { - reject(error); - return; - } - resolve(response); - } - ); - }); - return requestTask; + reqStream.push(null); + reqStream.pipe(request[method.toLowerCase()]( + url, + requestData, + (error, response, body) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + )) + } + const commonReq = (resolve, reject) => { + requestData.url = url; + requestData.method = method; + requestData.qs = params; + request( + requestData, + (error, response, body) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + ); + } + const requestTask = new Promise((resolve, reject) => { + if (method === 'POST' || method === 'PUT') { + streamReq(resolve, reject); + } else { + commonReq(resolve, reject); + } + }); + return requestTask; } -function doUpload (url, method, filepath, formParams, headers = {}, isProxy) { - let formData = { - file: fs.createReadStream(filepath) - }; - formData = Object.assign({}, formData, formParams); - headers = Object.assign({}, headers); +function doUpload(url, method, filepath, formParams, headers = {}, isProxy) { + let formData = { + file: fs.createReadStream(filepath) + }; - const requestData = { - formData: formData, - url: url, - method: method, - headers: headers, - json: true, - rejectUnauthorized: false - }; + formData = Object.assign({}, formData, formParams); + headers = Object.assign({}, headers); - if (isProxy) { - requestData.proxy = PROXY_HOST; - requestData.headers['via-proxy'] = 'true'; - } - const requestTask = new Promise((resolve, reject) => { - request( - requestData, - function (error, response, body) { - if (error) { - reject(error); - return; - } - resolve(response); - } - ); - }); - return requestTask; + const requestData = { + formData, + url, + method, + headers, + json: true, + rejectUnauthorized: false + }; + + if (isProxy) { + requestData.proxy = PROXY_HOST; + requestData.headers['via-proxy'] = 'true'; + } + const requestTask = new Promise((resolve, reject) => { + request( + requestData, + (error, response, body) => { + if (error) { + reject(error); + return; + } + resolve(response); + } + ); + }); + return requestTask; } function doWebSocket(url, isProxy) { - let ws; - if (isProxy) { - const agent = new HttpsProxyAgent(SOCKET_PROXY_HOST); - ws = new WebSocket(url, { - agent: agent, - rejectUnauthorized: false - }); - } else { - ws = new WebSocket(url, { - rejectUnauthorized: false - }); - } + let ws; + if (isProxy) { + const agent = new HttpsProxyAgent(SOCKET_PROXY_HOST); + ws = new WebSocket(url, { + agent, + rejectUnauthorized: false + }); + } else { + ws = new WebSocket(url, { + rejectUnauthorized: false + }); + } - return ws; + return ws; } -function proxyGet (url, params, headers = {}) { - return proxyRequest('GET', url, params, headers); +function proxyGet(url, params, headers = {}) { + return proxyRequest('GET', url, params, headers); } -function proxyPost (url, params, headers = {}) { - return proxyRequest('POST', url, params, headers); +function proxyPost(url, params, headers = {}) { + return proxyRequest('POST', url, params, headers); } -function proxyPut (url, params, headers = {}) { - return proxyRequest('PUT', url, params, headers); +function proxyPut(url, params, headers = {}) { + return proxyRequest('PUT', url, params, headers); } -function proxyDelete (url, params, headers = {}) { - return proxyRequest('DELETE', url, params, headers); +function proxyDelete(url, params, headers = {}) { + return proxyRequest('DELETE', url, params, headers); } function proxyHead(url, headers = {}) { - return proxyRequest('HEAD', url, {}, headers); + return proxyRequest('HEAD', url, {}, headers); } function proxyOptions(url, headers = {}) { - return proxyRequest('OPTIONS', url, {}, headers); + return proxyRequest('OPTIONS', url, {}, headers); } -function directGet (url, params, headers = {}) { - return directRequest('GET', url, params, headers); +function directGet(url, params, headers = {}) { + return directRequest('GET', url, params, headers); } -function directPost (url, params, headers = {}) { - return directRequest('POST', url, params, headers); +function directPost(url, params, headers = {}) { + return directRequest('POST', url, params, headers); } -function directPut (url, params, headers = {}) { - return directRequest('PUT', url, params, headers); +function directPut(url, params, headers = {}) { + return directRequest('PUT', url, params, headers); } -function directDelete (url, params, headers = {}) { - return directRequest('DELETE', url, params, headers); +function directDelete(url, params, headers = {}) { + return directRequest('DELETE', url, params, headers); } -function directHead (url, headers = {}) { - return directRequest('HEAD', url, {} , headers); +function directHead(url, headers = {}) { + return directRequest('HEAD', url, {}, headers); } -function directOptions (url, headers ={}) { - return directRequest('OPTIONS', url, {}, headers); +function directOptions(url, headers = {}) { + return directRequest('OPTIONS', url, {}, headers); } -function proxyWs (url) { - return doWebSocket(url, true); +function proxyWs(url) { + return doWebSocket(url, true); } -function directWs (url) { - return doWebSocket(url); +function directWs(url) { + return doWebSocket(url); } /** * generate the final url based on protocol and path * */ -function generateUrl (protocol, urlPath) { - return protocol === 'http' ? HTTP_SERVER_BASE + urlPath : HTTPS_SERVER_BASE + urlPath; +function generateUrl(protocol, urlPath) { + return protocol === 'http' ? HTTP_SERVER_BASE + urlPath : HTTPS_SERVER_BASE + urlPath; } -function generateWsUrl (protocol, urlPath) { - return protocol === 'wss' ? WSS_SERVER_BASE + urlPath : WS_SERVER_BASE + urlPath; +function generateWsUrl(protocol, urlPath) { + return protocol === 'wss' ? WSS_SERVER_BASE + urlPath : WS_SERVER_BASE + urlPath; } +/* +* verify if the request data is a valid proxy request, by checking specified header +*/ +function isViaProxy(req) { + return req.headers['via-proxy'] === 'true'; +} + +/* +* check if url is supported by request moudle +*/ +function isSupportedProtocol(requestPath) { + return requestPath.indexOf('http://') === 0 || requestPath.indexOf('https://') === 0; +} + +/* +* collect all request data in one url +*/ +function getRequestListFromPage(pageUrl, cb) { + let _ph; + let _page; + let _outObj; + const phantom = require('phantom'); + console.log(`collecting requests from ${pageUrl}...`); + return phantom.create().then(ph => { + _ph = ph; + return _ph.createPage(); + }).then(page => { + _page = page; + _outObj = _ph.createOutObject(); + _outObj.urls = []; + page.property('onResourceRequested', function (requestData, networkRequest, out) { + out.urls.push(requestData); + }, _outObj); + return _page.open(pageUrl); + }) + .then(status => _outObj.property('urls')) + .then(urls => { + _page.close(); + _ph.exit(); + return urls; + }) + .catch((err) => { + console.log(`failed to collecting requests from ${pageUrl}`); + console.log(err); + }); +} + + module.exports = { - proxyGet, - proxyPost, - directGet, - directPost, - directUpload, - proxyUpload, - generateUrl, - proxyWs, - directWs, - generateWsUrl, - directPut, - proxyPut, - directDelete, - proxyDelete, - directHead, - proxyHead, - directOptions, - proxyOptions, - directPutUpload, - proxyPutUpload -}; \ No newline at end of file + getHostFromUrl, + getPathFromUrl, + getPortFromUrl, + proxyGet, + proxyPost, + directGet, + directPost, + directUpload, + proxyUpload, + generateUrl, + proxyWs, + directWs, + generateWsUrl, + directPut, + proxyPut, + directDelete, + proxyDelete, + directHead, + proxyHead, + directOptions, + proxyOptions, + directPutUpload, + proxyPutUpload, + isViaProxy, + getRequestListFromPage, + directRequest, + proxyRequest, + isSupportedProtocol +}; diff --git a/test/util/ProxyServerUtil.js b/test/util/ProxyServerUtil.js index 0a333ec..e3ce885 100644 --- a/test/util/ProxyServerUtil.js +++ b/test/util/ProxyServerUtil.js @@ -3,33 +3,36 @@ * */ -let proxy = require('../../proxy.js'); const util = require('../../lib/util.js'); const DEFAULT_OPTIONS = { - type: "http", - port: 8001, - hostname: "localhost", - dbFile: null, // optional, save request data to a specified file, will use in-memory db if not specified + type: 'http', + port: 8001, + webInterface: { + enable: true, webPort: 8002, // optional, port for web interface - socketPort: 8003, // optional, internal port for web socket, replace this when it is conflict with your own service - throttle: 10000, // optional, speed limit in kb/s - disableWebInterface: false, //optional, set it when you don't want to use the web interface - setAsGlobalProxy: false, //set anyproxy as your system proxy - interceptHttps: true, // intercept https as well - silent: false //optional, do not print anything into terminal. do not set it when you are still debugging. + wsPort: 8003, // optional, internal port for web socket + }, + throttle: 10000, // optional, speed limit in kb/s + forceProxyHttps: true, // intercept https as well + dangerouslyIgnoreUnauthorized: true, + silent: false //optional, do not print anything into terminal. do not set it when you are still debugging. }; /** * * @return An instance of proxy, could be closed by calling `instance.close()` */ -function defaultProxyServer () { - proxy = util.freshRequire('../proxy.js'); +function defaultProxyServer() { + const AnyProxy = util.freshRequire('../proxy.js'); - const options = util.merge({}, DEFAULT_OPTIONS); - options.rule = util.freshRequire('./rule_default.js'); - return new proxy.proxyServer(options); + const options = util.merge({}, DEFAULT_OPTIONS); + const instance = new AnyProxy.ProxyServer(options); + instance.on('error', e => { + console.log('server instance error', e); + }); + instance.start(); + return instance; } /* @@ -38,28 +41,40 @@ function defaultProxyServer () { Object, the rule object which contains required intercept method @return An instance of proxy, could be closed by calling `instance.close()` */ -function proxyServerWithRule (rule) { - proxy = util.freshRequire('../proxy.js'); +function proxyServerWithRule(rule, overrideConfig) { + const AnyProxy = util.freshRequire('../proxy.js'); - const options = util.merge({}, DEFAULT_OPTIONS); - options.rule = rule; + const options = Object.assign({}, DEFAULT_OPTIONS, overrideConfig); + options.rule = rule; - return new proxy.proxyServer(options); + const instance = new AnyProxy.ProxyServer(options); + instance.on('error', e => { + console.log('server instance error', e); + }); + instance.start(); + + return instance; } -function proxyServerWithoutHttpsIntercept (rule) { - proxy = util.freshRequire('../proxy.js'); +function proxyServerWithoutHttpsIntercept(rule) { + const AnyProxy = util.freshRequire('../proxy.js'); - const options = util.merge({}, DEFAULT_OPTIONS); - if (rule) { - options.rule = rule; - } - options.interceptHttps = false; - return new proxy.proxyServer(options); + const options = util.merge({}, DEFAULT_OPTIONS); + if (rule) { + options.rule = rule; + } + options.forceProxyHttps = false; + + const instance = new AnyProxy.ProxyServer(options); + instance.on('error', e => { + console.log('server instance error', e); + }); + instance.start(); + return instance; } module.exports = { - defaultProxyServer, - proxyServerWithoutHttpsIntercept, - proxyServerWithRule -}; \ No newline at end of file + defaultProxyServer, + proxyServerWithoutHttpsIntercept, + proxyServerWithRule +}; diff --git a/test/util/SimHash.js b/test/util/SimHash.js new file mode 100644 index 0000000..e69de29 diff --git a/web/404.html b/web/404.html deleted file mode 100644 index fc1b062..0000000 --- a/web/404.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - Anyproxy - 404 NOT FOUND - - - - -

          404 NOT FOUND

          - - \ No newline at end of file diff --git a/web/build.sh b/web/build.sh deleted file mode 100755 index b2fb455..0000000 --- a/web/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -jsx /src /build -webpack --progress --colors \ No newline at end of file diff --git a/web/build/anyproxy_wsUtil.js b/web/build/anyproxy_wsUtil.js deleted file mode 100644 index 938394b..0000000 --- a/web/build/anyproxy_wsUtil.js +++ /dev/null @@ -1,93 +0,0 @@ -/* -web socket util for AnyProxy -https://github.com/alibaba/anyproxy -*/ - -/* -{ - baseUrl : "" -} -config -config.baseUrl -config.port -config.onOpen -config.onClose -config.onError -config.onGetData -config.onGetUpdate -config.onGetBody -config.onError -*/ -function anyproxy_wsUtil(config){ - config = config || {}; - if(!WebSocket){ - throw (new Error("webSocket is not available on this browser")); - } - - var self = this; - var baseUrl = config.baseUrl || "127.0.0.1", - socketPort = config.port || 8003; - - var dataSocket = new WebSocket("ws://" + baseUrl + ":" + socketPort); - - self.bodyCbMap = {}; - dataSocket.onmessage = function(event){ - config.onGetData && config.onGetData.call(self,event.data); - - try{ - var data = JSON.parse(event.data), - type = data.type, - content = data.content, - reqRef = data.reqRef; - }catch(e){ - config.onError && config.onError.call(self, new Error("failed to parse socket data - " + e.toString()) ); - } - - if(type == "update"){ - config.onGetUpdate && config.onGetUpdate.call(self, content); - - }else if(type == "body"){ - config.onGetBody && config.onGetBody.call(self, content, reqRef); - - if(data.reqRef && self.bodyCbMap[reqRef]){ - self.bodyCbMap[reqRef].call(self,content); - } - } - } - - dataSocket.onopen = function(e){ - config.onOpen && config.onOpen.call(self,e); - } - dataSocket.onclose = function(e){ - config.onClose && config.onClose.call(self,e); - } - dataSocket.onerror = function(e){ - config.onError && config.onError.call(self,e); - } - - self.dataSocket = dataSocket; -}; - -anyproxy_wsUtil.prototype.send = function(data){ - if(typeof data == "object"){ - data = JSON.stringify(data); - } - this.dataSocket.send(data); -}; - -anyproxy_wsUtil.prototype.reqBody = function(id,callback){ - if(!id) return; - - var payload = { - type : "reqBody", - id : id - }; - if(callback){ - var reqRef = "r_" + Math.random()*100 + "_" + (new Date().getTime()); - payload.reqRef = reqRef; - this.bodyCbMap[reqRef] = callback; - } - this.send(payload); -}; - -module.exports = anyproxy_wsUtil; \ No newline at end of file diff --git a/web/build/detail.js b/web/build/detail.js deleted file mode 100644 index 05fbffe..0000000 --- a/web/build/detail.js +++ /dev/null @@ -1,27 +0,0 @@ -define(function(require,exports,module){ - var $ = require("$"); - - - - - // var cbMap = {}; - // function render(data,cb){ - // var resultEl = $(_.template(tpl, data)), - // id = data._id; - // try{ - // //if finished - // var reqRef = "r" + Math.random() + "_" + new Date().getTime(); - // if(data.statusCode){ - // //fetch body - // ws.reqBody(id,function(content){ - // $(".J_responseBody", resultEl).html(he.encode(content.body)); - // cb(resultEl); - // }); - // } - // }catch(e){ - // cb(resultEl); - // }; - // } - - module.exports = DetailPanel; -}); \ No newline at end of file diff --git a/web/build/detailPanel.js b/web/build/detailPanel.js deleted file mode 100644 index 1f4c4f5..0000000 --- a/web/build/detailPanel.js +++ /dev/null @@ -1,120 +0,0 @@ -function init(React){ - - var DetailPanel = React.createClass({displayName: "DetailPanel", - getInitialState : function(){ - return { - body : {id : -1, content : null}, - left : "35%" - }; - }, - loadBody:function(){ - var self = this, - id = self.props.data.id; - if(!id) return; - - jQuery.get("/fetchBody?id=" + id ,function(resObj){ - if(resObj && resObj.id){ - if(resObj.type && resObj.type == "image" && resObj.ref){ - self.setState({ - body : { - img : resObj.ref, - id : resObj.id - } - }); - }else if(resObj.content){ - self.setState({ - body : { - body : resObj.content, - id : resObj.id - } - }); - } - } - }); - }, - render : function(){ - var reqHeaderSection = [], - resHeaderSection = [], - summarySection, - detailSection, - bodyContent; - - if(this.props.data.reqHeader){ - for(var key in this.props.data.reqHeader){ - reqHeaderSection.push(React.createElement("li", {key: "reqHeader_" + key}, React.createElement("strong", null, key), " : ", this.props.data.reqHeader[key])) - } - } - - summarySection = ( - React.createElement("div", null, - React.createElement("section", {className: "req"}, - React.createElement("h4", {className: "subTitle"}, "request"), - React.createElement("div", {className: "detail"}, - React.createElement("ul", {className: "uk-list"}, - React.createElement("li", null, this.props.data.method, " ", React.createElement("span", {title: "{this.props.data.path}"}, this.props.data.path), " HTTP/1.1"), - reqHeaderSection - ) - ) - ), - - React.createElement("section", {className: "reqBody"}, - React.createElement("h4", {className: "subTitle"}, "request body"), - React.createElement("div", {className: "detail"}, - React.createElement("p", null, this.props.data.reqBody) - ) - ) - ) - ); - - if(this.props.data.statusCode){ - if(this.state.body.id == this.props.data.id){ - if(this.state.body.img){ - var imgEl = { __html : ''}; - bodyContent = (React.createElement("div", {dangerouslySetInnerHTML: imgEl})); - }else{ - bodyContent = (React.createElement("pre", {className: "resBodyContent"}, this.state.body.body)); - } - }else{ - bodyContent = null; - this.loadBody(); - } - - if(this.props.data.resHeader){ - for(var key in this.props.data.resHeader){ - resHeaderSection.push(React.createElement("li", {key: "resHeader_" + key}, React.createElement("strong", null, key), " : ", this.props.data.resHeader[key])) - } - } - - detailSection = ( - React.createElement("div", null, - React.createElement("section", {className: "resHeader"}, - React.createElement("h4", {className: "subTitle"}, "response header"), - React.createElement("div", {className: "detail"}, - React.createElement("ul", {className: "uk-list"}, - React.createElement("li", null, "HTTP/1.1 ", React.createElement("span", {className: "http_status http_status_" + this.props.data.statusCode}, this.props.data.statusCode)), - resHeaderSection - ) - ) - ), - - React.createElement("section", {className: "resBody"}, - React.createElement("h4", {className: "subTitle"}, "response body"), - React.createElement("div", {className: "detail"}, bodyContent) - ) - ) - ); - } - - return ( - React.createElement("div", null, - summarySection, - detailSection - ) - ); - } - }); - - return DetailPanel; -} - -module.exports.init = init; \ No newline at end of file diff --git a/web/build/event.js b/web/build/event.js deleted file mode 100644 index 4504b32..0000000 --- a/web/build/event.js +++ /dev/null @@ -1,43 +0,0 @@ -//Ref : http://jsfiddle.net/JxYca/3/ -var EventManager = function() { - this.initialize(); -}; -EventManager.prototype = { - initialize: function() { - //declare listeners as an object - this.listeners = {}; - }, - // public methods - addListener: function(event, fn) { - if (!this.listeners[event]) { - this.listeners[event] = []; - } - if (fn instanceof Function) { - this.listeners[event].push(fn); - } - return this; - }, - dispatchEvent: function(event, params) { - // loop through listeners array - for (var index = 0, l = this.listeners[event].length; index < l; index++) { - // execute matching 'event' - loop through all indices and - // when ev is found, execute - this.listeners[event][index].call(window, params); - } - }, - removeListener: function(event, fn) { - // split array 1 item after our listener - // shorten to remove it - // join the two arrays back together - if (this.listeners[event]) { - for (var i = 0, l = this.listners[event].length; i < l; i++) { - if (this.listners[event][i] === fn) { - this.listners[event].slice(i, 1); - break; - } - } - } - } -}; - -module.exports = EventManager; \ No newline at end of file diff --git a/web/build/filter.js b/web/build/filter.js deleted file mode 100644 index 5e9adea..0000000 --- a/web/build/filter.js +++ /dev/null @@ -1,43 +0,0 @@ -function init(React){ - - var Filter = React.createClass({displayName: "Filter", - - dealChange:function(){ - var self = this, - userInput = React.findDOMNode(self.refs.keywordInput).value; - - self.props.onChangeKeyword && self.props.onChangeKeyword.call(null,userInput); - }, - setFocus:function(){ - var self = this; - React.findDOMNode(self.refs.keywordInput).focus(); - }, - componentDidUpdate:function(){ - this.setFocus(); - }, - render:function(){ - var self = this; - - return ( - React.createElement("div", null, - React.createElement("h4", {className: "subTitle"}, "Log Filter"), - React.createElement("div", {className: "filterSection"}, - React.createElement("div", {className: "uk-form"}, - React.createElement("input", {className: "uk-form-large", ref: "keywordInput", onChange: self.dealChange, type: "text", placeholder: "keywords or /^regExp$/", width: "300"}) - ) - ), - React.createElement("p", null, - React.createElement("i", {className: "uk-icon-magic"}), "  type ", React.createElement("strong", null, "/id=\\d", 3, "/"), " will give you all the logs containing ", React.createElement("strong", null, "id=123") - ) - ) - ); - }, - componentDidMount:function(){ - this.setFocus(); - } - }); - - return Filter; -} - -module.exports.init = init; \ No newline at end of file diff --git a/web/build/index.js b/web/build/index.js deleted file mode 100644 index 828de9f..0000000 --- a/web/build/index.js +++ /dev/null @@ -1,273 +0,0 @@ -window.$ = window.jQuery = require("../lib/jquery"); - -var EventManager = require('../lib/event'), - Anyproxy_wsUtil = require("../lib/anyproxy_wsUtil"), - React = require("../lib/react"); - -var WsIndicator = require("./wsIndicator").init(React), - RecordPanel = require("./recordPanel").init(React), - Popup = require("./popup").init(React); - -var PopupContent = { - map : require("./mapPanel").init(React), - detail : require("./detailPanel").init(React), - filter : require("./filter").init(React) -}; - -var ifPause = false, - recordSet = []; - -//Event : wsGetUpdate -//Event : recordSetUpdated -//Event : wsOpen -//Event : wsEnd -var eventCenter = new EventManager(); - -//merge : right --> left -function util_merge(left,right){ - for(var key in right){ - left[key] = right[key]; - } - return left; -} - -//invoke AnyProxy web socket util -(function(){ - try{ - var ws = window.ws = new Anyproxy_wsUtil({ - baseUrl : document.getElementById("baseUrl").value, - port : document.getElementById("socketPort").value, - onOpen : function(){ - eventCenter.dispatchEvent("wsOpen"); - }, - onGetUpdate : function(record){ - eventCenter.dispatchEvent("wsGetUpdate",record); - }, - onError : function(e){ - eventCenter.dispatchEvent("wsEnd"); - }, - onClose : function(e){ - eventCenter.dispatchEvent("wsEnd"); - } - }); - window.ws = ws; - - }catch(e){ - alert("failed to invoking web socket on this browser"); - } -})(); - - -//websocket status indicator -(function(){ - var wsIndicator = React.render( - React.createElement(WsIndicator, null), - document.getElementById("J_indicatorEl") - ); - - eventCenter.addListener("wsOpen",function(){ - wsIndicator.setState({ - isValid : true - }); - }); - - eventCenter.addListener("wsEnd",function(){ - wsIndicator.setState({ - isValid : false - }); - }); -})(); - - -//init popup -var showPop; -(function(){ - $("body").append('
          '); - var pop = React.render( - React.createElement(Popup, null), - document.getElementById("J_popOuter") - ); - - showPop = function(popArg){ - var stateArg = util_merge({show : true },popArg); - pop.setState(stateArg); - }; -})(); - -//init record panel -var recorder; -(function(){ - function updateRecordSet(newRecord){ - if(ifPause) return; - - if(newRecord && newRecord.id){ - if(!recordSet[newRecord.id]){ - recordSet[newRecord.id] = newRecord; - }else{ - util_merge(recordSet[newRecord.id],newRecord); - } - - recordSet[newRecord.id]._justUpdated = true; - // React.addons.Perf.start(); - eventCenter.dispatchEvent("recordSetUpdated"); - // React.addons.Perf.stop(); - } - } - - function initRecordSet(){ - $.getJSON("/lastestLog",function(res){ - if(typeof res == "object"){ - res.map(function(item){ - if(item.id){ - recordSet[item.id] = item; - } - }); - eventCenter.dispatchEvent("recordSetUpdated"); - } - }); - } - - eventCenter.addListener("wsGetUpdate",updateRecordSet); - - eventCenter.addListener('recordSetUpdated',function(){ - recorder.setState({ - list : recordSet - }); - }); - - eventCenter.addListener("filterUpdated",function(newKeyword){ - recorder.setState({ - filter: newKeyword - }); - }); - - function showDetail(data){ - showPop({left:"35%",content:React.createElement(PopupContent["detail"], {data:data})}); - } - - //init recorder panel - recorder = React.render( - React.createElement(RecordPanel, {onSelect: showDetail}), - document.getElementById("J_content") - ); - - initRecordSet(); -})(); - - -//action bar -(function(){ - - //clear log - function clearLogs(){ - recordSet = []; - eventCenter.dispatchEvent("recordSetUpdated"); - } - - $(document).on("keyup",function(e){ - if(e.keyCode == 88 && e.ctrlKey){ // ctrl + x - clearLogs(); - } - }); - - var clearLogBtn = $(".J_clearBtn"); - clearLogBtn.on("click",function(e){ - e.stopPropagation(); - e.preventDefault(); - clearLogs(); - }); - - //start , pause - var statusBtn = $(".J_statusBtn"); - statusBtn.on("click",function(e){ - e.stopPropagation(); - e.preventDefault(); - - $(".J_statusBtn").removeClass("btn_disable"); - $(this).addClass("btn_disable"); - - if(/stop/i.test($(this).html()) ){ - ifPause = true; - }else{ - ifPause = false; - } - }); - - //preset button - (function (){ - var TopBtn = React.createClass({displayName: "TopBtn", - getInitialState: function(){ - return { - inUse : false - }; - }, - render: function(){ - var self = this, - iconClass = self.state.inUse ? "uk-icon-check" : self.props.icon, - btnClass = self.state.inUse ? "topBtn topBtnInUse" : "topBtn"; - - return React.createElement("a", {href: "#"}, React.createElement("span", {className: btnClass, onClick: self.props.onClick}, React.createElement("i", {className: iconClass}), self.props.title)) - } - }); - - // filter - var filterBtn, - FilterPanel = PopupContent["filter"], - filterPanelEl; - - filterBtn = React.render(React.createElement(TopBtn, {icon: "uk-icon-filter", title: "Filter", onClick: filterBtnClick}), document.getElementById("J_filterBtnContainer")); - filterPanelEl = (React.createElement(FilterPanel, {onChangeKeyword: updateKeyword}) ); - - function updateKeyword(key){ - eventCenter.dispatchEvent("filterUpdated",key); - filterBtn.setState({inUse : !!key}); - } - function filterBtnClick(){ - showPop({ left:"60%", content:filterPanelEl }); - } - - // map local - var mapBtn, - mapPanelEl; - function onChangeMapConfig(cfg){ - mapBtn.setState({inUse : cfg && cfg.length}); - } - - function mapBtnClick(){ - showPop({left:"60%", content:mapPanelEl }); - } - - //detect whether to show the map btn - require("./mapPanel").fetchConfig(function(initConfig){ - var MapPanel = PopupContent["map"]; - mapBtn = React.render(React.createElement(TopBtn, {icon: "uk-icon-shield", title: "Map Local", onClick: mapBtnClick}),document.getElementById("J_filterContainer")); - mapPanelEl = (React.createElement(MapPanel, {onChange: onChangeMapConfig})); - onChangeMapConfig(initConfig); - }); - - var t = true; - setInterval(function(){ - t = !t; - // mapBtn && mapBtn.setState({inUse : t}) - },300); - - - - })(); - - //other button - (function(){ - $(".J_customButton").on("click",function(){ - var thisEl = $(this), - iframeUrl = thisEl.attr("iframeUrl"); - - if(!iframeUrl){ - throw new Error("cannot find the url assigned for this button"); - } - - var iframeEl = React.createElement("iframe",{src:iframeUrl,frameBorder:0}); - showPop({left:"35%", content: iframeEl }); - }); - })(); - -})(); \ No newline at end of file diff --git a/web/build/mapForm.js b/web/build/mapForm.js deleted file mode 100644 index 32fc14f..0000000 --- a/web/build/mapForm.js +++ /dev/null @@ -1,120 +0,0 @@ -require("../lib/jstree"); - -function init(React){ - function fetchTree(root,cb){ - if(!root || root == "#"){ - root = ""; - } - - $.getJSON("/filetree?root=" + root,function(resObj){ - var ret = []; - try{ - $.each(resObj.directory, function(k,item){ - if(item.name.indexOf(".") == 0) return; - ret.push({ - text : item.name, - id : item.fullPath, - icon : "uk-icon-folder", - children : true - }); - }); - - $.each(resObj.file, function(k,item){ - if(item.name.indexOf(".") == 0) return; - ret.push({ - text : item.name, - id : item.fullPath, - icon : 'uk-icon-file-o', - children : false - }); - }); - }catch(e){} - cb && cb.call(null,ret); - }); - } - - var MapForm = React.createClass({displayName: "MapForm", - - submitData:function(){ - var self = this, - result = {}; - - var filePathInput = React.findDOMNode(self.refs.localFilePath), - filePath = filePathInput.value, - keywordInput = React.findDOMNode(self.refs.keywordInput), - keyword = keywordInput.value; - - if(filePath && keyword){ - self.props.onSubmit.call(null,{ - keyword : keyword, - local : filePath - }); - - filePathInput.value = ""; - keywordInput.value = ""; - } - }, - - render:function(){ - var self = this; - return ( - React.createElement("div", null, - React.createElement("form", {className: "uk-form uk-form-stacked mapAddNewForm"}, - React.createElement("fieldset", null, - React.createElement("div", {className: "uk-form-row"}, - React.createElement("label", {className: "uk-form-label", htmlFor: "map_keywordInput"}, "keyword"), - React.createElement("div", {className: "uk-form-controls"}, - React.createElement("input", {className: "mapConfigInputs", type: "text", id: "map_keywordInput", ref: "keywordInput", placeholder: "keyword"}) - ) - ), - - React.createElement("div", {className: "uk-form-row"}, - React.createElement("label", {className: "uk-form-label", htmlFor: "map_localFilePath"}, "local file"), - React.createElement("div", {className: "uk-form-controls"}, - React.createElement("input", {className: "mapConfigInputs pathInput", type: "text", id: "map_localFilePath", ref: "localFilePath", placeholder: "local file path"}) - ), - React.createElement("div", {ref: "treeWrapper", className: "treeWrapper"}) - ), - - React.createElement("div", {className: "uk-form-row"}, - React.createElement("button", {type: "button", className: "uk-button", onClick: self.submitData}, "Add") - ) - - ) - ) - - ) - ); - }, - - componentDidMount :function(){ - var self = this; - var wrapperEl = $(React.findDOMNode(self.refs.treeWrapper)), - filePathInput = React.findDOMNode(self.refs.localFilePath); - - wrapperEl.jstree({ - 'core' : { - 'data' : function (node, cb) { - fetchTree(node.id,cb); - } - } - }); - - wrapperEl.on("changed.jstree", function (e, data) { - if(data && data.selected && data.selected.length){ - //is folder - if(/folder/.test(data.node.icon)) return; - - var item = data.selected[0]; - filePathInput.value = item; - } - }); - - }, - componentDidUpdate:function(){} - }); - - return MapForm; -} - -module.exports.init = init; \ No newline at end of file diff --git a/web/build/mapList.js b/web/build/mapList.js deleted file mode 100644 index cf7f949..0000000 --- a/web/build/mapList.js +++ /dev/null @@ -1,86 +0,0 @@ -function fetchConfig(cb){ - return $.getJSON("/getMapConfig",cb); -} - -function init(React){ - var MapList = React.createClass({displayName: "MapList", - getInitialState:function(){ - return { - ruleList : [] - } - }, - appendRecord:function(data){ - var self = this, - newState = self.state.ruleList; - - if(data && data.keyword && data.local){ - newState.push({ - keyword : data.keyword, - local : data.local - }); - - self.setState({ - ruleList: newState - }); - } - }, - - removeRecord:function(index){ - var self = this, - newList = self.state.ruleList; - - newList.splice(index,1); - self.setState({ - ruleList : newList - }); - }, - render:function(){ - var self = this, - collection = []; - - collection = self.state.ruleList.map(function(item,index){ - return ( - React.createElement("li", null, - React.createElement("strong", null, item.keyword), React.createElement("a", {className: "removeBtn", href: "#", onClick: self.removeRecord.bind(self,index)}, "remove"), React.createElement("br", null), - React.createElement("span", null, item.local) - ) - ); - }); - - return ( - React.createElement("ul", {className: "mapRuleList"}, - collection - ) - ); - }, - componentDidMount :function(){ - var self = this; - fetchConfig(function(data){ - self.setState({ - ruleList : data - }); - }); - }, - componentDidUpdate:function(){ - var self = this; - - //upload config to server - var currentList = self.state.ruleList; - $.ajax({ - method : "POST", - url : "/setMapConfig", - contentType :"application/json", - data : JSON.stringify(currentList), - dataType : "json", - success :function(res){} - }); - - self.props.onChange && self.props.onChange(self.state.ruleList); - } - }); - - return MapList; -} - -module.exports.init = init; -module.exports.fetchConfig = fetchConfig; \ No newline at end of file diff --git a/web/build/mapPanel.js b/web/build/mapPanel.js deleted file mode 100644 index 63c6efd..0000000 --- a/web/build/mapPanel.js +++ /dev/null @@ -1,33 +0,0 @@ -require("../lib/jstree"); - -function init(React){ - var MapForm = require("./mapForm").init(React), - MapList = require("./mapList").init(React); - - var MapPanel = React.createClass({displayName: "MapPanel", - appendRecord : function(data){ - var self = this, - listComponent = self.refs.list; - - listComponent.appendRecord(data); - }, - - render:function(){ - var self = this; - return ( - React.createElement("div", {className: "mapWrapper"}, - React.createElement("h4", {className: "subTitle"}, "Current Config"), - React.createElement(MapList, {ref: "list", onChange: self.props.onChange}), - - React.createElement("h4", {className: "subTitle"}, "Add Map Rule"), - React.createElement(MapForm, {onSubmit: self.appendRecord}) - ) - ); - } - }); - - return MapPanel; -} - -module.exports.init = init; -module.exports.fetchConfig = require("./mapList").fetchConfig; \ No newline at end of file diff --git a/web/build/popup.js b/web/build/popup.js deleted file mode 100644 index c245670..0000000 --- a/web/build/popup.js +++ /dev/null @@ -1,94 +0,0 @@ -function init(React){ - - function dragableBar(initX,cb){ - var self = this, - dragging = true; - - var ghostbar = $('
          ').css("left",initX).prependTo('body'); - - $(document).mousemove(function(e){ - e.preventDefault(); - ghostbar.css("left",e.pageX + "px"); - }); - - $(document).mouseup(function(e){ - if(!dragging) return; - - dragging = false; - - var deltaPageX = e.pageX - initX; - cb && cb.call(null,{ - delta : deltaPageX, - finalX : e.pageX - }); - - ghostbar.remove(); - $(document).unbind('mousemove'); - }); - } - - var Popup = React.createClass({displayName: "Popup", - getInitialState : function(){ - return { - show : false, - left : "35%", - content : null - }; - }, - componentDidMount:function(){ - var self = this; - $(document).on("keyup",function(e){ - if(e.keyCode == 27){ //ESC - self.setState({ - show : false - }); - } - }); - }, - setHide:function(){ - this.setState({ - show : false - }); - }, - setShow:function(ifShow){ - this.setState({ - show : true - }); - }, - dealDrag:function(){ - var self = this, - leftVal = $(React.findDOMNode(this.refs.mainOverlay)).css("left"); - dragableBar(leftVal, function(data){ - if(data && data.finalX){ - if(window.innerWidth - data.finalX < 200){ - data.finalX = window.innerWidth - 200; - } - self.setState({ - left : data.finalX + "px" - }); - } - }); - }, - componentDidUpdate:function(){ - - }, - render : function(){ - return ( - React.createElement("div", {style: {display:this.state.show ? "block" :"none"}}, - React.createElement("div", {className: "overlay_mask", onClick: this.setHide}), - React.createElement("div", {className: "recordDetailOverlay", ref: "mainOverlay", style: {left: this.state.left}}, - React.createElement("div", {className: "dragbar", onMouseDown: this.dealDrag}), - React.createElement("span", {className: "escBtn", onClick: this.setHide}, React.createElement("i", {className: "uk-icon-times"})), - React.createElement("div", null, - this.state.content - ) - ) - ) - ); - } - }); - - return Popup; -} - -module.exports.init = init; \ No newline at end of file diff --git a/web/build/react.js b/web/build/react.js deleted file mode 100644 index 2c885d0..0000000 --- a/web/build/react.js +++ /dev/null @@ -1,21598 +0,0 @@ -/** - * React (with addons) v0.13.2 - */ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.React = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 8 && documentMode <= 11) - ) -); - -/** - * Opera <= 12 includes TextEvent in window, but does not fire - * text input events. Rely on keypress instead. - */ -function isPresto() { - var opera = window.opera; - return ( - typeof opera === 'object' && - typeof opera.version === 'function' && - parseInt(opera.version(), 10) <= 12 - ); -} - -var SPACEBAR_CODE = 32; -var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); - -var topLevelTypes = EventConstants.topLevelTypes; - -// Events and their corresponding property names. -var eventTypes = { - beforeInput: { - phasedRegistrationNames: { - bubbled: keyOf({onBeforeInput: null}), - captured: keyOf({onBeforeInputCapture: null}) - }, - dependencies: [ - topLevelTypes.topCompositionEnd, - topLevelTypes.topKeyPress, - topLevelTypes.topTextInput, - topLevelTypes.topPaste - ] - }, - compositionEnd: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionEnd: null}), - captured: keyOf({onCompositionEndCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topCompositionEnd, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyPress, - topLevelTypes.topKeyUp, - topLevelTypes.topMouseDown - ] - }, - compositionStart: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionStart: null}), - captured: keyOf({onCompositionStartCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topCompositionStart, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyPress, - topLevelTypes.topKeyUp, - topLevelTypes.topMouseDown - ] - }, - compositionUpdate: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionUpdate: null}), - captured: keyOf({onCompositionUpdateCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topCompositionUpdate, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyPress, - topLevelTypes.topKeyUp, - topLevelTypes.topMouseDown - ] - } -}; - -// Track whether we've ever handled a keypress on the space key. -var hasSpaceKeypress = false; - -/** - * Return whether a native keypress event is assumed to be a command. - * This is required because Firefox fires `keypress` events for key commands - * (cut, copy, select-all, etc.) even though no character is inserted. - */ -function isKeypressCommand(nativeEvent) { - return ( - (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && - // ctrlKey && altKey is equivalent to AltGr, and is not a command. - !(nativeEvent.ctrlKey && nativeEvent.altKey) - ); -} - - -/** - * Translate native top level events into event types. - * - * @param {string} topLevelType - * @return {object} - */ -function getCompositionEventType(topLevelType) { - switch (topLevelType) { - case topLevelTypes.topCompositionStart: - return eventTypes.compositionStart; - case topLevelTypes.topCompositionEnd: - return eventTypes.compositionEnd; - case topLevelTypes.topCompositionUpdate: - return eventTypes.compositionUpdate; - } -} - -/** - * Does our fallback best-guess model think this event signifies that - * composition has begun? - * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} - */ -function isFallbackCompositionStart(topLevelType, nativeEvent) { - return ( - topLevelType === topLevelTypes.topKeyDown && - nativeEvent.keyCode === START_KEYCODE - ); -} - -/** - * Does our fallback mode think that this event is the end of composition? - * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} - */ -function isFallbackCompositionEnd(topLevelType, nativeEvent) { - switch (topLevelType) { - case topLevelTypes.topKeyUp: - // Command keys insert or clear IME input. - return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1); - case topLevelTypes.topKeyDown: - // Expect IME keyCode on each keydown. If we get any other - // code we must have exited earlier. - return (nativeEvent.keyCode !== START_KEYCODE); - case topLevelTypes.topKeyPress: - case topLevelTypes.topMouseDown: - case topLevelTypes.topBlur: - // Events are not possible without cancelling IME. - return true; - default: - return false; - } -} - -/** - * Google Input Tools provides composition data via a CustomEvent, - * with the `data` property populated in the `detail` object. If this - * is available on the event object, use it. If not, this is a plain - * composition event and we have nothing special to extract. - * - * @param {object} nativeEvent - * @return {?string} - */ -function getDataFromCustomEvent(nativeEvent) { - var detail = nativeEvent.detail; - if (typeof detail === 'object' && 'data' in detail) { - return detail.data; - } - return null; -} - -// Track the current IME composition fallback object, if any. -var currentComposition = null; - -/** - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {?object} A SyntheticCompositionEvent. - */ -function extractCompositionEvent( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent -) { - var eventType; - var fallbackData; - - if (canUseCompositionEvent) { - eventType = getCompositionEventType(topLevelType); - } else if (!currentComposition) { - if (isFallbackCompositionStart(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionStart; - } - } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionEnd; - } - - if (!eventType) { - return null; - } - - if (useFallbackCompositionData) { - // The current composition is stored statically and must not be - // overwritten while composition continues. - if (!currentComposition && eventType === eventTypes.compositionStart) { - currentComposition = FallbackCompositionState.getPooled(topLevelTarget); - } else if (eventType === eventTypes.compositionEnd) { - if (currentComposition) { - fallbackData = currentComposition.getData(); - } - } - } - - var event = SyntheticCompositionEvent.getPooled( - eventType, - topLevelTargetID, - nativeEvent - ); - - if (fallbackData) { - // Inject data generated from fallback path into the synthetic event. - // This matches the property of native CompositionEventInterface. - event.data = fallbackData; - } else { - var customData = getDataFromCustomEvent(nativeEvent); - if (customData !== null) { - event.data = customData; - } - } - - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; -} - -/** - * @param {string} topLevelType Record from `EventConstants`. - * @param {object} nativeEvent Native browser event. - * @return {?string} The string corresponding to this `beforeInput` event. - */ -function getNativeBeforeInputChars(topLevelType, nativeEvent) { - switch (topLevelType) { - case topLevelTypes.topCompositionEnd: - return getDataFromCustomEvent(nativeEvent); - case topLevelTypes.topKeyPress: - /** - * If native `textInput` events are available, our goal is to make - * use of them. However, there is a special case: the spacebar key. - * In Webkit, preventing default on a spacebar `textInput` event - * cancels character insertion, but it *also* causes the browser - * to fall back to its default spacebar behavior of scrolling the - * page. - * - * Tracking at: - * https://code.google.com/p/chromium/issues/detail?id=355103 - * - * To avoid this issue, use the keypress event as if no `textInput` - * event is available. - */ - var which = nativeEvent.which; - if (which !== SPACEBAR_CODE) { - return null; - } - - hasSpaceKeypress = true; - return SPACEBAR_CHAR; - - case topLevelTypes.topTextInput: - // Record the characters to be added to the DOM. - var chars = nativeEvent.data; - - // If it's a spacebar character, assume that we have already handled - // it at the keypress level and bail immediately. Android Chrome - // doesn't give us keycodes, so we need to blacklist it. - if (chars === SPACEBAR_CHAR && hasSpaceKeypress) { - return null; - } - - return chars; - - default: - // For other native event types, do nothing. - return null; - } -} - -/** - * For browsers that do not provide the `textInput` event, extract the - * appropriate string to use for SyntheticInputEvent. - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {object} nativeEvent Native browser event. - * @return {?string} The fallback string for this `beforeInput` event. - */ -function getFallbackBeforeInputChars(topLevelType, nativeEvent) { - // If we are currently composing (IME) and using a fallback to do so, - // try to extract the composed characters from the fallback object. - if (currentComposition) { - if ( - topLevelType === topLevelTypes.topCompositionEnd || - isFallbackCompositionEnd(topLevelType, nativeEvent) - ) { - var chars = currentComposition.getData(); - FallbackCompositionState.release(currentComposition); - currentComposition = null; - return chars; - } - return null; - } - - switch (topLevelType) { - case topLevelTypes.topPaste: - // If a paste event occurs after a keypress, throw out the input - // chars. Paste events should not lead to BeforeInput events. - return null; - case topLevelTypes.topKeyPress: - /** - * As of v27, Firefox may fire keypress events even when no character - * will be inserted. A few possibilities: - * - * - `which` is `0`. Arrow keys, Esc key, etc. - * - * - `which` is the pressed key code, but no char is available. - * Ex: 'AltGr + d` in Polish. There is no modified character for - * this key combination and no character is inserted into the - * document, but FF fires the keypress for char code `100` anyway. - * No `input` event will occur. - * - * - `which` is the pressed key code, but a command combination is - * being used. Ex: `Cmd+C`. No character is inserted, and no - * `input` event will occur. - */ - if (nativeEvent.which && !isKeypressCommand(nativeEvent)) { - return String.fromCharCode(nativeEvent.which); - } - return null; - case topLevelTypes.topCompositionEnd: - return useFallbackCompositionData ? null : nativeEvent.data; - default: - return null; - } -} - -/** - * Extract a SyntheticInputEvent for `beforeInput`, based on either native - * `textInput` or fallback behavior. - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {?object} A SyntheticInputEvent. - */ -function extractBeforeInputEvent( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent -) { - var chars; - - if (canUseTextInputEvent) { - chars = getNativeBeforeInputChars(topLevelType, nativeEvent); - } else { - chars = getFallbackBeforeInputChars(topLevelType, nativeEvent); - } - - // If no characters are being inserted, no BeforeInput event should - // be fired. - if (!chars) { - return null; - } - - var event = SyntheticInputEvent.getPooled( - eventTypes.beforeInput, - topLevelTargetID, - nativeEvent - ); - - event.data = chars; - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; -} - -/** - * Create an `onBeforeInput` event to match - * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents. - * - * This event plugin is based on the native `textInput` event - * available in Chrome, Safari, Opera, and IE. This event fires after - * `onKeyPress` and `onCompositionEnd`, but before `onInput`. - * - * `beforeInput` is spec'd but not implemented in any browsers, and - * the `input` event does not provide any useful information about what has - * actually been added, contrary to the spec. Thus, `textInput` is the best - * available event to identify the characters that have actually been inserted - * into the target node. - * - * This plugin is also responsible for emitting `composition` events, thus - * allowing us to share composition fallback code for both `beforeInput` and - * `composition` event types. - */ -var BeforeInputEventPlugin = { - - eventTypes: eventTypes, - - /** - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {*} An accumulation of synthetic events. - * @see {EventPluginHub.extractEvents} - */ - extractEvents: function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent - ) { - return [ - extractCompositionEvent( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent - ), - extractBeforeInputEvent( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent - ) - ]; - } -}; - -module.exports = BeforeInputEventPlugin; - -},{"106":106,"110":110,"157":157,"16":16,"21":21,"22":22,"23":23}],4:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule CSSCore - * @typechecks - */ - -var invariant = _dereq_(150); - -/** - * The CSSCore module specifies the API (and implements most of the methods) - * that should be used when dealing with the display of elements (via their - * CSS classes and visibility on screen. It is an API focused on mutating the - * display and not reading it as no logical state should be encoded in the - * display of elements. - */ - -var CSSCore = { - - /** - * Adds the class passed in to the element if it doesn't already have it. - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @return {DOMElement} the element passed in - */ - addClass: function(element, className) { - ("production" !== "development" ? invariant( - !/\s/.test(className), - 'CSSCore.addClass takes only a single class name. "%s" contains ' + - 'multiple classes.', className - ) : invariant(!/\s/.test(className))); - - if (className) { - if (element.classList) { - element.classList.add(className); - } else if (!CSSCore.hasClass(element, className)) { - element.className = element.className + ' ' + className; - } - } - return element; - }, - - /** - * Removes the class passed in from the element - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @return {DOMElement} the element passed in - */ - removeClass: function(element, className) { - ("production" !== "development" ? invariant( - !/\s/.test(className), - 'CSSCore.removeClass takes only a single class name. "%s" contains ' + - 'multiple classes.', className - ) : invariant(!/\s/.test(className))); - - if (className) { - if (element.classList) { - element.classList.remove(className); - } else if (CSSCore.hasClass(element, className)) { - element.className = element.className - .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1') - .replace(/\s+/g, ' ') // multiple spaces to one - .replace(/^\s*|\s*$/g, ''); // trim the ends - } - } - return element; - }, - - /** - * Helper to add or remove a class from an element based on a condition. - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @param {*} bool condition to whether to add or remove the class - * @return {DOMElement} the element passed in - */ - conditionClass: function(element, className, bool) { - return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); - }, - - /** - * Tests whether the element has the class specified. - * - * @param {DOMNode|DOMWindow} element the element to set the class on - * @param {string} className the CSS className - * @return {boolean} true if the element has the class, false if not - */ - hasClass: function(element, className) { - ("production" !== "development" ? invariant( - !/\s/.test(className), - 'CSS.hasClass takes only a single class name.' - ) : invariant(!/\s/.test(className))); - if (element.classList) { - return !!className && element.classList.contains(className); - } - return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1; - } - -}; - -module.exports = CSSCore; - -},{"150":150}],5:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule CSSProperty - */ - -'use strict'; - -/** - * CSS properties which accept numbers but are not in units of "px". - */ -var isUnitlessNumber = { - boxFlex: true, - boxFlexGroup: true, - columnCount: true, - flex: true, - flexGrow: true, - flexPositive: true, - flexShrink: true, - flexNegative: true, - fontWeight: true, - lineClamp: true, - lineHeight: true, - opacity: true, - order: true, - orphans: true, - widows: true, - zIndex: true, - zoom: true, - - // SVG-related properties - fillOpacity: true, - strokeDashoffset: true, - strokeOpacity: true, - strokeWidth: true -}; - -/** - * @param {string} prefix vendor-specific prefix, eg: Webkit - * @param {string} key style name, eg: transitionDuration - * @return {string} style name prefixed with `prefix`, properly camelCased, eg: - * WebkitTransitionDuration - */ -function prefixKey(prefix, key) { - return prefix + key.charAt(0).toUpperCase() + key.substring(1); -} - -/** - * Support style names that may come passed in prefixed by adding permutations - * of vendor prefixes. - */ -var prefixes = ['Webkit', 'ms', 'Moz', 'O']; - -// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an -// infinite loop, because it iterates over the newly added props too. -Object.keys(isUnitlessNumber).forEach(function(prop) { - prefixes.forEach(function(prefix) { - isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; - }); -}); - -/** - * Most style properties can be unset by doing .style[prop] = '' but IE8 - * doesn't like doing that with shorthand properties so for the properties that - * IE8 breaks on, which are listed here, we instead unset each of the - * individual properties. See http://bugs.jquery.com/ticket/12385. - * The 4-value 'clock' properties like margin, padding, border-width seem to - * behave without any problems. Curiously, list-style works too without any - * special prodding. - */ -var shorthandPropertyExpansions = { - background: { - backgroundImage: true, - backgroundPosition: true, - backgroundRepeat: true, - backgroundColor: true - }, - border: { - borderWidth: true, - borderStyle: true, - borderColor: true - }, - borderBottom: { - borderBottomWidth: true, - borderBottomStyle: true, - borderBottomColor: true - }, - borderLeft: { - borderLeftWidth: true, - borderLeftStyle: true, - borderLeftColor: true - }, - borderRight: { - borderRightWidth: true, - borderRightStyle: true, - borderRightColor: true - }, - borderTop: { - borderTopWidth: true, - borderTopStyle: true, - borderTopColor: true - }, - font: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - lineHeight: true, - fontFamily: true - } -}; - -var CSSProperty = { - isUnitlessNumber: isUnitlessNumber, - shorthandPropertyExpansions: shorthandPropertyExpansions -}; - -module.exports = CSSProperty; - -},{}],6:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule CSSPropertyOperations - * @typechecks static-only - */ - -'use strict'; - -var CSSProperty = _dereq_(5); -var ExecutionEnvironment = _dereq_(22); - -var camelizeStyleName = _dereq_(121); -var dangerousStyleValue = _dereq_(128); -var hyphenateStyleName = _dereq_(148); -var memoizeStringOnly = _dereq_(159); -var warning = _dereq_(171); - -var processStyleName = memoizeStringOnly(function(styleName) { - return hyphenateStyleName(styleName); -}); - -var styleFloatAccessor = 'cssFloat'; -if (ExecutionEnvironment.canUseDOM) { - // IE8 only supports accessing cssFloat (standard) as styleFloat - if (document.documentElement.style.cssFloat === undefined) { - styleFloatAccessor = 'styleFloat'; - } -} - -if ("production" !== "development") { - // 'msTransform' is correct, but the other prefixes should be capitalized - var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/; - - // style values shouldn't contain a semicolon - var badStyleValueWithSemicolonPattern = /;\s*$/; - - var warnedStyleNames = {}; - var warnedStyleValues = {}; - - var warnHyphenatedStyleName = function(name) { - if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { - return; - } - - warnedStyleNames[name] = true; - ("production" !== "development" ? warning( - false, - 'Unsupported style property %s. Did you mean %s?', - name, - camelizeStyleName(name) - ) : null); - }; - - var warnBadVendoredStyleName = function(name) { - if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { - return; - } - - warnedStyleNames[name] = true; - ("production" !== "development" ? warning( - false, - 'Unsupported vendor-prefixed style property %s. Did you mean %s?', - name, - name.charAt(0).toUpperCase() + name.slice(1) - ) : null); - }; - - var warnStyleValueWithSemicolon = function(name, value) { - if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { - return; - } - - warnedStyleValues[value] = true; - ("production" !== "development" ? warning( - false, - 'Style property values shouldn\'t contain a semicolon. ' + - 'Try "%s: %s" instead.', - name, - value.replace(badStyleValueWithSemicolonPattern, '') - ) : null); - }; - - /** - * @param {string} name - * @param {*} value - */ - var warnValidStyle = function(name, value) { - if (name.indexOf('-') > -1) { - warnHyphenatedStyleName(name); - } else if (badVendoredStyleNamePattern.test(name)) { - warnBadVendoredStyleName(name); - } else if (badStyleValueWithSemicolonPattern.test(value)) { - warnStyleValueWithSemicolon(name, value); - } - }; -} - -/** - * Operations for dealing with CSS properties. - */ -var CSSPropertyOperations = { - - /** - * Serializes a mapping of style properties for use as inline styles: - * - * > createMarkupForStyles({width: '200px', height: 0}) - * "width:200px;height:0;" - * - * Undefined values are ignored so that declarative programming is easier. - * The result should be HTML-escaped before insertion into the DOM. - * - * @param {object} styles - * @return {?string} - */ - createMarkupForStyles: function(styles) { - var serialized = ''; - for (var styleName in styles) { - if (!styles.hasOwnProperty(styleName)) { - continue; - } - var styleValue = styles[styleName]; - if ("production" !== "development") { - warnValidStyle(styleName, styleValue); - } - if (styleValue != null) { - serialized += processStyleName(styleName) + ':'; - serialized += dangerousStyleValue(styleName, styleValue) + ';'; - } - } - return serialized || null; - }, - - /** - * Sets the value for multiple styles on a node. If a value is specified as - * '' (empty string), the corresponding style property will be unset. - * - * @param {DOMElement} node - * @param {object} styles - */ - setValueForStyles: function(node, styles) { - var style = node.style; - for (var styleName in styles) { - if (!styles.hasOwnProperty(styleName)) { - continue; - } - if ("production" !== "development") { - warnValidStyle(styleName, styles[styleName]); - } - var styleValue = dangerousStyleValue(styleName, styles[styleName]); - if (styleName === 'float') { - styleName = styleFloatAccessor; - } - if (styleValue) { - style[styleName] = styleValue; - } else { - var expansion = CSSProperty.shorthandPropertyExpansions[styleName]; - if (expansion) { - // Shorthand property that IE8 won't like unsetting, so unset each - // component to placate it - for (var individualStyleName in expansion) { - style[individualStyleName] = ''; - } - } else { - style[styleName] = ''; - } - } - } - } - -}; - -module.exports = CSSPropertyOperations; - -},{"121":121,"128":128,"148":148,"159":159,"171":171,"22":22,"5":5}],7:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule CallbackQueue - */ - -'use strict'; - -var PooledClass = _dereq_(30); - -var assign = _dereq_(29); -var invariant = _dereq_(150); - -/** - * A specialized pseudo-event module to help keep track of components waiting to - * be notified when their DOM representations are available for use. - * - * This implements `PooledClass`, so you should never need to instantiate this. - * Instead, use `CallbackQueue.getPooled()`. - * - * @class ReactMountReady - * @implements PooledClass - * @internal - */ -function CallbackQueue() { - this._callbacks = null; - this._contexts = null; -} - -assign(CallbackQueue.prototype, { - - /** - * Enqueues a callback to be invoked when `notifyAll` is invoked. - * - * @param {function} callback Invoked when `notifyAll` is invoked. - * @param {?object} context Context to call `callback` with. - * @internal - */ - enqueue: function(callback, context) { - this._callbacks = this._callbacks || []; - this._contexts = this._contexts || []; - this._callbacks.push(callback); - this._contexts.push(context); - }, - - /** - * Invokes all enqueued callbacks and clears the queue. This is invoked after - * the DOM representation of a component has been created or updated. - * - * @internal - */ - notifyAll: function() { - var callbacks = this._callbacks; - var contexts = this._contexts; - if (callbacks) { - ("production" !== "development" ? invariant( - callbacks.length === contexts.length, - 'Mismatched list of contexts in callback queue' - ) : invariant(callbacks.length === contexts.length)); - this._callbacks = null; - this._contexts = null; - for (var i = 0, l = callbacks.length; i < l; i++) { - callbacks[i].call(contexts[i]); - } - callbacks.length = 0; - contexts.length = 0; - } - }, - - /** - * Resets the internal queue. - * - * @internal - */ - reset: function() { - this._callbacks = null; - this._contexts = null; - }, - - /** - * `PooledClass` looks for this. - */ - destructor: function() { - this.reset(); - } - -}); - -PooledClass.addPoolingTo(CallbackQueue); - -module.exports = CallbackQueue; - -},{"150":150,"29":29,"30":30}],8:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ChangeEventPlugin - */ - -'use strict'; - -var EventConstants = _dereq_(16); -var EventPluginHub = _dereq_(18); -var EventPropagators = _dereq_(21); -var ExecutionEnvironment = _dereq_(22); -var ReactUpdates = _dereq_(100); -var SyntheticEvent = _dereq_(108); - -var isEventSupported = _dereq_(151); -var isTextInputElement = _dereq_(153); -var keyOf = _dereq_(157); - -var topLevelTypes = EventConstants.topLevelTypes; - -var eventTypes = { - change: { - phasedRegistrationNames: { - bubbled: keyOf({onChange: null}), - captured: keyOf({onChangeCapture: null}) - }, - dependencies: [ - topLevelTypes.topBlur, - topLevelTypes.topChange, - topLevelTypes.topClick, - topLevelTypes.topFocus, - topLevelTypes.topInput, - topLevelTypes.topKeyDown, - topLevelTypes.topKeyUp, - topLevelTypes.topSelectionChange - ] - } -}; - -/** - * For IE shims - */ -var activeElement = null; -var activeElementID = null; -var activeElementValue = null; -var activeElementValueProp = null; - -/** - * SECTION: handle `change` event - */ -function shouldUseChangeEvent(elem) { - return ( - elem.nodeName === 'SELECT' || - (elem.nodeName === 'INPUT' && elem.type === 'file') - ); -} - -var doesChangeEventBubble = false; -if (ExecutionEnvironment.canUseDOM) { - // See `handleChange` comment below - doesChangeEventBubble = isEventSupported('change') && ( - (!('documentMode' in document) || document.documentMode > 8) - ); -} - -function manualDispatchChangeEvent(nativeEvent) { - var event = SyntheticEvent.getPooled( - eventTypes.change, - activeElementID, - nativeEvent - ); - EventPropagators.accumulateTwoPhaseDispatches(event); - - // If change and propertychange bubbled, we'd just bind to it like all the - // other events and have it go through ReactBrowserEventEmitter. Since it - // doesn't, we manually listen for the events and so we have to enqueue and - // process the abstract event manually. - // - // Batching is necessary here in order to ensure that all event handlers run - // before the next rerender (including event handlers attached to ancestor - // elements instead of directly on the input). Without this, controlled - // components don't work properly in conjunction with event bubbling because - // the component is rerendered and the value reverted before all the event - // handlers can run. See https://github.com/facebook/react/issues/708. - ReactUpdates.batchedUpdates(runEventInBatch, event); -} - -function runEventInBatch(event) { - EventPluginHub.enqueueEvents(event); - EventPluginHub.processEventQueue(); -} - -function startWatchingForChangeEventIE8(target, targetID) { - activeElement = target; - activeElementID = targetID; - activeElement.attachEvent('onchange', manualDispatchChangeEvent); -} - -function stopWatchingForChangeEventIE8() { - if (!activeElement) { - return; - } - activeElement.detachEvent('onchange', manualDispatchChangeEvent); - activeElement = null; - activeElementID = null; -} - -function getTargetIDForChangeEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topChange) { - return topLevelTargetID; - } -} -function handleEventsForChangeEventIE8( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topFocus) { - // stopWatching() should be a noop here but we call it just in case we - // missed a blur event somehow. - stopWatchingForChangeEventIE8(); - startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID); - } else if (topLevelType === topLevelTypes.topBlur) { - stopWatchingForChangeEventIE8(); - } -} - - -/** - * SECTION: handle `input` event - */ -var isInputEventSupported = false; -if (ExecutionEnvironment.canUseDOM) { - // IE9 claims to support the input event but fails to trigger it when - // deleting text, so we ignore its input events - isInputEventSupported = isEventSupported('input') && ( - (!('documentMode' in document) || document.documentMode > 9) - ); -} - -/** - * (For old IE.) Replacement getter/setter for the `value` property that gets - * set on the active element. - */ -var newValueProp = { - get: function() { - return activeElementValueProp.get.call(this); - }, - set: function(val) { - // Cast to a string so we can do equality checks. - activeElementValue = '' + val; - activeElementValueProp.set.call(this, val); - } -}; - -/** - * (For old IE.) Starts tracking propertychange events on the passed-in element - * and override the value property so that we can distinguish user events from - * value changes in JS. - */ -function startWatchingForValueChange(target, targetID) { - activeElement = target; - activeElementID = targetID; - activeElementValue = target.value; - activeElementValueProp = Object.getOwnPropertyDescriptor( - target.constructor.prototype, - 'value' - ); - - Object.defineProperty(activeElement, 'value', newValueProp); - activeElement.attachEvent('onpropertychange', handlePropertyChange); -} - -/** - * (For old IE.) Removes the event listeners from the currently-tracked element, - * if any exists. - */ -function stopWatchingForValueChange() { - if (!activeElement) { - return; - } - - // delete restores the original property definition - delete activeElement.value; - activeElement.detachEvent('onpropertychange', handlePropertyChange); - - activeElement = null; - activeElementID = null; - activeElementValue = null; - activeElementValueProp = null; -} - -/** - * (For old IE.) Handles a propertychange event, sending a `change` event if - * the value of the active element has changed. - */ -function handlePropertyChange(nativeEvent) { - if (nativeEvent.propertyName !== 'value') { - return; - } - var value = nativeEvent.srcElement.value; - if (value === activeElementValue) { - return; - } - activeElementValue = value; - - manualDispatchChangeEvent(nativeEvent); -} - -/** - * If a `change` event should be fired, returns the target's ID. - */ -function getTargetIDForInputEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topInput) { - // In modern browsers (i.e., not IE8 or IE9), the input event is exactly - // what we want so fall through here and trigger an abstract event - return topLevelTargetID; - } -} - -// For IE8 and IE9. -function handleEventsForInputEventIE( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topFocus) { - // In IE8, we can capture almost all .value changes by adding a - // propertychange handler and looking for events with propertyName - // equal to 'value' - // In IE9, propertychange fires for most input events but is buggy and - // doesn't fire when text is deleted, but conveniently, selectionchange - // appears to fire in all of the remaining cases so we catch those and - // forward the event if the value has changed - // In either case, we don't want to call the event handler if the value - // is changed from JS so we redefine a setter for `.value` that updates - // our activeElementValue variable, allowing us to ignore those changes - // - // stopWatching() should be a noop here but we call it just in case we - // missed a blur event somehow. - stopWatchingForValueChange(); - startWatchingForValueChange(topLevelTarget, topLevelTargetID); - } else if (topLevelType === topLevelTypes.topBlur) { - stopWatchingForValueChange(); - } -} - -// For IE8 and IE9. -function getTargetIDForInputEventIE( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topSelectionChange || - topLevelType === topLevelTypes.topKeyUp || - topLevelType === topLevelTypes.topKeyDown) { - // On the selectionchange event, the target is just document which isn't - // helpful for us so just check activeElement instead. - // - // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire - // propertychange on the first input event after setting `value` from a - // script and fires only keydown, keypress, keyup. Catching keyup usually - // gets it and catching keydown lets us fire an event for the first - // keystroke if user does a key repeat (it'll be a little delayed: right - // before the second keystroke). Other input methods (e.g., paste) seem to - // fire selectionchange normally. - if (activeElement && activeElement.value !== activeElementValue) { - activeElementValue = activeElement.value; - return activeElementID; - } - } -} - - -/** - * SECTION: handle `click` event - */ -function shouldUseClickEvent(elem) { - // Use the `click` event to detect changes to checkbox and radio inputs. - // This approach works across all browsers, whereas `change` does not fire - // until `blur` in IE8. - return ( - elem.nodeName === 'INPUT' && - (elem.type === 'checkbox' || elem.type === 'radio') - ); -} - -function getTargetIDForClickEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topClick) { - return topLevelTargetID; - } -} - -/** - * This plugin creates an `onChange` event that normalizes change events - * across form elements. This event fires at a time when it's possible to - * change the element's value without seeing a flicker. - * - * Supported elements are: - * - input (see `isTextInputElement`) - * - textarea - * - select - */ -var ChangeEventPlugin = { - - eventTypes: eventTypes, - - /** - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {*} An accumulation of synthetic events. - * @see {EventPluginHub.extractEvents} - */ - extractEvents: function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent) { - - var getTargetIDFunc, handleEventFunc; - if (shouldUseChangeEvent(topLevelTarget)) { - if (doesChangeEventBubble) { - getTargetIDFunc = getTargetIDForChangeEvent; - } else { - handleEventFunc = handleEventsForChangeEventIE8; - } - } else if (isTextInputElement(topLevelTarget)) { - if (isInputEventSupported) { - getTargetIDFunc = getTargetIDForInputEvent; - } else { - getTargetIDFunc = getTargetIDForInputEventIE; - handleEventFunc = handleEventsForInputEventIE; - } - } else if (shouldUseClickEvent(topLevelTarget)) { - getTargetIDFunc = getTargetIDForClickEvent; - } - - if (getTargetIDFunc) { - var targetID = getTargetIDFunc( - topLevelType, - topLevelTarget, - topLevelTargetID - ); - if (targetID) { - var event = SyntheticEvent.getPooled( - eventTypes.change, - targetID, - nativeEvent - ); - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; - } - } - - if (handleEventFunc) { - handleEventFunc( - topLevelType, - topLevelTarget, - topLevelTargetID - ); - } - } - -}; - -module.exports = ChangeEventPlugin; - -},{"100":100,"108":108,"151":151,"153":153,"157":157,"16":16,"18":18,"21":21,"22":22}],9:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ClientReactRootIndex - * @typechecks - */ - -'use strict'; - -var nextReactRootIndex = 0; - -var ClientReactRootIndex = { - createReactRootIndex: function() { - return nextReactRootIndex++; - } -}; - -module.exports = ClientReactRootIndex; - -},{}],10:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule DOMChildrenOperations - * @typechecks static-only - */ - -'use strict'; - -var Danger = _dereq_(13); -var ReactMultiChildUpdateTypes = _dereq_(79); - -var setTextContent = _dereq_(165); -var invariant = _dereq_(150); - -/** - * Inserts `childNode` as a child of `parentNode` at the `index`. - * - * @param {DOMElement} parentNode Parent node in which to insert. - * @param {DOMElement} childNode Child node to insert. - * @param {number} index Index at which to insert the child. - * @internal - */ -function insertChildAt(parentNode, childNode, index) { - // By exploiting arrays returning `undefined` for an undefined index, we can - // rely exclusively on `insertBefore(node, null)` instead of also using - // `appendChild(node)`. However, using `undefined` is not allowed by all - // browsers so we must replace it with `null`. - parentNode.insertBefore( - childNode, - parentNode.childNodes[index] || null - ); -} - -/** - * Operations for updating with DOM children. - */ -var DOMChildrenOperations = { - - dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup, - - updateTextContent: setTextContent, - - /** - * Updates a component's children by processing a series of updates. The - * update configurations are each expected to have a `parentNode` property. - * - * @param {array} updates List of update configurations. - * @param {array} markupList List of markup strings. - * @internal - */ - processUpdates: function(updates, markupList) { - var update; - // Mapping from parent IDs to initial child orderings. - var initialChildren = null; - // List of children that will be moved or removed. - var updatedChildren = null; - - for (var i = 0; i < updates.length; i++) { - update = updates[i]; - if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || - update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) { - var updatedIndex = update.fromIndex; - var updatedChild = update.parentNode.childNodes[updatedIndex]; - var parentID = update.parentID; - - ("production" !== "development" ? invariant( - updatedChild, - 'processUpdates(): Unable to find child %s of element. This ' + - 'probably means the DOM was unexpectedly mutated (e.g., by the ' + - 'browser), usually due to forgetting a when using tables, ' + - 'nesting tags like
          ,

          , or , or using non-SVG elements ' + - 'in an parent. Try inspecting the child nodes of the element ' + - 'with React ID `%s`.', - updatedIndex, - parentID - ) : invariant(updatedChild)); - - initialChildren = initialChildren || {}; - initialChildren[parentID] = initialChildren[parentID] || []; - initialChildren[parentID][updatedIndex] = updatedChild; - - updatedChildren = updatedChildren || []; - updatedChildren.push(updatedChild); - } - } - - var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList); - - // Remove updated children first so that `toIndex` is consistent. - if (updatedChildren) { - for (var j = 0; j < updatedChildren.length; j++) { - updatedChildren[j].parentNode.removeChild(updatedChildren[j]); - } - } - - for (var k = 0; k < updates.length; k++) { - update = updates[k]; - switch (update.type) { - case ReactMultiChildUpdateTypes.INSERT_MARKUP: - insertChildAt( - update.parentNode, - renderedMarkup[update.markupIndex], - update.toIndex - ); - break; - case ReactMultiChildUpdateTypes.MOVE_EXISTING: - insertChildAt( - update.parentNode, - initialChildren[update.parentID][update.fromIndex], - update.toIndex - ); - break; - case ReactMultiChildUpdateTypes.TEXT_CONTENT: - setTextContent( - update.parentNode, - update.textContent - ); - break; - case ReactMultiChildUpdateTypes.REMOVE_NODE: - // Already removed by the for-loop above. - break; - } - } - } - -}; - -module.exports = DOMChildrenOperations; - -},{"13":13,"150":150,"165":165,"79":79}],11:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule DOMProperty - * @typechecks static-only - */ - -/*jslint bitwise: true */ - -'use strict'; - -var invariant = _dereq_(150); - -function checkMask(value, bitmask) { - return (value & bitmask) === bitmask; -} - -var DOMPropertyInjection = { - /** - * Mapping from normalized, camelcased property names to a configuration that - * specifies how the associated DOM property should be accessed or rendered. - */ - MUST_USE_ATTRIBUTE: 0x1, - MUST_USE_PROPERTY: 0x2, - HAS_SIDE_EFFECTS: 0x4, - HAS_BOOLEAN_VALUE: 0x8, - HAS_NUMERIC_VALUE: 0x10, - HAS_POSITIVE_NUMERIC_VALUE: 0x20 | 0x10, - HAS_OVERLOADED_BOOLEAN_VALUE: 0x40, - - /** - * Inject some specialized knowledge about the DOM. This takes a config object - * with the following properties: - * - * isCustomAttribute: function that given an attribute name will return true - * if it can be inserted into the DOM verbatim. Useful for data-* or aria-* - * attributes where it's impossible to enumerate all of the possible - * attribute names, - * - * Properties: object mapping DOM property name to one of the - * DOMPropertyInjection constants or null. If your attribute isn't in here, - * it won't get written to the DOM. - * - * DOMAttributeNames: object mapping React attribute name to the DOM - * attribute name. Attribute names not specified use the **lowercase** - * normalized name. - * - * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties. - * Property names not specified use the normalized name. - * - * DOMMutationMethods: Properties that require special mutation methods. If - * `value` is undefined, the mutation method should unset the property. - * - * @param {object} domPropertyConfig the config as described above. - */ - injectDOMPropertyConfig: function(domPropertyConfig) { - var Properties = domPropertyConfig.Properties || {}; - var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {}; - var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {}; - var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {}; - - if (domPropertyConfig.isCustomAttribute) { - DOMProperty._isCustomAttributeFunctions.push( - domPropertyConfig.isCustomAttribute - ); - } - - for (var propName in Properties) { - ("production" !== "development" ? invariant( - !DOMProperty.isStandardName.hasOwnProperty(propName), - 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' + - '\'%s\' which has already been injected. You may be accidentally ' + - 'injecting the same DOM property config twice, or you may be ' + - 'injecting two configs that have conflicting property names.', - propName - ) : invariant(!DOMProperty.isStandardName.hasOwnProperty(propName))); - - DOMProperty.isStandardName[propName] = true; - - var lowerCased = propName.toLowerCase(); - DOMProperty.getPossibleStandardName[lowerCased] = propName; - - if (DOMAttributeNames.hasOwnProperty(propName)) { - var attributeName = DOMAttributeNames[propName]; - DOMProperty.getPossibleStandardName[attributeName] = propName; - DOMProperty.getAttributeName[propName] = attributeName; - } else { - DOMProperty.getAttributeName[propName] = lowerCased; - } - - DOMProperty.getPropertyName[propName] = - DOMPropertyNames.hasOwnProperty(propName) ? - DOMPropertyNames[propName] : - propName; - - if (DOMMutationMethods.hasOwnProperty(propName)) { - DOMProperty.getMutationMethod[propName] = DOMMutationMethods[propName]; - } else { - DOMProperty.getMutationMethod[propName] = null; - } - - var propConfig = Properties[propName]; - DOMProperty.mustUseAttribute[propName] = - checkMask(propConfig, DOMPropertyInjection.MUST_USE_ATTRIBUTE); - DOMProperty.mustUseProperty[propName] = - checkMask(propConfig, DOMPropertyInjection.MUST_USE_PROPERTY); - DOMProperty.hasSideEffects[propName] = - checkMask(propConfig, DOMPropertyInjection.HAS_SIDE_EFFECTS); - DOMProperty.hasBooleanValue[propName] = - checkMask(propConfig, DOMPropertyInjection.HAS_BOOLEAN_VALUE); - DOMProperty.hasNumericValue[propName] = - checkMask(propConfig, DOMPropertyInjection.HAS_NUMERIC_VALUE); - DOMProperty.hasPositiveNumericValue[propName] = - checkMask(propConfig, DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE); - DOMProperty.hasOverloadedBooleanValue[propName] = - checkMask(propConfig, DOMPropertyInjection.HAS_OVERLOADED_BOOLEAN_VALUE); - - ("production" !== "development" ? invariant( - !DOMProperty.mustUseAttribute[propName] || - !DOMProperty.mustUseProperty[propName], - 'DOMProperty: Cannot require using both attribute and property: %s', - propName - ) : invariant(!DOMProperty.mustUseAttribute[propName] || - !DOMProperty.mustUseProperty[propName])); - ("production" !== "development" ? invariant( - DOMProperty.mustUseProperty[propName] || - !DOMProperty.hasSideEffects[propName], - 'DOMProperty: Properties that have side effects must use property: %s', - propName - ) : invariant(DOMProperty.mustUseProperty[propName] || - !DOMProperty.hasSideEffects[propName])); - ("production" !== "development" ? invariant( - !!DOMProperty.hasBooleanValue[propName] + - !!DOMProperty.hasNumericValue[propName] + - !!DOMProperty.hasOverloadedBooleanValue[propName] <= 1, - 'DOMProperty: Value can be one of boolean, overloaded boolean, or ' + - 'numeric value, but not a combination: %s', - propName - ) : invariant(!!DOMProperty.hasBooleanValue[propName] + - !!DOMProperty.hasNumericValue[propName] + - !!DOMProperty.hasOverloadedBooleanValue[propName] <= 1)); - } - } -}; -var defaultValueCache = {}; - -/** - * DOMProperty exports lookup objects that can be used like functions: - * - * > DOMProperty.isValid['id'] - * true - * > DOMProperty.isValid['foobar'] - * undefined - * - * Although this may be confusing, it performs better in general. - * - * @see http://jsperf.com/key-exists - * @see http://jsperf.com/key-missing - */ -var DOMProperty = { - - ID_ATTRIBUTE_NAME: 'data-reactid', - - /** - * Checks whether a property name is a standard property. - * @type {Object} - */ - isStandardName: {}, - - /** - * Mapping from lowercase property names to the properly cased version, used - * to warn in the case of missing properties. - * @type {Object} - */ - getPossibleStandardName: {}, - - /** - * Mapping from normalized names to attribute names that differ. Attribute - * names are used when rendering markup or with `*Attribute()`. - * @type {Object} - */ - getAttributeName: {}, - - /** - * Mapping from normalized names to properties on DOM node instances. - * (This includes properties that mutate due to external factors.) - * @type {Object} - */ - getPropertyName: {}, - - /** - * Mapping from normalized names to mutation methods. This will only exist if - * mutation cannot be set simply by the property or `setAttribute()`. - * @type {Object} - */ - getMutationMethod: {}, - - /** - * Whether the property must be accessed and mutated as an object property. - * @type {Object} - */ - mustUseAttribute: {}, - - /** - * Whether the property must be accessed and mutated using `*Attribute()`. - * (This includes anything that fails ` in `.) - * @type {Object} - */ - mustUseProperty: {}, - - /** - * Whether or not setting a value causes side effects such as triggering - * resources to be loaded or text selection changes. We must ensure that - * the value is only set if it has changed. - * @type {Object} - */ - hasSideEffects: {}, - - /** - * Whether the property should be removed when set to a falsey value. - * @type {Object} - */ - hasBooleanValue: {}, - - /** - * Whether the property must be numeric or parse as a - * numeric and should be removed when set to a falsey value. - * @type {Object} - */ - hasNumericValue: {}, - - /** - * Whether the property must be positive numeric or parse as a positive - * numeric and should be removed when set to a falsey value. - * @type {Object} - */ - hasPositiveNumericValue: {}, - - /** - * Whether the property can be used as a flag as well as with a value. Removed - * when strictly equal to false; present without a value when strictly equal - * to true; present with a value otherwise. - * @type {Object} - */ - hasOverloadedBooleanValue: {}, - - /** - * All of the isCustomAttribute() functions that have been injected. - */ - _isCustomAttributeFunctions: [], - - /** - * Checks whether a property name is a custom attribute. - * @method - */ - isCustomAttribute: function(attributeName) { - for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) { - var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i]; - if (isCustomAttributeFn(attributeName)) { - return true; - } - } - return false; - }, - - /** - * Returns the default property value for a DOM property (i.e., not an - * attribute). Most default values are '' or false, but not all. Worse yet, - * some (in particular, `type`) vary depending on the type of element. - * - * TODO: Is it better to grab all the possible properties when creating an - * element to avoid having to create the same element twice? - */ - getDefaultValueForProperty: function(nodeName, prop) { - var nodeDefaults = defaultValueCache[nodeName]; - var testElement; - if (!nodeDefaults) { - defaultValueCache[nodeName] = nodeDefaults = {}; - } - if (!(prop in nodeDefaults)) { - testElement = document.createElement(nodeName); - nodeDefaults[prop] = testElement[prop]; - } - return nodeDefaults[prop]; - }, - - injection: DOMPropertyInjection -}; - -module.exports = DOMProperty; - -},{"150":150}],12:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule DOMPropertyOperations - * @typechecks static-only - */ - -'use strict'; - -var DOMProperty = _dereq_(11); - -var quoteAttributeValueForBrowser = _dereq_(163); -var warning = _dereq_(171); - -function shouldIgnoreValue(name, value) { - return value == null || - (DOMProperty.hasBooleanValue[name] && !value) || - (DOMProperty.hasNumericValue[name] && isNaN(value)) || - (DOMProperty.hasPositiveNumericValue[name] && (value < 1)) || - (DOMProperty.hasOverloadedBooleanValue[name] && value === false); -} - -if ("production" !== "development") { - var reactProps = { - children: true, - dangerouslySetInnerHTML: true, - key: true, - ref: true - }; - var warnedProperties = {}; - - var warnUnknownProperty = function(name) { - if (reactProps.hasOwnProperty(name) && reactProps[name] || - warnedProperties.hasOwnProperty(name) && warnedProperties[name]) { - return; - } - - warnedProperties[name] = true; - var lowerCasedName = name.toLowerCase(); - - // data-* attributes should be lowercase; suggest the lowercase version - var standardName = ( - DOMProperty.isCustomAttribute(lowerCasedName) ? - lowerCasedName : - DOMProperty.getPossibleStandardName.hasOwnProperty(lowerCasedName) ? - DOMProperty.getPossibleStandardName[lowerCasedName] : - null - ); - - // For now, only warn when we have a suggested correction. This prevents - // logging too much when using transferPropsTo. - ("production" !== "development" ? warning( - standardName == null, - 'Unknown DOM property %s. Did you mean %s?', - name, - standardName - ) : null); - - }; -} - -/** - * Operations for dealing with DOM properties. - */ -var DOMPropertyOperations = { - - /** - * Creates markup for the ID property. - * - * @param {string} id Unescaped ID. - * @return {string} Markup string. - */ - createMarkupForID: function(id) { - return DOMProperty.ID_ATTRIBUTE_NAME + '=' + - quoteAttributeValueForBrowser(id); - }, - - /** - * Creates markup for a property. - * - * @param {string} name - * @param {*} value - * @return {?string} Markup string, or null if the property was invalid. - */ - createMarkupForProperty: function(name, value) { - if (DOMProperty.isStandardName.hasOwnProperty(name) && - DOMProperty.isStandardName[name]) { - if (shouldIgnoreValue(name, value)) { - return ''; - } - var attributeName = DOMProperty.getAttributeName[name]; - if (DOMProperty.hasBooleanValue[name] || - (DOMProperty.hasOverloadedBooleanValue[name] && value === true)) { - return attributeName; - } - return attributeName + '=' + quoteAttributeValueForBrowser(value); - } else if (DOMProperty.isCustomAttribute(name)) { - if (value == null) { - return ''; - } - return name + '=' + quoteAttributeValueForBrowser(value); - } else if ("production" !== "development") { - warnUnknownProperty(name); - } - return null; - }, - - /** - * Sets the value for a property on a node. - * - * @param {DOMElement} node - * @param {string} name - * @param {*} value - */ - setValueForProperty: function(node, name, value) { - if (DOMProperty.isStandardName.hasOwnProperty(name) && - DOMProperty.isStandardName[name]) { - var mutationMethod = DOMProperty.getMutationMethod[name]; - if (mutationMethod) { - mutationMethod(node, value); - } else if (shouldIgnoreValue(name, value)) { - this.deleteValueForProperty(node, name); - } else if (DOMProperty.mustUseAttribute[name]) { - // `setAttribute` with objects becomes only `[object]` in IE8/9, - // ('' + value) makes it output the correct toString()-value. - node.setAttribute(DOMProperty.getAttributeName[name], '' + value); - } else { - var propName = DOMProperty.getPropertyName[name]; - // Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the - // property type before comparing; only `value` does and is string. - if (!DOMProperty.hasSideEffects[name] || - ('' + node[propName]) !== ('' + value)) { - // Contrary to `setAttribute`, object properties are properly - // `toString`ed by IE8/9. - node[propName] = value; - } - } - } else if (DOMProperty.isCustomAttribute(name)) { - if (value == null) { - node.removeAttribute(name); - } else { - node.setAttribute(name, '' + value); - } - } else if ("production" !== "development") { - warnUnknownProperty(name); - } - }, - - /** - * Deletes the value for a property on a node. - * - * @param {DOMElement} node - * @param {string} name - */ - deleteValueForProperty: function(node, name) { - if (DOMProperty.isStandardName.hasOwnProperty(name) && - DOMProperty.isStandardName[name]) { - var mutationMethod = DOMProperty.getMutationMethod[name]; - if (mutationMethod) { - mutationMethod(node, undefined); - } else if (DOMProperty.mustUseAttribute[name]) { - node.removeAttribute(DOMProperty.getAttributeName[name]); - } else { - var propName = DOMProperty.getPropertyName[name]; - var defaultValue = DOMProperty.getDefaultValueForProperty( - node.nodeName, - propName - ); - if (!DOMProperty.hasSideEffects[name] || - ('' + node[propName]) !== defaultValue) { - node[propName] = defaultValue; - } - } - } else if (DOMProperty.isCustomAttribute(name)) { - node.removeAttribute(name); - } else if ("production" !== "development") { - warnUnknownProperty(name); - } - } - -}; - -module.exports = DOMPropertyOperations; - -},{"11":11,"163":163,"171":171}],13:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule Danger - * @typechecks static-only - */ - -/*jslint evil: true, sub: true */ - -'use strict'; - -var ExecutionEnvironment = _dereq_(22); - -var createNodesFromMarkup = _dereq_(126); -var emptyFunction = _dereq_(129); -var getMarkupWrap = _dereq_(142); -var invariant = _dereq_(150); - -var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/; -var RESULT_INDEX_ATTR = 'data-danger-index'; - -/** - * Extracts the `nodeName` from a string of markup. - * - * NOTE: Extracting the `nodeName` does not require a regular expression match - * because we make assumptions about React-generated markup (i.e. there are no - * spaces surrounding the opening tag and there is at least one attribute). - * - * @param {string} markup String of markup. - * @return {string} Node name of the supplied markup. - * @see http://jsperf.com/extract-nodename - */ -function getNodeName(markup) { - return markup.substring(1, markup.indexOf(' ')); -} - -var Danger = { - - /** - * Renders markup into an array of nodes. The markup is expected to render - * into a list of root nodes. Also, the length of `resultList` and - * `markupList` should be the same. - * - * @param {array} markupList List of markup strings to render. - * @return {array} List of rendered nodes. - * @internal - */ - dangerouslyRenderMarkup: function(markupList) { - ("production" !== "development" ? invariant( - ExecutionEnvironment.canUseDOM, - 'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' + - 'thread. Make sure `window` and `document` are available globally ' + - 'before requiring React when unit testing or use ' + - 'React.renderToString for server rendering.' - ) : invariant(ExecutionEnvironment.canUseDOM)); - var nodeName; - var markupByNodeName = {}; - // Group markup by `nodeName` if a wrap is necessary, else by '*'. - for (var i = 0; i < markupList.length; i++) { - ("production" !== "development" ? invariant( - markupList[i], - 'dangerouslyRenderMarkup(...): Missing markup.' - ) : invariant(markupList[i])); - nodeName = getNodeName(markupList[i]); - nodeName = getMarkupWrap(nodeName) ? nodeName : '*'; - markupByNodeName[nodeName] = markupByNodeName[nodeName] || []; - markupByNodeName[nodeName][i] = markupList[i]; - } - var resultList = []; - var resultListAssignmentCount = 0; - for (nodeName in markupByNodeName) { - if (!markupByNodeName.hasOwnProperty(nodeName)) { - continue; - } - var markupListByNodeName = markupByNodeName[nodeName]; - - // This for-in loop skips the holes of the sparse array. The order of - // iteration should follow the order of assignment, which happens to match - // numerical index order, but we don't rely on that. - var resultIndex; - for (resultIndex in markupListByNodeName) { - if (markupListByNodeName.hasOwnProperty(resultIndex)) { - var markup = markupListByNodeName[resultIndex]; - - // Push the requested markup with an additional RESULT_INDEX_ATTR - // attribute. If the markup does not start with a < character, it - // will be discarded below (with an appropriate console.error). - markupListByNodeName[resultIndex] = markup.replace( - OPEN_TAG_NAME_EXP, - // This index will be parsed back out below. - '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" ' - ); - } - } - - // Render each group of markup with similar wrapping `nodeName`. - var renderNodes = createNodesFromMarkup( - markupListByNodeName.join(''), - emptyFunction // Do nothing special with

          ; - * } - * }); - * - * The class specification supports a specific protocol of methods that have - * special meaning (e.g. `render`). See `ReactClassInterface` for - * more the comprehensive protocol. Any other properties and methods in the - * class specification will available on the prototype. - * - * @interface ReactClassInterface - * @internal - */ -var ReactClassInterface = { - - /** - * An array of Mixin objects to include when defining your component. - * - * @type {array} - * @optional - */ - mixins: SpecPolicy.DEFINE_MANY, - - /** - * An object containing properties and methods that should be defined on - * the component's constructor instead of its prototype (static methods). - * - * @type {object} - * @optional - */ - statics: SpecPolicy.DEFINE_MANY, - - /** - * Definition of prop types for this component. - * - * @type {object} - * @optional - */ - propTypes: SpecPolicy.DEFINE_MANY, - - /** - * Definition of context types for this component. - * - * @type {object} - * @optional - */ - contextTypes: SpecPolicy.DEFINE_MANY, - - /** - * Definition of context types this component sets for its children. - * - * @type {object} - * @optional - */ - childContextTypes: SpecPolicy.DEFINE_MANY, - - // ==== Definition methods ==== - - /** - * Invoked when the component is mounted. Values in the mapping will be set on - * `this.props` if that prop is not specified (i.e. using an `in` check). - * - * This method is invoked before `getInitialState` and therefore cannot rely - * on `this.state` or use `this.setState`. - * - * @return {object} - * @optional - */ - getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED, - - /** - * Invoked once before the component is mounted. The return value will be used - * as the initial value of `this.state`. - * - * getInitialState: function() { - * return { - * isOn: false, - * fooBaz: new BazFoo() - * } - * } - * - * @return {object} - * @optional - */ - getInitialState: SpecPolicy.DEFINE_MANY_MERGED, - - /** - * @return {object} - * @optional - */ - getChildContext: SpecPolicy.DEFINE_MANY_MERGED, - - /** - * Uses props from `this.props` and state from `this.state` to render the - * structure of the component. - * - * No guarantees are made about when or how often this method is invoked, so - * it must not have side effects. - * - * render: function() { - * var name = this.props.name; - * return
          Hello, {name}!
          ; - * } - * - * @return {ReactComponent} - * @nosideeffects - * @required - */ - render: SpecPolicy.DEFINE_ONCE, - - - - // ==== Delegate methods ==== - - /** - * Invoked when the component is initially created and about to be mounted. - * This may have side effects, but any external subscriptions or data created - * by this method must be cleaned up in `componentWillUnmount`. - * - * @optional - */ - componentWillMount: SpecPolicy.DEFINE_MANY, - - /** - * Invoked when the component has been mounted and has a DOM representation. - * However, there is no guarantee that the DOM node is in the document. - * - * Use this as an opportunity to operate on the DOM when the component has - * been mounted (initialized and rendered) for the first time. - * - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidMount: SpecPolicy.DEFINE_MANY, - - /** - * Invoked before the component receives new props. - * - * Use this as an opportunity to react to a prop transition by updating the - * state using `this.setState`. Current props are accessed via `this.props`. - * - * componentWillReceiveProps: function(nextProps, nextContext) { - * this.setState({ - * likesIncreasing: nextProps.likeCount > this.props.likeCount - * }); - * } - * - * NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop - * transition may cause a state change, but the opposite is not true. If you - * need it, you are probably looking for `componentWillUpdate`. - * - * @param {object} nextProps - * @optional - */ - componentWillReceiveProps: SpecPolicy.DEFINE_MANY, - - /** - * Invoked while deciding if the component should be updated as a result of - * receiving new props, state and/or context. - * - * Use this as an opportunity to `return false` when you're certain that the - * transition to the new props/state/context will not require a component - * update. - * - * shouldComponentUpdate: function(nextProps, nextState, nextContext) { - * return !equal(nextProps, this.props) || - * !equal(nextState, this.state) || - * !equal(nextContext, this.context); - * } - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @return {boolean} True if the component should update. - * @optional - */ - shouldComponentUpdate: SpecPolicy.DEFINE_ONCE, - - /** - * Invoked when the component is about to update due to a transition from - * `this.props`, `this.state` and `this.context` to `nextProps`, `nextState` - * and `nextContext`. - * - * Use this as an opportunity to perform preparation before an update occurs. - * - * NOTE: You **cannot** use `this.setState()` in this method. - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @param {ReactReconcileTransaction} transaction - * @optional - */ - componentWillUpdate: SpecPolicy.DEFINE_MANY, - - /** - * Invoked when the component's DOM representation has been updated. - * - * Use this as an opportunity to operate on the DOM when the component has - * been updated. - * - * @param {object} prevProps - * @param {?object} prevState - * @param {?object} prevContext - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidUpdate: SpecPolicy.DEFINE_MANY, - - /** - * Invoked when the component is about to be removed from its parent and have - * its DOM representation destroyed. - * - * Use this as an opportunity to deallocate any external resources. - * - * NOTE: There is no `componentDidUnmount` since your component will have been - * destroyed by that point. - * - * @optional - */ - componentWillUnmount: SpecPolicy.DEFINE_MANY, - - - - // ==== Advanced methods ==== - - /** - * Updates the component's currently mounted DOM representation. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. - * - * @param {ReactReconcileTransaction} transaction - * @internal - * @overridable - */ - updateComponent: SpecPolicy.OVERRIDE_BASE - -}; - -/** - * Mapping from class specification keys to special processing functions. - * - * Although these are declared like instance properties in the specification - * when defining classes using `React.createClass`, they are actually static - * and are accessible on the constructor instead of the prototype. Despite - * being static, they must be defined outside of the "statics" key under - * which all other static methods are defined. - */ -var RESERVED_SPEC_KEYS = { - displayName: function(Constructor, displayName) { - Constructor.displayName = displayName; - }, - mixins: function(Constructor, mixins) { - if (mixins) { - for (var i = 0; i < mixins.length; i++) { - mixSpecIntoComponent(Constructor, mixins[i]); - } - } - }, - childContextTypes: function(Constructor, childContextTypes) { - if ("production" !== "development") { - validateTypeDef( - Constructor, - childContextTypes, - ReactPropTypeLocations.childContext - ); - } - Constructor.childContextTypes = assign( - {}, - Constructor.childContextTypes, - childContextTypes - ); - }, - contextTypes: function(Constructor, contextTypes) { - if ("production" !== "development") { - validateTypeDef( - Constructor, - contextTypes, - ReactPropTypeLocations.context - ); - } - Constructor.contextTypes = assign( - {}, - Constructor.contextTypes, - contextTypes - ); - }, - /** - * Special case getDefaultProps which should move into statics but requires - * automatic merging. - */ - getDefaultProps: function(Constructor, getDefaultProps) { - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps = createMergedResultFunction( - Constructor.getDefaultProps, - getDefaultProps - ); - } else { - Constructor.getDefaultProps = getDefaultProps; - } - }, - propTypes: function(Constructor, propTypes) { - if ("production" !== "development") { - validateTypeDef( - Constructor, - propTypes, - ReactPropTypeLocations.prop - ); - } - Constructor.propTypes = assign( - {}, - Constructor.propTypes, - propTypes - ); - }, - statics: function(Constructor, statics) { - mixStaticSpecIntoComponent(Constructor, statics); - } -}; - -function validateTypeDef(Constructor, typeDef, location) { - for (var propName in typeDef) { - if (typeDef.hasOwnProperty(propName)) { - // use a warning instead of an invariant so components - // don't show up in prod but not in __DEV__ - ("production" !== "development" ? warning( - typeof typeDef[propName] === 'function', - '%s: %s type `%s` is invalid; it must be a function, usually from ' + - 'React.PropTypes.', - Constructor.displayName || 'ReactClass', - ReactPropTypeLocationNames[location], - propName - ) : null); - } - } -} - -function validateMethodOverride(proto, name) { - var specPolicy = ReactClassInterface.hasOwnProperty(name) ? - ReactClassInterface[name] : - null; - - // Disallow overriding of base class methods unless explicitly allowed. - if (ReactClassMixin.hasOwnProperty(name)) { - ("production" !== "development" ? invariant( - specPolicy === SpecPolicy.OVERRIDE_BASE, - 'ReactClassInterface: You are attempting to override ' + - '`%s` from your class specification. Ensure that your method names ' + - 'do not overlap with React methods.', - name - ) : invariant(specPolicy === SpecPolicy.OVERRIDE_BASE)); - } - - // Disallow defining methods more than once unless explicitly allowed. - if (proto.hasOwnProperty(name)) { - ("production" !== "development" ? invariant( - specPolicy === SpecPolicy.DEFINE_MANY || - specPolicy === SpecPolicy.DEFINE_MANY_MERGED, - 'ReactClassInterface: You are attempting to define ' + - '`%s` on your component more than once. This conflict may be due ' + - 'to a mixin.', - name - ) : invariant(specPolicy === SpecPolicy.DEFINE_MANY || - specPolicy === SpecPolicy.DEFINE_MANY_MERGED)); - } -} - -/** - * Mixin helper which handles policy validation and reserved - * specification keys when building React classses. - */ -function mixSpecIntoComponent(Constructor, spec) { - if (!spec) { - return; - } - - ("production" !== "development" ? invariant( - typeof spec !== 'function', - 'ReactClass: You\'re attempting to ' + - 'use a component class as a mixin. Instead, just use a regular object.' - ) : invariant(typeof spec !== 'function')); - ("production" !== "development" ? invariant( - !ReactElement.isValidElement(spec), - 'ReactClass: You\'re attempting to ' + - 'use a component as a mixin. Instead, just use a regular object.' - ) : invariant(!ReactElement.isValidElement(spec))); - - var proto = Constructor.prototype; - - // By handling mixins before any other properties, we ensure the same - // chaining order is applied to methods with DEFINE_MANY policy, whether - // mixins are listed before or after these methods in the spec. - if (spec.hasOwnProperty(MIXINS_KEY)) { - RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins); - } - - for (var name in spec) { - if (!spec.hasOwnProperty(name)) { - continue; - } - - if (name === MIXINS_KEY) { - // We have already handled mixins in a special case above - continue; - } - - var property = spec[name]; - validateMethodOverride(proto, name); - - if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { - RESERVED_SPEC_KEYS[name](Constructor, property); - } else { - // Setup methods on prototype: - // The following member methods should not be automatically bound: - // 1. Expected ReactClass methods (in the "interface"). - // 2. Overridden methods (that were mixed in). - var isReactClassMethod = - ReactClassInterface.hasOwnProperty(name); - var isAlreadyDefined = proto.hasOwnProperty(name); - var markedDontBind = property && property.__reactDontBind; - var isFunction = typeof property === 'function'; - var shouldAutoBind = - isFunction && - !isReactClassMethod && - !isAlreadyDefined && - !markedDontBind; - - if (shouldAutoBind) { - if (!proto.__reactAutoBindMap) { - proto.__reactAutoBindMap = {}; - } - proto.__reactAutoBindMap[name] = property; - proto[name] = property; - } else { - if (isAlreadyDefined) { - var specPolicy = ReactClassInterface[name]; - - // These cases should already be caught by validateMethodOverride - ("production" !== "development" ? invariant( - isReactClassMethod && ( - (specPolicy === SpecPolicy.DEFINE_MANY_MERGED || specPolicy === SpecPolicy.DEFINE_MANY) - ), - 'ReactClass: Unexpected spec policy %s for key %s ' + - 'when mixing in component specs.', - specPolicy, - name - ) : invariant(isReactClassMethod && ( - (specPolicy === SpecPolicy.DEFINE_MANY_MERGED || specPolicy === SpecPolicy.DEFINE_MANY) - ))); - - // For methods which are defined more than once, call the existing - // methods before calling the new property, merging if appropriate. - if (specPolicy === SpecPolicy.DEFINE_MANY_MERGED) { - proto[name] = createMergedResultFunction(proto[name], property); - } else if (specPolicy === SpecPolicy.DEFINE_MANY) { - proto[name] = createChainedFunction(proto[name], property); - } - } else { - proto[name] = property; - if ("production" !== "development") { - // Add verbose displayName to the function, which helps when looking - // at profiling tools. - if (typeof property === 'function' && spec.displayName) { - proto[name].displayName = spec.displayName + '_' + name; - } - } - } - } - } - } -} - -function mixStaticSpecIntoComponent(Constructor, statics) { - if (!statics) { - return; - } - for (var name in statics) { - var property = statics[name]; - if (!statics.hasOwnProperty(name)) { - continue; - } - - var isReserved = name in RESERVED_SPEC_KEYS; - ("production" !== "development" ? invariant( - !isReserved, - 'ReactClass: You are attempting to define a reserved ' + - 'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' + - 'as an instance property instead; it will still be accessible on the ' + - 'constructor.', - name - ) : invariant(!isReserved)); - - var isInherited = name in Constructor; - ("production" !== "development" ? invariant( - !isInherited, - 'ReactClass: You are attempting to define ' + - '`%s` on your component more than once. This conflict may be ' + - 'due to a mixin.', - name - ) : invariant(!isInherited)); - Constructor[name] = property; - } -} - -/** - * Merge two objects, but throw if both contain the same key. - * - * @param {object} one The first object, which is mutated. - * @param {object} two The second object - * @return {object} one after it has been mutated to contain everything in two. - */ -function mergeIntoWithNoDuplicateKeys(one, two) { - ("production" !== "development" ? invariant( - one && two && typeof one === 'object' && typeof two === 'object', - 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.' - ) : invariant(one && two && typeof one === 'object' && typeof two === 'object')); - - for (var key in two) { - if (two.hasOwnProperty(key)) { - ("production" !== "development" ? invariant( - one[key] === undefined, - 'mergeIntoWithNoDuplicateKeys(): ' + - 'Tried to merge two objects with the same key: `%s`. This conflict ' + - 'may be due to a mixin; in particular, this may be caused by two ' + - 'getInitialState() or getDefaultProps() methods returning objects ' + - 'with clashing keys.', - key - ) : invariant(one[key] === undefined)); - one[key] = two[key]; - } - } - return one; -} - -/** - * Creates a function that invokes two functions and merges their return values. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ -function createMergedResultFunction(one, two) { - return function mergedResult() { - var a = one.apply(this, arguments); - var b = two.apply(this, arguments); - if (a == null) { - return b; - } else if (b == null) { - return a; - } - var c = {}; - mergeIntoWithNoDuplicateKeys(c, a); - mergeIntoWithNoDuplicateKeys(c, b); - return c; - }; -} - -/** - * Creates a function that invokes two functions and ignores their return vales. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ -function createChainedFunction(one, two) { - return function chainedFunction() { - one.apply(this, arguments); - two.apply(this, arguments); - }; -} - -/** - * Binds a method to the component. - * - * @param {object} component Component whose method is going to be bound. - * @param {function} method Method to be bound. - * @return {function} The bound method. - */ -function bindAutoBindMethod(component, method) { - var boundMethod = method.bind(component); - if ("production" !== "development") { - boundMethod.__reactBoundContext = component; - boundMethod.__reactBoundMethod = method; - boundMethod.__reactBoundArguments = null; - var componentName = component.constructor.displayName; - var _bind = boundMethod.bind; - /* eslint-disable block-scoped-var, no-undef */ - boundMethod.bind = function(newThis ) {for (var args=[],$__0=1,$__1=arguments.length;$__0<$__1;$__0++) args.push(arguments[$__0]); - // User is trying to bind() an autobound method; we effectively will - // ignore the value of "this" that the user is trying to use, so - // let's warn. - if (newThis !== component && newThis !== null) { - ("production" !== "development" ? warning( - false, - 'bind(): React component methods may only be bound to the ' + - 'component instance. See %s', - componentName - ) : null); - } else if (!args.length) { - ("production" !== "development" ? warning( - false, - 'bind(): You are binding a component method to the component. ' + - 'React does this for you automatically in a high-performance ' + - 'way, so you can safely remove this call. See %s', - componentName - ) : null); - return boundMethod; - } - var reboundMethod = _bind.apply(boundMethod, arguments); - reboundMethod.__reactBoundContext = component; - reboundMethod.__reactBoundMethod = method; - reboundMethod.__reactBoundArguments = args; - return reboundMethod; - /* eslint-enable */ - }; - } - return boundMethod; -} - -/** - * Binds all auto-bound methods in a component. - * - * @param {object} component Component whose method is going to be bound. - */ -function bindAutoBindMethods(component) { - for (var autoBindKey in component.__reactAutoBindMap) { - if (component.__reactAutoBindMap.hasOwnProperty(autoBindKey)) { - var method = component.__reactAutoBindMap[autoBindKey]; - component[autoBindKey] = bindAutoBindMethod( - component, - ReactErrorUtils.guard( - method, - component.constructor.displayName + '.' + autoBindKey - ) - ); - } - } -} - -var typeDeprecationDescriptor = { - enumerable: false, - get: function() { - var displayName = this.displayName || this.name || 'Component'; - ("production" !== "development" ? warning( - false, - '%s.type is deprecated. Use %s directly to access the class.', - displayName, - displayName - ) : null); - Object.defineProperty(this, 'type', { - value: this - }); - return this; - } -}; - -/** - * Add more to the ReactClass base class. These are all legacy features and - * therefore not already part of the modern ReactComponent. - */ -var ReactClassMixin = { - - /** - * TODO: This will be deprecated because state should always keep a consistent - * type signature and the only use case for this, is to avoid that. - */ - replaceState: function(newState, callback) { - ReactUpdateQueue.enqueueReplaceState(this, newState); - if (callback) { - ReactUpdateQueue.enqueueCallback(this, callback); - } - }, - - /** - * Checks whether or not this composite component is mounted. - * @return {boolean} True if mounted, false otherwise. - * @protected - * @final - */ - isMounted: function() { - if ("production" !== "development") { - var owner = ReactCurrentOwner.current; - if (owner !== null) { - ("production" !== "development" ? warning( - owner._warnedAboutRefsInRender, - '%s is accessing isMounted inside its render() function. ' + - 'render() should be a pure function of props and state. It should ' + - 'never access something that requires stale data from the previous ' + - 'render, such as refs. Move this logic to componentDidMount and ' + - 'componentDidUpdate instead.', - owner.getName() || 'A component' - ) : null); - owner._warnedAboutRefsInRender = true; - } - } - var internalInstance = ReactInstanceMap.get(this); - return ( - internalInstance && - internalInstance !== ReactLifeCycle.currentlyMountingInstance - ); - }, - - /** - * Sets a subset of the props. - * - * @param {object} partialProps Subset of the next props. - * @param {?function} callback Called after props are updated. - * @final - * @public - * @deprecated - */ - setProps: function(partialProps, callback) { - ReactUpdateQueue.enqueueSetProps(this, partialProps); - if (callback) { - ReactUpdateQueue.enqueueCallback(this, callback); - } - }, - - /** - * Replace all the props. - * - * @param {object} newProps Subset of the next props. - * @param {?function} callback Called after props are updated. - * @final - * @public - * @deprecated - */ - replaceProps: function(newProps, callback) { - ReactUpdateQueue.enqueueReplaceProps(this, newProps); - if (callback) { - ReactUpdateQueue.enqueueCallback(this, callback); - } - } -}; - -var ReactClassComponent = function() {}; -assign( - ReactClassComponent.prototype, - ReactComponent.prototype, - ReactClassMixin -); - -/** - * Module for creating composite components. - * - * @class ReactClass - */ -var ReactClass = { - - /** - * Creates a composite component class given a class specification. - * - * @param {object} spec Class specification (which must define `render`). - * @return {function} Component constructor function. - * @public - */ - createClass: function(spec) { - var Constructor = function(props, context) { - // This constructor is overridden by mocks. The argument is used - // by mocks to assert on what gets mounted. - - if ("production" !== "development") { - ("production" !== "development" ? warning( - this instanceof Constructor, - 'Something is calling a React component directly. Use a factory or ' + - 'JSX instead. See: http://fb.me/react-legacyfactory' - ) : null); - } - - // Wire up auto-binding - if (this.__reactAutoBindMap) { - bindAutoBindMethods(this); - } - - this.props = props; - this.context = context; - this.state = null; - - // ReactClasses doesn't have constructors. Instead, they use the - // getInitialState and componentWillMount methods for initialization. - - var initialState = this.getInitialState ? this.getInitialState() : null; - if ("production" !== "development") { - // We allow auto-mocks to proceed as if they're returning null. - if (typeof initialState === 'undefined' && - this.getInitialState._isMockFunction) { - // This is probably bad practice. Consider warning here and - // deprecating this convenience. - initialState = null; - } - } - ("production" !== "development" ? invariant( - typeof initialState === 'object' && !Array.isArray(initialState), - '%s.getInitialState(): must return an object or null', - Constructor.displayName || 'ReactCompositeComponent' - ) : invariant(typeof initialState === 'object' && !Array.isArray(initialState))); - - this.state = initialState; - }; - Constructor.prototype = new ReactClassComponent(); - Constructor.prototype.constructor = Constructor; - - injectedMixins.forEach( - mixSpecIntoComponent.bind(null, Constructor) - ); - - mixSpecIntoComponent(Constructor, spec); - - // Initialize the defaultProps property after all mixins have been merged - if (Constructor.getDefaultProps) { - Constructor.defaultProps = Constructor.getDefaultProps(); - } - - if ("production" !== "development") { - // This is a tag to indicate that the use of these method names is ok, - // since it's used with createClass. If it's not, then it's likely a - // mistake so we'll warn you to use the static property, property - // initializer or constructor respectively. - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps.isReactClassApproved = {}; - } - if (Constructor.prototype.getInitialState) { - Constructor.prototype.getInitialState.isReactClassApproved = {}; - } - } - - ("production" !== "development" ? invariant( - Constructor.prototype.render, - 'createClass(...): Class specification must implement a `render` method.' - ) : invariant(Constructor.prototype.render)); - - if ("production" !== "development") { - ("production" !== "development" ? warning( - !Constructor.prototype.componentShouldUpdate, - '%s has a method called ' + - 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - 'The name is phrased as a question because the function is ' + - 'expected to return a value.', - spec.displayName || 'A component' - ) : null); - } - - // Reduce time spent doing lookups by setting these on the prototype. - for (var methodName in ReactClassInterface) { - if (!Constructor.prototype[methodName]) { - Constructor.prototype[methodName] = null; - } - } - - // Legacy hook - Constructor.type = Constructor; - if ("production" !== "development") { - try { - Object.defineProperty(Constructor, 'type', typeDeprecationDescriptor); - } catch (x) { - // IE will fail on defineProperty (es5-shim/sham too) - } - } - - return Constructor; - }, - - injection: { - injectMixin: function(mixin) { - injectedMixins.push(mixin); - } - } - -}; - -module.exports = ReactClass; - -},{"150":150,"156":156,"157":157,"171":171,"29":29,"39":39,"45":45,"63":63,"66":66,"73":73,"74":74,"84":84,"85":85,"99":99}],39:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactComponent - */ - -'use strict'; - -var ReactUpdateQueue = _dereq_(99); - -var invariant = _dereq_(150); -var warning = _dereq_(171); - -/** - * Base class helpers for the updating state of a component. - */ -function ReactComponent(props, context) { - this.props = props; - this.context = context; -} - -/** - * Sets a subset of the state. Always use this to mutate - * state. You should treat `this.state` as immutable. - * - * There is no guarantee that `this.state` will be immediately updated, so - * accessing `this.state` after calling this method may return the old value. - * - * There is no guarantee that calls to `setState` will run synchronously, - * as they may eventually be batched together. You can provide an optional - * callback that will be executed when the call to setState is actually - * completed. - * - * When a function is provided to setState, it will be called at some point in - * the future (not synchronously). It will be called with the up to date - * component arguments (state, props, context). These values can be different - * from this.* because your function may be called after receiveProps but before - * shouldComponentUpdate, and this new state, props, and context will not yet be - * assigned to this. - * - * @param {object|function} partialState Next partial state or function to - * produce next partial state to be merged with current state. - * @param {?function} callback Called after state is updated. - * @final - * @protected - */ -ReactComponent.prototype.setState = function(partialState, callback) { - ("production" !== "development" ? invariant( - typeof partialState === 'object' || - typeof partialState === 'function' || - partialState == null, - 'setState(...): takes an object of state variables to update or a ' + - 'function which returns an object of state variables.' - ) : invariant(typeof partialState === 'object' || - typeof partialState === 'function' || - partialState == null)); - if ("production" !== "development") { - ("production" !== "development" ? warning( - partialState != null, - 'setState(...): You passed an undefined or null state object; ' + - 'instead, use forceUpdate().' - ) : null); - } - ReactUpdateQueue.enqueueSetState(this, partialState); - if (callback) { - ReactUpdateQueue.enqueueCallback(this, callback); - } -}; - -/** - * Forces an update. This should only be invoked when it is known with - * certainty that we are **not** in a DOM transaction. - * - * You may want to call this when you know that some deeper aspect of the - * component's state has changed but `setState` was not called. - * - * This will not invoke `shouldComponentUpdate`, but it will invoke - * `componentWillUpdate` and `componentDidUpdate`. - * - * @param {?function} callback Called after update is complete. - * @final - * @protected - */ -ReactComponent.prototype.forceUpdate = function(callback) { - ReactUpdateQueue.enqueueForceUpdate(this); - if (callback) { - ReactUpdateQueue.enqueueCallback(this, callback); - } -}; - -/** - * Deprecated APIs. These APIs used to exist on classic React classes but since - * we would like to deprecate them, we're not going to move them over to this - * modern base class. Instead, we define a getter that warns if it's accessed. - */ -if ("production" !== "development") { - var deprecatedAPIs = { - getDOMNode: 'getDOMNode', - isMounted: 'isMounted', - replaceProps: 'replaceProps', - replaceState: 'replaceState', - setProps: 'setProps' - }; - var defineDeprecationWarning = function(methodName, displayName) { - try { - Object.defineProperty(ReactComponent.prototype, methodName, { - get: function() { - ("production" !== "development" ? warning( - false, - '%s(...) is deprecated in plain JavaScript React classes.', - displayName - ) : null); - return undefined; - } - }); - } catch (x) { - // IE will fail on defineProperty (es5-shim/sham too) - } - }; - for (var fnName in deprecatedAPIs) { - if (deprecatedAPIs.hasOwnProperty(fnName)) { - defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); - } - } -} - -module.exports = ReactComponent; - -},{"150":150,"171":171,"99":99}],40:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactComponentBrowserEnvironment - */ - -/*jslint evil: true */ - -'use strict'; - -var ReactDOMIDOperations = _dereq_(50); -var ReactMount = _dereq_(77); - -/** - * Abstracts away all functionality of the reconciler that requires knowledge of - * the browser context. TODO: These callers should be refactored to avoid the - * need for this injection. - */ -var ReactComponentBrowserEnvironment = { - - processChildrenUpdates: - ReactDOMIDOperations.dangerouslyProcessChildrenUpdates, - - replaceNodeWithMarkupByID: - ReactDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID, - - /** - * If a particular environment requires that some resources be cleaned up, - * specify this in the injected Mixin. In the DOM, we would likely want to - * purge any cached node ID lookups. - * - * @private - */ - unmountIDFromEnvironment: function(rootNodeID) { - ReactMount.purgeID(rootNodeID); - } - -}; - -module.exports = ReactComponentBrowserEnvironment; - -},{"50":50,"77":77}],41:[function(_dereq_,module,exports){ -/** - * Copyright 2014-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactComponentEnvironment - */ - -'use strict'; - -var invariant = _dereq_(150); - -var injected = false; - -var ReactComponentEnvironment = { - - /** - * Optionally injectable environment dependent cleanup hook. (server vs. - * browser etc). Example: A browser system caches DOM nodes based on component - * ID and must remove that cache entry when this instance is unmounted. - */ - unmountIDFromEnvironment: null, - - /** - * Optionally injectable hook for swapping out mount images in the middle of - * the tree. - */ - replaceNodeWithMarkupByID: null, - - /** - * Optionally injectable hook for processing a queue of child updates. Will - * later move into MultiChildComponents. - */ - processChildrenUpdates: null, - - injection: { - injectEnvironment: function(environment) { - ("production" !== "development" ? invariant( - !injected, - 'ReactCompositeComponent: injectEnvironment() can only be called once.' - ) : invariant(!injected)); - ReactComponentEnvironment.unmountIDFromEnvironment = - environment.unmountIDFromEnvironment; - ReactComponentEnvironment.replaceNodeWithMarkupByID = - environment.replaceNodeWithMarkupByID; - ReactComponentEnvironment.processChildrenUpdates = - environment.processChildrenUpdates; - injected = true; - } - } - -}; - -module.exports = ReactComponentEnvironment; - -},{"150":150}],42:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * -* @providesModule ReactComponentWithPureRenderMixin -*/ - -'use strict'; - -var shallowEqual = _dereq_(166); - -/** - * If your React component's render function is "pure", e.g. it will render the - * same result given the same props and state, provide this Mixin for a - * considerable performance boost. - * - * Most React components have pure render functions. - * - * Example: - * - * var ReactComponentWithPureRenderMixin = - * require('ReactComponentWithPureRenderMixin'); - * React.createClass({ - * mixins: [ReactComponentWithPureRenderMixin], - * - * render: function() { - * return
          foo
          ; - * } - * }); - * - * Note: This only checks shallow equality for props and state. If these contain - * complex data structures this mixin may have false-negatives for deeper - * differences. Only mixin to components which have simple props and state, or - * use `forceUpdate()` when you know deep data structures have changed. - */ -var ReactComponentWithPureRenderMixin = { - shouldComponentUpdate: function(nextProps, nextState) { - return !shallowEqual(this.props, nextProps) || - !shallowEqual(this.state, nextState); - } -}; - -module.exports = ReactComponentWithPureRenderMixin; - -},{"166":166}],43:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactCompositeComponent - */ - -'use strict'; - -var ReactComponentEnvironment = _dereq_(41); -var ReactContext = _dereq_(44); -var ReactCurrentOwner = _dereq_(45); -var ReactElement = _dereq_(63); -var ReactElementValidator = _dereq_(64); -var ReactInstanceMap = _dereq_(73); -var ReactLifeCycle = _dereq_(74); -var ReactNativeComponent = _dereq_(80); -var ReactPerf = _dereq_(82); -var ReactPropTypeLocations = _dereq_(85); -var ReactPropTypeLocationNames = _dereq_(84); -var ReactReconciler = _dereq_(89); -var ReactUpdates = _dereq_(100); - -var assign = _dereq_(29); -var emptyObject = _dereq_(130); -var invariant = _dereq_(150); -var shouldUpdateReactComponent = _dereq_(167); -var warning = _dereq_(171); - -function getDeclarationErrorAddendum(component) { - var owner = component._currentElement._owner || null; - if (owner) { - var name = owner.getName(); - if (name) { - return ' Check the render method of `' + name + '`.'; - } - } - return ''; -} - -/** - * ------------------ The Life-Cycle of a Composite Component ------------------ - * - * - constructor: Initialization of state. The instance is now retained. - * - componentWillMount - * - render - * - [children's constructors] - * - [children's componentWillMount and render] - * - [children's componentDidMount] - * - componentDidMount - * - * Update Phases: - * - componentWillReceiveProps (only called if parent updated) - * - shouldComponentUpdate - * - componentWillUpdate - * - render - * - [children's constructors or receive props phases] - * - componentDidUpdate - * - * - componentWillUnmount - * - [children's componentWillUnmount] - * - [children destroyed] - * - (destroyed): The instance is now blank, released by React and ready for GC. - * - * ----------------------------------------------------------------------------- - */ - -/** - * An incrementing ID assigned to each component when it is mounted. This is - * used to enforce the order in which `ReactUpdates` updates dirty components. - * - * @private - */ -var nextMountID = 1; - -/** - * @lends {ReactCompositeComponent.prototype} - */ -var ReactCompositeComponentMixin = { - - /** - * Base constructor for all composite component. - * - * @param {ReactElement} element - * @final - * @internal - */ - construct: function(element) { - this._currentElement = element; - this._rootNodeID = null; - this._instance = null; - - // See ReactUpdateQueue - this._pendingElement = null; - this._pendingStateQueue = null; - this._pendingReplaceState = false; - this._pendingForceUpdate = false; - - this._renderedComponent = null; - - this._context = null; - this._mountOrder = 0; - this._isTopLevel = false; - - // See ReactUpdates and ReactUpdateQueue. - this._pendingCallbacks = null; - }, - - /** - * Initializes the component, renders markup, and registers event listeners. - * - * @param {string} rootID DOM ID of the root node. - * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction - * @return {?string} Rendered markup to be inserted into the DOM. - * @final - * @internal - */ - mountComponent: function(rootID, transaction, context) { - this._context = context; - this._mountOrder = nextMountID++; - this._rootNodeID = rootID; - - var publicProps = this._processProps(this._currentElement.props); - var publicContext = this._processContext(this._currentElement._context); - - var Component = ReactNativeComponent.getComponentClassForElement( - this._currentElement - ); - - // Initialize the public class - var inst = new Component(publicProps, publicContext); - - if ("production" !== "development") { - // This will throw later in _renderValidatedComponent, but add an early - // warning now to help debugging - ("production" !== "development" ? warning( - inst.render != null, - '%s(...): No `render` method found on the returned component ' + - 'instance: you may have forgotten to define `render` in your ' + - 'component or you may have accidentally tried to render an element ' + - 'whose type is a function that isn\'t a React component.', - Component.displayName || Component.name || 'Component' - ) : null); - } - - // These should be set up in the constructor, but as a convenience for - // simpler class abstractions, we set them up after the fact. - inst.props = publicProps; - inst.context = publicContext; - inst.refs = emptyObject; - - this._instance = inst; - - // Store a reference from the instance back to the internal representation - ReactInstanceMap.set(inst, this); - - if ("production" !== "development") { - this._warnIfContextsDiffer(this._currentElement._context, context); - } - - if ("production" !== "development") { - // Since plain JS classes are defined without any special initialization - // logic, we can not catch common errors early. Therefore, we have to - // catch them here, at initialization time, instead. - ("production" !== "development" ? warning( - !inst.getInitialState || - inst.getInitialState.isReactClassApproved, - 'getInitialState was defined on %s, a plain JavaScript class. ' + - 'This is only supported for classes created using React.createClass. ' + - 'Did you mean to define a state property instead?', - this.getName() || 'a component' - ) : null); - ("production" !== "development" ? warning( - !inst.getDefaultProps || - inst.getDefaultProps.isReactClassApproved, - 'getDefaultProps was defined on %s, a plain JavaScript class. ' + - 'This is only supported for classes created using React.createClass. ' + - 'Use a static property to define defaultProps instead.', - this.getName() || 'a component' - ) : null); - ("production" !== "development" ? warning( - !inst.propTypes, - 'propTypes was defined as an instance property on %s. Use a static ' + - 'property to define propTypes instead.', - this.getName() || 'a component' - ) : null); - ("production" !== "development" ? warning( - !inst.contextTypes, - 'contextTypes was defined as an instance property on %s. Use a ' + - 'static property to define contextTypes instead.', - this.getName() || 'a component' - ) : null); - ("production" !== "development" ? warning( - typeof inst.componentShouldUpdate !== 'function', - '%s has a method called ' + - 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - 'The name is phrased as a question because the function is ' + - 'expected to return a value.', - (this.getName() || 'A component') - ) : null); - } - - var initialState = inst.state; - if (initialState === undefined) { - inst.state = initialState = null; - } - ("production" !== "development" ? invariant( - typeof initialState === 'object' && !Array.isArray(initialState), - '%s.state: must be set to an object or null', - this.getName() || 'ReactCompositeComponent' - ) : invariant(typeof initialState === 'object' && !Array.isArray(initialState))); - - this._pendingStateQueue = null; - this._pendingReplaceState = false; - this._pendingForceUpdate = false; - - var renderedElement; - - var previouslyMounting = ReactLifeCycle.currentlyMountingInstance; - ReactLifeCycle.currentlyMountingInstance = this; - try { - if (inst.componentWillMount) { - inst.componentWillMount(); - // When mounting, calls to `setState` by `componentWillMount` will set - // `this._pendingStateQueue` without triggering a re-render. - if (this._pendingStateQueue) { - inst.state = this._processPendingState(inst.props, inst.context); - } - } - - renderedElement = this._renderValidatedComponent(); - } finally { - ReactLifeCycle.currentlyMountingInstance = previouslyMounting; - } - - this._renderedComponent = this._instantiateReactComponent( - renderedElement, - this._currentElement.type // The wrapping type - ); - - var markup = ReactReconciler.mountComponent( - this._renderedComponent, - rootID, - transaction, - this._processChildContext(context) - ); - if (inst.componentDidMount) { - transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); - } - - return markup; - }, - - /** - * Releases any resources allocated by `mountComponent`. - * - * @final - * @internal - */ - unmountComponent: function() { - var inst = this._instance; - - if (inst.componentWillUnmount) { - var previouslyUnmounting = ReactLifeCycle.currentlyUnmountingInstance; - ReactLifeCycle.currentlyUnmountingInstance = this; - try { - inst.componentWillUnmount(); - } finally { - ReactLifeCycle.currentlyUnmountingInstance = previouslyUnmounting; - } - } - - ReactReconciler.unmountComponent(this._renderedComponent); - this._renderedComponent = null; - - // Reset pending fields - this._pendingStateQueue = null; - this._pendingReplaceState = false; - this._pendingForceUpdate = false; - this._pendingCallbacks = null; - this._pendingElement = null; - - // These fields do not really need to be reset since this object is no - // longer accessible. - this._context = null; - this._rootNodeID = null; - - // Delete the reference from the instance to this internal representation - // which allow the internals to be properly cleaned up even if the user - // leaks a reference to the public instance. - ReactInstanceMap.remove(inst); - - // Some existing components rely on inst.props even after they've been - // destroyed (in event handlers). - // TODO: inst.props = null; - // TODO: inst.state = null; - // TODO: inst.context = null; - }, - - /** - * Schedule a partial update to the props. Only used for internal testing. - * - * @param {object} partialProps Subset of the next props. - * @param {?function} callback Called after props are updated. - * @final - * @internal - */ - _setPropsInternal: function(partialProps, callback) { - // This is a deoptimized path. We optimize for always having an element. - // This creates an extra internal element. - var element = this._pendingElement || this._currentElement; - this._pendingElement = ReactElement.cloneAndReplaceProps( - element, - assign({}, element.props, partialProps) - ); - ReactUpdates.enqueueUpdate(this, callback); - }, - - /** - * Filters the context object to only contain keys specified in - * `contextTypes` - * - * @param {object} context - * @return {?object} - * @private - */ - _maskContext: function(context) { - var maskedContext = null; - // This really should be getting the component class for the element, - // but we know that we're not going to need it for built-ins. - if (typeof this._currentElement.type === 'string') { - return emptyObject; - } - var contextTypes = this._currentElement.type.contextTypes; - if (!contextTypes) { - return emptyObject; - } - maskedContext = {}; - for (var contextName in contextTypes) { - maskedContext[contextName] = context[contextName]; - } - return maskedContext; - }, - - /** - * Filters the context object to only contain keys specified in - * `contextTypes`, and asserts that they are valid. - * - * @param {object} context - * @return {?object} - * @private - */ - _processContext: function(context) { - var maskedContext = this._maskContext(context); - if ("production" !== "development") { - var Component = ReactNativeComponent.getComponentClassForElement( - this._currentElement - ); - if (Component.contextTypes) { - this._checkPropTypes( - Component.contextTypes, - maskedContext, - ReactPropTypeLocations.context - ); - } - } - return maskedContext; - }, - - /** - * @param {object} currentContext - * @return {object} - * @private - */ - _processChildContext: function(currentContext) { - var inst = this._instance; - var childContext = inst.getChildContext && inst.getChildContext(); - if (childContext) { - ("production" !== "development" ? invariant( - typeof inst.constructor.childContextTypes === 'object', - '%s.getChildContext(): childContextTypes must be defined in order to ' + - 'use getChildContext().', - this.getName() || 'ReactCompositeComponent' - ) : invariant(typeof inst.constructor.childContextTypes === 'object')); - if ("production" !== "development") { - this._checkPropTypes( - inst.constructor.childContextTypes, - childContext, - ReactPropTypeLocations.childContext - ); - } - for (var name in childContext) { - ("production" !== "development" ? invariant( - name in inst.constructor.childContextTypes, - '%s.getChildContext(): key "%s" is not defined in childContextTypes.', - this.getName() || 'ReactCompositeComponent', - name - ) : invariant(name in inst.constructor.childContextTypes)); - } - return assign({}, currentContext, childContext); - } - return currentContext; - }, - - /** - * Processes props by setting default values for unspecified props and - * asserting that the props are valid. Does not mutate its argument; returns - * a new props object with defaults merged in. - * - * @param {object} newProps - * @return {object} - * @private - */ - _processProps: function(newProps) { - if ("production" !== "development") { - var Component = ReactNativeComponent.getComponentClassForElement( - this._currentElement - ); - if (Component.propTypes) { - this._checkPropTypes( - Component.propTypes, - newProps, - ReactPropTypeLocations.prop - ); - } - } - return newProps; - }, - - /** - * Assert that the props are valid - * - * @param {object} propTypes Map of prop name to a ReactPropType - * @param {object} props - * @param {string} location e.g. "prop", "context", "child context" - * @private - */ - _checkPropTypes: function(propTypes, props, location) { - // TODO: Stop validating prop types here and only use the element - // validation. - var componentName = this.getName(); - for (var propName in propTypes) { - if (propTypes.hasOwnProperty(propName)) { - var error; - try { - // This is intentionally an invariant that gets caught. It's the same - // behavior as without this statement except with a better message. - ("production" !== "development" ? invariant( - typeof propTypes[propName] === 'function', - '%s: %s type `%s` is invalid; it must be a function, usually ' + - 'from React.PropTypes.', - componentName || 'React class', - ReactPropTypeLocationNames[location], - propName - ) : invariant(typeof propTypes[propName] === 'function')); - error = propTypes[propName](props, propName, componentName, location); - } catch (ex) { - error = ex; - } - if (error instanceof Error) { - // We may want to extend this logic for similar errors in - // React.render calls, so I'm abstracting it away into - // a function to minimize refactoring in the future - var addendum = getDeclarationErrorAddendum(this); - - if (location === ReactPropTypeLocations.prop) { - // Preface gives us something to blacklist in warning module - ("production" !== "development" ? warning( - false, - 'Failed Composite propType: %s%s', - error.message, - addendum - ) : null); - } else { - ("production" !== "development" ? warning( - false, - 'Failed Context Types: %s%s', - error.message, - addendum - ) : null); - } - } - } - } - }, - - receiveComponent: function(nextElement, transaction, nextContext) { - var prevElement = this._currentElement; - var prevContext = this._context; - - this._pendingElement = null; - - this.updateComponent( - transaction, - prevElement, - nextElement, - prevContext, - nextContext - ); - }, - - /** - * If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate` - * is set, update the component. - * - * @param {ReactReconcileTransaction} transaction - * @internal - */ - performUpdateIfNecessary: function(transaction) { - if (this._pendingElement != null) { - ReactReconciler.receiveComponent( - this, - this._pendingElement || this._currentElement, - transaction, - this._context - ); - } - - if (this._pendingStateQueue !== null || this._pendingForceUpdate) { - if ("production" !== "development") { - ReactElementValidator.checkAndWarnForMutatedProps( - this._currentElement - ); - } - - this.updateComponent( - transaction, - this._currentElement, - this._currentElement, - this._context, - this._context - ); - } - }, - - /** - * Compare two contexts, warning if they are different - * TODO: Remove this check when owner-context is removed - */ - _warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) { - ownerBasedContext = this._maskContext(ownerBasedContext); - parentBasedContext = this._maskContext(parentBasedContext); - var parentKeys = Object.keys(parentBasedContext).sort(); - var displayName = this.getName() || 'ReactCompositeComponent'; - for (var i = 0; i < parentKeys.length; i++) { - var key = parentKeys[i]; - ("production" !== "development" ? warning( - ownerBasedContext[key] === parentBasedContext[key], - 'owner-based and parent-based contexts differ ' + - '(values: `%s` vs `%s`) for key (%s) while mounting %s ' + - '(see: http://fb.me/react-context-by-parent)', - ownerBasedContext[key], - parentBasedContext[key], - key, - displayName - ) : null); - } - }, - - /** - * Perform an update to a mounted component. The componentWillReceiveProps and - * shouldComponentUpdate methods are called, then (assuming the update isn't - * skipped) the remaining update lifecycle methods are called and the DOM - * representation is updated. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. - * - * @param {ReactReconcileTransaction} transaction - * @param {ReactElement} prevParentElement - * @param {ReactElement} nextParentElement - * @internal - * @overridable - */ - updateComponent: function( - transaction, - prevParentElement, - nextParentElement, - prevUnmaskedContext, - nextUnmaskedContext - ) { - var inst = this._instance; - - var nextContext = inst.context; - var nextProps = inst.props; - - // Distinguish between a props update versus a simple state update - if (prevParentElement !== nextParentElement) { - nextContext = this._processContext(nextParentElement._context); - nextProps = this._processProps(nextParentElement.props); - - if ("production" !== "development") { - if (nextUnmaskedContext != null) { - this._warnIfContextsDiffer( - nextParentElement._context, - nextUnmaskedContext - ); - } - } - - // An update here will schedule an update but immediately set - // _pendingStateQueue which will ensure that any state updates gets - // immediately reconciled instead of waiting for the next batch. - - if (inst.componentWillReceiveProps) { - inst.componentWillReceiveProps(nextProps, nextContext); - } - } - - var nextState = this._processPendingState(nextProps, nextContext); - - var shouldUpdate = - this._pendingForceUpdate || - !inst.shouldComponentUpdate || - inst.shouldComponentUpdate(nextProps, nextState, nextContext); - - if ("production" !== "development") { - ("production" !== "development" ? warning( - typeof shouldUpdate !== 'undefined', - '%s.shouldComponentUpdate(): Returned undefined instead of a ' + - 'boolean value. Make sure to return true or false.', - this.getName() || 'ReactCompositeComponent' - ) : null); - } - - if (shouldUpdate) { - this._pendingForceUpdate = false; - // Will set `this.props`, `this.state` and `this.context`. - this._performComponentUpdate( - nextParentElement, - nextProps, - nextState, - nextContext, - transaction, - nextUnmaskedContext - ); - } else { - // If it's determined that a component should not update, we still want - // to set props and state but we shortcut the rest of the update. - this._currentElement = nextParentElement; - this._context = nextUnmaskedContext; - inst.props = nextProps; - inst.state = nextState; - inst.context = nextContext; - } - }, - - _processPendingState: function(props, context) { - var inst = this._instance; - var queue = this._pendingStateQueue; - var replace = this._pendingReplaceState; - this._pendingReplaceState = false; - this._pendingStateQueue = null; - - if (!queue) { - return inst.state; - } - - var nextState = assign({}, replace ? queue[0] : inst.state); - for (var i = replace ? 1 : 0; i < queue.length; i++) { - var partial = queue[i]; - assign( - nextState, - typeof partial === 'function' ? - partial.call(inst, nextState, props, context) : - partial - ); - } - - return nextState; - }, - - /** - * Merges new props and state, notifies delegate methods of update and - * performs update. - * - * @param {ReactElement} nextElement Next element - * @param {object} nextProps Next public object to set as properties. - * @param {?object} nextState Next object to set as state. - * @param {?object} nextContext Next public object to set as context. - * @param {ReactReconcileTransaction} transaction - * @param {?object} unmaskedContext - * @private - */ - _performComponentUpdate: function( - nextElement, - nextProps, - nextState, - nextContext, - transaction, - unmaskedContext - ) { - var inst = this._instance; - - var prevProps = inst.props; - var prevState = inst.state; - var prevContext = inst.context; - - if (inst.componentWillUpdate) { - inst.componentWillUpdate(nextProps, nextState, nextContext); - } - - this._currentElement = nextElement; - this._context = unmaskedContext; - inst.props = nextProps; - inst.state = nextState; - inst.context = nextContext; - - this._updateRenderedComponent(transaction, unmaskedContext); - - if (inst.componentDidUpdate) { - transaction.getReactMountReady().enqueue( - inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), - inst - ); - } - }, - - /** - * Call the component's `render` method and update the DOM accordingly. - * - * @param {ReactReconcileTransaction} transaction - * @internal - */ - _updateRenderedComponent: function(transaction, context) { - var prevComponentInstance = this._renderedComponent; - var prevRenderedElement = prevComponentInstance._currentElement; - var nextRenderedElement = this._renderValidatedComponent(); - if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { - ReactReconciler.receiveComponent( - prevComponentInstance, - nextRenderedElement, - transaction, - this._processChildContext(context) - ); - } else { - // These two IDs are actually the same! But nothing should rely on that. - var thisID = this._rootNodeID; - var prevComponentID = prevComponentInstance._rootNodeID; - ReactReconciler.unmountComponent(prevComponentInstance); - - this._renderedComponent = this._instantiateReactComponent( - nextRenderedElement, - this._currentElement.type - ); - var nextMarkup = ReactReconciler.mountComponent( - this._renderedComponent, - thisID, - transaction, - this._processChildContext(context) - ); - this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup); - } - }, - - /** - * @protected - */ - _replaceNodeWithMarkupByID: function(prevComponentID, nextMarkup) { - ReactComponentEnvironment.replaceNodeWithMarkupByID( - prevComponentID, - nextMarkup - ); - }, - - /** - * @protected - */ - _renderValidatedComponentWithoutOwnerOrContext: function() { - var inst = this._instance; - var renderedComponent = inst.render(); - if ("production" !== "development") { - // We allow auto-mocks to proceed as if they're returning null. - if (typeof renderedComponent === 'undefined' && - inst.render._isMockFunction) { - // This is probably bad practice. Consider warning here and - // deprecating this convenience. - renderedComponent = null; - } - } - - return renderedComponent; - }, - - /** - * @private - */ - _renderValidatedComponent: function() { - var renderedComponent; - var previousContext = ReactContext.current; - ReactContext.current = this._processChildContext( - this._currentElement._context - ); - ReactCurrentOwner.current = this; - try { - renderedComponent = - this._renderValidatedComponentWithoutOwnerOrContext(); - } finally { - ReactContext.current = previousContext; - ReactCurrentOwner.current = null; - } - ("production" !== "development" ? invariant( - // TODO: An `isValidNode` function would probably be more appropriate - renderedComponent === null || renderedComponent === false || - ReactElement.isValidElement(renderedComponent), - '%s.render(): A valid ReactComponent must be returned. You may have ' + - 'returned undefined, an array or some other invalid object.', - this.getName() || 'ReactCompositeComponent' - ) : invariant(// TODO: An `isValidNode` function would probably be more appropriate - renderedComponent === null || renderedComponent === false || - ReactElement.isValidElement(renderedComponent))); - return renderedComponent; - }, - - /** - * Lazily allocates the refs object and stores `component` as `ref`. - * - * @param {string} ref Reference name. - * @param {component} component Component to store as `ref`. - * @final - * @private - */ - attachRef: function(ref, component) { - var inst = this.getPublicInstance(); - var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs; - refs[ref] = component.getPublicInstance(); - }, - - /** - * Detaches a reference name. - * - * @param {string} ref Name to dereference. - * @final - * @private - */ - detachRef: function(ref) { - var refs = this.getPublicInstance().refs; - delete refs[ref]; - }, - - /** - * Get a text description of the component that can be used to identify it - * in error messages. - * @return {string} The name or null. - * @internal - */ - getName: function() { - var type = this._currentElement.type; - var constructor = this._instance && this._instance.constructor; - return ( - type.displayName || (constructor && constructor.displayName) || - type.name || (constructor && constructor.name) || - null - ); - }, - - /** - * Get the publicly accessible representation of this component - i.e. what - * is exposed by refs and returned by React.render. Can be null for stateless - * components. - * - * @return {ReactComponent} the public component instance. - * @internal - */ - getPublicInstance: function() { - return this._instance; - }, - - // Stub - _instantiateReactComponent: null - -}; - -ReactPerf.measureMethods( - ReactCompositeComponentMixin, - 'ReactCompositeComponent', - { - mountComponent: 'mountComponent', - updateComponent: 'updateComponent', - _renderValidatedComponent: '_renderValidatedComponent' - } -); - -var ReactCompositeComponent = { - - Mixin: ReactCompositeComponentMixin - -}; - -module.exports = ReactCompositeComponent; - -},{"100":100,"130":130,"150":150,"167":167,"171":171,"29":29,"41":41,"44":44,"45":45,"63":63,"64":64,"73":73,"74":74,"80":80,"82":82,"84":84,"85":85,"89":89}],44:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactContext - */ - -'use strict'; - -var assign = _dereq_(29); -var emptyObject = _dereq_(130); -var warning = _dereq_(171); - -var didWarn = false; - -/** - * Keeps track of the current context. - * - * The context is automatically passed down the component ownership hierarchy - * and is accessible via `this.context` on ReactCompositeComponents. - */ -var ReactContext = { - - /** - * @internal - * @type {object} - */ - current: emptyObject, - - /** - * Temporarily extends the current context while executing scopedCallback. - * - * A typical use case might look like - * - * render: function() { - * var children = ReactContext.withContext({foo: 'foo'}, () => ( - * - * )); - * return
          {children}
          ; - * } - * - * @param {object} newContext New context to merge into the existing context - * @param {function} scopedCallback Callback to run with the new context - * @return {ReactComponent|array} - */ - withContext: function(newContext, scopedCallback) { - if ("production" !== "development") { - ("production" !== "development" ? warning( - didWarn, - 'withContext is deprecated and will be removed in a future version. ' + - 'Use a wrapper component with getChildContext instead.' - ) : null); - - didWarn = true; - } - - var result; - var previousContext = ReactContext.current; - ReactContext.current = assign({}, previousContext, newContext); - try { - result = scopedCallback(); - } finally { - ReactContext.current = previousContext; - } - return result; - } - -}; - -module.exports = ReactContext; - -},{"130":130,"171":171,"29":29}],45:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactCurrentOwner - */ - -'use strict'; - -/** - * Keeps track of the current owner. - * - * The current owner is the component who should own any components that are - * currently being constructed. - * - * The depth indicate how many composite components are above this render level. - */ -var ReactCurrentOwner = { - - /** - * @internal - * @type {ReactComponent} - */ - current: null - -}; - -module.exports = ReactCurrentOwner; - -},{}],46:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactDOM - * @typechecks static-only - */ - -'use strict'; - -var ReactElement = _dereq_(63); -var ReactElementValidator = _dereq_(64); - -var mapObject = _dereq_(158); - -/** - * Create a factory that creates HTML tag elements. - * - * @param {string} tag Tag name (e.g. `div`). - * @private - */ -function createDOMFactory(tag) { - if ("production" !== "development") { - return ReactElementValidator.createFactory(tag); - } - return ReactElement.createFactory(tag); -} - -/** - * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes. - * This is also accessible via `React.DOM`. - * - * @public - */ -var ReactDOM = mapObject({ - a: 'a', - abbr: 'abbr', - address: 'address', - area: 'area', - article: 'article', - aside: 'aside', - audio: 'audio', - b: 'b', - base: 'base', - bdi: 'bdi', - bdo: 'bdo', - big: 'big', - blockquote: 'blockquote', - body: 'body', - br: 'br', - button: 'button', - canvas: 'canvas', - caption: 'caption', - cite: 'cite', - code: 'code', - col: 'col', - colgroup: 'colgroup', - data: 'data', - datalist: 'datalist', - dd: 'dd', - del: 'del', - details: 'details', - dfn: 'dfn', - dialog: 'dialog', - div: 'div', - dl: 'dl', - dt: 'dt', - em: 'em', - embed: 'embed', - fieldset: 'fieldset', - figcaption: 'figcaption', - figure: 'figure', - footer: 'footer', - form: 'form', - h1: 'h1', - h2: 'h2', - h3: 'h3', - h4: 'h4', - h5: 'h5', - h6: 'h6', - head: 'head', - header: 'header', - hr: 'hr', - html: 'html', - i: 'i', - iframe: 'iframe', - img: 'img', - input: 'input', - ins: 'ins', - kbd: 'kbd', - keygen: 'keygen', - label: 'label', - legend: 'legend', - li: 'li', - link: 'link', - main: 'main', - map: 'map', - mark: 'mark', - menu: 'menu', - menuitem: 'menuitem', - meta: 'meta', - meter: 'meter', - nav: 'nav', - noscript: 'noscript', - object: 'object', - ol: 'ol', - optgroup: 'optgroup', - option: 'option', - output: 'output', - p: 'p', - param: 'param', - picture: 'picture', - pre: 'pre', - progress: 'progress', - q: 'q', - rp: 'rp', - rt: 'rt', - ruby: 'ruby', - s: 's', - samp: 'samp', - script: 'script', - section: 'section', - select: 'select', - small: 'small', - source: 'source', - span: 'span', - strong: 'strong', - style: 'style', - sub: 'sub', - summary: 'summary', - sup: 'sup', - table: 'table', - tbody: 'tbody', - td: 'td', - textarea: 'textarea', - tfoot: 'tfoot', - th: 'th', - thead: 'thead', - time: 'time', - title: 'title', - tr: 'tr', - track: 'track', - u: 'u', - ul: 'ul', - 'var': 'var', - video: 'video', - wbr: 'wbr', - - // SVG - circle: 'circle', - defs: 'defs', - ellipse: 'ellipse', - g: 'g', - line: 'line', - linearGradient: 'linearGradient', - mask: 'mask', - path: 'path', - pattern: 'pattern', - polygon: 'polygon', - polyline: 'polyline', - radialGradient: 'radialGradient', - rect: 'rect', - stop: 'stop', - svg: 'svg', - text: 'text', - tspan: 'tspan' - -}, createDOMFactory); - -module.exports = ReactDOM; - -},{"158":158,"63":63,"64":64}],47:[function(_dereq_,module,exports){ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactDOMButton - */ - -'use strict'; - -var AutoFocusMixin = _dereq_(2); -var ReactBrowserComponentMixin = _dereq_(32); -var ReactClass = _dereq_(38); -var ReactElement = _dereq_(63); - -var keyMirror = _dereq_(156); - -var button = ReactElement.createFactory('button'); - -var mouseListenerNames = keyMirror({ - onClick: true, - onDoubleClick: true, - onMouseDown: true, - onMouseMove: true, - onMouseUp: true, - onClickCapture: true, - onDoubleClickCapture: true, - onMouseDownCapture: true, - onMouseMoveCapture: true, - onMouseUpCapture: true -}); - -/** - * Implements a