-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathproxy_auth.go
More file actions
68 lines (61 loc) · 2.53 KB
/
Copy pathproxy_auth.go
File metadata and controls
68 lines (61 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package pivnet
import (
"context"
"fmt"
"net/http"
"net/url"
)
// ProxyAuthTransport wraps an http.RoundTripper and adds proxy authentication
// using a pluggable ProxyAuthenticator interface
type ProxyAuthTransport struct {
Transport http.RoundTripper
Authenticator ProxyAuthenticator
}
// NewProxyAuthTransport creates a new ProxyAuthTransport with the given authenticator
// It configures the underlying transport to add authentication headers to both
// regular HTTP requests and HTTPS CONNECT requests.
//
// Authentication Flow:
// - HTTP requests: Authenticate() called in RoundTrip for each request
// - HTTPS requests: Authenticate() called in GetProxyConnectHeader for CONNECT,
// then called again in RoundTrip for requests through the tunnel (harmless, as
// the header goes through encrypted tunnel and proxy doesn't see it)
func NewProxyAuthTransport(transport http.RoundTripper, authenticator ProxyAuthenticator) (*ProxyAuthTransport, error) {
if transport == nil {
return nil, fmt.Errorf("transport cannot be nil")
}
if authenticator == nil {
return nil, fmt.Errorf("authenticator cannot be nil")
}
// If the transport is an *http.Transport, configure GetProxyConnectHeader
// to add authentication headers to CONNECT requests (for HTTPS through proxy)
if httpTransport, ok := transport.(*http.Transport); ok {
httpTransport.GetProxyConnectHeader = func(ctx context.Context, proxyURL *url.URL, target string) (http.Header, error) {
header := http.Header{}
// Create a dummy request to get the auth header
// This is called once per CONNECT (once per HTTPS connection, not per request)
dummyReq := &http.Request{Header: http.Header{}}
if err := authenticator.Authenticate(dummyReq); err != nil {
return header, fmt.Errorf("failed to authenticate CONNECT request: %w", err)
}
// Copy the Proxy-Authorization header
if authHeader := dummyReq.Header.Get("Proxy-Authorization"); authHeader != "" {
header.Set("Proxy-Authorization", authHeader)
}
return header, nil
}
}
return &ProxyAuthTransport{
Transport: transport,
Authenticator: authenticator,
}, nil
}
// RoundTrip executes a single HTTP transaction, adding proxy authentication
func (t *ProxyAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// Add authentication to the request
if err := t.Authenticator.Authenticate(req); err != nil {
return nil, fmt.Errorf("failed to authenticate proxy request: %w", err)
}
// Execute the request with the underlying transport
return t.Transport.RoundTrip(req)
}