Aug 17, 2022

DropNow: Peer to Peer Data Sharing Through WebRTC

Quite often we need to transfer files between devices. Unfortunately, even in the modern age of technology, we still have trouble doing this securely and conveniently. We explore the implementation of WebRTC in a web application to enable peer to peer data transfer.

The Problem

It is quite often that I find myself needing to transfer files between devices. It usually resolves into sending files through email, a form of social media, or some other untrusted file hosting platform.

WebRTC

WebRTC is an open source project that provides an interface for peer to peer communication between devices. Using WebRTC’s data channels, we can send data completely securely from one peer to another, without a middle man (assuming that a connection is made without a TURN server).

Using WebRTC, I decided to create DropNow.

DropNow

DropNow is a free service designed to fix the convenience, privacy, and quality issues of data sharing today. It is available now at dropnow.elguindi.xyz. Try it out!

Technical Details

DropNow uses a room-type signalling server to enable peer to peer WebRTC connections. A user can create a room, identified by a room name (the string that they input on the homepage of the website). From there, another user can join that room by entering the same room name; there can only be 2 users in one room at a time. The room system is in place to allow for the relaying of messages from peer to the other.

// main.go
var rooms = make(map[string]*User)

type User struct {
	*websocket.Conn
    ...
	pair *User // the user's peer
}

The signalling server handles websocket connections from every user on a new goroutine, when a message is received from a User, it is relayed to the User.pair.

After the WebRTC connection is made through the signalling server, the signalling server is not used for any data transfer other than for WebRTC purposes (like renegotiation). Nobody sees your data except you and the connected peer.

Technical Limitations and Solutions

Due to the nature of storing JavaScript blobs on mobile devices, it is not possible to transfer large files (greater than ~300MiB). To download a file, we store the received blobs from WebRTC in an array, then, once all blobs are received, a data URI is generated from the array for the download. This results in the need for the browser to be able to store 2 times the size of the transferred data.

let data: ArrayBuffer[] = [];

/* on data chunk receive */
data.push(chunkData);

/* on data save/download */
const received = new Blob(data, { type: mime });
downloadAnchor.href = URL.createObjectURL(received);
downloadAnchor.download = filename;
downloadAnchor.click();

If a better filesystem API comes along in the future, it should theoretically be possible to transfer files of any size, writing directly to non-volatile storage, as opposed to memory.

For desktop browsers, they have a greater capacity (due to larger RAM size) and have some caching ability to store large blobs. I currently do not see an issue with large file transfers to a desktop browser.

A current possible solution would consist of desktop or mobile applications to handle the data transfer, writing directly to the disk with a small in-memory buffer. This would mean that the download size is not limited by the memory of the device, but its physical storage.