Skip to content

Instantly share code, notes, and snippets.

@Segmentational
Created August 13, 2024 08:15
Show Gist options
  • Save Segmentational/8e475a5d66566f6edd2576170a96ab85 to your computer and use it in GitHub Desktop.
Save Segmentational/8e475a5d66566f6edd2576170a96ab85 to your computer and use it in GitHub Desktop.
Go HTTP Proxy
package main
import (
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
)
type Proxy struct {
target *url.URL
proxy *httputil.ReverseProxy
endpoint string
}
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
p.proxy.ServeHTTP(w, r)
}
func (p *Proxy) Handler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("[ PROXY SERVER ] Request received at %s at %s\n", r.URL, time.Now().UTC())
// Update the headers to allow for SSL redirection
r.URL.Host = p.target.Host
r.URL.Scheme = p.target.Scheme
r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
r.Host = p.target.Host
// trim reverseProxyRouterPrefix
path := r.URL.Path
r.URL.Path = strings.TrimLeft(path, p.endpoint)
// Note that ServeHttp is non blocking and uses a go routine under the hood
fmt.Printf("[ PROXY SERVER ]Proxying request to %s at %s\n", r.URL, time.Now().UTC())
p.ServeHTTP(w, r)
}
}
func New(target *url.URL, endpoint string) *Proxy {
proxy := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
r.SetURL(target)
r.Out.Host = r.In.Host
},
ModifyResponse: func(response *http.Response) error {
response.Header.Set("X-Forwarded-Host", target.Host)
response.Header.Set("X-Forwarded-Proto", target.Scheme)
response.Header.Set("Access-Control-Allow-Origin", "*")
response.Header.Set("Access-Control-Allow-Headers", "Content-Type")
return nil
},
// ErrorHandler is an optional function that handles errors reaching the backend or errors from ModifyResponse.
// - the default is to log the provided error and return a 502 Status Bad Gateway response.
ErrorHandler: func(w http.ResponseWriter, r *http.Request, e error) {
e = fmt.Errorf("failed to proxy request: %w", e)
log.Println(e)
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.WriteHeader(http.StatusBadGateway)
},
}
return &Proxy{target: target, proxy: proxy, endpoint: endpoint}
}
func main() {
target, e := url.Parse("https://api.example.com")
if e != nil {
e = fmt.Errorf("failed to parse target proxy url: %w", e)
panic(e)
}
const endpoint = "/api/*"
// create proxy
proxy := New(target, endpoint)
// Creates a new router
mux := http.NewServeMux()
// register health check endpoint
mux.HandleFunc(endpoint, proxy.Handler())
server := http.Server{
Addr: fmt.Sprintf("localhost:8080"),
Handler: mux,
DisableGeneralOptionsHandler: true,
}
if e := server.ListenAndServe(); e != nil {
e = fmt.Errorf("failed to start proxy server, listening on port 8080: %w", e)
panic(e)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment