是否可以无缝重定向websockets?

Ale*_*ndr 7 javascript nginx websocket

我知道可以通过反向代理(如Nginx,HAproxy等)传递请求,但我需要将请求重定向到同一域后缀中的另一个公共服务器.即wss://example.comwss://ws1.example.com.
这是一个例子: 在此输入图像描述

我需要重定向来自Nginx或Java的请求.是否有可能组织?我是否需要在客户端处理重定向或此代码是否足够?

var socket = new WebSocket("wss://example.com");

socket.onopen = function() {
  alert("Connection established.");
};

socket.onclose = function(event) {
  alert('Connection closed');
};

socket.onmessage = function(event) {
  alert("Data: " + event.data);
};

socket.onerror = function(error) {
  alert("Error " + error.message);
};
Run Code Online (Sandbox Code Playgroud)

Sep*_*ehr 7

自从提出这个问题以来已经过去四年了,所以今天你会如何做:

在 npmws包中:

在您的套接字客户端构造函数()中设置followRedirects为。true

wscat实用性上:

传递-L标志(来源)。

在“大多数”浏览器中:

如果您不关心语义,我为此编写了一个包。否则,可以通过挂钩到 WebSocket 构造函数来切换 WebSocket 构造函数中的 URL,并最终支持重定向响应。

'use strict';

/* global window, WebSocket*/

(function (WebSocketNative = window.WebSocket) {
  const debug = console.log;
  debug('patching native WebSocket class to support HTTP redirects');
  window.WebSocket = class {
    constructor(url, ...args) {
      this.work = [];
      this.endpoint = url.replace(/^http/, 'ws');
      debug('ws constructed with url: "%s" and endpoint: "%s"', url, this.endpoint);
      return (
        (async () => {
          try {
            const resolver =
              window.WEBSOCKET_REDIRECT_RESOLVER || `${location.protocol}//${location.host}${location.pathname}`;
            debug('websocket address resolver: "%s"', resolver);
            const endpoint = `${resolver}?url=${encodeURIComponent(this.endpoint)}`;
            debug('resolver query endpoint: "%s"', endpoint);
            const response = await fetch(endpoint);
            debug('resolver query response: "%o"', response);
            const { dig, dug } = await response.json();
            debug('resolver dig: "%o" dug: "%o"', dig, dug);
            this.endpoint = dug;
          } finally {
            debug('websocket final endpoint: "%s"', this.endpoint);
            this.socket = new WebSocketNative(this.endpoint, ...args);
            debug('flushing %d queued operations', this.work.length);
            for (const op of this.work) op(this.socket);
          }
        })(),
        new Proxy(this, {
          get: function (target, prop) {
            debug('GETTER trace: "%s"', prop);
            switch (prop) {
              case 'construct':
              case 'endpoint':
              case 'socket':
              case 'work':
                debug('GETTER "%s" does not go through proxy', prop);
                return Reflect.get(...arguments);
              case 'OPEN':
              case 'CLOSED':
              case 'CLOSING':
              case 'CONNECTING':
                debug('GETTER "%s" is a static', prop);
                return Reflect.get(WebSocketNative, prop);
              case 'readyState':
              case 'bufferedAmount':
                return this.socket ? Reflect.get(this.socket, prop) : 0;
              case 'binaryType':
                return this.socket ? Reflect.get(this.socket, prop) : 'blob';
              case 'extensions':
              case 'protocol':
              case 'url':
                return this.socket ? Reflect.get(this.socket, prop) : '';
              default:
                return this.socket
                  ? Reflect.get(this.socket, prop)
                  : function (...args) {
                      if (target.socket) {
                        debug('socket is ready, skip queueing get "%s" with args "%o"', prop, args);
                        return Reflect.get(target.socket, prop).call(target.socket, ...args);
                      } else {
                        debug('socket is not ready yet. queueing get "%s" with args "%o"', prop, args);
                        target.work.push((socket) => Reflect.get(socket, prop).call(socket, ...args));
                      }
                    };
            }
          },
          set: function (target, prop, value) {
            debug('SETTER trace: "%s"="%o"', prop, value);
            switch (prop) {
              case 'endpoint':
              case 'socket':
              case 'work':
                return Reflect.set(...arguments);
              default:
                return this.socket
                  ? Reflect.set(this.socket, prop, value)
                  : (() => {
                      if (target.socket) {
                        debug('socket is ready, skip queueing set "%s" with value "%o"', prop, value);
                        Reflect.set(socket, prop, value);
                      } else {
                        debug('socket is not ready yet, queueing set "%s" with value "%o"', prop, value);
                        target.work.push((socket) => Reflect.set(socket, prop, value));
                      }
                      return true;
                    })();
            }
          },
        })
      );
    }
  };
})(window.WebSocket);
Run Code Online (Sandbox Code Playgroud)

ALTERNATE_WEBSOCKET_ADDRESS此代码片段使用支持动态切换 URL 的类来覆盖浏览器的本机 WebSocket 类。您需要ALTERNATE_WEBSOCKET_ADDRESS在某处定义,因为不可能在浏览器环境中找出重定向响应的最终 URL(无论是 with fetch,还是 with XMLHttpRequest)。fetch仅用于检测重定向。您需要想出一种机制,以便ALTERNATE_WEBSOCKET_ADDRESS在需要重定向时做好准备。


jfr*_*d00 5

根据webSocket规范:

一旦发送了客户端的打开握手,客户端必须等待服务器的响应,然后再发送任何其他数据.客户端必须按如下方式验证服务器的响应:

  1. 如果从服务器收到的状态代码不是101,则客户端根据HTTP [RFC2616]过程处理响应.特别是,如果客户端收到401状态代码,则可能会执行身份验证; 服务器可能使用3xx状态代码重定向客户端(但客户端不需要遵循它们),等等.

因此,完全取决于客户是否要支持重定向,并且显然不是您可以依赖的东西,除非您在广泛的测试中发现所有相关客户都支持它(他们显然不支持).

您将不得不使用服务器端代理或客户端方案等手动将连接移动到另一台服务器.