Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions pkg/router/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,17 @@ func (s *Server) handleGetSandboxError(c *gin.Context, err error) {

func determineUpstreamURL(sandbox *types.SandboxInfo, path string) (*url.URL, error) {
// prefer matched entrypoint by path
var matched types.SandboxEntryPoint
found := false
Comment thread
avinxshKD marked this conversation as resolved.
for _, ep := range sandbox.EntryPoints {
if strings.HasPrefix(path, ep.Path) {
return buildURL(ep.Protocol, ep.Endpoint)
if entryPointPathMatches(path, ep.Path) && (!found || len(ep.Path) > len(matched.Path)) {
matched = ep
found = true
}
}
if found {
return buildURL(matched.Protocol, matched.Endpoint)
}
// fallback to first entrypoint
if len(sandbox.EntryPoints) == 0 {
return nil, fmt.Errorf("no entry point found for sandbox")
Expand All @@ -120,6 +126,17 @@ func determineUpstreamURL(sandbox *types.SandboxInfo, path string) (*url.URL, er
return buildURL(ep.Protocol, ep.Endpoint)
}

func entryPointPathMatches(path, prefix string) bool {
if path != "" && !strings.HasPrefix(path, "/") {
path = "/" + path
}
prefix = strings.TrimSuffix(prefix, "/")
if prefix == "" {
return true
}
return path == prefix || strings.HasPrefix(path, prefix+"/")
}
Comment thread
avinxshKD marked this conversation as resolved.

func buildURL(protocol, endpoint string) (*url.URL, error) {
if protocol != "" && !strings.Contains(endpoint, "://") {
endpoint = (strings.ToLower(protocol) + "://" + endpoint)
Expand Down
57 changes: 57 additions & 0 deletions pkg/router/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,63 @@ func TestHandleInvoke_NoEntryPoints(t *testing.T) {
}
}

func TestDetermineUpstreamURL_PathPrefix(t *testing.T) {
tests := []struct {
name string
path string
entryPoints []types.SandboxEntryPoint
wantHost string
}{
{
name: "longest prefix wins",
path: "/api/run",
entryPoints: []types.SandboxEntryPoint{
{Endpoint: "10.0.0.1:8080", Protocol: "HTTP", Path: "/"},
{Endpoint: "10.0.0.2:8080", Protocol: "HTTP", Path: "/api"},
},
wantHost: "10.0.0.2:8080",
},
{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test only covers the boundary case when a / entrypoint exists. With only /api configured, /api2/run still falls through to the first entrypoint, so it can still route to /api; is that fallback behavior intended?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree, added a case for that.

With only /api configured, /api2/run does not match /api, it falls back to the first entrypoint, same as the existing router behavior. This change only affects which matched prefix wins. PTAL

name: "prefix must end on boundary",
path: "/api2/run",
entryPoints: []types.SandboxEntryPoint{
{Endpoint: "10.0.0.1:8080", Protocol: "HTTP", Path: "/api"},
{Endpoint: "10.0.0.2:8080", Protocol: "HTTP", Path: "/"},
},
wantHost: "10.0.0.2:8080",
},
{
name: "normalizes slashes",
path: "api/run",
entryPoints: []types.SandboxEntryPoint{
{Endpoint: "10.0.0.1:8080", Protocol: "HTTP", Path: "/api/"},
},
wantHost: "10.0.0.1:8080",
},
{
name: "falls back when no prefix matches",
path: "/api2/run",
entryPoints: []types.SandboxEntryPoint{
{Endpoint: "10.0.0.1:8080", Protocol: "HTTP", Path: "/api"},
},
wantHost: "10.0.0.1:8080",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sandbox := &types.SandboxInfo{EntryPoints: tt.entryPoints}
upstreamURL, err := determineUpstreamURL(sandbox, tt.path)
if err != nil {
t.Fatalf("determineUpstreamURL returned error: %v", err)
}
if upstreamURL.Host != tt.wantHost {
t.Fatalf("expected host %s, got %s", tt.wantHost, upstreamURL.Host)
}
})
}
}

func TestHandleAgentInvoke(t *testing.T) {
// Set required environment variables
setupEnv()
Expand Down