tsw.jstsw.js

插件

一. 插件是什么?

TSW 核心的实现方式是 Hack NodeJS 自身的 http.request 以及 http.createServer, 以此来实现抓包机制。在服务器处理请求的前后,在服务器向其他服务器发包的前后,等等,都会有相应的事件抛出,以供用户来进行自定义处理。
为了让用户更加方便地复用、传播这样一组组自定义处理,我们将他们抽象出来,形成了插件机制。

一个最简单的插件

export.modules = class MyPlugin() {
  constructor() {
    this.name = "MyPlugin"
  }

  async init(eventBus, config) {
    eventBus.on("RESPONSE_CLOSE", (payload) => {
      console.log(payload);
    })
  }
}

init 方法是必须的,这个方法在插件加载开始时会被调用,可以是同步也可以是异步。

eventBus

eventBus 是通过 new EventEmitter() 得到的。TSW 核心会在各个关键时机触发上面的事件。

事件列表:

key 含义(触发时机) payload
DNS_LOOKUP_SUCCESS 在每次 DNS 查询成功之后触发 string | dns.LookupAddress[]
DNS_LOOKUP_ERROR 在每次 DNS 查询失败之后触发 NodeJS.ErrorException
RESPONSE_START 在每次服务器开始返回响应(执行 writeHead)时触发 ResponseEventPayload
RESPONSE_FINISH 在响应结束时(res.on("finish"))触发 ResponseEventPayload
RESPONSE_CLOSE 在底层链接关闭时 (res.on("close"))触发 ResponseEventPayload
REQUEST_START 在每次服务器接受到新的请求时触发 RequestEventPayload

二. 开放平台插件

默认的情况下,TSW 只会把所有的日志和抓包内容抓取并且送到事件总线上,以供 插件 消费。所以将日志和抓包内容落地查看一般需要用户自己编写插件以及提供存储,使用成本过于高昂。
因此,TSW 官方提供了公共的服务平台( https://tswjs.org ), 让用户低成本、更快、更方便地使用 TSW 的特性。

使用 TSW 开放平台

  1. 登录 https://tswjs.org 并在其上新建一个应用

create-app

  1. 打开应用,获取 appidappkey

appid-appkey

  1. 安装开放平台插件

    yarn add @tswjs/open-platform-plugin
    // npm i @tswjs/open-platform-plugin
    
  2. 在配置文件 tswconfig.js中添加相关配置,具体参照 开放平台插件配置指引

  3. 向之前启动的 Koa 或者原生 http server 发送请求,即可在开放平台上查看对应的日志和抓包。查看地址为下方地址拼接而成 https://${appid}.tswjs.org/log/view/${uid}

log-view

日志记录

log

在线查看抓包内容

capture

开放平台插件配置指引

const OpenPlatformPlugin = require("@tswjs/open-platform-plugin");

module.exports = {
  plugins: [
    new OpenPlatformPlugin({
      appid: "***",,
      appkey: "***",
      reportStrategy: "proxied",
      // 只支持同步写法
      getUid: (request) => {
        const cookie = request.headers.cookie;

        if (!cookie) return;

        const uid = /quid=([^;]*);?/g.exec(cookie);
        return uid ? uid[2] : '';
      },
      // 同步或者异步函数
      getProxyInfo: () => {
        return {
          "port": 80,
          "name": "2.0demo",
          "group": "TSW",
          "groupName": "TSW团队",
          "desc": "2.0demo测试环境",
          "order": 30,
          "owner": "demoUser",
          "alphaList": ["demoUser"]
        };
      },
      // 请求回调函数
      hooks: {
        // 请求开始前回调,返回 false 则提前返回
        requestStart(payload) {
          const { req, context } = payload
          if (req.method === 'HEAD') return false
        },
        // 结束开始前回调,返回 false 则提前返回
        responseFinish(payload) {
          const { req, context } = payload
          if (req.method === 'HEAD') return false
        },
      },
    })
  ]
};

配置详解:

1.appid

  • String
  • 必填

项目在 TSW 开放平台 申请的应用 id。

2.appkey

  • String
  • 必填

项目在 TSW 开放平台 申请的应用 key。

3.reportStrategy

  • "always" | "never" | "proxied"
  • 选填
  • 默认值:proxied

always,表示在任何情况下都上报日志数据。 never,表示在任何情况下都不上报日志数据。 proxied,表示在被代理时上报数据。

4.getUid

  • () => string 同步函数
  • 选填
  • 默认值:() => {}

从每个请求中提取用户 uid

5.getProxyInfo

  • Function 同步或者异步函数
  • 选填
  • 默认值 () => {}

返回值如果为 undefined,表示这台机器不被允许通过代理到达。

6.hooks.requestStart

  • Function 同步函数
  • 选填

返回值如果为 false,则不做 uid 提取和匹配检查。

7.hooks.responseFinish

  • Function 同步函数
  • 选填

返回值如果为 false,则跳过上报逻辑。

如果返回一个对象,那么根据对象参数不同有几种情况:

{
  "port": 80,
  "name": "2.0demo",
  "group": "TSW",
  "groupName": "TSW团队",
  "desc": "2.0demo测试环境",
  "order": 30,
  "owner": "demoUser",
  "alphaOnly": false,
  "alphaList": ["demoUser"]
}

8.alphaOnly

  • 默认为 false
  • 若值为 false,认为这台机器会被注册到开放平台上,可以通过在开放平台上配置代理到达。
  • 若值为 true,认为这台机器只是负责染色号码以记录日志。不可从开放平台配置代理。一般生产环境开启此参数。

9.alphaList

表示本机希望抓包的用户列表,值的比对对象是 getUid 方法返回值。

alphaList.includes(getUid())