Skip to content

Commit

Permalink
Add request_body_limit option #44
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Ludwig committed Oct 19, 2020
1 parent f9f7612 commit 80f1a0b
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 148 deletions.
41 changes: 23 additions & 18 deletions config/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import (
)

type Backend struct {
ConnectTimeout string `hcl:"connect_timeout,optional"`
Hostname string `hcl:"hostname,optional"`
Name string `hcl:"name,label"`
Options hcl.Body `hcl:",remain"`
Origin string `hcl:"origin,optional"` // mixed, not required for overrides
Path string `hcl:"path,optional"`
Timeout string `hcl:"timeout,optional"`
TTFBTimeout string `hcl:"ttfb_timeout,optional"`
ConnectTimeout string `hcl:"connect_timeout,optional"`
Hostname string `hcl:"hostname,optional"`
Name string `hcl:"name,label"`
Options hcl.Body `hcl:",remain"`
Origin string `hcl:"origin,optional"` // mixed, not required for overrides
Path string `hcl:"path,optional"`
RequestBodyLimit string `hcl:"request_body_limit,optional"`
TTFBTimeout string `hcl:"ttfb_timeout,optional"`
Timeout string `hcl:"timeout,optional"`
}

// Merge overrides the left backend configuration and returns a new instance.
Expand All @@ -33,14 +34,6 @@ func (b *Backend) Merge(other *Backend) (*Backend, []hcl.Body) {
result.Name = other.Name
}

if other.Origin != "" {
result.Origin = other.Origin
}

if other.Path != "" {
result.Path = other.Path
}

if result.Options != nil {
bodies = append(bodies, result.Options)
}
Expand All @@ -50,17 +43,29 @@ func (b *Backend) Merge(other *Backend) (*Backend, []hcl.Body) {
result.Options = other.Options
}

if other.Timeout != "" {
result.Timeout = other.Timeout
if other.Origin != "" {
result.Origin = other.Origin
}

if other.Path != "" {
result.Path = other.Path
}

if other.ConnectTimeout != "" {
result.ConnectTimeout = other.ConnectTimeout
}

if other.RequestBodyLimit != "" {
result.RequestBodyLimit = other.RequestBodyLimit
}

if other.TTFBTimeout != "" {
result.TTFBTimeout = other.TTFBTimeout
}

if other.Timeout != "" {
result.Timeout = other.Timeout
}

return &result, bodies
}
47 changes: 19 additions & 28 deletions config/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,35 @@ import (
)

func TestBackend_Merge(t *testing.T) {
type fields struct {
Hostname string
Name string
Origin string
Path string
Timeout string
ConnectTimeout string
TTFBTimeout string
Options hcl.Body
}
type args struct {
other *Backend
}
tests := []struct {
name string
fields fields
fields Backend
args args
want *Backend
}{
{"nil", fields{}, args{nil}, nil},
{"empty", fields{}, args{&Backend{}}, &Backend{}},
{"left", fields{
{"nil", Backend{}, args{nil}, nil},
{"empty", Backend{}, args{&Backend{}}, &Backend{}},
{"left", Backend{
Hostname: "a", Name: "a", Origin: "a", Path: "a", Timeout: "s", ConnectTimeout: "s", Options: hcl.EmptyBody(),
}, args{&Backend{}}, &Backend{
Hostname: "a", Name: "a", Origin: "a", Path: "a", Timeout: "s", ConnectTimeout: "s", Options: hcl.EmptyBody(),
}},
{"right", fields{}, args{&Backend{
{"right", Backend{}, args{&Backend{
Hostname: "a", Name: "a", Origin: "a", Path: "a", Timeout: "s", ConnectTimeout: "s", Options: hcl.EmptyBody(),
}}, &Backend{
Hostname: "a", Name: "a", Origin: "a", Path: "a", Timeout: "s", ConnectTimeout: "s", Options: hcl.EmptyBody(),
}},
{"override", fields{
Hostname: "a", Name: "a", Origin: "a", Path: "a", Timeout: "s", ConnectTimeout: "s", TTFBTimeout: "t", Options: hcl.EmptyBody(),
{"override", Backend{
Hostname: "a", Name: "a", Origin: "a", Path: "a", Timeout: "s", ConnectTimeout: "s", RequestBodyLimit: "2M", TTFBTimeout: "t", Options: hcl.EmptyBody(),
}, args{&Backend{
Hostname: "b", Name: "b", Origin: "b", Path: "b", Timeout: "m", ConnectTimeout: "h", TTFBTimeout: "o", Options: hcl.EmptyBody(),
Hostname: "b", Name: "b", Origin: "b", Path: "b", Timeout: "m", ConnectTimeout: "h", RequestBodyLimit: "20M", TTFBTimeout: "o", Options: hcl.EmptyBody(),
}}, &Backend{
Hostname: "b", Name: "b", Origin: "b", Path: "b", Timeout: "m", ConnectTimeout: "h", TTFBTimeout: "o", Options: hcl.EmptyBody(),
Hostname: "b", Name: "b", Origin: "b", Path: "b", Timeout: "m", ConnectTimeout: "h", RequestBodyLimit: "20M", TTFBTimeout: "o", Options: hcl.EmptyBody(),
}},
{"partial override", fields{
{"partial override", Backend{
Hostname: "a", Name: "b", Origin: "c", Path: "d", Timeout: "e", ConnectTimeout: "f", TTFBTimeout: "t", Options: hcl.EmptyBody(),
}, args{&Backend{
Hostname: "c", ConnectTimeout: "d",
Expand All @@ -57,14 +47,15 @@ func TestBackend_Merge(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &Backend{
ConnectTimeout: tt.fields.ConnectTimeout,
Hostname: tt.fields.Hostname,
Name: tt.fields.Name,
Options: tt.fields.Options,
Origin: tt.fields.Origin,
Path: tt.fields.Path,
Timeout: tt.fields.Timeout,
TTFBTimeout: tt.fields.TTFBTimeout,
ConnectTimeout: tt.fields.ConnectTimeout,
Hostname: tt.fields.Hostname,
Name: tt.fields.Name,
Options: tt.fields.Options,
Origin: tt.fields.Origin,
Path: tt.fields.Path,
RequestBodyLimit: tt.fields.RequestBodyLimit,
Timeout: tt.fields.Timeout,
TTFBTimeout: tt.fields.TTFBTimeout,
}
if got, _ := b.Merge(tt.args.other); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Merge() = %v, want %v", got, tt.want)
Expand Down
142 changes: 79 additions & 63 deletions config/runtime/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"time"

"github.com/docker/go-units"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/sirupsen/logrus"
Expand All @@ -28,11 +29,12 @@ type entrypointHandler struct {
files, spa http.Handler
}

const (
backendDefaultConnectTimeout = "10s"
backendDefaultTimeout = "300s"
backendDefaultTTFBTimeout = "60s"
)
var defaultBackendConf = &config.Backend{
ConnectTimeout: "10s",
RequestBodyLimit: "64MiB",
TTFBTimeout: "60s",
Timeout: "300s",
}

var errorMissingBackend = fmt.Errorf("no backend attribute reference or block")

Expand Down Expand Up @@ -61,25 +63,24 @@ func BuildEntrypointHandlers(conf *config.Gateway, httpConf *HTTPConfig, log *lo
log.Fatalf("backend %q: origin attribute is required", beConf.Name)
}

if beConf.Timeout == "" {
beConf.Timeout = backendDefaultTimeout
}
if beConf.TTFBTimeout == "" {
beConf.TTFBTimeout = backendDefaultTTFBTimeout
}
if beConf.ConnectTimeout == "" {
beConf.ConnectTimeout = backendDefaultConnectTimeout
beConf, _ = defaultBackendConf.Merge(beConf)

bodyLimit, err := units.FromHumanSize(beConf.RequestBodyLimit)
if err != nil {
log.Fatalf("backend bodyLimit: %v", err)
}

t, ttfbt, ct := parseBackendTimings(beConf)
proxy, err := handler.NewProxy(&handler.ProxyOptions{
BackendName: beConf.Name,
ConnectTimeout: ct,
Context: []hcl.Body{beConf.Options},
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
Timeout: t,
TTFBTimeout: ttfbt,
RequestBodyLimit: bodyLimit,
BackendName: beConf.Name,
ConnectTimeout: ct,
Context: []hcl.Body{beConf.Options},
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
Timeout: t,
TTFBTimeout: ttfbt,
}, log, conf.Context)
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -178,21 +179,29 @@ func BuildEntrypointHandlers(conf *config.Gateway, httpConf *HTTPConfig, log *lo
// prefer endpoint 'path' definition over 'backend.Path'
if endpoint.Path != "" {
beConf, remainCtx := protectedBackend.conf.Merge(&config.Backend{Path: endpoint.Path})

t, ttfbt, ct := parseBackendTimings(beConf)

bodyLimit, err := units.FromHumanSize(beConf.RequestBodyLimit)
if err != nil {
log.Fatalf("backend bodyLimit: %v", err)
}

corsOptions, err := handler.NewCORSOptions(server.API.CORS)
if err != nil {
log.Fatal(err)
}
proxy, err := handler.NewProxy(&handler.ProxyOptions{
BackendName: beConf.Name,
ConnectTimeout: ct,
Context: remainCtx,
CORS: corsOptions,
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
Timeout: t,
TTFBTimeout: ttfbt,
BackendName: beConf.Name,
CORS: corsOptions,
ConnectTimeout: ct,
Context: remainCtx,
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
RequestBodyLimit: bodyLimit,
TTFBTimeout: ttfbt,
Timeout: t,
}, log, conf.Context)
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -248,20 +257,27 @@ func BuildEntrypointHandlers(conf *config.Gateway, httpConf *HTTPConfig, log *lo

beConf, remainCtx := backends[inlineConf.Name].conf.Merge(inlineConf)
t, ttfbt, ct := parseBackendTimings(beConf)

bodyLimit, err := units.FromHumanSize(beConf.RequestBodyLimit)
if err != nil {
log.Fatalf("backend bodyLimit: %v", err)
}

corsOptions, err := handler.NewCORSOptions(server.API.CORS)
if err != nil {
log.Fatal(err)
}
proxy, err := handler.NewProxy(&handler.ProxyOptions{
BackendName: beConf.Name,
ConnectTimeout: ct,
Context: remainCtx,
CORS: corsOptions,
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
Timeout: t,
TTFBTimeout: ttfbt,
BackendName: beConf.Name,
CORS: corsOptions,
ConnectTimeout: ct,
Context: remainCtx,
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
RequestBodyLimit: bodyLimit,
TTFBTimeout: ttfbt,
Timeout: t,
}, log, conf.Context)
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -464,47 +480,47 @@ func newInlineBackend(evalCtx *hcl.EvalContext, inlineDef hcl.Body, cors *config
beConf.Name = content.Blocks[0].Labels[0]
}

beConf, _ = defaultBackendConf.Merge(beConf)

t, ttfbt, ct := parseBackendTimings(beConf)

bodyLimit, err := units.FromHumanSize(beConf.RequestBodyLimit)
if err != nil {
log.Fatalf("backend bodyLimit: %v", err)
}

corsOptions, err := handler.NewCORSOptions(cors)
if err != nil {
return nil, nil, err
}

proxy, err := handler.NewProxy(&handler.ProxyOptions{
BackendName: beConf.Name,
ConnectTimeout: ct,
Context: []hcl.Body{beConf.Options},
CORS: corsOptions,
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
Timeout: t,
TTFBTimeout: ttfbt,
BackendName: beConf.Name,
CORS: corsOptions,
ConnectTimeout: ct,
Context: []hcl.Body{beConf.Options},
Hostname: beConf.Hostname,
Origin: beConf.Origin,
Path: beConf.Path,
RequestBodyLimit: bodyLimit,
TTFBTimeout: ttfbt,
Timeout: t,
}, log, evalCtx)
return proxy, beConf, err
}

func parseBackendTimings(conf *config.Backend) (time.Duration, time.Duration, time.Duration) {
t := conf.Timeout
ttfb := conf.TTFBTimeout
c := conf.ConnectTimeout
if t == "" {
t = backendDefaultTimeout
}
if ttfb == "" {
ttfb = backendDefaultTTFBTimeout
}
if c == "" {
c = backendDefaultConnectTimeout
}
totalD, err := time.ParseDuration(t)
c, _ := defaultBackendConf.Merge(conf)

totalD, err := time.ParseDuration(c.Timeout)
if err != nil {
panic(err)
}
ttfbD, err := time.ParseDuration(ttfb)
ttfbD, err := time.ParseDuration(c.TTFBTimeout)
if err != nil {
panic(err)
}
connectD, err := time.ParseDuration(c)
connectD, err := time.ParseDuration(c.ConnectTimeout)
if err != nil {
panic(err)
}
Expand Down
8 changes: 5 additions & 3 deletions errors/code.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
APIError Code = 4000 + iota
APIRouteNotFound
APIConnect
APIReqBodySizeExceeded
)

const (
Expand All @@ -44,9 +45,10 @@ var codes = map[Code]string{
FilesError: "Files failed",
FilesRouteNotFound: "FilesRouteNotFound",
// 4xxx
APIError: "API failed",
APIRouteNotFound: "API route not found",
APIConnect: "API upstream connection error",
APIError: "API failed",
APIRouteNotFound: "API route not found",
APIConnect: "API upstream connection error",
APIReqBodySizeExceeded: "Request body size exceeded",
// 5xxx
AuthorizationRequired: "Authorization required",
AuthorizationFailed: "Authorization failed",
Expand Down
2 changes: 2 additions & 0 deletions errors/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ func httpStatus(code Code) int {
return http.StatusNotFound
case APIConnect:
return http.StatusBadGateway
case APIReqBodySizeExceeded:
return http.StatusRequestEntityTooLarge
case InvalidRequest:
return http.StatusBadRequest
case AuthorizationRequired, BasicAuthFailed:
Expand Down
Loading

0 comments on commit 80f1a0b

Please sign in to comment.