[MISC] Extend support of URL Configuration (#645)

pull/3405/head
Johannes Batzill 2023-10-03 19:32:35 +00:00 committed by Harness
parent d83552f288
commit 7334c4b8ed
3 changed files with 215 additions and 32 deletions

View File

@ -39,6 +39,11 @@ import (
"golang.org/x/text/unicode/norm"
)
const (
schemeHTTP = "http"
schemeHTTPS = "https"
)
// LoadConfig returns the system configuration from the
// host environment.
func LoadConfig() (*types.Config, error) {
@ -61,18 +66,24 @@ func LoadConfig() (*types.Config, error) {
return config, nil
}
//nolint:gocognit // refactor if required
func backfillURLs(config *types.Config) error {
// default base url
scheme, host, port, path := "http", "localhost", fmt.Sprint(config.Server.HTTP.Port), ""
// TODO: once we actually use the config.Server.HTTP.Proto, we have to update that here.
scheme, host, port, path := schemeHTTP, "localhost", "", ""
// TODO: handle https of server bind
// by default drop scheme's default port
if (scheme != schemeHTTP || config.Server.HTTP.Port != 80) &&
(scheme != schemeHTTPS || config.Server.HTTP.Port != 443) {
port = fmt.Sprint(config.Server.HTTP.Port)
}
// backfil internal URLS before continuing override with user provided base (which is external facing)
if config.URL.Internal == "" {
config.URL.Internal = fmt.Sprintf("%s://localhost:%s", scheme, port)
config.URL.Internal = combineToRawURL(scheme, "localhost", port, "")
}
if config.URL.Container == "" {
config.URL.Container = fmt.Sprintf("%s://host.docker.internal:%s", scheme, port)
config.URL.Container = combineToRawURL(scheme, "host.docker.internal", port, "")
}
// override base with whatever user explicit override
@ -82,23 +93,30 @@ func backfillURLs(config *types.Config) error {
if err != nil {
return fmt.Errorf("failed to parse base url '%s': %w", config.URL.Base, err)
}
if u.Scheme != "" {
scheme = u.Scheme
if u.Scheme != schemeHTTP && u.Scheme != schemeHTTPS {
return fmt.Errorf(
"base url scheme '%s' is not supported (valid values: %v)",
u.Scheme,
[]string{
schemeHTTP,
schemeHTTPS,
},
)
}
if u.Hostname() != "" {
host = u.Hostname()
}
if u.Port() != "" {
port = u.Port()
}
if u.Path != "" {
// ensure path starts with a slash (so we only add slash if path is provided)
path = "/" + strings.Trim(u.Path, "/")
// url parsing allows empty hostname - we don't want that
if u.Hostname() == "" {
return fmt.Errorf("a non-empty base url host has to be provided")
}
// take everything as is (e.g. if user explicitly adds port 80 for http we take it)
scheme = u.Scheme
host = u.Hostname()
port = u.Port()
path = u.Path
}
// create base URL object
baseURLRaw := fmt.Sprintf("%s://%s:%s%s", scheme, host, port, path)
baseURLRaw := combineToRawURL(scheme, host, port, path)
baseURL, err := url.Parse(baseURLRaw)
if err != nil {
return fmt.Errorf("failed to parse derived base url '%s': %w", baseURLRaw, err)
@ -118,6 +136,23 @@ func backfillURLs(config *types.Config) error {
return nil
}
func combineToRawURL(scheme, host, port, path string) string {
urlRAW := scheme + "://" + host
// only add port if explicitly provided
if port != "" {
urlRAW += ":" + port
}
// only add path if it's not empty and non-root
path = strings.Trim(path, "/")
if path != "" {
urlRAW += "/" + path
}
return urlRAW
}
// getSanitizedMachineName gets the name of the machine and returns it in sanitized format.
func getSanitizedMachineName() (string, error) {
// use the hostname as default id of the instance

View File

@ -22,7 +22,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestBackfilURLsPortBind(t *testing.T) {
func TestBackfillURLsHTTPPort(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
@ -37,7 +37,70 @@ func TestBackfilURLsPortBind(t *testing.T) {
require.Equal(t, "http://localhost:1234", config.URL.UI)
}
func TestBackfilURLsBase(t *testing.T) {
func TestBackfillURLsHTTPPortStripsDefaultHTTP(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 80
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://localhost", config.URL.Internal)
require.Equal(t, "http://host.docker.internal", config.URL.Container)
require.Equal(t, "http://localhost/api", config.URL.API)
require.Equal(t, "http://localhost/git", config.URL.Git)
require.Equal(t, "http://localhost", config.URL.UI)
}
// TODO: Update once we add proper https support - as of now nothing is stripped!
func TestBackfillURLsHTTPPortStripsDefaultHTTPS(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 443
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://localhost:443", config.URL.Internal)
require.Equal(t, "http://host.docker.internal:443", config.URL.Container)
require.Equal(t, "http://localhost:443/api", config.URL.API)
require.Equal(t, "http://localhost:443/git", config.URL.Git)
require.Equal(t, "http://localhost:443", config.URL.UI)
}
func TestBackfillURLsBaseInvalidProtocol(t *testing.T) {
config := &types.Config{}
config.URL.Base = "abc://xyz:4321/test"
err := backfillURLs(config)
require.ErrorContains(t, err, "base url scheme 'abc' is not supported")
}
func TestBackfillURLsBaseNoHost(t *testing.T) {
config := &types.Config{}
config.URL.Base = "http:///test"
err := backfillURLs(config)
require.ErrorContains(t, err, "a non-empty base url host has to be provided")
}
func TestBackfillURLsBaseNoHostWithPort(t *testing.T) {
config := &types.Config{}
config.URL.Base = "http://:4321/test"
err := backfillURLs(config)
require.ErrorContains(t, err, "a non-empty base url host has to be provided")
}
func TestBackfillURLsBaseInvalidPort(t *testing.T) {
config := &types.Config{}
config.URL.Base = "http://localhost:abc/test"
err := backfillURLs(config)
require.ErrorContains(t, err, "invalid port \":abc\" after host")
}
func TestBackfillURLsBase(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Base = "https://xyz:4321/test"
@ -53,23 +116,103 @@ func TestBackfilURLsBase(t *testing.T) {
require.Equal(t, "https://xyz:4321/test", config.URL.UI)
}
func TestBackfilURLsCustom(t *testing.T) {
func TestBackfillURLsBaseDefaultPortHTTP(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Base = "https://xyz:4321/test"
config.URL.API = "http://API:1111/API/p"
config.URL.Internal = "http://APIInternal:1111/APIInternal/p"
config.URL.Git = "http://Git:1111/Git/p"
config.URL.Container = "http://GitContainer:1111/GitContainer/p"
config.URL.UI = "http://UI:1111/UI/p"
config.URL.Base = "http://xyz/test"
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://APIInternal:1111/APIInternal/p", config.URL.Internal)
require.Equal(t, "http://GitContainer:1111/GitContainer/p", config.URL.Container)
require.Equal(t, "http://localhost:1234", config.URL.Internal)
require.Equal(t, "http://host.docker.internal:1234", config.URL.Container)
require.Equal(t, "http://xyz/test/api", config.URL.API)
require.Equal(t, "http://xyz/test/git", config.URL.Git)
require.Equal(t, "http://xyz/test", config.URL.UI)
}
func TestBackfillURLsBaseDefaultPortHTTPExplicit(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Base = "http://xyz:80/test"
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://localhost:1234", config.URL.Internal)
require.Equal(t, "http://host.docker.internal:1234", config.URL.Container)
require.Equal(t, "http://xyz:80/test/api", config.URL.API)
require.Equal(t, "http://xyz:80/test/git", config.URL.Git)
require.Equal(t, "http://xyz:80/test", config.URL.UI)
}
func TestBackfillURLsBaseDefaultPortHTTPS(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Base = "https://xyz/test"
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://localhost:1234", config.URL.Internal)
require.Equal(t, "http://host.docker.internal:1234", config.URL.Container)
require.Equal(t, "https://xyz/test/api", config.URL.API)
require.Equal(t, "https://xyz/test/git", config.URL.Git)
require.Equal(t, "https://xyz/test", config.URL.UI)
}
func TestBackfillURLsBaseDefaultPortHTTPSExplicit(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Base = "https://xyz:443/test"
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://localhost:1234", config.URL.Internal)
require.Equal(t, "http://host.docker.internal:1234", config.URL.Container)
require.Equal(t, "https://xyz:443/test/api", config.URL.API)
require.Equal(t, "https://xyz:443/test/git", config.URL.Git)
require.Equal(t, "https://xyz:443/test", config.URL.UI)
}
func TestBackfillURLsBaseRootPathStripped(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Base = "https://xyz:4321/"
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://localhost:1234", config.URL.Internal)
require.Equal(t, "http://host.docker.internal:1234", config.URL.Container)
require.Equal(t, "https://xyz:4321/api", config.URL.API)
require.Equal(t, "https://xyz:4321/git", config.URL.Git)
require.Equal(t, "https://xyz:4321", config.URL.UI)
}
func TestBackfillURLsCustom(t *testing.T) {
config := &types.Config{}
config.Server.HTTP.Port = 1234
config.URL.Internal = "http://APIInternal/APIInternal/p"
config.URL.Container = "https://GitContainer/GitContainer/p"
config.URL.Base = "https://xyz:4321/test"
config.URL.API = "http://API:1111/API/p"
config.URL.Git = "https://Git:443/Git/p"
config.URL.UI = "http://UI:80/UI/p"
err := backfillURLs(config)
require.NoError(t, err)
require.Equal(t, "http://APIInternal/APIInternal/p", config.URL.Internal)
require.Equal(t, "https://GitContainer/GitContainer/p", config.URL.Container)
require.Equal(t, "http://API:1111/API/p", config.URL.API)
require.Equal(t, "http://Git:1111/Git/p", config.URL.Git)
require.Equal(t, "http://UI:1111/UI/p", config.URL.UI)
require.Equal(t, "https://Git:443/Git/p", config.URL.Git)
require.Equal(t, "http://UI:80/UI/p", config.URL.UI)
}

View File

@ -38,6 +38,8 @@ var UI embed.FS
// Handler returns an http.HandlerFunc that servers the
// static content from the embedded file system.
//
//nolint:gocognit // refactor if required.
func Handler() http.HandlerFunc {
// Load the files subdirectory
fs, err := fs.Sub(UI, "dist")
@ -56,9 +58,12 @@ func Handler() http.HandlerFunc {
// we need to always load the index.html file
// in the root of the project, unless the path
// points to a file with an extension (css, js, etc)
if filepath.Ext(r.URL.Path) == "" || // No ext: (1) a browser URL request, not a static asset request
(strings.Contains(r.URL.Path, "...") && // "..." : (2a) browser URL with ... in it
filepath.Ext(strings.ReplaceAll(r.URL.Path, "...", "")) == "") { // (2b) filter out static asset URLs that browsers make along with it
// No ext: (1) a browser URL request, not a static asset request
if filepath.Ext(r.URL.Path) == "" ||
// "..." : (2a) browser URL with ... in it
(strings.Contains(r.URL.Path, "...") &&
// (2b) filter out static asset URLs that browsers make along with it
filepath.Ext(strings.ReplaceAll(r.URL.Path, "...", "")) == "") {
// HACK: alter the path to point to the
// root of the project.
r.URL.Path = "/"