126

I want to construct a WebSocket URI relative to the page URI at the browser side. Say, in my case convert HTTP URIs like

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

to

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

What I'm doing currently is replace the first 4 letters "http" by "ws", and append "/to/ws" to it. Is there any better way for that?

2
  • 1
    What do you mean by path/to/ws? Where does this leads exactly? Thanks
    – slevin
    Commented Jan 11, 2014 at 16:57
  • 2
    "ws://" + window.location.host + ":6666" -done
    – Fattie
    Commented Oct 14, 2020 at 17:25

9 Answers 9

126

If your Web server has support for WebSockets (or a WebSocket handler module) then you can use the same host and port and just change the scheme like you are showing. There are many options for running a Web server and Websocket server/module together.

I would suggest that you look at the individual pieces of the window.location global and join them back together instead of doing blind string substitution.

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

Note that some web servers (i.e. Jetty based ones) currently use the path (rather than the upgrade header) to determine whether a specific request should be passed on to the WebSocket handler. So you may be limited in whether you can transform the path in the way you want.

5
  • 2
    Using pathname I get such url: 'ws://localhost:8080/Chat/index.html/chat'. And it's uncorrct url.
    – Denis535
    Commented Oct 24, 2015 at 17:10
  • 1
    @wishmaster35 how that is handled is going to depend on your use case and setup. There is no sure-fire way to determine if example.com/part1/part2 refers to a file named part2 within a directory called part1, or wether part2 is a directory within part1, or something completely different (e.g. part1 and part2 are keys within a object database). The meaning of "paths" in a URL is up to the web server and its configuration. You could infer that anything ending in "*.html" should be stripped off. But again, this will depend on your specific setup and requirements.
    – kanaka
    Commented Oct 25, 2015 at 20:34
  • 3
    @socketpair no, port is there. window.location.host contains the hostname and the port (location.hostname is the hostname only).
    – kanaka
    Commented Oct 28, 2015 at 15:19
  • Can I leave out "/to/ws"? If not, what should be the value for that part?
    – tet
    Commented Apr 11, 2017 at 20:00
  • 1
    @tet that's the GET request path (i.e. the HTTP GET path) used when the initial WebSocket connection is established. Whether it is used or not depends on your setup. If you have a single purpose websocket server (that may happen to also serve static web files) then it is probably ignored. If you have multiple websocket servers behind a dedicated web server, then the path is probably being used to route to the right websocket server. The path can also be used for other purposes by the websocket server such as passing tokens (e.g. via query params), etc.
    – kanaka
    Commented Apr 11, 2017 at 20:54
51

Here is my version which adds the tcp port in case it's not 80 or 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;
}

Edit 1: Improved version as by suggestion of @kanaka :

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

Edit 2: Nowadays I create the WebSocket this:

var s = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/ws");
1
  • 18
    You don't need to do port mangling, just use location.host instead of location.hostname
    – kanaka
    Commented Oct 28, 2015 at 15:21
41

Using the Window.URL API - https://developer.mozilla.org/en-US/docs/Web/API/Window/URL

Works with http(s), ports 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
1
  • 2
    I should mention that this also works with https/wss ( replace 'http' with 'ws' => 'https' => 'wss' )
    – Eadz
    Commented Oct 31, 2019 at 9:15
9

Assuming your WebSocket server is listening on the same port as from which the page is being requested, I would suggest:

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

Then, for your case, call it as follows:

var socket = createWebSocket(location.pathname + '/to/ws');
0
7

easy:

location.href.replace(/^http/, 'ws') + '/to/ws'
// or if you hate regexp:
location.href.replace('http://', 'ws://').replace('https://', 'wss://') + '/to/ws'
5
  • I would use /^http/ instead of 'http' just in case http is inside the URL bar.
    – phk
    Commented Jun 10, 2017 at 22:16
  • window.location.href includes the full path, so you could end up /page.html/path/to/ws
    – Eadz
    Commented Nov 24, 2017 at 11:54
  • Can be problematic if your location contains http. For instance: testhttp.com/http.html Commented Dec 2, 2017 at 17:52
  • 2
    Just replace ‘http://‘ with ‘ws://‘ that simple idea should be obvious to any developers, even juniors Commented Dec 8, 2017 at 16:46
  • You may want to use the location.origin instead of location.href.
    – lanoxx
    Commented Mar 8 at 9:50
5

I agree with @Eadz, something like this is cleaner and safer:

const url = new URL('./ws', location.href);
url.protocol = url.protocol.replace('http', 'ws');
const webSocket = new WebSocket(url);

The URL class saves work and deals with things like query parameters, etc.

2

On localhost you should consider context path.

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;
}
1
  • 4
    what is context path?
    – amirouche
    Commented Jan 31, 2017 at 15:20
2

In typescript:

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

Usage:

alert(WebsocketUtils.websocketUrlByPath("/websocket"));
-1

Dead easy solution, ws and port, tested:

var ws = new WebSocket("ws://" + window.location.host + ":6666");

ws.onopen = function() { ws.send( .. etc

Not the answer you're looking for? Browse other questions tagged or ask your own question.