Skip to content

Commit 84e9e06

Browse files
authored
Merge pull request #900 from Pangjiping/fix/sdk-go-forward-endpoint-headers
fix(sdk-go): forward all GetEndpoint headers on subsequent requests
2 parents 00de63a + 8539764 commit 84e9e06

4 files changed

Lines changed: 118 additions & 31 deletions

File tree

sdks/sandbox/go/config.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,22 +177,24 @@ func (c *ConnectionConfig) lifecycleClient() *LifecycleClient {
177177
return NewLifecycleClient(c.GetBaseURL()+"/"+APIVersion, c.GetAPIKey(), c.clientOpts(true)...)
178178
}
179179

180-
// execdClient creates an ExecdClient for a resolved endpoint.
181-
// endpointHeaders are additional headers from the endpoint resolution (e.g. routing headers).
182-
func (c *ConnectionConfig) execdClient(endpointURL, token string, endpointHeaders map[string]string) *ExecdClient {
180+
// execdClient creates an ExecdClient for a resolved endpoint. All headers
181+
// returned by the lifecycle GetEndpoint call (auth tokens, routing hints,
182+
// sticky-session keys, etc.) are forwarded as-is on every subsequent request.
183+
func (c *ConnectionConfig) execdClient(endpointURL string, endpointHeaders map[string]string) *ExecdClient {
183184
opts := c.clientOpts(true)
184185
if len(endpointHeaders) > 0 {
185186
opts = append(opts, WithHeaders(endpointHeaders))
186187
}
187-
return NewExecdClient(endpointURL, token, opts...)
188+
return NewExecdClient(endpointURL, "", opts...)
188189
}
189190

190-
// egressClient creates an EgressClient for a resolved endpoint.
191-
// endpointHeaders are additional headers from the endpoint resolution (e.g. routing headers).
192-
func (c *ConnectionConfig) egressClient(endpointURL, token string, endpointHeaders map[string]string) *EgressClient {
191+
// egressClient creates an EgressClient for a resolved endpoint. All headers
192+
// returned by the lifecycle GetEndpoint call are forwarded as-is on every
193+
// subsequent request.
194+
func (c *ConnectionConfig) egressClient(endpointURL string, endpointHeaders map[string]string) *EgressClient {
193195
opts := c.clientOpts(false)
194196
if len(endpointHeaders) > 0 {
195197
opts = append(opts, WithHeaders(endpointHeaders))
196198
}
197-
return NewEgressClient(endpointURL, token, opts...)
199+
return NewEgressClient(endpointURL, "", opts...)
198200
}

sdks/sandbox/go/egress.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ type EgressClient struct {
2222
*Client
2323
}
2424

25+
// egressAuthHeader is the authentication header used by the Egress sidecar API.
26+
const egressAuthHeader = "OPENSANDBOX-EGRESS-AUTH"
27+
2528
// NewEgressClient creates a new EgressClient.
2629
// baseURL is the sandbox-specific egress sidecar endpoint
2730
// (e.g. "http://localhost:18080").
2831
// authToken is the value for the OPENSANDBOX-EGRESS-AUTH header; pass ""
2932
// if the sidecar does not require authentication.
3033
func NewEgressClient(baseURL, authToken string, opts ...Option) *EgressClient {
3134
return &EgressClient{
32-
Client: NewClient(baseURL, authToken, "OPENSANDBOX-EGRESS-AUTH", opts...),
35+
Client: NewClient(baseURL, authToken, egressAuthHeader, opts...),
3336
}
3437
}
3538

sdks/sandbox/go/opensandbox_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,92 @@ func TestExecdAuthHeader(t *testing.T) {
10221022
require.NoErrorf(t, err, "Ping")
10231023
}
10241024

1025+
// TestResolveExecdForwardsAllEndpointHeaders verifies that every header
1026+
// returned by GetEndpoint (auth tokens, routing hints, sticky-session keys,
1027+
// etc.) is forwarded as-is on subsequent execd requests, mirroring the
1028+
// Python SDK behavior.
1029+
func TestResolveExecdForwardsAllEndpointHeaders(t *testing.T) {
1030+
endpointHeaders := map[string]string{
1031+
"X-EXECD-ACCESS-TOKEN": "execd-tok",
1032+
"X-Route-Hint": "vip-pool",
1033+
"X-Sticky-Session": "sess-abc",
1034+
}
1035+
1036+
execdSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1037+
for k, want := range endpointHeaders {
1038+
if got := r.Header.Get(k); got != want {
1039+
assert.Fail(t, fmt.Sprintf("header %s = %q, want %q", k, got, want))
1040+
}
1041+
}
1042+
w.WriteHeader(http.StatusOK)
1043+
}))
1044+
defer execdSrv.Close()
1045+
1046+
lifecycleSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1047+
if r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/endpoints/") {
1048+
jsonResponse(w, http.StatusOK, Endpoint{
1049+
Endpoint: execdSrv.URL,
1050+
Headers: endpointHeaders,
1051+
})
1052+
return
1053+
}
1054+
w.WriteHeader(http.StatusNotFound)
1055+
}))
1056+
defer lifecycleSrv.Close()
1057+
1058+
config := ConnectionConfig{Domain: lifecycleSrv.URL}
1059+
sb := &Sandbox{
1060+
id: "sbx-headers",
1061+
config: &config,
1062+
lifecycle: config.lifecycleClient(),
1063+
}
1064+
1065+
require.NoErrorf(t, sb.resolveExecd(context.Background()), "resolveExecd")
1066+
require.NoErrorf(t, sb.execd.Ping(context.Background()), "Ping")
1067+
}
1068+
1069+
// TestResolveEgressForwardsAllEndpointHeaders verifies the same forwarding
1070+
// behavior for the egress sidecar client.
1071+
func TestResolveEgressForwardsAllEndpointHeaders(t *testing.T) {
1072+
endpointHeaders := map[string]string{
1073+
"OPENSANDBOX-EGRESS-AUTH": "egress-tok",
1074+
"X-Route-Hint": "egress-vip",
1075+
}
1076+
1077+
egressSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1078+
for k, want := range endpointHeaders {
1079+
if got := r.Header.Get(k); got != want {
1080+
assert.Fail(t, fmt.Sprintf("header %s = %q, want %q", k, got, want))
1081+
}
1082+
}
1083+
jsonResponse(w, http.StatusOK, PolicyStatusResponse{Status: "ok"})
1084+
}))
1085+
defer egressSrv.Close()
1086+
1087+
lifecycleSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1088+
if r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/endpoints/") {
1089+
jsonResponse(w, http.StatusOK, Endpoint{
1090+
Endpoint: egressSrv.URL,
1091+
Headers: endpointHeaders,
1092+
})
1093+
return
1094+
}
1095+
w.WriteHeader(http.StatusNotFound)
1096+
}))
1097+
defer lifecycleSrv.Close()
1098+
1099+
config := ConnectionConfig{Domain: lifecycleSrv.URL}
1100+
sb := &Sandbox{
1101+
id: "sbx-egress-headers",
1102+
config: &config,
1103+
lifecycle: config.lifecycleClient(),
1104+
}
1105+
1106+
require.NoErrorf(t, sb.resolveEgress(context.Background()), "resolveEgress")
1107+
_, err := sb.egress.GetPolicy(context.Background())
1108+
require.NoErrorf(t, err, "GetPolicy")
1109+
}
1110+
10251111
func TestSandboxManager_ListFilter(t *testing.T) {
10261112
now := time.Now().UTC().Truncate(time.Second)
10271113
want := ListSandboxesResponse{

sdks/sandbox/go/sandbox.go

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -411,23 +411,19 @@ func (s *Sandbox) resolveExecd(ctx context.Context) error {
411411
execdURL = s.config.GetProtocol() + "://" + execdURL
412412
}
413413

414-
token := ""
415-
var extraHeaders map[string]string
416-
if endpoint.Headers != nil {
417-
token = endpoint.Headers["X-EXECD-ACCESS-TOKEN"]
418-
// Preserve all endpoint headers (e.g. routing headers) except the auth token
419-
extraHeaders = make(map[string]string, len(endpoint.Headers))
420-
for k, v := range endpoint.Headers {
421-
if k != "X-EXECD-ACCESS-TOKEN" {
422-
extraHeaders[k] = v
414+
headers := make(map[string]string, len(endpoint.Headers)+1)
415+
for k, v := range endpoint.Headers {
416+
headers[k] = v
417+
}
418+
if s.config.UseServerProxy {
419+
if _, ok := headers[execdAuthHeader]; !ok {
420+
if apiKey := s.config.GetAPIKey(); apiKey != "" {
421+
headers[execdAuthHeader] = apiKey
423422
}
424423
}
425424
}
426-
if s.config.UseServerProxy && token == "" {
427-
token = s.config.GetAPIKey()
428-
}
429425

430-
s.execd = s.config.execdClient(execdURL, token, extraHeaders)
426+
s.execd = s.config.execdClient(execdURL, headers)
431427
return nil
432428
}
433429

@@ -451,18 +447,18 @@ func (s *Sandbox) resolveEgress(ctx context.Context) error {
451447
egressURL = s.config.GetProtocol() + "://" + egressURL
452448
}
453449

454-
token := ""
455-
var extraHeaders map[string]string
456-
if endpoint.Headers != nil {
457-
token = endpoint.Headers["OPENSANDBOX-EGRESS-AUTH"]
458-
extraHeaders = make(map[string]string, len(endpoint.Headers))
459-
for k, v := range endpoint.Headers {
460-
if k != "OPENSANDBOX-EGRESS-AUTH" {
461-
extraHeaders[k] = v
450+
headers := make(map[string]string, len(endpoint.Headers)+1)
451+
for k, v := range endpoint.Headers {
452+
headers[k] = v
453+
}
454+
if s.config.UseServerProxy {
455+
if _, ok := headers[egressAuthHeader]; !ok {
456+
if apiKey := s.config.GetAPIKey(); apiKey != "" {
457+
headers[egressAuthHeader] = apiKey
462458
}
463459
}
464460
}
465461

466-
s.egress = s.config.egressClient(egressURL, token, extraHeaders)
462+
s.egress = s.config.egressClient(egressURL, headers)
467463
return nil
468464
}

0 commit comments

Comments
 (0)