The problem
There is no simple way to create TCP sockets in Javascript on a browser side. Although solutions like Websockets allow to create something that resemble sockets, you can use them to connect only to servers that support Websockets. Not to any random servers that know nothing about HTTP.
Why bother
If creating such connections were possible, then we could connect to any external server from browser and keep all logic in client-side Javascript without needing to implement a backend app.
For instance, it would be possible to create (or just port them from node.js) client libraries for things like: Memcache, Redis, MySQL, Riak, RabbitMQ or any other server.
While in many situations such usage would be questionable and insecure, there are cases when it could be quite useful:
- using server-side cache from JS
- using pub/sub servers to deliver notifications to browsers. (Redis, RabbitMQ, Apache Kafka, etc)
- making HTTP request to any server bypassing same-origin policies :|
Solution
As an experiment I implemented this small library: WebTCP. Here is how it works.
It is impossible to make browser to initiate raw TCP connections to a server, but it is possible to use some proxy (or a “bridge” would be a better name) that will receive connection requests from the browser, create real sockets and then redirect responses back to the browser. So on a client side we will have fake socket objects that will be mapped to real socket connections on a proxy side.
How client connects to the bridge
This is when Websockets or something like Socket.IO or SockJS comes in handy. Client can use Websockets (or fall back to xhr/jsonp-polling or whatever is supported) to talk to the bridge and bridge will talk to TCP servers using real socket connections. I decided to use SockJS although Socket.IO is fine too. Here is how the entire thing looks like:
How is it different than having a backend app
The difference is in where to put logic to handle different servers’ protocols. The normal way would be to handle it on a server side. So, for instance, if browser wants to get something out of Memcache – it will make a request to some backend app that knows how to get data out of Memcache. If suddenly you want to do something with Redis then you will need to modify backend code and restart server to add support for that.
On the other hand, if browser can operate on a socket level such protocol logic could be implemented on a client side. There can be just a bridge (or easily a cluster of bridges behind HAproxy) that knows nothing about servers’ protocols and just pass data to sockets back and forth. Whatever you want to connect to from the browser just include a JS client library in the page, no need to touch bridge at all.
Examples
Here are some examples for sockets, http, memcache client and redis client. I ported memcache client easily from node.js version just by using node-browserify and by replacing net library with WebTCP connection. Redis client for node.js relies on C library, so I had to implement client from scratch, but luckily Redis protocol is pretty simple.
Socket example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
It’s also possible to specify advanced options when creating a socket connection
1 2 3 4 5 6 7 |
|
And then pass those options when creating socket
1
|
|
HTTP example
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Redis example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Memcache example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
That’s about it. Although this project still need more thought to figure out good use cases, I had really fun time playing with it.