57

So I'm doing some research on websockets, and I have a few questions I can't seem to find a definitive answer for:

  • How can I set up a web socket on my Linux server? Is there an Apache module? Would I have to use 3rd-party PHP code or similar?

  • Are there any kinds of drawbacks to the method described in question 1 that I should be aware of other than browser compatibility?

  • How could I "upgrade" my websocket installation to a secure websocket installation (ws:// to wss://)? Would this be made easier or more difficult if SSL was already set up on my Apache server?

  • Is there any language I could use to connect to my web socket other than JavaScript?

  • What is the default request method for a web socket?

  • Just a word of advice now that I've got a few years of experience dealing with web sockets... NGINX is much better than Apache for this. NGINX was originally made to be a proxy server, so, naturally, it's much more stable as a proxy that Apache. As for which is a better web server is another topic... –  Jun 27 '17 at 12:27
  • Did you try Apache's mod_proxy_wstunnel? And how did you made communication from PHP? Did you use any 3rd Party Library to do so? – Airy Jul 18 '17 at 09:24
  • 2
    Again, NGINX is much better for proxying wstunnel in my experience. I did try mod_proxy_wstunnel and it worked fine, but it was causing problems for connections that stayed open for multiple days. I used PHP at the start and don't remember what I used, but now I write my web socket backend in C, and C is great for handling sockets. PHP wasn't made to run as a process for long periods of time. http://socketo.me/ is a good reference for a PHP web socket server though. https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers is a nice guide to help learn. –  Jul 19 '17 at 12:56

3 Answers3

41

The new version 2.4 of Apache HTTP Server has a module called mod_proxy_wstunnel which is a websocket proxy.

http://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Udo
  • 593
  • 5
  • 6
  • 15
    You need to enable `mod_proxy` and `mod_proxy_wstunnel` modules and add `ProxyPass /wss2/ ws://YOUR_DOMAIN:WS_PORT/` in _httpd.conf_ file. Use secured scheme w/o port number url in JS call (e.g. `var ws = new WebSocket("wss://YOUR_DOMAIN/wss2/NNN");` [See Ref](http://stackoverflow.com/questions/16979793/php-ratchet-websocket-ssl-connect/28393526#28393526) – webcoder Feb 09 '15 at 03:44
  • Did this fully support websocket – Veshraj Joshi Dec 26 '18 at 04:16
31

I can't answer all questions, but I will do my best.

As you already know, WS is only a persistent full-duplex TCP connection with framed messages where the initial handshaking is HTTP-like. You need some server that's listening for incoming WS requests and that binds a handler to them.

Now it might be possible with Apache HTTP Server, and I've seen some examples, but there's no official support and it gets complicated. What would Apache do? Where would be your handler? There's a module that forwards incoming WS requests to an external shared library, but this is not necessary with the other great tools to work with WS.

WS server trends now include: Autobahn (Python) and Socket.IO (Node.js = JavaScript on the server). The latter also supports other hackish "persistent" connections like long polling and all the COMET stuff. There are other little known WS server frameworks like Ratchet (PHP, if you're only familiar with that).

In any case, you will need to listen on a port, and of course that port cannot be the same as the Apache HTTP Server already running on your machine (default = 80). You could use something like 8080, but even if this particular one is a popular choice, some firewalls might still block it since it's not supposed to be Web traffic. This is why many people choose 443, which is the HTTP Secure port that, for obvious reasons, firewalls do not block. If you're not using SSL, you can use 80 for HTTP and 443 for WS. The WS server doesn't need to be secure; we're just using the port.

Edit: According to Iharob Al Asimi, the previous paragraph is wrong. I have no time to investigate this, so please see his work for more details.

About the protocol, as Wikipedia shows, it looks like this:

Client sends:

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

Server replies:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

and keeps the connection alive. If you can implement this handshaking and the basic message framing (encapsulating each message with a small header describing it), then you can use any client-side language you want. JavaScript is only used in Web browsers because it's built-in.

As you can see, the default "request method" is an initial HTTP GET, although this is not really HTTP and looses everything in common with HTTP after this handshaking. I guess servers that do not support

Upgrade: websocket
Connection: Upgrade

will reply with an error or with a page content.

eepp
  • 7,255
  • 1
  • 38
  • 56
  • Thanks for the input. I was fiddling around with this a lot lastnight, and your answer helped me make sense of a few things. While it doesn't answer everything, it's definitely points me in the right direction. I'll probably use Rachet because the server's already running PHP. I suppose my biggest question at the moment that won't be answered by fiddling around with your recommendations is "Why do the websocket default ports overlap the default HTTP ports?". –  Jun 27 '13 at 15:44
  • Because it's meant to be integrated with an HTTP server. However, I don't think Apache HTTP Server has a simple way of doing this for the moment. But with the aforementioned frameworks, it should be easy to also serve HTTP so you could drop Apache HTTP Server completely. You will need to provide different handlers for different context paths (e.g. `/myWsEndpoint` leads to a WS handler and `/hello` to an HTTP endpoint). Also I forgot to mention [Jetty](http://www.eclipse.org/jetty/) which is quite nice and easy for the Java programmer (serves both HTTP and WS). – eepp Jun 27 '13 at 15:56
  • I see. Thanks for all of your help. I'll be doing some more experimenting with this after work. –  Jun 27 '13 at 16:07
  • The only thing I don't really like about Ratchet is that it has a bunch of dependencies. I'd rather fiddle around with the Apache module when the time comes. I really do appreciate your help though, and I'm sure a lot of other people will find it useful. I'll accept this since it's a perfectly valid answer that doesn't solve my problem only because I'm picky. –  Jun 28 '13 at 02:07
  • 3
    I downvote becuse **of course it can be the same port apache is using for regular HTTP**. – Iharob Al Asimi Jun 12 '16 at 17:24
  • 2
    In fact, I succeeded in writing an apache module to handle the WS protocol handshake and communicate with another program that will be in your words "*the handler*". It is really easy, and robust. Oh, and it uses the port 80. Actually that is the default in javascript websocket API. – Iharob Al Asimi Sep 29 '16 at 12:18
18

I struggled to understand the proxy settings for websockets for https therefore let me put clarity here what i realized.

First you need to enable proxy and proxy_wstunnel apache modules and the apache configuration file will look like this.

<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
      ServerName www.example.com
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/your_project_public_folder

      SSLEngine on
      SSLCertificateFile    /etc/ssl/certs/path_to_your_ssl_certificate
      SSLCertificateKeyFile /etc/ssl/private/path_to_your_ssl_key

      <Directory /var/www/your_project_public_folder>
              Options Indexes FollowSymLinks
              AllowOverride All
              Require all granted
              php_flag display_errors On
      </Directory>
      ProxyRequests Off 
      ProxyPass /wss/  ws://example.com:port_no

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
</IfModule>

in your frontend application use the url "wss://example.com/wss/" this is very important mostly if you are stuck with websockets you might be making mistake in the front end url. You probably putting url wrongly like below.

wss://example.com:8080/wss/ -> port no should not be mentioned
ws://example.com/wss/ -> url should start with wss only.
wss://example.com/wss -> url should end with / -> most important

also interesting part is the last /wss/ is same as proxypass value if you writing proxypass /ws/ then in the front end you should write /ws/ in the end of url.

Sunil Kumar
  • 759
  • 7
  • 17
  • Just a note, the trailing slash can be omitted. What matters is that the websocket url is the same in all of the codebase. For some reason Apache2 makes it hard to make wss work on HTTPS. – Dale Ryan Aug 04 '23 at 23:38