Created
August 13, 2024 08:15
-
-
Save Segmentational/8e475a5d66566f6edd2576170a96ab85 to your computer and use it in GitHub Desktop.
Go HTTP Proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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