File
Blob: gateway/internal_proxy.go
| 1 | package gateway |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "fmt" |
| 6 | "net" |
| 7 | "net/http" |
| 8 | "net/http/httputil" |
| 9 | "net/url" |
| 10 | |
| 11 | "go.miragespace.co/specter/spec/protocol" |
| 12 | "go.miragespace.co/specter/spec/rpc" |
| 13 | "go.miragespace.co/specter/spec/transport" |
| 14 | |
| 15 | "go.uber.org/zap" |
| 16 | ) |
| 17 | |
| 18 | const ( |
| 19 | internalProxyNodeAddress = "x-internal-proxy-node-address" |
| 20 | internalProxyForwarded = "x-internal-proxy-forwarded" |
| 21 | ) |
| 22 | |
| 23 | func (g *Gateway) getInternalProxyHandler() func(http.Handler) http.Handler { |
| 24 | targetURL := &url.URL{ |
| 25 | Scheme: "http", |
| 26 | Host: g.RootDomains[0], |
| 27 | } |
| 28 | proxy := &httputil.ReverseProxy{} |
| 29 | proxy.Transport = &http.Transport{ |
| 30 | DisableKeepAlives: true, |
| 31 | MaxIdleConnsPerHost: -1, |
| 32 | DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, err error) { |
| 33 | defer func() { |
| 34 | if pr := recover(); pr != nil { |
| 35 | err = fmt.Errorf("internalDialer panic: %v", pr) |
| 36 | } |
| 37 | }() |
| 38 | |
| 39 | target := rpc.GetNode(ctx) |
| 40 | if target == nil { |
| 41 | return nil, transport.ErrNoDirect |
| 42 | } |
| 43 | return g.TunnelServer.DialInternal(ctx, target) |
| 44 | }, |
| 45 | } |
| 46 | proxy.Rewrite = func(preq *httputil.ProxyRequest) { |
| 47 | preq.SetURL(targetURL) |
| 48 | preq.Out.Host = preq.Out.URL.Host |
| 49 | preq.SetXForwarded() |
| 50 | preq.Out.Header.Set(internalProxyForwarded, "true") |
| 51 | preq.Out.Header.Del(internalProxyNodeAddress) |
| 52 | } |
| 53 | proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { |
| 54 | w.WriteHeader(http.StatusBadGateway) |
| 55 | fmt.Fprintf(w, "error proxying internal request: %v", err) |
| 56 | } |
| 57 | return func(h http.Handler) http.Handler { |
| 58 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 59 | var ( |
| 60 | forwarded = r.Header.Get(internalProxyForwarded) != "" |
| 61 | targetAddress = r.Header.Get(internalProxyNodeAddress) |
| 62 | ) |
| 63 | if forwarded || targetAddress == "" { |
| 64 | h.ServeHTTP(w, r) |
| 65 | return |
| 66 | } |
| 67 | |
| 68 | target := &protocol.Node{ |
| 69 | Address: targetAddress, |
| 70 | } |
| 71 | r = r.WithContext(rpc.WithNode(r.Context(), target)) |
| 72 | |
| 73 | g.Logger.Debug("Proxying internal request", zap.Object("target", target)) |
| 74 | proxy.ServeHTTP(w, r) |
| 75 | }) |
| 76 | } |
| 77 | } |