Skip to content

Commit

Permalink
Add error_file attribute to server block #59
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Ludwig committed Nov 9, 2020
1 parent b5d1bf6 commit 51127d8
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 61 deletions.
14 changes: 13 additions & 1 deletion config/runtime/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,31 @@ type MuxOptions struct {
FileBasePath string
FileErrTpl *errors.Template
FileRoutes map[string]http.Handler
ServerErrTpl *errors.Template
SPABasePath string
SPARoutes map[string]http.Handler
}

func NewMuxOptions(conf *config.Server) (*MuxOptions, error) {
serverErrTpl := errors.DefaultHTML

options := &MuxOptions{
APIErrTpl: errors.DefaultJSON,
FileErrTpl: errors.DefaultHTML,
FileErrTpl: serverErrTpl,
ServerErrTpl: serverErrTpl,
EndpointRoutes: make(map[string]http.Handler),
FileRoutes: make(map[string]http.Handler),
SPARoutes: make(map[string]http.Handler),
}

if conf.ErrorFile != "" {
tpl, err := errors.NewTemplateFromFile(conf.ErrorFile)
if err != nil {
return nil, err
}
*serverErrTpl = *tpl
}

if conf.API != nil {
options.APIBasePath = path.Join("/", conf.BasePath, conf.API.BasePath)

Expand Down
2 changes: 1 addition & 1 deletion config/runtime/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func NewServerConfiguration(conf *config.Gateway, httpConf *HTTPConfig, log *log
log.Fatal(err)
}

spaHandler = configureProtectedHandler(accessControls, errors.DefaultHTML, // TODO: server err tpl
spaHandler = configureProtectedHandler(accessControls, muxOptions.ServerErrTpl,
config.NewAccessControl(srvConf.AccessControl, srvConf.DisableAccessControl),
config.NewAccessControl(srvConf.Spa.AccessControl, srvConf.Spa.DisableAccessControl), spaHandler)

Expand Down
1 change: 1 addition & 0 deletions config/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Server struct {
DisableAccessControl []string `hcl:"disable_access_control,optional"`
API *Api `hcl:"api,block"`
BasePath string `hcl:"base_path,optional"`
ErrorFile string `hcl:"error_file,optional"`
Files *Files `hcl:"files,block"`
Hosts []string `hcl:"hosts,optional"`
Name string `hcl:"name,label"`
Expand Down
3 changes: 2 additions & 1 deletion errors/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func NewTemplateFromFile(path string) (*Template, error) {
if err != nil {
return nil, err
}
return NewTemplate(mime.TypeByExtension(absPath), tplFile)

return NewTemplate(mime.TypeByExtension(path), tplFile)
}

func NewTemplate(mime string, src []byte) (*Template, error) {
Expand Down
33 changes: 10 additions & 23 deletions server/http_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,12 @@ import (
"github.com/avenga/couper/command"
"github.com/avenga/couper/config"
"github.com/avenga/couper/config/runtime"
"github.com/avenga/couper/errors"
"github.com/avenga/couper/internal/test"
)

var (
testBackend *test.Backend
testWorkingDir string

defaultErrorTpl = []byte("<html>{{.error_code}}</html>")
defaultJSONErrorTpl = []byte(`{"code": {{.error_code}}}`)
tmpErrTpl errors.Template
tmpJSONErrTpl errors.Template
)

func TestMain(m *testing.M) {
Expand All @@ -50,24 +44,11 @@ func setup() {
panic(err)
}
testWorkingDir = wd
println("working directory: ", testWorkingDir)

tmpErrTpl = *errors.DefaultHTML
tmpJSONErrTpl = *errors.DefaultHTML

testTpl, _ := errors.NewTemplate("text/html", defaultErrorTpl)
errors.DefaultHTML = testTpl

testApiTpl, _ := errors.NewTemplate("application/json", defaultJSONErrorTpl)
errors.DefaultJSON = testApiTpl
}

func teardown() {
println("close test backend...")
testBackend.Close()

errors.DefaultHTML = &tmpErrTpl
errors.DefaultJSON = &tmpJSONErrTpl
}

func newCouper(file string, helper *test.Helper) (func(), *logrustest.Hook) {
Expand All @@ -88,18 +69,24 @@ func newCouper(file string, helper *test.Helper) (func(), *logrustest.Hook) {
log, hook := logrustest.NewNullLogger()

ctx, cancel := context.WithCancel(test.NewContext(context.Background()))
cancelFn := func() {
cancel()
time.Sleep(time.Second / 2)
}
//log.Out = os.Stdout
go func() {
helper.Must(command.NewRun(ctx).Execute([]string{file}, gatewayConf, log.WithContext(ctx)))
}()
time.Sleep(time.Second / 2)

entry := hook.LastEntry()
if entry.Level < logrus.InfoLevel {
helper.Must(fmt.Errorf(entry.String()))
for _, entry := range hook.AllEntries() {
if entry.Level < logrus.InfoLevel {
helper.Must(fmt.Errorf("error: %#v: %s", entry.Data, entry.Message))
}
}

hook.Reset() // no startup logs
return cancel, hook
return cancelFn, hook
}

func TestHTTPServer_ServeHTTP(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion server/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestHTTPServer_ServeHTTP_Files(t *testing.T) {
helper.Must(res.Body.Close())

if !bytes.Contains(result, testCase.expectedBody) {
t.Errorf("%.2d: expected body:\n%s\ngot:\n%s", i+1, string(testCase.expectedBody), string(result))
t.Errorf("%.2d: expected body should contain:\n%s\ngot:\n%s", i+1, string(testCase.expectedBody), string(result))
}
}
}
Expand Down
59 changes: 25 additions & 34 deletions server/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,11 @@ import (
// Mux is a http request router and dispatches requests
// to their corresponding http handlers.
type Mux struct {
apiErrHandler *errors.Template
apiBasePath string
endpointRoot *pathpattern.Node
fileBasePath string
fileErrHandler *errors.Template
fileRoot *pathpattern.Node
router *openapi3filter.Router
spaBasePath string
spaRoot *pathpattern.Node
endpointRoot *pathpattern.Node
fileRoot *pathpattern.Node
opts *runtime.MuxOptions
router *openapi3filter.Router
spaRoot *pathpattern.Node
}

var allowedMethods = []string{
Expand All @@ -51,20 +47,17 @@ func NewMux(options *runtime.MuxOptions) *Mux {
opts := options
if opts == nil {
opts = &runtime.MuxOptions{
APIErrTpl: errors.DefaultJSON,
FileErrTpl: errors.DefaultHTML,
APIErrTpl: errors.DefaultJSON,
FileErrTpl: errors.DefaultHTML,
ServerErrTpl: errors.DefaultHTML,
}
}

mux := &Mux{
apiErrHandler: opts.APIErrTpl,
apiBasePath: opts.APIBasePath,
endpointRoot: &pathpattern.Node{},
fileBasePath: opts.FileBasePath,
fileErrHandler: opts.FileErrTpl,
fileRoot: &pathpattern.Node{},
spaBasePath: opts.SPABasePath,
spaRoot: &pathpattern.Node{},
opts: opts,
endpointRoot: &pathpattern.Node{},
fileRoot: &pathpattern.Node{},
spaRoot: &pathpattern.Node{},
}

for path, h := range opts.EndpointRoutes {
Expand Down Expand Up @@ -107,6 +100,7 @@ func (m *Mux) mustAddRoute(root *pathpattern.Node, methods []string, path string
const wildcardReplacement = "/{_couper_wildcardMatch*}"
const wildcardSearch = "/**"

// TODO: Unique Options per method if configurable later on
pathOptions := &pathpattern.Options{}

for _, method := range methods {
Expand Down Expand Up @@ -137,8 +131,8 @@ func (m *Mux) FindHandler(req *http.Request) http.Handler {
// No matches for api or free endpoints. Determine if we have entered an api basePath
// and handle api related errors accordingly.
// Otherwise look for existing files or spa fallback.
if isConfigured(m.apiBasePath) && m.isAPIError(req.URL.Path) {
return m.apiErrHandler.ServeError(errors.APIRouteNotFound)
if isConfigured(m.opts.APIBasePath) && m.isAPIError(req.URL.Path) {
return m.opts.APIErrTpl.ServeError(errors.APIRouteNotFound)
}

fileHandler, exist := m.hasFileResponse(req)
Expand All @@ -148,14 +142,11 @@ func (m *Mux) FindHandler(req *http.Request) http.Handler {

node, paramValues = m.match(m.spaRoot, req)
if node == nil {
if isConfigured(m.fileBasePath) && m.isFileError(req.URL.Path) {
return m.fileErrHandler.ServeError(errors.FilesRouteNotFound)
if isConfigured(m.opts.FileBasePath) && m.isFileError(req.URL.Path) {
return m.opts.FileErrTpl.ServeError(errors.FilesRouteNotFound)
}
// TODO: server err handler
if isConfigured(m.fileBasePath) {
return m.fileErrHandler.ServeError(errors.Configuration)
}
return errors.DefaultHTML.ServeError(errors.Configuration)

return m.opts.ServerErrTpl.ServeError(errors.Configuration)
}
}

Expand Down Expand Up @@ -217,18 +208,18 @@ func (m *Mux) hasFileResponse(req *http.Request) (http.Handler, bool) {
}

func (m *Mux) isAPIError(reqPath string) bool {
p1 := m.apiBasePath
p2 := m.apiBasePath
p1 := m.opts.APIBasePath
p2 := m.opts.APIBasePath

if p2 != "/" && strings.HasSuffix(p2, "/") {
p2 = p2[:len(p2)-len("/")]
}

if strings.HasPrefix(reqPath, p1) || reqPath == p2 {
if isConfigured(m.fileBasePath) && m.apiBasePath == m.fileBasePath {
if isConfigured(m.opts.FileBasePath) && m.opts.APIBasePath == m.opts.FileBasePath {
return false
}
if isConfigured(m.spaBasePath) && m.apiBasePath == m.spaBasePath {
if isConfigured(m.opts.SPABasePath) && m.opts.APIBasePath == m.opts.SPABasePath {
return false
}

Expand All @@ -239,8 +230,8 @@ func (m *Mux) isAPIError(reqPath string) bool {
}

func (m *Mux) isFileError(reqPath string) bool {
p1 := m.fileBasePath
p2 := m.fileBasePath
p1 := m.opts.FileBasePath
p2 := m.opts.FileBasePath

if p2 != "/" && strings.HasSuffix(p2, "/") {
p2 = p2[:len(p2)-len("/")]
Expand Down
3 changes: 3 additions & 0 deletions server/testdata/integration/api/01_couper.hcl
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
server "api" {
error_file = "./../server_error.html"
api {
base_path = "/v1"

error_file = "./../api_error.json"

endpoint "/" {
backend {
path = "/anything"
Expand Down
1 change: 1 addition & 0 deletions server/testdata/integration/api_error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"code": {{.error_code}}}
1 change: 1 addition & 0 deletions server/testdata/integration/files/01_couper.hcl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
server "files" {
error_file = "./../server_error.html"
files {
document_root = "../spa/app.html"
}
Expand Down
2 changes: 2 additions & 0 deletions server/testdata/integration/files_spa_api/01_couper.hcl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
server "spa" {
error_file = "./../server_error.html"
files {
document_root = "./"
}
Expand All @@ -7,6 +8,7 @@ server "spa" {
paths = ["/**"]
}
api {
error_file = "./../api_error.json"
endpoint "/api" {
path = "/"
backend {
Expand Down
1 change: 1 addition & 0 deletions server/testdata/integration/server_error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<html>{{.error_code}}</html>
1 change: 1 addition & 0 deletions server/testdata/integration/spa/01_couper.hcl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
server "spa" {
error_file = "./../server_error.html"
spa {
bootstrap_file = "app.html"
paths = ["/"]
Expand Down

0 comments on commit 51127d8

Please sign in to comment.