Como construir um URI WebSocket em relação ao URI da página?

92

Eu quero construir um URI de WebSocket em relação ao URI da página no lado do navegador. Digamos, no meu caso, converter URIs HTTP como

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

para

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

O que estou fazendo atualmente é substituir as primeiras 4 letras "http" por "ws" e acrescentar "/ to / ws" a ele. Existe alguma maneira melhor para isso?

neuronte
fonte
1
O que você quer dizer com path/to/ws? Aonde isso leva exatamente? Obrigado
slevin

Respostas:

95

Se o seu servidor Web tiver suporte para WebSockets (ou um módulo manipulador WebSocket), você pode usar o mesmo host e porta e apenas alterar o esquema como está sendo mostrado. Existem muitas opções para executar um servidor Web e um servidor / módulo Websocket juntos.

Eu sugeriria que você olhasse para as partes individuais do window.location global e junte-as novamente em vez de fazer a substituição cega de strings.

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";

Observe que alguns servidores web (ou seja, os baseados em Jetty) atualmente usam o caminho (em vez do cabeçalho de atualização) para determinar se uma solicitação específica deve ser passada para o manipulador WebSocket. Portanto, você pode estar limitado quanto à possibilidade de transformar o caminho da maneira que deseja.

Kanaka
fonte
Usando o nome do caminho, obtenho esse url: 'ws: // localhost: 8080 / Chat / index.html / chat'. E é url incorreto.
Denis535
1
@ wishmaster35 como isso é tratado vai depender do seu caso de uso e configuração. Não há uma maneira segura de determinar se example.com/part1/part2 se refere a um arquivo chamado part2 dentro de um diretório chamado part1, ou se part2 é um diretório dentro de part1, ou algo completamente diferente (por exemplo, part1 e part2 são chaves dentro um banco de dados de objetos). O significado de "caminhos" em uma URL depende do servidor web e de sua configuração. Você pode inferir que tudo o que termina em "* .html" deve ser removido. Mas, novamente, isso dependerá de sua configuração e requisitos específicos.
Kanaka,
3
@socketpair não, a porta está lá. window.location.host contém o nome do host e a porta (location.hostname é apenas o nome do host).
Kanaka,
Posso deixar de fora "/to/ws"? Se não, qual deve ser o valor dessa parte?
tet
1
@tet é o caminho da solicitação GET (ou seja, o caminho HTTP GET) usado quando a conexão WebSocket inicial é estabelecida. Se ele é usado ou não depende da sua configuração. Se você tiver um servidor de websocket de propósito único (que também pode servir arquivos da web estáticos), ele provavelmente será ignorado. Se você tiver vários servidores websocket atrás de um servidor web dedicado, o caminho provavelmente está sendo usado para rotear para o servidor websocket correto. O caminho também pode ser usado para outros fins pelo servidor websocket, como passar tokens (por exemplo, via parâmetros de consulta), etc.
kanaka
32

Aqui está minha versão que adiciona a porta tcp caso não seja 80 ou 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;
}

Edição 1: versão aprimorada conforme sugestão de @kanaka:

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

Edição 2: Hoje em dia eu crio WebSocketeste:

var s = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/ws");
yglodt
fonte
14
Você não precisa alterar a porta, basta usar location.host em vez de location.hostname
kanaka
24

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

Funciona com http (s), portas 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
fonte
Devo mencionar que isso também funciona com https / wss (substitua 'http' por 'ws' => 'https' => 'wss')
Eadz
7

Supondo que seu servidor WebSocket esteja escutando na mesma porta em que a página está sendo solicitada, sugiro:

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

Então, para o seu caso, chame-o da seguinte forma:

var socket = createWebSocket(location.pathname + '/to/ws');
Pavel
fonte
location.path não está correto. Você deve usar o nome do caminho.
Denis535
@ wishmaster35: Boa pegada! Fixo.
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
fonte
Eu usaria em /^http/vez de 'http'apenas no caso de httpestar dentro da barra de URL.
phk
window.location.href inclui o caminho completo, então você pode terminar /page.html/path/to/ws
Eadz
Pode ser problemático se seu local contiver http. Por exemplo: testhttp.com/http.html
Dániel Kis
1
Basta substituir 'http: //' por 'ws: //', essa ideia simples deve ser óbvia para qualquer desenvolvedor, mesmo os juniores
Maksim Kostromin
2

No localhost, você deve considerar o caminho do 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
fonte
4
qual é o caminho do contexto?
amirouche
1

No texto datilografado:

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
fonte