Skip to content

Commit c062d75

Browse files
authored
Merge pull request #46 from krakend/content_typed_errors
Support a third optional argument in the custom_error helper for the content-type
2 parents 3a2f876 + 4dab457 commit c062d75

File tree

6 files changed

+178
-29
lines changed

6 files changed

+178
-29
lines changed

errors.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ func (e ErrInternalHTTP) Error() string {
5050
return e.msg
5151
}
5252

53+
type ErrInternalHTTPWithContentType struct {
54+
ErrInternalHTTP
55+
contentType string
56+
}
57+
58+
func (e ErrInternalHTTPWithContentType) Encoding() string {
59+
return e.contentType
60+
}
61+
62+
const separator = " || "
63+
5364
func ToError(e error) error {
5465
if e == nil {
5566
return nil
@@ -60,17 +71,29 @@ func ToError(e error) error {
6071
}
6172

6273
originalMsg := e.Error()
63-
start := strings.Index(originalMsg, ":")
74+
errMsgParts := strings.Split(originalMsg[strings.Index(originalMsg, ":")+2:], separator)
6475

65-
if l := len(originalMsg); originalMsg[l-1] == ')' && originalMsg[l-5] == '(' {
66-
code, err := strconv.Atoi(originalMsg[l-4 : l-1])
67-
if err != nil {
68-
code = 500
69-
}
70-
return ErrInternalHTTP{msg: originalMsg[start+2 : l-6], code: code}
76+
if len(errMsgParts) == 0 {
77+
return e
7178
}
79+
if len(errMsgParts) == 1 {
80+
return ErrInternal(errMsgParts[0])
81+
}
82+
83+
code, err := strconv.Atoi(errMsgParts[1])
84+
if err != nil {
85+
code = 500
86+
}
87+
errHTTP := ErrInternalHTTP{msg: errMsgParts[0], code: code}
7288

73-
return ErrInternal(originalMsg[start+2:])
89+
if len(errMsgParts) == 2 {
90+
return errHTTP
91+
}
92+
93+
return ErrInternalHTTPWithContentType{
94+
ErrInternalHTTP: errHTTP,
95+
contentType: errMsgParts[2],
96+
}
7497
}
7598

7699
func RegisterErrors(b *binder.Binder) {
@@ -80,8 +103,10 @@ func RegisterErrors(b *binder.Binder) {
80103
return errNeedsArguments
81104
case 1:
82105
return errors.New(c.Arg(1).String())
106+
case 2:
107+
return fmt.Errorf("%s%s%d", c.Arg(1).String(), separator, int(c.Arg(2).Number()))
83108
default:
84-
return fmt.Errorf("%s (%d)", c.Arg(1).String(), int(c.Arg(2).Number()))
109+
return fmt.Errorf("%s%s%d%s%s", c.Arg(1).String(), separator, int(c.Arg(2).Number()), separator, c.Arg(3).String())
85110
}
86111
})
87112
}

proxy/proxy_test.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,26 @@ import (
2020
)
2121

2222
func TestProxyFactory_error(t *testing.T) {
23-
testProxyFactoryError(t, `custom_error('expect me')`, "expect me", false, 0)
24-
testProxyFactoryPostError(t, `custom_error('expect me')`, "expect me", false, 0)
23+
testProxyFactoryError(t, `custom_error('expect me')`, "expect me", "", false, 0)
24+
testProxyFactoryPostError(t, `custom_error('expect me')`, "expect me", "", false, 0)
2525
}
2626

2727
func TestProxyFactory_errorHTTP(t *testing.T) {
28-
testProxyFactoryError(t, `custom_error('expect me', 404)`, "expect me", true, 404)
29-
testProxyFactoryPostError(t, `custom_error('expect me', 404)`, "expect me", true, 404)
28+
testProxyFactoryError(t, `custom_error('expect me', 404)`, "expect me", "", true, 404)
29+
testProxyFactoryPostError(t, `custom_error('expect me', 404)`, "expect me", "", true, 404)
3030
}
3131

3232
func TestProxyFactory_errorHTTPJson(t *testing.T) {
33-
testProxyFactoryError(t, `custom_error('{"msg":"expect me"}', 404)`, `{"msg":"expect me"}`, true, 404)
34-
testProxyFactoryPostError(t, `custom_error('{"msg":"expect me"}', 404)`, `{"msg":"expect me"}`, true, 404)
33+
testProxyFactoryError(t, `custom_error('{"msg":"expect me"}', 404)`, `{"msg":"expect me"}`, "", true, 404)
34+
testProxyFactoryPostError(t, `custom_error('{"msg":"expect me"}', 404)`, `{"msg":"expect me"}`, "", true, 404)
3535
}
3636

37-
func testProxyFactoryError(t *testing.T, code, errMsg string, isHTTP bool, statusCode int) {
37+
func TestProxyFactory_errorHTTPWithContentType(t *testing.T) {
38+
testProxyFactoryError(t, `custom_error('{"msg":"expect me"}', 404, 'application/json')`, `{"msg":"expect me"}`, "application/json", true, 404)
39+
testProxyFactoryPostError(t, `custom_error('{"msg":"expect me"}', 404, 'application/json')`, `{"msg":"expect me"}`, "application/json", true, 404)
40+
}
41+
42+
func testProxyFactoryError(t *testing.T, code, errMsg, contentType string, isHTTP bool, statusCode int) {
3843
buff := bytes.NewBuffer(make([]byte, 1024))
3944
logger, err := logging.NewLogger("ERROR", buff, "pref")
4045
if err != nil {
@@ -99,6 +104,19 @@ func testProxyFactoryError(t *testing.T, code, errMsg string, isHTTP bool, statu
99104
t.Errorf("unexpected internal error: %v (%T)", err, err)
100105
return
101106
}
107+
case lua.ErrInternalHTTPWithContentType:
108+
if !isHTTP {
109+
t.Errorf("unexpected http error: %v (%T)", err, err)
110+
return
111+
}
112+
if sc := err.StatusCode(); sc != statusCode {
113+
t.Errorf("unexpected http status code: %d", sc)
114+
return
115+
}
116+
if ct := err.Encoding(); ct != contentType {
117+
t.Errorf("unexpected content type: %s", ct)
118+
return
119+
}
102120
default:
103121
t.Errorf("unexpected error: %v (%T)", err, err)
104122
return
@@ -110,7 +128,7 @@ func testProxyFactoryError(t *testing.T, code, errMsg string, isHTTP bool, statu
110128
}
111129
}
112130

113-
func testProxyFactoryPostError(t *testing.T, code, errMsg string, isHTTP bool, statusCode int) {
131+
func testProxyFactoryPostError(t *testing.T, code, errMsg, contentType string, isHTTP bool, statusCode int) {
114132
buff := bytes.NewBuffer(make([]byte, 1024))
115133
logger, err := logging.NewLogger("ERROR", buff, "pref")
116134
if err != nil {
@@ -173,6 +191,19 @@ func testProxyFactoryPostError(t *testing.T, code, errMsg string, isHTTP bool, s
173191
t.Errorf("unexpected internal error: %v (%T)", err, err)
174192
return
175193
}
194+
case lua.ErrInternalHTTPWithContentType:
195+
if !isHTTP {
196+
t.Errorf("unexpected http error: %v (%T)", err, err)
197+
return
198+
}
199+
if sc := err.StatusCode(); sc != statusCode {
200+
t.Errorf("unexpected http status code: %d", sc)
201+
return
202+
}
203+
if ct := err.Encoding(); ct != contentType {
204+
t.Errorf("unexpected content type: %s", ct)
205+
return
206+
}
176207
default:
177208
t.Errorf("unexpected error: %v (%T)", err, err)
178209
return

router/gin/lua.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ func HandlerFactory(l logging.Logger, next krakendgin.HandlerFactory) krakendgin
5858
if err := process(c, &cfg); err != nil {
5959
err = lua.ToError(err)
6060
if errhttp, ok := err.(errHTTP); ok {
61+
if e, ok := err.(errHTTPWithContentType); ok {
62+
c.Writer.Header().Add("content-type", e.Encoding())
63+
}
6164
c.AbortWithError(errhttp.StatusCode(), err)
6265
return
6366
}
@@ -75,6 +78,11 @@ type errHTTP interface {
7578
StatusCode() int
7679
}
7780

81+
type errHTTPWithContentType interface {
82+
errHTTP
83+
Encoding() string
84+
}
85+
7886
func process(c *gin.Context, cfg *lua.Config) error {
7987
b := binder.New(binder.Options{
8088
SkipOpenLibs: !cfg.AllowOpenLibs,

router/gin/lua_test.go

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestHandlerFactory(t *testing.T) {
7373
engine := gin.New()
7474
engine.GET("/some-path/:id", handler)
7575

76-
req, _ := http.NewRequest("GET", "/some-path/42?id=1", nil)
76+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
7777
req.Host = "domain.tld"
7878
req.Header.Set("Accept", "application/json")
7979
w := httptest.NewRecorder()
@@ -98,7 +98,7 @@ func TestHandlerFactory_error(t *testing.T) {
9898
}
9999

100100
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) gin.HandlerFunc {
101-
return func(c *gin.Context) {
101+
return func(_ *gin.Context) {
102102
t.Error("the handler shouldn't be executed")
103103
}
104104
}
@@ -107,7 +107,7 @@ func TestHandlerFactory_error(t *testing.T) {
107107
engine := gin.New()
108108
engine.GET("/some-path/:id", handler)
109109

110-
req, _ := http.NewRequest("GET", "/some-path/42?id=1", nil)
110+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
111111
req.Header.Set("Accept", "application/json")
112112
w := httptest.NewRecorder()
113113

@@ -131,7 +131,40 @@ func TestHandlerFactory_errorHTTP(t *testing.T) {
131131
}
132132

133133
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) gin.HandlerFunc {
134-
return func(c *gin.Context) {
134+
return func(_ *gin.Context) {
135+
t.Error("the handler shouldn't be executed")
136+
}
137+
}
138+
handler := HandlerFactory(logging.NoOp, hf)(cfg, proxy.NoopProxy)
139+
140+
engine := gin.New()
141+
engine.GET("/some-path/:id", handler)
142+
143+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
144+
req.Header.Set("Accept", "application/json")
145+
w := httptest.NewRecorder()
146+
147+
engine.ServeHTTP(w, req)
148+
149+
if w.Code != 999 {
150+
t.Errorf("unexpected status code %d", w.Code)
151+
return
152+
}
153+
}
154+
155+
func TestHandlerFactory_errorHTTPWithContentType(t *testing.T) {
156+
gin.SetMode(gin.TestMode)
157+
cfg := &config.EndpointConfig{
158+
Endpoint: "/",
159+
ExtraConfig: config.ExtraConfig{
160+
router.Namespace: map[string]interface{}{
161+
"pre": `custom_error('expect me', 999, 'foo/bar')`,
162+
},
163+
},
164+
}
165+
166+
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) gin.HandlerFunc {
167+
return func(_ *gin.Context) {
135168
t.Error("the handler shouldn't be executed")
136169
}
137170
}
@@ -140,7 +173,7 @@ func TestHandlerFactory_errorHTTP(t *testing.T) {
140173
engine := gin.New()
141174
engine.GET("/some-path/:id", handler)
142175

143-
req, _ := http.NewRequest("GET", "/some-path/42?id=1", nil)
176+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
144177
req.Header.Set("Accept", "application/json")
145178
w := httptest.NewRecorder()
146179

@@ -150,4 +183,9 @@ func TestHandlerFactory_errorHTTP(t *testing.T) {
150183
t.Errorf("unexpected status code %d", w.Code)
151184
return
152185
}
186+
187+
if h := w.Header().Get("content-type"); h != "foo/bar" {
188+
t.Errorf("unexpected content-type %s", h)
189+
return
190+
}
153191
}

router/mux/lua.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package mux
33
import (
44
"bytes"
55
"errors"
6+
"fmt"
67
"io"
78
"net/http"
89
"net/url"
@@ -66,7 +67,12 @@ func HandlerFactory(l logging.Logger, next mux.HandlerFactory, pe mux.ParamExtra
6667
if err := process(r, pe, &cfg); err != nil {
6768
err = lua.ToError(err)
6869
if errhttp, ok := err.(errHTTP); ok {
69-
http.Error(w, err.Error(), errhttp.StatusCode())
70+
if e, ok := err.(errHTTPWithContentType); ok {
71+
fmt.Println(e.Encoding())
72+
w.Header().Add("content-type", e.Encoding())
73+
}
74+
w.WriteHeader(errhttp.StatusCode())
75+
w.Write([]byte(err.Error()))
7076
return
7177
}
7278

@@ -84,6 +90,11 @@ type errHTTP interface {
8490
StatusCode() int
8591
}
8692

93+
type errHTTPWithContentType interface {
94+
errHTTP
95+
Encoding() string
96+
}
97+
8798
func process(r *http.Request, pe mux.ParamExtractor, cfg *lua.Config) error {
8899
b := binder.New(binder.Options{
89100
SkipOpenLibs: !cfg.AllowOpenLibs,

router/mux/lua_test.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestHandlerFactory(t *testing.T) {
3333
}
3434

3535
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) http.HandlerFunc {
36-
return func(w http.ResponseWriter, r *http.Request) {
36+
return func(_ http.ResponseWriter, r *http.Request) {
3737
if URL := r.URL.String(); URL != "/some-path/42?extra=foo&id=1&more=true" {
3838
t.Errorf("unexpected URL: %s", URL)
3939
}
@@ -66,7 +66,7 @@ func TestHandlerFactory(t *testing.T) {
6666
return map[string]string{}
6767
})(cfg, proxy.NoopProxy)
6868

69-
req, _ := http.NewRequest("GET", "/some-path/42?id=1", nil)
69+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
7070
req.Header.Set("Accept", "application/json")
7171
w := httptest.NewRecorder()
7272

@@ -89,15 +89,15 @@ func TestHandlerFactory_error(t *testing.T) {
8989
}
9090

9191
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) http.HandlerFunc {
92-
return func(w http.ResponseWriter, r *http.Request) {
92+
return func(_ http.ResponseWriter, _ *http.Request) {
9393
t.Error("the handler shouldn't be executed")
9494
}
9595
}
9696
handler := HandlerFactory(logging.NoOp, hf, func(_ *http.Request) map[string]string {
9797
return map[string]string{}
9898
})(cfg, proxy.NoopProxy)
9999

100-
req, _ := http.NewRequest("GET", "/some-path/42?id=1", nil)
100+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
101101
req.Header.Set("Accept", "application/json")
102102
w := httptest.NewRecorder()
103103

@@ -120,15 +120,15 @@ func TestHandlerFactory_errorHTTP(t *testing.T) {
120120
}
121121

122122
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) http.HandlerFunc {
123-
return func(w http.ResponseWriter, r *http.Request) {
123+
return func(_ http.ResponseWriter, _ *http.Request) {
124124
t.Error("the handler shouldn't be executed")
125125
}
126126
}
127127
handler := HandlerFactory(logging.NoOp, hf, func(_ *http.Request) map[string]string {
128128
return map[string]string{}
129129
})(cfg, proxy.NoopProxy)
130130

131-
req, _ := http.NewRequest("GET", "/some-path/42?id=1", nil)
131+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
132132
req.Header.Set("Accept", "application/json")
133133
w := httptest.NewRecorder()
134134

@@ -139,3 +139,39 @@ func TestHandlerFactory_errorHTTP(t *testing.T) {
139139
return
140140
}
141141
}
142+
143+
func TestHandlerFactory_errorHTTPWithContentType(t *testing.T) {
144+
cfg := &config.EndpointConfig{
145+
Endpoint: "/",
146+
ExtraConfig: config.ExtraConfig{
147+
router.Namespace: map[string]interface{}{
148+
"pre": `custom_error('expect me', 999, 'foo/bar')`,
149+
},
150+
},
151+
}
152+
153+
hf := func(_ *config.EndpointConfig, _ proxy.Proxy) http.HandlerFunc {
154+
return func(_ http.ResponseWriter, _ *http.Request) {
155+
t.Error("the handler shouldn't be executed")
156+
}
157+
}
158+
handler := HandlerFactory(logging.NoOp, hf, func(_ *http.Request) map[string]string {
159+
return map[string]string{}
160+
})(cfg, proxy.NoopProxy)
161+
162+
req, _ := http.NewRequest("GET", "/some-path/42?id=1", http.NoBody)
163+
req.Header.Set("Accept", "application/json")
164+
w := httptest.NewRecorder()
165+
166+
handler(w, req)
167+
168+
if w.Code != 999 {
169+
t.Errorf("unexpected status code %d", w.Code)
170+
return
171+
}
172+
173+
if h := w.Header().Get("content-type"); h != "foo/bar" {
174+
t.Errorf("unexpected content-type %s", h)
175+
return
176+
}
177+
}

0 commit comments

Comments
 (0)