带 SSL 的 Nestjs websocket 网关不工作

Nestjs websocket gateway with SSL is not working

我已经为我的 nest.js server according to this answer 添加了 SSL 支持:

  const privateKey = fs.readFileSync('certs/localhost.key', 'utf8');
  const certificate = fs.readFileSync('certs/localhost.cert', 'utf8');
  const httpsOptions = {key: privateKey, cert: certificate};

  const server = express();
  const app = await NestFactory.create(AppModule, new ExpressAdapter(server));
  await app.init();

  app.use(cookieParser());
  app.enableCors({
    origin: true,
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    credentials: true,
  });

  if (process.env.DEV) {
    https.createServer(httpsOptions, server).listen(3006);
    console.log('Running in DEV mode using secure HTTPS');
  } else {
    http.createServer(server).listen(3006);
  }

一切正常,我可以使用 HTTPS 访问。

现在我创建了我的 websocket 网关,当我使用 HTTP 时它工作正常:

const options = {
    cors: {
        origin: true,
        methods: ["GET", "POST"],
        credentials: true,
    }
}
@WebSocketGateway(3007, options)
export class SessionWebsocket {

    @WebSocketServer()
    private server: Server;
    ...... And more irrelevant code.
}

每当我尝试通过 HTTPS 访问 websocket 服务器时,我都会在网络中遇到以下错误:

我需要对网关进行任何更改才能使其正常工作吗? The page explaining about the gateways interface 未说明任何有关 SSL 或 TLS 的信息。

编辑:

nestjs服务器运行的端口是3006,websocket服务器的端口是3007,不能运行两个相同的端口。

我连接websocket的方式:

export const SESSION_SOCKET_URL = '//localhost:3007';

this.sessionClient.connect(sessionId, SESSION_SOCKET_URL);

  public connect(sessionId: string, socketConnectionString: string) {
    const options = {
      transportOptions: {
        polling: {
          extraHeaders: { // This is unrelated to the question, just custom headers, shouldn't affect my issue
            'sessionId': sessionId // If undefined, server will generate and send a notify event
          }
        }
      },
    }

    this.socketClient = io(socketConnectionString, options).connect();

    this.socketClient.on('connect', () => {
      this.notifyOnConnected.next({
        clientId: this.socketClient.id
      });
    });
  }

NestJS Gateway 默认使用 AbstractWsAdapter,不确定是什么 Adapter 默认实现它。

但是,如果没有自定义适配器,我找不到控制服务器创建的方法,而且我不想启动另一个专用于 websocket 的服务器,而是使用已使用的 http 服务器休息 API.

所以我解决这个问题的方法是,首先在 bootstrap 你的 nestjs 应用程序的地方实现 HTTPS 服务器:

async function bootstrap() {

    const privateKey = fs.readFileSync('certs/localhost.key', 'utf8');
    const certificate = fs.readFileSync('certs/localhost.cert', 'utf8');
    const httpsOptions = {key: privateKey, cert: certificate};

    const server = express();
    const app = await NestFactory.create(AppModule, new ExpressAdapter(server));

    const httpsServer = https.createServer(httpsOptions);
    app.useWebSocketAdapter(new ExtendedSocketIoAdapter(httpsServer));

    app.use(cookieParser());
    app.enableCors({
        origin: true,
        methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
        credentials: true,
    });

    await app.init();

    httpsServer.listen(3006);
}

基本上在这里你初始化一个 HTTPS 服务器,正如你所看到的,我创建了我自己的适配器,它扩展 IoAdapter 为 socket.io:

export class ExtendedSocketIoAdapter extends IoAdapter {
    protected ioServer: io.Server;

    constructor(protected server: https.Server) {
        super();

        const options = {
            cors: {
                origin: true,
                methods: ["GET", "POST"],
                credentials: true,
            }
        }

        this.ioServer = new io.Server(server, options);
    }

    create (port: number) {
        console.log('websocket gateway port argument is ignored by ExtendedSocketIoAdapter, use the same port of http instead')
        return this.ioServer
    }
}

因此,您无需创建新的 https 服务器,而是创建一个新的 IoServer 实例,并传入您在初始化 nest 应用程序时创建的 HTTPS 服务器。

此时忽略@WebSocketGateway({port, options)中的参数,所以此时可以为空->@WebSocketGateway()

现在 websocket 服务器将 运行 在 HTTPS 服务器之上。

这解决了我的问题。