Hi all. Today, I'd like to show you the power of pure HTTP stream, which allows us to do screen sharing, video chatting, real-time text messaging, SSH, VNC, and so on. The demo videos below show SSH and VNC over pure HTTP. Let's dive into this!
HTTP is everywhere for everyone and everything, which allows us to communicate in any situation. HTTP is one of the most mature protocols and wildly used. You can find HTTP in web browser in personal computers including Windows, Mac and Linux, smartphones and tablets including Android, iPhone and iPad, curl, wget commands in your terminal, some IoT devices, automation tools such as Shortcuts iOS app and Microsoft Flow, Web Hooks and so on. Additionally, HTTP is an evolving protocol as HTTP/2, HTTP/3 and getting faster.
Usuary, an HTTP request has a short life, used for fetching HTML/CSS/JavaScript, media and API requests. So, this post introduces long-life HTTP request, which does real-time communications over pure HTTP stream without WebSocket and WebRTC.
Piping Server
I made Piping Server, which allows us to transfer data between every device.
Piping Server transfers data to POST /hello or PUT /hello into GET /hello. The path /hello can be anything such as /mypath or /mypath/123/. A sender and receivers who specify the same path can transfer. Both the sender and the recipient can start the transfer first. The first one waits for the other.
You can also use Web UI like https://ppng.io on your browser. A more modern UI is found in https://piping-ui.org, which supports E2E encryption.
Stream
The most important thing is that the data are streamed. This means that you can transfer any data infinitely. The demo below transfers an infinite text stream with seq inf.
Piping Server is simple. A sender and recipient who specify the same path such as /hello can transfer. The image below is the concept of transfer.
The image shows the sender who POSTs /mypath and the recipient GETs /mypath can transfer. Both the sender and the recipient can start the transfer first. The first one waits for the other. Both POST and PUT methods are the same in Piping Server.
You can transfer a text by using fetch() in JavaScript like below.
You can also transfer binary data such as an image and a video like below. As you can see, the recipient just opened the URL on the browser to get the image.
HTTP is everywhere. So we can transfer data freely without any extra tools.
Infinitely transfer
The most notable feature of Piping Server is allowing you to transfer infinite data. The demo below shows inputting text streams into the web browser.
Send folder
You can transfer a folder (a directory), which has multiple files as follows.
# Send foldertar c ./mydir | curl -T - https://ppng.io/mypath
# Get folder
curl https://ppng.io/mypath | tar xv
The files are packing while uploading in the sender and unpacking while downloading in the recipient. Stream makes this possible without creating a temporary file.
It is easy to end-to-end encrypt your data and transfer as follows.
send: ... | openssl aes-256-cbc | curl -T ...
get: curl ... | openssl aes-256-cbc -d
It is also easy to reduce the size by compression as follows.
send: ... | gzip | curl -T ...
get: curl ... | zcat
You can transform data as you want such as gpg, zip or tools invented in the future. Combining pipe is efficient in terms of both time and memory. Unix pipe is an amazing way to combine software. The name of Piping Server is derived from Unix pipe.
The most common use case of Piping Server is a file transfer. For transferring files, you can use Piping UI, which allows you to transfer securely with end-to-end encryption over many devices.
Transfer huge data for a long time
Here are simple experiments to transfer data over HTTP using local and remotePiping Server.
The demo video below shows 45TB is transferred for 2,092 hours (87 days) over HTTP via remote Piping Server, using cat /dev/zero | curl -T- ....
The image below shows transferred 1,110TB (≈ 1PB) for 64 days over HTTP via local Piping Server.
These experiments show a huge amount of data can be continuously transferred over a single HTTP request and a single HTTP request lives long enough.
Infinite stream for Web browser
Infinite stream sending over HTTP had come to Web browser at last!
Google Chrome 85 or above has the feature as origin trial. Open chrome://flags and enable "Experimental Web Platform features" as follows
Other main browsers such as Firefox and Safari are also interested in this feature.
Here is a simple text messaging on Web browser with fetch() and ReadableStream.
The code below creates ReadableStream from user input and sends the input stream to Piping Server. The recipient just opens the URL on the browser and sees streamed text messages.
You can share your screen in almost the same way as the text streaming above. Get MediaStream and convert to ReadableStream and send the stream to Piping Server with fetch().
The function mediaStreamToReadableStream() below converts MediaStream to ReadableStream.
(async()=>{// Get displayconstmediaStream=awaitnavigator.mediaDevices.getDisplayMedia({video:true});// Convert MediaStream to ReadableStreamconstreadableStream=mediaStreamToReadableStream(mediaStream,100);fetch("https://ppng.io/myvideo",{method:'POST',body:readableStream,allowHTTP1ForStreamingUpload:true,});})();// Convert MediaStream to ReadableStreamfunctionmediaStreamToReadableStream(mediaStream,timeslice){returnnewReadableStream({start(ctrl){constrecorder=newMediaRecorder(mediaStream);recorder.ondataavailable=async(e)=>{ctrl.enqueue(newUint8Array(awaite.data.arrayBuffer()));};recorder.start(timeslice);}});}
This way is friendy to command-line tools too. You can also view the screen with curl https://ppng.io/myvideo | ffplay -. You can also send your screen with ffmpeg command. See Capture/Desktop – FFmpeg for more info.
Voice and video chatting
For voice or video chatting, all you need to do is to replace the code, const mediaStream = above with:
Then, you can use the mediaStreamToReadableStream() to converts those MediaStreams to ReadableStreams to specify body: in fetch().
Video filtering
You can get MediaStream from the canvas. The function below creates a video and a canvas in memory and transforms a MediaStream to another one. JSManipulate is used. You may create a filter app like Snap Camera.
// Filter for sepiaasyncfunctionsepiaMediaStream(mediaStream){constmemVideo=document.createElement('video');memVideo.srcObject=mediaStream;awaitmemVideo.play();constwidth=memVideo.videoWidth;constheight=memVideo.videoHeight;constsrcCanvas=document.createElement('canvas');constdstCanvas=document.createElement('canvas');srcCanvas.width=dstCanvas.width=width;srcCanvas.height=dstCanvas.height=height;constsrcCtx=srcCanvas.getContext('2d');constdstCtx=dstCanvas.getContext('2d');(functionloop(){srcCtx.drawImage(memVideo,0,0,width,height);constframe=srcCtx.getImageData(0,0,width,height);JSManipulate.sepia.filter(frame);dstCtx.putImageData(frame,0,0);setTimeout(loop,0);})();returndstCanvas.captureStream();}
Compress data with gzip as follows. In Chrome, you can easily compress a stream with readableStream.pipeThrough(new CompressionStream('gzip')).
constreadableStream=newReadableStream({pull(ctrl){// random bytesctrl.enqueue(window.crypto.getRandomValues(newUint32Array(128)));}}).pipeThrough(newCompressionStream('gzip'))fetch("https://ppng.io/mytext",{method:'POST',body:readableStream,allowHTTP1ForStreamingUpload:true,});
You can safely transfer your stream even if a server is untrustable. You can encrypt any ReadableStream with the code below using OpenPGP.js.
// Encrypt ReadableStream with password by OpenPGPasyncfunctionencryptStream(readableStream,password){constoptions={message:openpgp.message.fromBinary(readableStream),passwords:[password],armor:false};constciphertext=awaitopenpgp.encrypt(options);returnciphertext.message.packets.write();}
Web browsers have Web Crypto, which can generate keys safely and do Diffie-Hellman key exchange an untrustable channel. For example, Piping UI, which is a file transfer tool, exchanges public keys and encrypts a file by using ECDH and OpenPGP.js.
As you see, any data can be streamed over HTTP. So, this means a protocol can be over HTTP via Piping Server.
Why Piping Server?
There are some environments that can not release ports public. For such kind of environment, when you have the only outward-connection to HTTP/HTTPS ports, you can use SSH. A possible example is for GitHub Actions, which does not support SSH debug like CircleCI (see: SSH in GitHub Actions over Piping Server).
SSH client in JavaScirpt
I found a wonderful project, SSHy whose JavaScript speaks SSH. The data communication is over WebSocket, so I just need to switch WebSocket to HTTP with fetch(). Unfortunately, although SSHy is not actively maintained now, this is a perfect fit for my proof of concept to speak SSH over HTTP using Piping Server. We could port OpenSSH by using Emscripten, write Rust and compile to Web Assembly, or do something in the future.
By using SSHy, it is possible to SSH with only Web browser and Piping Server. The data streamed to Piping Server is securely encrypted since the communication is SSH.
How to SSH over Piping Server?
Create two sets of connections over Piping Server for duplex communication. One of them is for sending data to your peer. The other one is for receiving data from your peer. On HTTP/2, multiple HTTP requests are bundled into one TCP connection.
You can do ssh -p 31376 <user>@localhost in the machine in another terminal. This is a versatile way to forward a port to another device, not only SSH.
In Chrome, an HTTP request is stopped when no bytes arrived for 60 seconds. To resolve the issue, you can set /etc/ssh/sshd_config as follows in your SSH server setting.
For Ubuntu 20.04 users, to enable VNC, you can turn on Settings > Sharing and run gsettings set org.gnome.Vino require-encryption false to avoid an error, "Failed when connecting: Unsupported security types (types: 18)".
VNC is also available for Windows. Here is a demo controlling Windows 10 from Chrome. It was more smooth on a real Windows machine since the windows machine in the demo below was running on VirtualBox. UltraVNC is running on the Windows machine.
The fetch-uploading feature is also available on Android Chrome. The demo below controls Windows 10 by an Android smartphone.
For windows users, you can a download tunneling tool over Piping Server here: https://github.com/nwtgck/go-piping-tunnel. It is convenient to create a simple .bat file as follows.
.\piping-tunnel server-p 5900path1path2
piping-tunnel has the same feature of the socat + curl command. For mac users, you can install by brew install nwtgck/piping-tunnel/piping-tunnel.
How it works
The application is fully based on noVNC, which is a VNC client written in JavaScript. Only transport implementations are replaced with the way of using fetch and Piping Server instead of WebSocket.
Here is the network in the Chrome DevTools. There are only two pure HTTPS connections. v86 is uploading and 7vk is downloading. As you can see the download size of v86 is increasing. Although 7vk is uploading, the view in the current Chrome says "pending".
fetch() upload streaming
I have been following this feature. Here are useful links to get information about the fetch() upload streaming feature.