[mirror] Scalable static site server for Git forges (like GitHub Pages)

Parse URLs while loading configuration. NFCI

This avoids spreading URL parse error handling code all over
the codebase. It's not even easy to trigger that error!

Changed files
+43 -23
src
+2 -6
src/caddy.go
··· 6 6 "fmt" 7 7 "net" 8 8 "net/http" 9 - "net/url" 10 9 "strings" 11 10 ) 12 11 ··· 55 54 } 56 55 57 56 func tryDialWithSNI(ctx context.Context, domain string) (bool, error) { 58 - if config.Fallback.ProxyTo == "" { 57 + if config.Fallback.ProxyTo == nil { 59 58 return false, nil 60 59 } 61 60 62 - fallbackURL, err := url.Parse(config.Fallback.ProxyTo) 63 - if err != nil { 64 - return false, err 65 - } 61 + fallbackURL := config.Fallback.ProxyTo 66 62 if fallbackURL.Scheme != "https" { 67 63 return false, nil 68 64 }
+39 -9
src/config.go
··· 4 4 "bytes" 5 5 "encoding/json" 6 6 "fmt" 7 + "net/url" 7 8 "os" 8 9 "reflect" 9 10 "slices" ··· 16 17 "github.com/pelletier/go-toml/v2" 17 18 ) 18 19 19 - // For some reason, the standard `time.Duration` type doesn't implement the standard 20 + // For an unknown reason, the standard `time.Duration` type doesn't implement the standard 20 21 // `encoding.{TextMarshaler,TextUnmarshaler}` interfaces. 21 22 type Duration time.Duration 22 23 ··· 26 27 27 28 func (t *Duration) UnmarshalText(data []byte) (err error) { 28 29 u, err := time.ParseDuration(string(data)) 29 - *t = Duration(u) 30 + if err == nil { 31 + *t = Duration(u) 32 + } 30 33 return 31 34 } 32 35 ··· 34 37 return []byte(t.String()), nil 35 38 } 36 39 40 + // For a known but upsetting reason, the standard `url.URL` type doesn't implement the standard 41 + // `encoding.{TextMarshaler,TextUnmarshaler}` interfaces. 42 + type URL struct { 43 + url.URL 44 + } 45 + 46 + func (t *URL) String() string { 47 + return fmt.Sprint(&t.URL) 48 + } 49 + 50 + func (t *URL) UnmarshalText(data []byte) (err error) { 51 + u, err := url.Parse(string(data)) 52 + if err == nil { 53 + *t = URL{*u} 54 + } 55 + return 56 + } 57 + 58 + func (t *URL) MarshalText() ([]byte, error) { 59 + return []byte(t.String()), nil 60 + } 61 + 37 62 type Config struct { 38 63 Insecure bool `toml:"-" env:"insecure"` 39 64 Features []string `toml:"features"` ··· 56 81 57 82 type WildcardConfig struct { 58 83 Domain string `toml:"domain"` 59 - CloneURL string `toml:"clone-url"` 84 + CloneURL string `toml:"clone-url"` // URL template, not an exact URL 60 85 IndexRepos []string `toml:"index-repos" default:"[]"` 61 86 IndexRepoBranch string `toml:"index-repo-branch" default:"pages"` 62 87 Authorization string `toml:"authorization"` 63 88 } 64 89 65 90 type FallbackConfig struct { 66 - ProxyTo string `toml:"proxy-to"` 67 - Insecure bool `toml:"insecure"` 91 + ProxyTo *URL `toml:"proxy-to"` 92 + Insecure bool `toml:"insecure"` 68 93 } 69 94 70 95 type CacheConfig struct { ··· 223 248 if valueCast, err = datasize.ParseString(repr); err == nil { 224 249 reflValue.Set(reflect.ValueOf(valueCast)) 225 250 } 226 - case time.Duration: 227 - if valueCast, err = time.ParseDuration(repr); err == nil { 228 - reflValue.Set(reflect.ValueOf(valueCast)) 229 - } 230 251 case Duration: 231 252 var parsed time.Duration 232 253 if parsed, err = time.ParseDuration(repr); err == nil { 233 254 reflValue.Set(reflect.ValueOf(Duration(parsed))) 255 + } 256 + case *URL: 257 + if repr == "" { 258 + reflValue.Set(reflect.ValueOf(nil)) 259 + } else { 260 + var parsed *url.URL 261 + if parsed, err = url.Parse(repr); err == nil { 262 + reflValue.Set(reflect.ValueOf(&URL{*parsed})) 263 + } 234 264 } 235 265 case []WildcardConfig: 236 266 var parsed []*WildcardConfig
+2 -8
src/main.go
··· 67 67 } 68 68 69 69 func configureFallback(_ context.Context) (err error) { 70 - if config.Fallback.ProxyTo != "" { 71 - var fallbackURL *url.URL 72 - fallbackURL, err = url.Parse(config.Fallback.ProxyTo) 73 - if err != nil { 74 - err = fmt.Errorf("fallback: %w", err) 75 - return 76 - } 77 - 70 + if config.Fallback.ProxyTo != nil { 71 + fallbackURL := &config.Fallback.ProxyTo.URL 78 72 fallback = &httputil.ReverseProxy{ 79 73 Rewrite: func(r *httputil.ProxyRequest) { 80 74 r.SetURL(fallbackURL)