Let's Write a Proxy Server
A tale about a humble proxy server
Amica - Devlog #0
A tale about a humble proxy server
Lately I’ve been trying to learn Rust using the book. While reading the book I was able to write a simple date convertions library, writing a library is a good way to get your feet wet, in my opinion. After finishing the book I was, naturally, looking for other project ideas and I landed with this. In these series we will be looking at implementing our own proxy server.
Goal
The initial goal is to build a simple
http proxy server. This server
won’t work with https for now. We will implement that in the next post. We
will also embed lua in the coming series to maniuplate the http
request
and response.
Get coding
To get started we will need two crates hyper, and tokio which both can be added to out project like the following, we will also enable some features these crates have.
|
|
From this point forward I will first be posting the code and then I will explain it.
Replace the code in src/main.rs
with the following.
|
|
This code is relatively simple to grasp so I’m just gonna skim over it. On line
3
we have where and which port we are listening on. Line 4
is where we will
create, and bind our tcp socket, Rust
makes it easy by combining two steps
into one in most popular languages you would have to create a socket and then
bind it to an available ip and port.
Note that we are using unwrap
, for now we won’t be worried about errors, we
will worry about them in a future post.
Starting on line 7
, upto 12
we have an endless loop. In this loop we accept
and send to the handle_client
function in a separate thread or as tokio
calls them green threads
. This allows us to handle multiple clients at a
time.
tokio
’s threads are not the same as os
threads.
|
|
We are creating a new Http
protocol instance with the underlying tcp stream
being client_tcp_stream
which means this will handle the protocol rules we
just have to send and receive based on the rules. On line 5
we also pass in a
service. A service, as far as I can understand, is something that will make a
Response
based on a Request
. In our case we take the clients Request
and
make the request as if we are making the request, using the hyper
http
client we created on line 6
. And then replay to our client with the
Response
we got from the hyper
http
client on line 9
.
We are adding String
as a generic, because the compiler cannot infer the type
of the E
in Result<T, E>
. So we are helping the compiler. at first I
thought of putting std::error::Error
but this is just a trait, and it’s size
cannot be know at compile time.
It can, however, infer the type of T
because we are passing it in.
Let’s test this with curl. Let’s start an http server using the following command.
|
|
Once the server is started all we have to do start the program like this.
|
|
And connect through it using curl like this.
|
|
You can check the source code here.