From 849b0696e5a517bdfe3219fc888a2ec7afe27512 Mon Sep 17 00:00:00 2001 From: Chris Freeman Date: Fri, 27 Feb 2026 12:25:04 -0700 Subject: [PATCH] Add server URL normalization hook Prepend https:// when no scheme is provided and strip trailing slashes from the base URL during SDK initialization. --- internal/hooks/registration.go | 2 + internal/hooks/server_url_normalizer.go | 20 ++++++ internal/hooks/server_url_normalizer_test.go | 65 ++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 internal/hooks/server_url_normalizer.go create mode 100644 internal/hooks/server_url_normalizer_test.go diff --git a/internal/hooks/registration.go b/internal/hooks/registration.go index d8f0c2ef..ebf2fd7e 100644 --- a/internal/hooks/registration.go +++ b/internal/hooks/registration.go @@ -9,6 +9,8 @@ package hooks */ func initHooks(h *Hooks) { + h.registerSDKInitHook(&ServerURLNormalizerHook{}) + agentFileUploadHook := &AgentFileUploadHook{} h.registerAfterErrorHook(agentFileUploadHook) diff --git a/internal/hooks/server_url_normalizer.go b/internal/hooks/server_url_normalizer.go new file mode 100644 index 00000000..52b560b4 --- /dev/null +++ b/internal/hooks/server_url_normalizer.go @@ -0,0 +1,20 @@ +package hooks + +import ( + "regexp" + "strings" +) + +var schemeRegex = regexp.MustCompile(`(?i)^https?://`) + +// ServerURLNormalizerHook normalizes server URLs by prepending https:// if no scheme is provided. +type ServerURLNormalizerHook struct{} + +func (h *ServerURLNormalizerHook) SDKInit(baseURL string, client HTTPClient) (string, HTTPClient) { + normalized := baseURL + if !schemeRegex.MatchString(normalized) { + normalized = "https://" + normalized + } + normalized = strings.TrimRight(normalized, "/") + return normalized, client +} diff --git a/internal/hooks/server_url_normalizer_test.go b/internal/hooks/server_url_normalizer_test.go new file mode 100644 index 00000000..08d5b456 --- /dev/null +++ b/internal/hooks/server_url_normalizer_test.go @@ -0,0 +1,65 @@ +package hooks + +import ( + "net/http" + "testing" +) + +func TestNormalizeServerURL(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "no scheme prepends https", + input: "example.glean.com", + expected: "https://example.glean.com", + }, + { + name: "https preserved", + input: "https://example.glean.com", + expected: "https://example.glean.com", + }, + { + name: "http localhost preserved", + input: "http://localhost:8080", + expected: "http://localhost:8080", + }, + { + name: "trailing slashes stripped", + input: "https://example.glean.com///", + expected: "https://example.glean.com", + }, + { + name: "url with path", + input: "https://example.glean.com/api/v1", + expected: "https://example.glean.com/api/v1", + }, + { + name: "no scheme with trailing slash", + input: "example.glean.com/", + expected: "https://example.glean.com", + }, + { + name: "no scheme with path", + input: "example.glean.com/api/v1", + expected: "https://example.glean.com/api/v1", + }, + } + + hook := &ServerURLNormalizerHook{} + client := http.DefaultClient + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, resultClient := hook.SDKInit(tt.input, client) + if result != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, result) + } + if resultClient != client { + t.Error("expected client to be unchanged") + } + }) + } +}