Skip to content

Commit

Permalink
Added possibility to disable error pages auto-localization (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
tarampampam authored Apr 12, 2022
1 parent a3389aa commit d21a6f2
Show file tree
Hide file tree
Showing 27 changed files with 216 additions and 134 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher

## UNRELEASED

### Added

- Possibility to disable error pages auto-localization (using `--disable-l10n` flag for the `serve` & `build` commands or environment variable `DISABLE_L10N`) [#91]

### Fixed

- User UID/GID changed to the numeric values in the dockerfile [#92]

[#92]:https://github.com/tarampampam/error-pages/issues/92
[#91]:https://github.com/tarampampam/error-pages/issues/91

## v2.12.1

Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ ENV LISTEN_PORT="8080" \
TEMPLATE_NAME="ghost" \
DEFAULT_ERROR_PAGE="404" \
DEFAULT_HTTP_CODE="404" \
SHOW_DETAILS="false"
SHOW_DETAILS="false" \
DISABLE_L10N="false"

# Docs: <https://docs.docker.com/engine/reference/builder/#healthcheck>
HEALTHCHECK --interval=7s --timeout=2s CMD ["/bin/error-pages", "healthcheck", "--log-json"]
Expand Down
13 changes: 11 additions & 2 deletions internal/cli/build/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
func NewCommand(log *zap.Logger, configFile *string) *cobra.Command {
var (
generateIndex bool
disableL10n bool
cfg *config.Config
)

Expand All @@ -39,7 +40,7 @@ func NewCommand(log *zap.Logger, configFile *string) *cobra.Command {
return errors.New("wrong arguments count")
}

return run(log, cfg, args[0], generateIndex)
return run(log, cfg, args[0], generateIndex, disableL10n)
},
}

Expand All @@ -50,6 +51,13 @@ func NewCommand(log *zap.Logger, configFile *string) *cobra.Command {
"generate index page",
)

cmd.Flags().BoolVarP(
&disableL10n,
"disable-l10n", "",
false,
"disable error pages localization",
)

return cmd
}

Expand All @@ -60,7 +68,7 @@ const (
outDirPerm = os.FileMode(0775)
)

func run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateIndex bool) error { //nolint:funlen
func run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateIndex, disableL10n bool) error { //nolint:funlen,lll
if len(cfg.Templates) == 0 {
return errors.New("no loaded templates")
}
Expand Down Expand Up @@ -92,6 +100,7 @@ func run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateI
Message: page.Message(),
Description: page.Description(),
ShowRequestDetails: false,
L10nDisabled: disableL10n,
})
if renderingErr != nil {
return renderingErr
Expand Down
44 changes: 19 additions & 25 deletions internal/cli/serve/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,26 @@ func NewCommand(ctx context.Context, log *zap.Logger, configFile *string) *cobra
return errors.New("path to the config file is required for this command")
}

if err = f.overrideUsingEnv(cmd.Flags()); err != nil {
if err = f.OverrideUsingEnv(cmd.Flags()); err != nil {
return err
}

if cfg, err = config.FromYamlFile(*configFile); err != nil {
return err
}

return f.validate()
return f.Validate()
},
RunE: func(*cobra.Command, []string) error { return run(ctx, log, f, cfg) },
RunE: func(*cobra.Command, []string) error { return run(ctx, log, cfg, f) },
}

f.init(cmd.Flags())
f.Init(cmd.Flags())

return cmd
}

// run current command.
func run(parentCtx context.Context, log *zap.Logger, f flags, cfg *config.Config) error { //nolint:funlen
func run(parentCtx context.Context, log *zap.Logger, cfg *config.Config, f flags) error { //nolint:funlen
var (
ctx, cancel = context.WithCancel(parentCtx) // serve context creation
oss = breaker.NewOSSignals(ctx) // OS signals listener
Expand All @@ -70,9 +70,11 @@ func run(parentCtx context.Context, log *zap.Logger, f flags, cfg *config.Config
var (
templateNames = cfg.TemplateNames()
picker interface{ Pick() string }

opt = f.ToOptions()
)

switch f.template.name {
switch opt.Template.Name {
case useRandomTemplate:
log.Info("A random template will be used")

Expand All @@ -99,28 +101,19 @@ func run(parentCtx context.Context, log *zap.Logger, f flags, cfg *config.Config
picker = pick.NewStringsSlice(templateNames, pick.First)

default:
if t, found := cfg.Template(f.template.name); found {
if t, found := cfg.Template(opt.Template.Name); found {
log.Info("We will use the requested template", zap.String("name", t.Name()))
picker = pick.NewStringsSlice([]string{t.Name()}, pick.First)
} else {
return errors.New("requested nonexistent template: " + f.template.name)
return errors.New("requested nonexistent template: " + opt.Template.Name)
}
}

var proxyHTTPHeaders = f.HeadersToProxy()

// create HTTP server
server := appHttp.NewServer(log)

// register server routes, middlewares, etc.
if err := server.Register(
cfg,
picker,
f.defaultErrorPage,
f.defaultHTTPCode,
f.showDetails,
proxyHTTPHeaders,
); err != nil {
if err := server.Register(cfg, picker, opt); err != nil {
return err
}

Expand All @@ -131,15 +124,16 @@ func run(parentCtx context.Context, log *zap.Logger, f flags, cfg *config.Config
defer close(errCh)

log.Info("Server starting",
zap.String("addr", f.listen.ip),
zap.Uint16("port", f.listen.port),
zap.String("default error page", f.defaultErrorPage),
zap.Uint16("default HTTP response code", f.defaultHTTPCode),
zap.Strings("proxy headers", proxyHTTPHeaders),
zap.Bool("show request details", f.showDetails),
zap.String("addr", f.Listen.IP),
zap.Uint16("port", f.Listen.Port),
zap.String("default error page", opt.Default.PageCode),
zap.Uint16("default HTTP response code", opt.Default.HTTPCode),
zap.Strings("proxy headers", opt.ProxyHTTPHeaders),
zap.Bool("show request details", opt.ShowDetails),
zap.Bool("localization disabled", opt.L10n.Disabled),
)

if err := server.Start(f.listen.ip, f.listen.port); err != nil {
if err := server.Start(f.Listen.IP, f.Listen.Port); err != nil {
errCh <- err
}
}(startingErrCh)
Expand Down
133 changes: 81 additions & 52 deletions internal/cli/serve/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,26 @@ import (

"github.com/spf13/pflag"
"github.com/tarampampam/error-pages/internal/env"
"github.com/tarampampam/error-pages/internal/options"
)

type flags struct {
listen struct {
ip string
port uint16
Listen struct {
IP string
Port uint16
}
template struct {
name string
}
l10n struct {
disabled bool
}
defaultErrorPage string
defaultHTTPCode uint16
showDetails bool
proxyHTTPHeaders string // comma-separated
}

// HeadersToProxy converts a comma-separated string with headers list into strings slice (with a sorting and without
// duplicates).
func (f *flags) HeadersToProxy() []string {
var raw = strings.Split(f.proxyHTTPHeaders, ",")

if len(raw) == 0 {
return []string{}
} else if len(raw) == 1 {
if h := strings.TrimSpace(raw[0]); h != "" {
return []string{h}
} else {
return []string{}
}
}

var m = make(map[string]struct{}, len(raw))

// make unique and ignore empty strings
for _, h := range raw {
if h = strings.TrimSpace(h); h != "" {
if _, ok := m[h]; !ok {
m[h] = struct{}{}
}
}
}

// convert map into slice
var headers = make([]string, 0, len(m))
for h := range m {
headers = append(headers, h)
}

// make sort
sort.Strings(headers)

return headers
}

const (
listenFlagName = "listen"
portFlagName = "port"
Expand All @@ -71,6 +37,7 @@ const (
defaultHTTPCodeFlagName = "default-http-code"
showDetailsFlagName = "show-details"
proxyHTTPHeadersFlagName = "proxy-headers"
disableL10nFlagName = "disable-l10n"
)

const (
Expand All @@ -80,18 +47,18 @@ const (
useRandomTemplateHourly = "random-hourly"
)

func (f *flags) init(flagSet *pflag.FlagSet) {
func (f *flags) Init(flagSet *pflag.FlagSet) {
flagSet.StringVarP(
&f.listen.ip,
&f.Listen.IP,
listenFlagName, "l",
"0.0.0.0",
fmt.Sprintf("IP address to listen on [$%s]", env.ListenAddr),
fmt.Sprintf("IP address to Listen on [$%s]", env.ListenAddr),
)
flagSet.Uint16VarP(
&f.listen.port,
&f.Listen.Port,
portFlagName, "p",
8080, //nolint:gomnd // must be same as default healthcheck `--port` flag value
fmt.Sprintf("TCP port number [$%s]", env.ListenPort),
fmt.Sprintf("TCP prt number [$%s]", env.ListenPort),
)
flagSet.StringVarP(
&f.template.name,
Expand Down Expand Up @@ -131,22 +98,28 @@ func (f *flags) init(flagSet *pflag.FlagSet) {
"",
fmt.Sprintf("proxy HTTP request headers list (comma-separated) [$%s]", env.ProxyHTTPHeaders),
)
flagSet.BoolVarP(
&f.l10n.disabled,
disableL10nFlagName, "",
false,
fmt.Sprintf("disable error pages localization [$%s]", env.DisableL10n),
)
}

func (f *flags) overrideUsingEnv(flagSet *pflag.FlagSet) (lastErr error) { //nolint:gocognit,gocyclo
func (f *flags) OverrideUsingEnv(flagSet *pflag.FlagSet) (lastErr error) { //nolint:gocognit,gocyclo
flagSet.VisitAll(func(flag *pflag.Flag) {
// flag was NOT defined using CLI (flags should have maximal priority)
if !flag.Changed { //nolint:nestif
switch flag.Name {
case listenFlagName:
if envVar, exists := env.ListenAddr.Lookup(); exists {
f.listen.ip = strings.TrimSpace(envVar)
f.Listen.IP = strings.TrimSpace(envVar)
}

case portFlagName:
if envVar, exists := env.ListenPort.Lookup(); exists {
if p, err := strconv.ParseUint(envVar, 10, 16); err == nil { //nolint:gomnd
f.listen.port = uint16(p)
f.Listen.Port = uint16(p)
} else {
lastErr = fmt.Errorf("wrong TCP port environment variable [%s] value", envVar)
}
Expand Down Expand Up @@ -182,16 +155,23 @@ func (f *flags) overrideUsingEnv(flagSet *pflag.FlagSet) (lastErr error) { //nol
if envVar, exists := env.ProxyHTTPHeaders.Lookup(); exists {
f.proxyHTTPHeaders = strings.TrimSpace(envVar)
}

case disableL10nFlagName:
if envVar, exists := env.DisableL10n.Lookup(); exists {
if b, err := strconv.ParseBool(envVar); err == nil {
f.l10n.disabled = b
}
}
}
}
})

return lastErr
}

func (f *flags) validate() error {
if net.ParseIP(f.listen.ip) == nil {
return fmt.Errorf("wrong IP address [%s] for listening", f.listen.ip)
func (f *flags) Validate() error {
if net.ParseIP(f.Listen.IP) == nil {
return fmt.Errorf("wrong IP address [%s] for listening", f.Listen.IP)
}

if f.defaultHTTPCode > 599 { //nolint:gomnd
Expand All @@ -204,3 +184,52 @@ func (f *flags) validate() error {

return nil
}

// headersToProxy converts a comma-separated string with headers list into strings slice (with a sorting and without
// duplicates).
func (f *flags) headersToProxy() []string {
var raw = strings.Split(f.proxyHTTPHeaders, ",")

if len(raw) == 0 {
return []string{}
} else if len(raw) == 1 {
if h := strings.TrimSpace(raw[0]); h != "" {
return []string{h}
} else {
return []string{}
}
}

var m = make(map[string]struct{}, len(raw))

// make unique and ignore empty strings
for _, h := range raw {
if h = strings.TrimSpace(h); h != "" {
if _, ok := m[h]; !ok {
m[h] = struct{}{}
}
}
}

// convert map into slice
var headers = make([]string, 0, len(m))
for h := range m {
headers = append(headers, h)
}

// make sort
sort.Strings(headers)

return headers
}

func (f *flags) ToOptions() (o options.ErrorPage) {
o.Default.PageCode = f.defaultErrorPage
o.Default.HTTPCode = f.defaultHTTPCode
o.L10n.Disabled = f.l10n.disabled
o.Template.Name = f.template.name
o.ShowDetails = f.showDetails
o.ProxyHTTPHeaders = f.headersToProxy()

return o
}
1 change: 1 addition & 0 deletions internal/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
DefaultHTTPCode envVariable = "DEFAULT_HTTP_CODE" // default HTTP response code
ShowDetails envVariable = "SHOW_DETAILS" // show request details in response
ProxyHTTPHeaders envVariable = "PROXY_HTTP_HEADERS" // proxy HTTP request headers list (request -> response)
DisableL10n envVariable = "DISABLE_L10N" // disable pages localization
)

// String returns environment variable name in the string representation.
Expand Down
Loading

0 comments on commit d21a6f2

Please sign in to comment.