Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement custom noroute html response #398

Merged
merged 8 commits into from
Dec 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,21 @@ type Registry struct {
}

type Static struct {
Routes string
NoRouteHTMLPath string
Routes string
}

type File struct {
Path string
NoRouteHTMLPath string
Path string
}

type Consul struct {
Addr string
Scheme string
Token string
KVPath string
NoRouteHTMLPath string
TagPrefix string
Register bool
ServiceAddr string
Expand Down
23 changes: 12 additions & 11 deletions config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,18 @@ var defaultConfig = &Config{
Registry: Registry{
Backend: "consul",
Consul: Consul{
Addr: "localhost:8500",
Scheme: "http",
KVPath: "/fabio/config",
TagPrefix: "urlprefix-",
Register: true,
ServiceAddr: ":9998",
ServiceName: "fabio",
ServiceStatus: []string{"passing"},
CheckInterval: time.Second,
CheckTimeout: 3 * time.Second,
CheckScheme: "http",
Addr: "localhost:8500",
Scheme: "http",
KVPath: "/fabio/config",
NoRouteHTMLPath: "/fabio/noroute.html",
TagPrefix: "urlprefix-",
Register: true,
ServiceAddr: ":9998",
ServiceName: "fabio",
ServiceStatus: []string{"passing"},
CheckInterval: time.Second,
CheckTimeout: 3 * time.Second,
CheckScheme: "http",
},
Timeout: 10 * time.Second,
Retry: 500 * time.Millisecond,
Expand Down
3 changes: 3 additions & 0 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,13 @@ func load(cmdline, environ, envprefix []string, props *properties.Properties) (c
f.DurationVar(&cfg.Registry.Timeout, "registry.timeout", defaultConfig.Registry.Timeout, "timeout for registry to become available")
f.DurationVar(&cfg.Registry.Retry, "registry.retry", defaultConfig.Registry.Retry, "retry interval during startup")
f.StringVar(&cfg.Registry.File.Path, "registry.file.path", defaultConfig.Registry.File.Path, "path to file based routing table")
f.StringVar(&cfg.Registry.File.NoRouteHTMLPath, "registry.file.noroutehtmlpath", defaultConfig.Registry.File.NoRouteHTMLPath, "path to file for HTML returned when no route is found")
f.StringVar(&cfg.Registry.Static.Routes, "registry.static.routes", defaultConfig.Registry.Static.Routes, "static routes")
f.StringVar(&cfg.Registry.Static.NoRouteHTMLPath, "registry.static.noroutehtmlpath", defaultConfig.Registry.Static.NoRouteHTMLPath, "path to file for HTML returned when no route is found")
f.StringVar(&cfg.Registry.Consul.Addr, "registry.consul.addr", defaultConfig.Registry.Consul.Addr, "address of the consul agent")
f.StringVar(&cfg.Registry.Consul.Token, "registry.consul.token", defaultConfig.Registry.Consul.Token, "token for consul agent")
f.StringVar(&cfg.Registry.Consul.KVPath, "registry.consul.kvpath", defaultConfig.Registry.Consul.KVPath, "consul KV path for manual overrides")
f.StringVar(&cfg.Registry.Consul.NoRouteHTMLPath, "registry.consul.noroutehtmlpath", defaultConfig.Registry.Consul.NoRouteHTMLPath, "consul KV path for HTML returned when no route is found")
f.StringVar(&cfg.Registry.Consul.TagPrefix, "registry.consul.tagprefix", defaultConfig.Registry.Consul.TagPrefix, "prefix for consul tags")
f.BoolVar(&cfg.Registry.Consul.Register, "registry.consul.register.enabled", defaultConfig.Registry.Consul.Register, "register fabio in consul")
f.StringVar(&cfg.Registry.Consul.ServiceAddr, "registry.consul.register.addr", defaultConfig.Registry.Consul.ServiceAddr, "service registration address")
Expand Down
24 changes: 24 additions & 0 deletions fabio.properties
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,14 @@
# registry.static.routes =


# registry.static.noroutehtmlpath configures the KV path for the HTML of the
# noroutes page.
#
# The default is
#
# registry.static.noroutehtmlpath =


# registry.file.path configures a file based routing table.
# The value configures the path to the file with the routing table.
#
Expand All @@ -543,6 +551,14 @@
# registry.file.path =


# registry.file.noroutehtmlpath configures the KV path for the HTML of the
# noroutes page.
#
# The default is
#
# registry.file.noroutehtmlpath =


# registry.consul.addr configures the address of the consul agent to connect to.
#
# The default is
Expand All @@ -567,6 +583,14 @@
#
# registry.consul.kvpath = /fabio/config

# registry.consul.noroutehtmlpath configures the KV path for the HTML of the
# noroutes page.
#
# The consul KV path is watched for changes.
#
# The default is
#
# registry.consul.noroutehtmlpath = /fabio/noroutes.html

# registry.consul.service.status configures the valid service status
# values for services included in the routing table.
Expand Down
22 changes: 20 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/fabiolb/fabio/exit"
"github.com/fabiolb/fabio/logger"
"github.com/fabiolb/fabio/metrics"
"github.com/fabiolb/fabio/noroute"
"github.com/fabiolb/fabio/proxy"
"github.com/fabiolb/fabio/proxy/tcp"
"github.com/fabiolb/fabio/registry"
Expand Down Expand Up @@ -115,6 +116,8 @@ func main() {
initBackend(cfg)
startAdmin(cfg)

go watchNoRouteHTML(cfg)

first := make(chan bool)
go watchBackend(cfg, first)
log.Print("[INFO] Waiting for first routing table")
Expand Down Expand Up @@ -341,9 +344,9 @@ func initBackend(cfg *config.Config) {
for {
switch cfg.Registry.Backend {
case "file":
registry.Default, err = file.NewBackend(cfg.Registry.File.Path)
registry.Default, err = file.NewBackend(&cfg.Registry.File)
case "static":
registry.Default, err = static.NewBackend(cfg.Registry.Static.Routes)
registry.Default, err = static.NewBackend(&cfg.Registry.Static)
case "consul":
registry.Default, err = consul.NewBackend(&cfg.Registry.Consul)
default:
Expand Down Expand Up @@ -406,6 +409,21 @@ func watchBackend(cfg *config.Config, first chan bool) {
}
}

func watchNoRouteHTML(cfg *config.Config) {
var last string
html := registry.Default.WatchNoRouteHTML()

for {
next := <-html

if next == last {
continue
}
noroute.SetHTML(next)
last = next
}
}

func logRoutes(t route.Table, last, next, format string) {
fmtDiff := func(diffs []dmp.Diff) string {
var b bytes.Buffer
Expand Down
29 changes: 29 additions & 0 deletions noroute/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package noroute

import (
"log"
"sync/atomic"
)

var store atomic.Value // string

func init() {
store.Store("")
}

// GetHTML returns the HTML for not found routes.
func GetHTML() string {
return store.Load().(string)
}

// SetHTML sets the current noroute html.
func SetHTML(h string) {
// html := HTML{h}
store.Store(h)

if h == "" {
log.Print("[INFO] Unset noroute HTML")
} else {
log.Printf("[INFO] Set noroute HTML (%d bytes)", len(h))
}
}
19 changes: 19 additions & 0 deletions noroute/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package noroute

import (
"testing"
)

func TestStoreSetGet(t *testing.T) {
got := GetHTML()
if got != "" {
t.Fatalf("Expected unset noroute html to be an empty string, got %s", got)
}

want := "<blink>Fancy!</blink>"
SetHTML(want)
got = GetHTML()
if got != want {
t.Fatalf("got %s, want %s", got, want)
}
}
18 changes: 17 additions & 1 deletion proxy/http_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/fabiolb/fabio/config"
"github.com/fabiolb/fabio/logger"
"github.com/fabiolb/fabio/noroute"
"github.com/fabiolb/fabio/proxy/internal"
"github.com/fabiolb/fabio/route"
"github.com/pascaldekloe/goe/verify"
Expand Down Expand Up @@ -75,7 +76,22 @@ func TestProxyRequestIDHeader(t *testing.T) {
}
}

func TestProxyNoRouteStaus(t *testing.T) {
func TestProxyNoRouteHTML(t *testing.T) {
want := "<html>503</html>"
noroute.SetHTML(want)
proxy := httptest.NewServer(&HTTPProxy{
Transport: http.DefaultTransport,
Lookup: func(*http.Request) *route.Target { return nil },
})
defer proxy.Close()

_, got := mustGet(proxy.URL)
if !bytes.Equal(got, []byte(want)) {
t.Fatalf("got %s want %s", got, want)
}
}

func TestProxyNoRouteStatus(t *testing.T) {
proxy := httptest.NewServer(&HTTPProxy{
Config: config.Proxy{NoRouteStatus: 999},
Transport: http.DefaultTransport,
Expand Down
6 changes: 6 additions & 0 deletions proxy/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package proxy

import (
"crypto/tls"
"io"
"net"
"net/http"
"net/http/httputil"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/fabiolb/fabio/config"
"github.com/fabiolb/fabio/logger"
"github.com/fabiolb/fabio/metrics"
"github.com/fabiolb/fabio/noroute"
"github.com/fabiolb/fabio/proxy/gzip"
"github.com/fabiolb/fabio/route"
"github.com/fabiolb/fabio/uuid"
Expand Down Expand Up @@ -63,6 +65,10 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t := p.Lookup(r)
if t == nil {
w.WriteHeader(p.Config.NoRouteStatus)
html := noroute.GetHTML()
if html != "" {
io.WriteString(w, html)
}
return
}

Expand Down
4 changes: 4 additions & 0 deletions registry/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ type Backend interface {
// WatchManual watches the registry for changes in the manual
// overrides and pushes them if there is a difference.
WatchManual() chan string

// WatchNoRouteHTML watches the registry for changes in the html returned
// when a requested route is not found
WatchNoRouteHTML() chan string
}

var Default Backend
8 changes: 8 additions & 0 deletions registry/consul/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ func (b *be) WatchManual() chan string {
return kv
}

func (b *be) WatchNoRouteHTML() chan string {
log.Printf("[INFO] consul: Watching KV path %q", b.cfg.NoRouteHTMLPath)

html := make(chan string)
go watchKV(b.c, b.cfg.NoRouteHTMLPath, html)
return html
}

// datacenter returns the datacenter of the local agent
func datacenter(c *api.Client) (string, error) {
self, err := c.Agent().Self()
Expand Down
10 changes: 6 additions & 4 deletions registry/file/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import (
"io/ioutil"
"log"

"github.com/fabiolb/fabio/config"
"github.com/fabiolb/fabio/registry"
"github.com/fabiolb/fabio/registry/static"
)

func NewBackend(filename string) (registry.Backend, error) {
data, err := ioutil.ReadFile(filename)
func NewBackend(cfg *config.File) (registry.Backend, error) {
data, err := ioutil.ReadFile(cfg.Path)
if err != nil {
log.Println("[ERROR] Cannot read routes from ", filename)
log.Println("[ERROR] Cannot read routes from ", cfg.Path)
return nil, err
}
return static.NewBackend(string(data))
staticCfg := config.Static{cfg.NoRouteHTMLPath, string(data)}
return static.NewBackend(&staticCfg)
}
31 changes: 24 additions & 7 deletions registry/static/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@
// backend which uses statically configured routes.
package static

import "github.com/fabiolb/fabio/registry"
import (
"io/ioutil"
"log"

type be struct{}
"github.com/fabiolb/fabio/config"
"github.com/fabiolb/fabio/noroute"
"github.com/fabiolb/fabio/registry"
)

var staticRoutes string
type be struct {
cfg *config.Static
}

func NewBackend(routes string) (registry.Backend, error) {
staticRoutes = routes
return &be{}, nil
func NewBackend(cfg *config.Static) (registry.Backend, error) {
return &be{cfg}, nil
}

func (b *be) Register() error {
Expand All @@ -31,10 +37,21 @@ func (b *be) WriteManual(value string, version uint64) (ok bool, err error) {

func (b *be) WatchServices() chan string {
ch := make(chan string, 1)
ch <- staticRoutes
ch <- b.cfg.Routes
return ch
}

func (b *be) WatchManual() chan string {
return make(chan string)
}

// WatchNoRouteHTML implementation that reads the noroute html from a
// noroute.html file if it exists
func (b *be) WatchNoRouteHTML() chan string {
data, err := ioutil.ReadFile(b.cfg.NoRouteHTMLPath)
if err != nil {
log.Printf("[WARN] Could not read NoRouteHTMLPath (%s)", b.cfg.NoRouteHTMLPath)
}
noroute.SetHTML(string(data))
return make(chan string)
}