首页 > 解决方案 > 如何对我的 http 服务器上的 /socket.io 路由进行速率限制?

问题描述

我正在使用 socket.io 并使用rate-limiter-flexiblelimiter来限制请求速率,但我注意到路由 /socket.io 也没有收到 429,事实上,我无法记录来自的任何请求这条路线通过使用app.use('/socket.io')..

我认为 socket.io 正在对这条路线进行一些处理,对吗?如果是这样,我如何确保在达到限制后对 /socket.io 的请求也收到 429?

Connect 上的速率限制器:

this.io.on(this.SocketConstants.CONNECTION, async (client) => {
      this.client = client;
      try {
        await this.rateLimiter.consume(client.handshake.address);
      } catch (rejRes) {
        // On flood
        client.error('Too many requests');
        client.disconnect(true);
      }

http服务器上的速率限制器:

class RateLimiting {
    constructor() {
        this.limiter = new RateLimiter(2, 'minute', true);
        this.index = this.index.bind(this);
    }

    index(req,res,next){
        try {
            this.limiter.removeTokens(1, function(err, remainingRequests) {
                if (remainingRequests < 1) {
                    res.writeHead(429, {'Content-Type': 'text/plain;charset=UTF-8'});
                    res.end('429 Too Many Requests - your IP is being rate limited');
                    // res.status(429).json({ message: 'Too many requests' });
                } else {
                    next();
                }
            });
        }
        catch(err){
            console.log('Error', err);
        }
    }
}

编辑

对于任何有类似问题的人,我最终尝试了几种不同的方法来实现这一点,最简单的方法是:

  1. 编写自己的allowRequest

AllowRequest 是一个通过/失败函数,您可以使用它来覆盖默认的“checkRequest”函数(参考此处

checkBucket(err, remainingRequests) {
    remainingRequests < 1 ? false : true;
  }

allowRequest(req, callback) {
    const limit = this.limiter.removeTokens(1, this.checkBucket);
    callback(limit ? null : 'Too many requests', limit);
  }

并且服务器启动为

this.io = this.socketServer(server, 
        { 
          allowRequest: this.allowRequest
        });
  1. 选择不同的路由,使用 express 添加任何需要的中间件并禁用默认路由

您可以通过设置serveClient为 false 并将 设置path为您需要的任何内容来完成此操作。

this.io = this.socketServer(server, { path: '/newSocketRoute', serveClient: false });

这对我来说不太奏效,所以我提供套接字文件的方式可能有问题,但这就是它的样子:

app.get("/socket", this.limiter.initialize(), function(req, res) {
        if (0 === req.url.indexOf('/newSocketRoute/socket.io.js.map')) {
          res.sendFile(join(__dirname, "../../node_modules/socket.io-client/dist/socket.io.js.map"));
        } else if (0 === req.url.indexOf('/newSocketRoute/socket.io.js')) {
          res.sendFile(join(__dirname, "../../node_modules/socket.io-client/dist/socket.io.js"));
        }
      });

标签: node.jssocket.iorate-limiting

解决方案


Socket.io 通过将自己插入为request事件的第一个侦听器(此处的 socket.io 代码参考)将自己附加到您的 http 服务器,这意味着它完全绕过了任何 Express 中间件。

如果您尝试对/socket.io/socket.io.js(客户端 socket.io 代码)的请求进行速率限制,那么您可以使用您自己的自定义路由器在 Express 中为该文件创建自己的路由,并使用不同的路径,让您的客户端只使用 Express版本的路径,然后您可以禁用通过 socket.io 提供该文件(有一个选项可以禁用它)。

如果您尝试对传入的 socket.io 连接进行速率限制,那么您可能需要修改您的速率限制器,以便它可以参与 socket.io 连接事件。


现在我想到了,您可以request像 socket.io 一样破解事件侦听器列表(您可以查看上面引用的代码链接了解它是如何做到的)并且您可以在它看到请求之前插入您自己的速率限制. socket.io 正在做的是实现一个穷人的中间件并切到行的最前面,以便他们可以首先破解任何传入的 http 请求,如果他们处理了它,则将其隐藏起来。你可以用你的速率限制器做同样的事情。然后,您将参加“军备竞赛”,看看谁先得手。connect就个人而言,如果违反了速率限制规则,我可能只是挂钩事件并终止那里的连接。但是,您可以破解并获得 socket.io 代码的前面。


推荐阅读