diff --git a/config_test.go b/config_test.go index 35fd37f..1a2b3a0 100644 --- a/config_test.go +++ b/config_test.go @@ -1,7 +1,6 @@ package lua import ( - "io/ioutil" "os" "testing" @@ -30,7 +29,7 @@ func TestParse(t *testing.T) { t.Errorf("unexpected error: %v", err) return } - b, _ := ioutil.ReadFile(source) + b, _ := os.ReadFile(source) if src, ok := cfg.Get(source); !ok || src != string(b) { t.Errorf("wrong content %s", string(b)) } @@ -46,7 +45,7 @@ func TestParse(t *testing.T) { } func TestParse_live(t *testing.T) { - tmpfile, err := ioutil.TempFile("", "test_parse_live") + tmpfile, err := os.CreateTemp("", "test_parse_live") if err != nil { t.Error(err) return @@ -85,7 +84,7 @@ func TestParse_live(t *testing.T) { t.Errorf("wrong content %s", src) } - if err := ioutil.WriteFile(source, []byte(finalContent), 0644); err != nil { + if err := os.WriteFile(source, []byte(finalContent), 0644); err != nil { t.Error(err) return } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index ea3a54a..0b7fa7d 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -16,6 +16,7 @@ import ( "github.com/luraproject/lura/v2/encoding" "github.com/luraproject/lura/v2/logging" "github.com/luraproject/lura/v2/proxy" + "github.com/luraproject/lura/v2/transport/http/client" ) func TestProxyFactory_error(t *testing.T) { @@ -219,7 +220,7 @@ func TestProxyFactory(t *testing.T) { } dummyProxyFactory := proxy.FactoryFunc(func(_ *config.EndpointConfig) (proxy.Proxy, error) { - return func(ctx context.Context, req *proxy.Request) (*proxy.Response, error) { + return func(_ context.Context, req *proxy.Request) (*proxy.Response, error) { if req.Method != "POST" { t.Errorf("unexpected method %s", req.Method) } @@ -415,7 +416,7 @@ func Test_Issue7(t *testing.T) { json.Unmarshal([]byte(response), &r) dummyProxyFactory := proxy.FactoryFunc(func(_ *config.EndpointConfig) (proxy.Proxy, error) { - return func(ctx context.Context, req *proxy.Request) (*proxy.Response, error) { + return func(_ context.Context, _ *proxy.Request) (*proxy.Response, error) { return &proxy.Response{ Data: r, Metadata: proxy.Metadata{ @@ -487,7 +488,7 @@ func Test_jsonNumber(t *testing.T) { encoding.JSONDecoder(strings.NewReader(response), &r) dummyProxyFactory := proxy.FactoryFunc(func(_ *config.EndpointConfig) (proxy.Proxy, error) { - return func(ctx context.Context, req *proxy.Request) (*proxy.Response, error) { + return func(_ context.Context, _ *proxy.Request) (*proxy.Response, error) { return &proxy.Response{ Data: r, Metadata: proxy.Metadata{ @@ -563,7 +564,7 @@ func Test_keyValConverter(t *testing.T) { } dummyProxyFactory := proxy.FactoryFunc(func(_ *config.EndpointConfig) (proxy.Proxy, error) { - return func(ctx context.Context, req *proxy.Request) (*proxy.Response, error) { + return func(_ context.Context, _ *proxy.Request) (*proxy.Response, error) { return &proxy.Response{ Data: r, Metadata: proxy.Metadata{ @@ -643,7 +644,7 @@ func Test_listGrowsWhenUpperIndexOutOfBound(t *testing.T) { r := map[string]interface{}{} dummyProxyFactory := proxy.FactoryFunc(func(_ *config.EndpointConfig) (proxy.Proxy, error) { - return func(ctx context.Context, req *proxy.Request) (*proxy.Response, error) { + return func(_ context.Context, _ *proxy.Request) (*proxy.Response, error) { return &proxy.Response{ Data: r, Metadata: proxy.Metadata{ @@ -713,3 +714,125 @@ responseData:set("grow_list", growingList) return } } + +func Test_tableGetSupportsClientErrors(t *testing.T) { + errA := client.HTTPResponseError{ + Code: 418, + Msg: "I'm a teapot", + Enc: "text/plain", + } + errB := client.NamedHTTPResponseError{ + HTTPResponseError: client.HTTPResponseError{ + Code: 481, + Msg: "I'm not a teapot", + Enc: "text/plain", + }, + } + + dummyProxyFactory := proxy.FactoryFunc(func(_ *config.EndpointConfig) (proxy.Proxy, error) { + return func(_ context.Context, _ *proxy.Request) (*proxy.Response, error) { + return &proxy.Response{ + Data: map[string]interface{}{ + "error_backend_alias_a": errA, + "error_backend_alias_b": errB, + }, + Metadata: proxy.Metadata{ + Headers: map[string][]string{}, + }, + }, nil + }, nil + }) + + prxy, err := ProxyFactory(logging.NoOp, dummyProxyFactory).New(&config.EndpointConfig{ + Endpoint: "/", + ExtraConfig: config.ExtraConfig{ + ProxyNamespace: map[string]interface{}{ + "post": ` +local resp = response.load() +local responseData = resp:data() +local errorA = responseData:get('error_backend_alias_a') +responseData:set("code_a", errorA:get('http_status_code')) +responseData:set("body_a", errorA:get('http_body')) +responseData:set("encoding_a", errorA:get('http_body_encoding')) +local errorB = responseData:get('error_backend_alias_b') +responseData:set("code_b", errorB:get('http_status_code')) +responseData:set("body_b", errorB:get('http_body')) +responseData:set("encoding_b", errorB:get('http_body_encoding')) +responseData:set("name_b", errorB:get('name')) +`, + }, + }, + }) + + if err != nil { + t.Error(err) + } + + URL, _ := url.Parse("https://some.host.tld/path/to/resource?and=querystring") + + resp, err := prxy(context.Background(), &proxy.Request{ + Method: "GET", + Path: "/some-path", + Params: map[string]string{"Id": "42"}, + Headers: map[string][]string{}, + URL: URL, + Body: io.NopCloser(strings.NewReader("initial req content")), + }) + if err != nil { + t.Error(err) + } + + bodyA, ok := resp.Data["body_a"].(string) + if !ok { + t.Errorf("cannot get 'body_a' %#v", resp.Data) + return + } + if bodyA != errA.Msg { + t.Errorf("unexpected body a. have %s, want %s", bodyA, errA.Msg) + } + + bodyB, ok := resp.Data["body_b"].(string) + if !ok { + t.Errorf("cannot get 'body_b' %#v", resp.Data) + return + } + if bodyB != errB.Msg { + t.Errorf("unexpected body b. have %s, want %s", bodyB, errB.Msg) + } + + codeA, ok := resp.Data["code_a"].(float64) + if !ok { + t.Errorf("cannot get 'code_a' %#v", resp.Data) + return + } + if int(codeA) != errA.Code { + t.Errorf("unexpected code a. have %d, want %d", int(codeA), errA.Code) + } + + codeB, ok := resp.Data["code_b"].(float64) + if !ok { + t.Errorf("cannot get 'code_b' %#v", resp.Data) + return + } + if int(codeB) != errB.Code { + t.Errorf("unexpected code b. have %d, want %d", int(codeB), errB.Code) + } + + encA, ok := resp.Data["encoding_a"].(string) + if !ok { + t.Errorf("cannot get 'encoding_a' %#v", resp.Data) + return + } + if encA != errA.Enc { + t.Errorf("unexpected encoding a. have %s, want %s", encA, errA.Enc) + } + + encB, ok := resp.Data["encoding_b"].(string) + if !ok { + t.Errorf("cannot get 'encoding_b' %#v", resp.Data) + return + } + if encB != errB.Enc { + t.Errorf("unexpected encoding b. have %s, want %s", encB, errB.Enc) + } +} diff --git a/proxy/response.go b/proxy/response.go index 1f65267..740a494 100644 --- a/proxy/response.go +++ b/proxy/response.go @@ -10,6 +10,7 @@ import ( "github.com/krakendio/binder" "github.com/luraproject/lura/v2/proxy" + "github.com/luraproject/lura/v2/transport/http/client" lua "github.com/yuin/gopher-lua" ) @@ -215,6 +216,12 @@ func tableGet(c *binder.Context) error { c.Push().Data(&luaList{data: t}, "luaList") case map[string]interface{}: c.Push().Data(&luaTable{data: t}, "luaTable") + case client.HTTPResponseError: + c.Push().Data(&luaTable{data: clientErrorToMap(t)}, "luaTable") + case client.NamedHTTPResponseError: + d := clientErrorToMap(t.HTTPResponseError) + d["name"] = t.Name() + c.Push().Data(&luaTable{data: d}, "luaTable") default: return fmt.Errorf("unknown type (%T) %v", t, t) } @@ -388,26 +395,10 @@ type luaTable struct { data map[string]interface{} } -func (l *luaTable) get(k string) interface{} { - return l.data[k] -} - -func (l *luaTable) set(k string, v interface{}) { - l.data[k] = v -} - type luaList struct { data []interface{} } -func (l *luaList) get(k int) interface{} { - return l.data[k] -} - -func (l *luaList) set(k int, v interface{}) { - l.data[k] = v -} - func parseToTable(k, v lua.LValue, acc map[string]interface{}) { switch v.Type() { @@ -442,3 +433,11 @@ func parseToTable(k, v lua.LValue, acc map[string]interface{}) { acc[k.String()] = res } } + +func clientErrorToMap(err client.HTTPResponseError) map[string]interface{} { + return map[string]interface{}{ + "http_status_code": err.StatusCode(), + "http_body": err.Error(), + "http_body_encoding": err.Encoding(), + } +} diff --git a/router/gin/lua_test.go b/router/gin/lua_test.go index c7d2ea3..50a3866 100644 --- a/router/gin/lua_test.go +++ b/router/gin/lua_test.go @@ -1,7 +1,7 @@ package gin import ( - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -58,7 +58,7 @@ func TestHandlerFactory(t *testing.T) { if e := c.Query("extra"); e != "foo" { t.Errorf("unexpected querystring extra: '%s'", e) } - b, err := ioutil.ReadAll(c.Request.Body) + b, err := io.ReadAll(c.Request.Body) if err != nil { t.Error(err) return diff --git a/router/mux/lua_test.go b/router/mux/lua_test.go index 84b45b3..c76665b 100644 --- a/router/mux/lua_test.go +++ b/router/mux/lua_test.go @@ -1,7 +1,7 @@ package mux import ( - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -52,7 +52,7 @@ func TestHandlerFactory(t *testing.T) { // if id := c.Param("id"); id != "42" { // t.Errorf("unexpected param id: %s", id) // } - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Error(err) return