Golang Http Proxy: A Complete Guide

Author

Reads 426

A minimalist design featuring a window-like lit icon centered on a dark background, emphasizing abstract art.
Credit: pexels.com, A minimalist design featuring a window-like lit icon centered on a dark background, emphasizing abstract art.

Golang HTTP proxy is a powerful tool for developers to inspect, manipulate, and forward HTTP requests and responses. It's a crucial part of any development workflow.

Golang provides a built-in `net/http/httputil` package that allows you to create an HTTP proxy server. This package contains the `ReverseProxy` type, which is a convenient way to create a reverse proxy.

With Golang, you can easily set up an HTTP proxy server to forward requests from one host to another. This is done by creating a `ReverseProxy` instance and setting its `Director` field to a function that determines the URL to forward the request to.

A different take: Install Golang Package

Setting Up

To start, you'll need to set up your own reverse proxy in Go, which is made easy with the net/http/httputil package.

The Go standard library provides a ReverseProxy type and a simple constructor named NewSingleHostReverseProxy to implement forwarding.

All you need to write is a simple reverse proxy that forwards from one address to another, listening on its default port of 9090.

Check this out: Golang Go

Credit: youtube.com, Tunnel SSH Directly To HTTP With GoLang!?

If you run this proxy and your debugging server on ports 8080 and 8080, curl requests will be redirected by the proxy to the server.

You can further configure the ReverseProxy type with several user-defined functions, including a custom Director field.

One step further is to implement a simplistic "load-balancing" reverse proxy that round-robins between two backend servers.

The proxy expects two backend servers to be provided with the --to1 and --to2 flags.

It then uses a custom Director to redirect HTTP requests to these two addresses in a simple round-robin fashion.

If you run two debugging servers on ports 8080 and 8081, and this proxy on its default port 9090, then curl requests to port 9090 will reach the debugging servers in turns.

You can extend this reverse proxy to include all the functionality from NewSingleHostReverseProxy and implement a more sophisticated load-balancing policy with an arbitrary number of backends.

Discover more: Azure Reverse Proxy

Proxy Modes

Proxy modes are a crucial aspect of Go's HTTP proxy implementation. There are four main modes: Regular HTTP proxy, HTTPS through CONNECT, HTTPS MITM proxy server, and "Hijacked" proxy connection.

Credit: youtube.com, How to write a reverse-proxy with Go in 25 minutes

A Regular HTTP proxy is the most straightforward mode, where the proxy server forwards HTTP requests and responses between a client and a server.

HTTPS through CONNECT is used to forward HTTPS requests, where the proxy server establishes a tunnel to the destination server.

An HTTPS MITM proxy server generates TLS certificates to parse request and response data, and perform actions on them. This mode is often used for debugging and testing purposes.

A "Hijacked" proxy connection allows the configured handler to access the raw net.Conn data. This mode is typically used for advanced use cases, such as modifying or inspecting the underlying network traffic.

Here's a summary of the four proxy modes:

Request Handling

Request handling is a crucial part of a GoLang HTTP proxy. The proxy can add headers to requests before sending them to the destination, such as adding the X-GoProxy: yxorPoG-X header to all requests.

You can use the OnRequest() input to specify a function that processes all incoming requests to the proxy. This function can add headers to the request and return it to the caller, who will then send the modified request to the destination.

Credit: youtube.com, How to Create an HTTP Proxy Pass in Go Without Loading the Body

If the returned response is nil, the proxy will discard the request and send a specified response to the client. But if the response is not nil, the proxy will send the modified request to the destination.

Conditional request handling allows you to specify conditions under which a request should be processed. For example, you can use the DstHostIs() function to return a ReqCondition that checks if the request is directed to a specific host, such as www.reddit.com.

The host equality check is case-insensitive, so even if the user types www.rEdDit.com, the comparison will satisfy the condition. This is because the behavior of DNS resolvers is reflected in the comparison.

You can directly return a response in the DoFunc() function if the condition is met, such as returning a "Don't waste your time!" response when the hour is between 8:00am and 5:59pm.

If this caught your attention, see: Golang Func Type

Error Handling

Error handling is a crucial aspect of building a reliable GoLang HTTP proxy. By default, the proxy returns HTTP error 500 with the error message as the body content if an error occurs while handling a request.

Here's an interesting read: Golang Create Error

Credit: youtube.com, A Beautiful Way To Deal With ERRORS in Golang HTTP Handlers

This behavior can be overridden by defining a custom RespHandler that changes the error response. The RespHandler has access to the context parameters, including ctx.Error, which contains the error occurred, if any, or the nil value, if no error happened.

You can handle the error as you wish, including returning a custom JSON as the body. A custom error handler can also be defined to handle connection errors that occur while sending data to the target remote server.

This connection error handler is called when an error occurs and it has access to the raw connection with the proxy client as an io.Writer. You can use this to send any HTTP data over it, containing the error data.

The proxy library will automatically close the connection after the error handler call, so you don't have to worry about it.

Implementation

Our Go HTTP proxy implementation lacks production-grade features, such as handling hop-by-hop headers and setting up timeouts.

Credit: youtube.com, How to write an http proxy in Go

The server will take two paths: handling HTTP requests or handling HTTP CONNECT tunneling. It uses a function called handleTunneling to set up the connection to the destination server. This involves setting up a connection to the destination server, hijacking the connection maintained by the HTTP server, and then setting up the tunnel.

In the handleTunneling function, data is copied in two goroutines, one from the client to the destination server and the other backward. This allows for bi-directional communication through the tunnel.

Implementing Our Own

A good way to understand how something works is to make it yourself, so we will be building our own HTTP proxy.

We'll start by initializing a new Go project, which will help us get a basic structure in place.

Having a little bit of Go know-how would definitely help, but it should also be understandable for anyone with basic programming knowledge.

This project will not be suitable for production use and its primary intent is to illustrate how things work.

Intriguing read: Golang vs Go

A minimalist design featuring a window-like lit icon centered on a dark background, emphasizing abstract art.
Credit: pexels.com, A minimalist design featuring a window-like lit icon centered on a dark background, emphasizing abstract art.

We'll be able to call pages such as http://example.com via our HTTP proxy.

Our server will take one of two paths: handling HTTP or handling HTTP CONNECT tunneling.

To handle HTTP, we'll use a function called handleHTTP, which is self-explanatory.

To handle tunneling, we'll set a connection to the destination server and then hijack the connection maintained by the HTTP server.

The hijacker interface allows us to take over the connection, and the caller is responsible for managing it.

We'll use two goroutines to copy data in both directions: from the client to the destination server and backward.

For testing, we'll start the proxy under localhost:8080 and test access via the proxy using curl.

We'll need to build curl with HTTPS-proxy support, which was introduced in version 7.52.0.

A different take: Golang Use Cases

Hostname Filter

We can start filtering or modifying HTTP traffic once we have the basic framework in place. The next step is to implement a new HTTP handler where we can place our filter logic.

Credit: youtube.com, How to Effectively Manage IPs and Host Names in Proxy Server Development

This handler can be used to block a list of hostnames. We pass a list of hostnames to it for initialization. If the hostname is in the list, the request is blocked.

We can also add a flag to block a list of hosts on startup. A request to one of the blocked pages will be blocked by the handler.

Security

Security is a top priority when using a GoLang HTTP proxy. Authentication is handled by the proxy, which can be configured to use various methods such as Basic Auth, Digest Auth, or even OAuth.

In our example, we used Basic Auth to secure the proxy. This is a simple and effective way to protect your proxy from unauthorized access.

The proxy can also be configured to use a reverse proxy pattern, which allows it to act as an intermediary between the client and the server. This can provide an additional layer of security.

Worth a look: Golang Security

Credit: youtube.com, Golang Project: Building a Secure Login Portal

By using a reverse proxy, you can hide the internal IP address of your server from the client, making it more difficult for attackers to launch a direct attack.

The GoLang HTTP proxy can also be configured to use HTTPS, which encrypts the data being transmitted between the client and the server. This provides an additional layer of security and protection against eavesdropping.

In our example, we used HTTPS to encrypt the data being transmitted through the proxy. This is a crucial step in securing your proxy and protecting sensitive data.

The proxy can also be configured to use IP blocking, which allows you to block specific IP addresses from accessing the proxy. This can be useful in preventing brute-force attacks or other types of malicious activity.

By using IP blocking, you can add an additional layer of security to your proxy and prevent unauthorized access.

Consider reading: Gcloud Api Using Golang

Forwarding

Forwarding is a fundamental concept in Go's HTTP proxying. A forward proxy sits between the user and the internet, allowing users to bypass browsing restrictions or access websites that are blocked in their region. This type of proxy can be implemented using Go's built-in httputil.ReverseProxy.

Credit: youtube.com, How to Forward Interfaces with Reverse Proxy in Golang

A forward proxy can be used to enforce browsing restrictions at the company or school level, or to protect the identity of users online by anonymizing their traffic. It can also provide ancillary services like logging, caching, and monitoring.

Here are some key features of forward proxies:

  • Enforce browsing restrictions
  • Bypass browsing restrictions
  • Anonymize traffic
  • Provide ancillary services

In Go, a forward proxy can be implemented using the httputil.ReverseProxy type. This type can be used to reverse-proxy requests to a target URL, allowing the proxy to act as a intermediary between the client and the target server.

The process of forwarding requests involves copying the headers and status code from the target server to the client, as well as copying the body of the response from the target server to the client. This can be done using the RoundTrip method of the DefaultTransport type, which sends the request to the target server and returns the response.

Here's a simple example of how to implement a forward proxy in Go:

```go

func forward(w http.ResponseWriter, r *http.Request) {

// Send the request to the target server

resp, err := DefaultTransport.RoundTrip(r)

if err != nil {

Credit: youtube.com, Multi-threaded Proxy server built in Go.

// Handle the error

}

// Copy the headers and status code from the target server to the client

w.WriteHeader(resp.StatusCode)

for k, v := range resp.Header {

w.Header().Set(k, v[0])

}

// Copy the body of the response from the target server to the client

io.Copy(w, resp.Body)

}

```

This code sets up an HTTP server that listens for incoming requests and forwards them to the target server using the RoundTrip method of the DefaultTransport type. The response from the target server is then copied to the client, including the headers, status code, and body.

Broaden your view: Golang Source Code

Reverse Proxy

A reverse proxy is a server that sits between the internet and your backend servers, intercepting requests and modifying them before forwarding them to the correct server. It's like a gatekeeper, deciding which server gets which request.

Reverse proxies can be used for load balancing, protecting against DDoS attacks, and terminating TLS encryption. They can also provide ancillary services like logging, caching, and monitoring.

A prominent example of reverse proxies is Content Delivery Networks (CDNs) like Cloudflare. If you connect to a large internet website or application, you're likely going through one or more reverse proxy servers.

Worth a look: Nordvpn Proxies

Credit: youtube.com, Proxy vs Reverse Proxy vs Load Balancer | Simply Explained

Here are some key features of reverse proxies:

  • Load balancing for high-traffic servers
  • Protection from DDoS attacks
  • TLS termination
  • Ancillary services like logging, caching, and monitoring

In Go, you can create a simple reverse proxy using the net/http/httputil package. The ReverseProxy type is fairly versatile and can be configured with several user-defined functions.

Customization

Customization is key to making your GoLang HTTP proxy server meet your specific needs. You can customize the HTTP transport to set timeouts, which is especially useful for preventing your server from hanging indefinitely.

To set a timeout, you can use the default HTTP transport provided by the `net/http` package. This package allows you to customize the transport according to your requirements.

Customizing the transport also gives you the option to enable or disable keep-alive, which can be useful depending on your server's configuration. Keep in mind that enabling keep-alive can lead to more efficient communication between your proxy server and target servers.

A unique perspective: Create a Package in Golang

Logging and Debugging

Logging and Debugging is crucial when building a GoLang HTTP proxy. To log the URL and headers for each request, we create a logging handler.

Credit: youtube.com, Logging in Go using logrus

Our previous forward handler can be wrapped in our new logRequest handler, a pattern in Go called the middleware pattern.

With a CONNECT request, the client asks the HTTP proxy to make a TCP connection to the target server and forward subsequent data through the TCP connection.

The TCP connection to the client and destination server is kept open and subsequent data is forwarded one-to-one, which is essential for debugging purposes.

The HTTP client then starts the actual TLS handshake directly with the destination server, making it easier to track and debug the connection process.

HTTP and TLS

To support HTTPS traffic, we need to terminate the TLS connection on the proxy and re-establish another TLS connection to the target server. This requires a valid certificate for each hostname to which the client wants to connect.

We can't obtain certificates for domains we don't control, which is a problem. Normally, we can only get certificates for domains we have control over.

To handle HTTPS traffic, we'll need to terminate the TLS connection on the proxy, which isn't what we want. The CONNECT method allows clients to make arbitrary connections to other servers and use protocols other than HTTP, which isn't what we want either.

Curious to learn more? Check out: T Golang

TLS Termination

Credit: youtube.com, SSL, TLS, HTTP, HTTPS Explained

TLS termination is a crucial aspect of HTTP and TLS. Unfortunately, it's not as straightforward as it seems.

For HTTPS requests after the CONNECT request, we can no longer see what data is being transferred because the data is encrypted by the TLS connection between the client and the target server.

This is a problem because our proxy can no longer detect and block "bad" data. Clients can also use the CONNECT method to make arbitrary connections to other servers and then use protocols other than HTTP.

To inspect HTTPS traffic, we need to terminate the TLS connection that's established after the CONNECT request and re-establish another TLS connection to the target server. This requires a valid certificate for each hostname to which the client wants to connect.

Normally, we can only obtain certificates for domains we control. This limitation makes it challenging to terminate TLS connections on the proxy.

Http Connect Tunneling

HTTP CONNECT tunneling is a technique used when a client wants to communicate with a server using a protocol that can't be sent over a simple HTTP request, such as HTTPS or WebSockets.

Credit: youtube.com, How HTTP Tunneling works, The CONNECT method, Pros & Cons and more

The client tells the proxy server to establish a TCP connection with the destination server using the HTTP CONNECT method. This tells the proxy to pass the data between the client and server without terminating the SSL connection.

The proxy server returns an OK response to the client, indicating that the connection has been established. It then takes over the TCP connection to the client, allowing the client and server to establish a secure connection.

To support the CONNECT method, a tunnel handler is implemented. This handler establishes the TCP connection to the destination server and returns an OK response to the client.

The tunnel handler then detaches the underlying TCP connection from the HTTP handler abstraction, allowing it to copy data from the client to the target server and vice versa on the TCP connections.

After implementing the tunnel handler, HTTPS requests can be handled by the proxy, making it possible to establish a secure connection between the client and server.

Additional reading: Golang Ok

Http

Credit: youtube.com, HTTP vs. HTTPS: How SSL/TLS Encryption Works

To support HTTP, we'll use built-in HTTP server and client. The role of proxy is to handle HTTP request, pass such request to destination server and send response back to the client.

You can instruct Go's HTTP client to use a proxy by setting an environment variable named http_proxy. This is similar to how curl works, and it's a widely supported approach.

Go's HTTP machinery also supports https_proxy for proxying HTTPS requests, and the uppercase versions of both variables - HTTP_PROXY and HTTPS_PROXY. Additionally, it supports no_proxy (and NO_PROXY) for listing domains that should never go through a proxy.

If you're developing your backend servers locally, you might run into an issue where Go HTTP clients won't proxy localhost addresses by default, even if http_proxy is set. However, there are easy workarounds to this problem.

To avoid this issue, you can add an alias in your machine's /etc/hosts file to map localhost to a different IP address, which will then go through the proxy. Alternatively, you can configure your HTTP client to use a custom proxy configuration in the transport.

Why and How

Credit: youtube.com, What Is An HTTP Proxy? - Next LVL Programming

A golang http proxy can be used to intercept and modify HTTP requests and responses, allowing for tasks like caching, authentication, and rate limiting. This is particularly useful for large-scale applications where a single proxy can handle multiple requests.

Golang's net/http/httputil package provides a ReverseProxy type that can be used to create a simple http proxy. This is a built-in solution that's easy to use and understand.

By using a golang http proxy, developers can improve the performance and security of their applications. For instance, a proxy can cache frequently accessed resources, reducing the load on the server.

The ReverseProxy type in golang's net/http/httputil package can be used to forward requests to a different server. This is done by setting the Handler field to a handler that knows how to forward requests.

A golang http proxy can also be used to implement authentication and rate limiting. This is done by modifying the requests and responses to include authentication tokens or rate limiting headers.

Danny Orlandini

Writer

Danny Orlandini is a passionate writer, known for his engaging and thought-provoking blog posts. He has been writing for several years and has developed a unique voice that resonates with readers from all walks of life. Danny's love for words and storytelling is evident in every piece he creates.

Love What You Read? Stay Updated!

Join our community for insights, tips, and more.