Recently got into proxying requests and socks5, so I started out looking at how we can do that simply with io.Copy and standard go library http server
package main
import (
"io"
"log"
"maps"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Get target URL from query parameter
targetURL := r.URL.Query().Get("url")
if targetURL == "" {
http.Error(w, "url parameter required", http.StatusBadRequest)
return
}
// Make the request
resp, err := http.Get(targetURL)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
defer resp.Body.Close()
// Copy headers
maps.Copy(w.Header(), resp.Header)
// Copy status code
w.WriteHeader(resp.StatusCode)
// Stream the body
io.Copy(w, resp.Body)
})
log.Printf("Starting proxy server on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
What this code essentially does is, accept incoming requests, and then do another HTTP GET to the url specified in the initial request
Now why would we want to do that? Well if our server is running in another country, and we make requests to this server, our request will look like it came from wherever the server is
To test this out, i deployed the exact same server above to Google Cloud Run, and its quite cool
curl 'https://helloworld-218563108188.asia-northeast1.run.app/?url=https://ipinfo.io/json'
{
"ip": "34.34.226.69",
"city": "Tokyo",
"region": "Tokyo",
"country": "JP",
"loc": "35.6895,139.6917",
"org": "AS396982 Google LLC",
"postal": "101-8656",
"timezone": "Asia/Tokyo",
"readme": "https://ipinfo.io/missingauth"
}
In future posts, I will explore a more generic way of proxying TCP connections, and will eventually write on socks5 protocol. The endgoal might be implementing a simple VPN using go and socks5