handleRequest intercepts HTTP requests after MITM decryption. - Requests to known AI providers are rewritten to point at aibridged. In centralized mode the Coder token is already in the Authorization header. For BYOK clients that cannot set custom headers, the proxy injects the BYOK header. - Unknow
(req *http.Request, ctx *goproxy.ProxyCtx)
| 902 | // headers, the proxy injects the BYOK header. |
| 903 | // - Unknown hosts are passed through to the original upstream. |
| 904 | func (s *Server) handleRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { |
| 905 | originalPath := req.URL.Path |
| 906 | |
| 907 | // Get the request context stored during CONNECT. |
| 908 | reqCtx, _ := ctx.UserData.(*requestContext) |
| 909 | if reqCtx == nil { |
| 910 | s.logger.Warn(s.ctx, "rejecting request with missing context", |
| 911 | slog.F("host", req.Host), |
| 912 | slog.F("method", req.Method), |
| 913 | slog.F("path", originalPath), |
| 914 | ) |
| 915 | |
| 916 | resp := goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusProxyAuthRequired, "Proxy authentication required") |
| 917 | resp.Header.Set("Proxy-Authenticate", `Basic realm="Coder AI Bridge Proxy"`) |
| 918 | return req, resp |
| 919 | } |
| 920 | |
| 921 | // Re-validate the CONNECT-time provider against the live router. |
| 922 | // A long-lived CONNECT tunnel can outlive a provider being disabled, |
| 923 | // removed, or renamed: the captured reqCtx.Provider is stale, but |
| 924 | // subsequent decrypted requests would still route to aibridged if we |
| 925 | // trusted it. Look up the provider for the current request's host |
| 926 | // and pass through if the mapping is gone or has changed. |
| 927 | host := req.URL.Hostname() |
| 928 | if host == "" { |
| 929 | host = req.Host |
| 930 | if h, _, splitErr := net.SplitHostPort(host); splitErr == nil { |
| 931 | host = h |
| 932 | } |
| 933 | } |
| 934 | liveProvider := s.loadProviderRouter().providerFromHost(host) |
| 935 | if liveProvider == "" || liveProvider != reqCtx.Provider { |
| 936 | s.logger.Warn(s.ctx, "provider mapping changed or removed since CONNECT, passing through", |
| 937 | slog.F("connect_id", reqCtx.ConnectSessionID.String()), |
| 938 | slog.F("host", req.Host), |
| 939 | slog.F("method", req.Method), |
| 940 | slog.F("path", originalPath), |
| 941 | slog.F("connect_provider", reqCtx.Provider), |
| 942 | slog.F("live_provider", liveProvider), |
| 943 | ) |
| 944 | return req, nil |
| 945 | } |
| 946 | |
| 947 | // Generate a unique request ID for this request. |
| 948 | // This ID is sent to aibridged for cross-service log correlation. |
| 949 | reqCtx.RequestID = uuid.New() |
| 950 | |
| 951 | logger := s.logger.With( |
| 952 | slog.F("connect_id", reqCtx.ConnectSessionID.String()), |
| 953 | slog.F("request_id", reqCtx.RequestID.String()), |
| 954 | slog.F("host", req.Host), |
| 955 | slog.F("method", req.Method), |
| 956 | slog.F("path", originalPath), |
| 957 | slog.F("provider", reqCtx.Provider), |
| 958 | ) |
| 959 | |
| 960 | // Reject unauthenticated requests to AI providers. |
| 961 | if reqCtx.CoderToken == "" { |
nothing calls this directly
no test coverage detected