Sorry, you need to enable JavaScript to visit this website.

A simple asynchronous reverse proxy in Rust

Do you need help with your Drupal website?

Fill in your email and query below and we can chat.

More info here

Submitted by abhaisasidharan on Fri, 09/25/2020 - 21:33
Getting Started with Rust

In my previous article, I had written a very simple and basic reverse proxy. It is just like Nginx but without all the usefulness. I learned a lot from writing that and I hope it is useful to others as well. The previous version of the proxy server had a few issues. It was not a continuous stream. The connection often broke and was requested again. While doing a wget, it would send multiple requests to get the whole page. It could also not handle too many concurrent requests.

In this new code, I have used tokio.rs. Tokio provides runtimes for asynchronous programming. A runtime bundles services together and makes a single type of (for want of a better word) process, service, runtime, etc. This makes it easier to shut down, started, etc. In the code below, I have used TcpStream and TcpListener from the tokio library and not std. I have also spawned for tokio threads and within that, tokio tasks, which are sort of like green threads, but not quite because Rust does not natively support it.

All functions are async and they return something called a Future, which is something like a Promise in JavaScript. But unlike, JavaScript it is quite easy to handle Futures. Using "match" you can handle all the possible outcomes of the function which the Future will return when it is executed. It is a "Future" because it is not immediately executed. It will be executed sometime in the Future, when it is either "polled" or "awaited". This might not make sense now, because every language explains it differently. In simple terms, asynchronous programs execute concurrently, or at least they try. We await on them so that the functions we do want to execute sequentially, will pause the execution at that point until it completes execution.

Async/await along with tokio tasks improved concurrency by a lot! Wget does not stall no matter how fast you execute the next one. I even tried bombarding it with 1000 requests concurrently using 2 threads. The connection broke around 20 times, but it is quite a good result given the simplicity of the code.

There are many more improvements and fixes to be made. I still cannot handle large streams. I get a memory overflow error, and a new request will be sent to get the rest. I would like to dynamically allocate the buffer to read so that this can work better. I will be asking the Rust community for help on this. If anyone who reads this article, gets a solution please let me know. Please reach out with anything.

Here's the code: