Normalize server URLs before connecting (#861)

* Fix #304: normalize server URLs before connecting

Prepend http:// when no scheme is present and strip trailing slashes
for all server types. For Jellyfin, additionally strip /web/index.html
and /web path suffixes that users copy from the browser URL bar.

Normalization is applied in ServerManager.connect() on the by-value
parameter, so stored config is never mutated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* move url normalize funcs

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Drew Weymouth
2026-02-17 19:34:47 -08:00
committed by GitHub
co-authored by Claude Sonnet 4.6
parent e21908cd24
commit 9dd606ce73
2 changed files with 83 additions and 0 deletions
+34
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"log"
"net/http"
"strings"
"time"
"github.com/dweymouth/go-jellyfin"
@@ -179,6 +180,14 @@ func (s *ServerManager) connect(connection ServerConnection, password string) (m
var cli, altCli mediaprovider.Server
timeout := time.Second * time.Duration(s.config.Application.RequestTimeoutSeconds)
if connection.ServerType == ServerTypeJellyfin {
connection.Hostname = NormalizeJellyfinURL(connection.Hostname)
connection.AltHostname = NormalizeJellyfinURL(connection.AltHostname)
} else {
connection.Hostname = NormalizeServerURL(connection.Hostname)
connection.AltHostname = NormalizeServerURL(connection.AltHostname)
}
if connection.ServerType == ServerTypeJellyfin {
client, err := jellyfin.NewClient(connection.Hostname, res.AppName, res.AppVersion, jellyfin.WithTimeout(timeout))
if err != nil {
@@ -266,3 +275,28 @@ func (s *ServerManager) checkSetInsecureSkipVerify(skip bool, cli *http.Client)
func (a *ServerManager) GetServer() mediaprovider.MediaProvider {
return a.Server
}
// NormalizeServerURL applies common normalization to a server URL:
// prepends "http://" if no scheme is present, then strips trailing slashes.
func NormalizeServerURL(rawURL string) string {
if rawURL == "" {
return ""
}
if !strings.Contains(rawURL, "://") {
rawURL = "http://" + rawURL
}
rawURL = strings.TrimRight(rawURL, "/")
return rawURL
}
// NormalizeJellyfinURL applies common normalization then additionally strips
// known Jellyfin web UI path suffixes (/web/index.html and /web).
func NormalizeJellyfinURL(rawURL string) string {
rawURL = NormalizeServerURL(rawURL)
if strings.HasSuffix(rawURL, "/web/index.html") {
rawURL = strings.TrimSuffix(rawURL, "/web/index.html")
} else if strings.HasSuffix(rawURL, "/web") {
rawURL = strings.TrimSuffix(rawURL, "/web")
}
return rawURL
}
+49
View File
@@ -0,0 +1,49 @@
package backend
import "testing"
func TestNormalizeServerURL(t *testing.T) {
tests := []struct {
input string
want string
}{
{"", ""},
{"http://192.168.1.1:8096", "http://192.168.1.1:8096"},
{"https://music.example.com", "https://music.example.com"},
{"192.168.1.1:4533", "http://192.168.1.1:4533"},
{"music.example.com", "http://music.example.com"},
{"http://192.168.1.1:8096/", "http://192.168.1.1:8096"},
{"http://192.168.1.1:8096///", "http://192.168.1.1:8096"},
{"192.168.1.1:8096/", "http://192.168.1.1:8096"},
}
for _, tt := range tests {
got := NormalizeServerURL(tt.input)
if got != tt.want {
t.Errorf("NormalizeServerURL(%q) = %q, want %q", tt.input, got, tt.want)
}
}
}
func TestNormalizeJellyfinURL(t *testing.T) {
tests := []struct {
input string
want string
}{
{"", ""},
{"http://192.168.1.1:8096", "http://192.168.1.1:8096"},
{"192.168.1.1:8096", "http://192.168.1.1:8096"},
{"192.168.1.1:8096/", "http://192.168.1.1:8096"},
{"192.168.1.1:8096/web/index.html", "http://192.168.1.1:8096"},
{"http://192.168.1.1:8096/web/index.html", "http://192.168.1.1:8096"},
{"http://192.168.1.1:8096/web/", "http://192.168.1.1:8096"},
{"http://192.168.1.1:8096/web", "http://192.168.1.1:8096"},
{"https://jellyfin.example.com/web/index.html", "https://jellyfin.example.com"},
{"https://jellyfin.example.com/web/", "https://jellyfin.example.com"},
}
for _, tt := range tests {
got := NormalizeJellyfinURL(tt.input)
if got != tt.want {
t.Errorf("NormalizeJellyfinURL(%q) = %q, want %q", tt.input, got, tt.want)
}
}
}