¿Cómo construir un URI de WebSocket relativo al URI de la página?

95

Quiero construir un URI de WebSocket relativo al URI de la página en el lado del navegador. Digamos, en mi caso, convierta HTTP URI como

http://example.com:8000/path
https://example.com:8000/path

a

ws://example.com:8000/path/to/ws
wss://example.com:8000/path/to/ws

Lo que estoy haciendo actualmente es reemplazar las primeras 4 letras "http" por "ws", y agregarle "/ a / ws". ¿Hay alguna forma mejor de hacerlo?

neurona
fuente
1
¿Qué quieres decir con path/to/ws? ¿A dónde lleva esto exactamente? Gracias
slevin

Respuestas:

96

Si su servidor web es compatible con WebSockets (o un módulo controlador de WebSocket), entonces puede usar el mismo host y puerto y simplemente cambiar el esquema como se muestra. Hay muchas opciones para ejecutar un servidor web y un servidor / módulo Websocket juntos.

Le sugiero que mire las piezas individuales de window.location global y las vuelva a unir en lugar de realizar una sustitución ciega de cadenas.

var loc = window.location, new_uri;
if (loc.protocol === "https:") {
    new_uri = "wss:";
} else {
    new_uri = "ws:";
}
new_uri += "//" + loc.host;
new_uri += loc.pathname + "/to/ws";

Tenga en cuenta que algunos servidores web (es decir, los basados ​​en Jetty) utilizan actualmente la ruta (en lugar del encabezado de actualización) para determinar si una solicitud específica debe transmitirse al controlador WebSocket. Por lo tanto, es posible que tenga limitaciones en cuanto a si puede transformar el camino de la manera que desee.

canaca
fuente
Al usar el nombre de ruta, obtengo esa URL: 'ws: // localhost: 8080 / Chat / index.html / chat'. Y su URL no es correcta.
Denis535
1
@ wishmaster35 cómo se maneja eso dependerá de su caso de uso y configuración. No hay una forma segura de determinar si example.com/part1/part2 se refiere a un archivo llamado part2 dentro de un directorio llamado part1, o si part2 es un directorio dentro de part1, o si es algo completamente diferente (por ejemplo, part1 y part2 son claves dentro de una base de datos de objetos). El significado de "rutas" en una URL depende del servidor web y su configuración. Podría inferir que todo lo que termine en "* .html" debería eliminarse. Pero nuevamente, esto dependerá de su configuración y requisitos específicos.
kanaka
3
@socketpair no, el puerto está ahí. window.location.host contiene el nombre de host y el puerto (location.hostname es solo el nombre de host).
kanaka
¿Puedo dejar de lado "/to/ws"? Si no es así, ¿cuál debería ser el valor de esa parte?
tet
1
@tet es la ruta de solicitud GET (es decir, la ruta HTTP GET) que se utiliza cuando se establece la conexión WebSocket inicial. Si se usa o no depende de su configuración. Si tiene un servidor websocket de propósito único (que también puede servir archivos web estáticos), probablemente se ignore. Si tiene varios servidores websocket detrás de un servidor web dedicado, es probable que la ruta se esté utilizando para enrutar al servidor websocket correcto. La ruta también se puede utilizar para otros fines por el servidor WebSocket tales como pasar tokens (por ejemplo a través de consulta params), etc.
kanaka
32

Aquí está mi versión que agrega el puerto tcp en caso de que no sea 80 o 443:

function url(s) {
    var l = window.location;
    return ((l.protocol === "https:") ? "wss://" : "ws://") + l.hostname + (((l.port != 80) && (l.port != 443)) ? ":" + l.port : "") + l.pathname + s;
}

Edición 1: versión mejorada por sugerencia de @kanaka:

function url(s) {
    var l = window.location;
    return ((l.protocol === "https:") ? "wss://" : "ws://") + l.host + l.pathname + s;
}

Edición 2: Hoy en día creo WebSocketesto:

var s = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/ws");
yglodt
fuente
14
No es necesario manipular puertos, solo use location.host en lugar de location.hostname
kanaka
24

Usando la API Window.URL - https://developer.mozilla.org/en-US/docs/Web/API/Window/URL

Funciona con http (s), puertos, etc.

var url = new URL('/path/to/websocket', window.location.href);

url.protocol = url.protocol.replace('http', 'ws');

url.href // => ws://www.example.com:9999/path/to/websocket
Eadz
fuente
Debo mencionar que esto también funciona con https / wss (reemplace 'http' con 'ws' => 'https' => 'wss')
Eadz
7

Suponiendo que su servidor WebSocket está escuchando en el mismo puerto desde el que se solicita la página, sugeriría:

function createWebSocket(path) {
    var protocolPrefix = (window.location.protocol === 'https:') ? 'wss:' : 'ws:';
    return new WebSocket(protocolPrefix + '//' + location.host + path);
}

Luego, para su caso, llámelo de la siguiente manera:

var socket = createWebSocket(location.pathname + '/to/ws');
Pavel
fuente
location.path no es correcto. Deberías usar el nombre de ruta.
Denis535
@ wishmaster35: ¡Buen partido! Fijo.
Pavel
4

fácil:

location.href.replace(/^http/, 'ws') + '/to/ws'
// or if you hate regexp:
location.href.replace('http://', 'ws://').replace('https://', 'wss://') + '/to/ws'
Maksim Kostromin
fuente
Usaría en /^http/lugar de 'http'solo en caso de que httpesté dentro de la barra de URL.
phk
window.location.href incluye la ruta completa, por lo que podría terminar /page.html/path/to/ws
Eadz
Puede ser problemático si su ubicación contiene http. Por ejemplo: testhttp.com/http.html
Dániel Kis
1
Simplemente reemplace 'http: //' con 'ws: //', esa idea simple debería ser obvia para cualquier desarrollador, incluso para los jóvenes
Maksim Kostromin
2

En localhost, debe considerar la ruta de contexto.

function wsURL(path) {
    var protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
    var url = protocol + location.host;
    if(location.hostname === 'localhost') {
        url += '/' + location.pathname.split('/')[1]; // add context path
    }
    return url + path;
}
Denis535
fuente
4
¿Qué es la ruta de contexto?
amirouche
2

Mecanografiado:

export class WebsocketUtils {

    public static websocketUrlByPath(path) {
        return this.websocketProtocolByLocation() +
            window.location.hostname +
            this.websocketPortWithColonByLocation() +
            window.location.pathname +
            path;
    }

    private static websocketProtocolByLocation() {
        return window.location.protocol === "https:" ? "wss://" : "ws://";
    }

    private static websocketPortWithColonByLocation() {
        const defaultPort = window.location.protocol === "https:" ? "443" : "80";
        if (window.location.port !== defaultPort) {
            return ":" + window.location.port;
        } else {
            return "";
        }
    }
}

Uso:

alert(WebsocketUtils.websocketUrlByPath("/websocket"));
Dániel Kis
fuente