Skip to content

Commit

Permalink
Implement custom noroute html response
Browse files Browse the repository at this point in the history
PR for #56
  • Loading branch information
tino authored and magiconair committed Dec 16, 2017
1 parent 693296b commit a221ea2
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 12 deletions.
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ type Consul struct {
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
1 change: 1 addition & 0 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func load(cmdline, environ, envprefix []string, props *properties.Properties) (c
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
8 changes: 8 additions & 0 deletions fabio.properties
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,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.kvpath = /fabio/noroutes.html

# registry.consul.service.status configures the valid service status
# values for services included in the routing table.
Expand Down
17 changes: 17 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,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 @@ -406,6 +408,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
}
route.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
5 changes: 5 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 Down Expand Up @@ -71,6 +72,10 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t := p.Lookup(r)
if t == nil {
w.WriteHeader(p.Config.NoRouteStatus)
html := route.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
19 changes: 18 additions & 1 deletion registry/static/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
// backend which uses statically configured routes.
package static

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

"github.com/fabiolb/fabio/registry"
"github.com/fabiolb/fabio/route"
)

type be struct{}

Expand Down Expand Up @@ -38,3 +44,14 @@ func (b *be) WatchServices() chan string {
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("noroute.html")
if err != nil {
log.Println("[WARN] No noroute.html to read noroute html from")
}
route.SetHTML(string(data))
return make(chan string)
}
44 changes: 44 additions & 0 deletions route/no_route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package route

import (
"log"
"sync"
"sync/atomic"
)

// HTML Wrapper struct so we can store the html string in an atomic.Value
type HTML struct {
value string
}

// html stores the no route html string
var store atomic.Value

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

// GetHTML returns the HTML for not found routes. The function is safe to be
// called from multiple goroutines.
func GetHTML() string {
return store.Load().(HTML).value
}

// hmu guards the atomic writes in SetHTML.
var hmu sync.Mutex

// SetHTML sets the current noroute html. The function is safe to be called from
// multiple goroutines.
func SetHTML(h string) {
hmu.Lock()
defer hmu.Unlock()

html := HTML{h}
store.Store(html)

if h == "" {
log.Print("[INFO] Unset noroute HTML")
} else {
log.Printf("[INFO] Set noroute HTML (%d bytes)", len(h))
}
}

0 comments on commit a221ea2

Please sign in to comment.