Introduction
Back in 2016, I’ve worked on an application called ShareTC that allowed users to share their files through their web browser using the WebRTC technology. The application was working well but the promise was to be able to share files without any server. The first version used PeerJS, a library allowing you to implement WebRTC data channel for file sharing easily with the drawback needing a server for what is called the signaling process. Thus, I’ve made some improvement to the project to allow user to share their files without even using a signaling server.
For those who are no interested by the technical implementation you can directly have a look to the demo on GitHub.
Migration
Originally, the project was barebone native JavaScript but I decided to upgrade the project to Angular 8, because exchanging the signaling configuration is a little complex, I needed some framework for easy DOM manipulation.
Because this is a side project, I didn’t want to recreate a component library so I used the @angular/material library for the forms and user feedbacks.
I also used @angular/flex-layout to easily define the layout, knowing that the application design is really basic as it is not the primary intent of the demonstration.
How it works
So, the interesting part now, the application will try to create a data channel between the sender and the receiver, using WebRTC. The protocol needs to know to whom it should send the data, this is named peer discovery, and it can be done automatically through what is called a signaling server, a server where you exchange your configuration to begin the communication and this is the component that I wanted to remove. For the manual configuration we use the Session Description Protocol (or SDP).
But because the network is a little complex, in some cases, you’ll need a Session Traversal Utilities for NAT (or STUN) server. This is used when you’re exchanging a file from behind a Network Address Translation (or NAT) firewall. There are some public servers available for this like:
- stun.services.mozilla.com
- stun.l.google.com:19302
In some other cases the communication channel will be blocked by firewalls. In this case you can eventually use a Traversal Using Relays around NAT (or TURN) server that will work as a Data relay between you and the peer. They are expensive and only necessary if you’re behind a blocking firewall so I didn’t integrate a solution for that 😊
So, what do we do is, we generate the sender configuration SDP, that you will need to share with the receiver. Then he’ll be able to generate his SDP configuration, that you’ll need to import in order to create the communication channel. Then you’ll be able to send the file using Datagram Transport Layer Security (or DTLS) which is a secure communication channel.
How to make it happen
Now that you have the WebRTC communication scheme in mind, let’s see some interesting parts of the project. You might want to have the code open to better understand this part by accessing the project repository flyersweb/sharetc.
First, I chose to use the great feross/simple-peer library for WebRTC communication channel. To use the library we had to add some polyfills (check polyfills.ts) to the project for the global, process and buffer objects like this:
(window as any).global = window; (window as any).process = { nextTick: setImmediate, env: { DEBUG: undefined }, }; (window as any).global.Buffer = (window as any).global.Buffer || require('buffer').Buffer;
If you’re using typescript you also might have to add the node declarations for native modules by modifying your tsconfig.app.json file with:
"types": [ "node" ],
I’ve also decided to compress SDP configuration using LZ-based compression so it is easier to share. This is why the configurations will look more like this:
N4IgLgngDgpiBcID2AzFMBOIA0IDOAJlAiAG4C8ADADobUB2S5AtkgF4CWANlwIYB0ggCoAJAJIBlAPqSpEgCIAFAIIAlIcoC0Adkr89ARgAEBgEwAWSuYCcZ7dZsAOc6e2Ptpo5SNiAcj8VzL30Qmjp6PHJNWgYwKi8Y+l5yPBh6AgwYAGNSROSUDnoAc0woDEKweDwAC15NUwBWADYjJuV4JoaO63gAZgAxeEcm+ABRAGF4bXN4W3h5Qf7JxvhzLoNBptGxyYbt0ZGG9tH5eGVtpp63eH7KVZ7lRzGDeHG769M7+Rf5O4AhUZ5chFDBIACuUHgfwAqr55AAZUYJcLJZh4DgETSpZi8ehgDhZeAAdQAshIjAAqRLMci8KBQLgE3j4pD0IwNRyOUwtaFKAD08iE8IkfIk4yEiiMAHcYAAjDBgLKaAjM3hZWr0egwLiJLLkPwBILWPTG-iNfgGXqOIFZXEEDHMmDwbzGXmS0xmUyNRrmXomPR6UwWhpGXraXq2IyQKBGapIPBgG12h1gJ1+4xCcbugyUBoNCzmez+kJBgwh6xR6Cx+NgKNZKDRmBGNX40gwJPpFNO4yupQmJrDXOmYZ+02m82WxzszncysxvAYFBcAAeRgwvAIGWLgeDa6gSAVofDtiBqXSmRyQLSmNQmltnZVqbwQIJME0UClBCm9m0TVzsosUwshgbRZRgdx7GaAhZQIT4slMFAAJfYDNDBFB1yKeAGmsD0DHA3ogWYDFnVPGAwAheAWygXg8GfFEUiyMAoHfA9KgaSgOMI3hl00ZgYFo3gSixDg2G7Shw2mAwuQI8IQAAXyAA
But by decompressing it you’ll get a valid SDP signal message.
And finally, in order to send and download the file we use the JavaScript File API so we can send it through the data channel. Thing is, the data will be stored as an ArrayBuffer so we generate a download link through a Blob for the file to be downloaded with the name file.dat as shown in the download component.
To exchange messages through the communication data channel we also need to convert the ArrayBuffer to string by using this function:
function ab2str(buf: ArrayBuffer): string { return String.fromCharCode.apply(null, new Uint16Array(buf)); }
You can check how it is used in the upload component.
The demo
Because you have to exchange the SDP configuration and create the data channel before sending the file, the application is a little more complex to grasp. So to use the demo application you’ll need to:
1. Send your sender configuration
2. Receiver will enter your configuration and hit connect (it connects to sender)
3. The receiver will the share his generated configuration with you the sender
4. Then the sender hit connect to create the data channel
5. The duplex stream is up and running
You can access it by accessing https://flyersweb.github.io/sharetc.
Conclusion
This was a long road to have the application that I wanted. To allow users to share their file securely without any server, but I’m really happy to have achieved it besides the network complexity and thanks to the projects that make using WebRTC each day easier.
You can check the project source code on the github repository flyersweb/sharetc and give me a tip so that I can buy me a coffee to work more on this 😊