Skip to content

Commit

Permalink
Adding headerList helper
Browse files Browse the repository at this point in the history
  • Loading branch information
thedae committed Dec 20, 2024
1 parent 18dfaf3 commit 6c699f8
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 1 deletion.
5 changes: 4 additions & 1 deletion proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,7 @@ func New(cfg lua.Config, next proxy.Proxy) proxy.Proxy {
}
}

var errNeedsArguments = errors.New("need arguments")
var (
errNeedsArguments = errors.New("need arguments")
errInvalidLuaList = errors.New("invalid header value, must be a luaList")
)
34 changes: 34 additions & 0 deletions proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ func TestProxyFactory_luaError(t *testing.T) {
},
ExpectedError: "attempt to index a non-table object(function) with key 'really_bad' (bad-code.lua:L5)",
},
{
Name: "Pre: Invalid headerList value",
Cfg: map[string]interface{}{
"pre": `local req = request.load(); req:headerList("X-Test", "string")`,
},
ExpectedError: "invalid header value, must be a luaList (pre-script:L1)",
},
{
Name: "Pre: Multiple sources, bad function call",
Cfg: map[string]interface{}{
Expand Down Expand Up @@ -200,6 +207,13 @@ func TestProxyFactory_luaError(t *testing.T) {
},
ExpectedError: "attempt to call a non-function object (bad-func.lua:L3)",
},
{
Name: "Post: Invalid headerList value",
Cfg: map[string]interface{}{
"post": `local res = response.load(); res:headerList("X-Test", "string")`,
},
ExpectedError: "invalid header value, must be a luaList (post-script:L1)",
},
}

for _, test := range luaErrorTestTable {
Expand Down Expand Up @@ -481,6 +495,7 @@ func TestProxyFactory(t *testing.T) { // skipcq: GO-R1005
Metadata: proxy.Metadata{
Headers: map[string][]string{
"X-Not-Needed": {"deleteme"},
"X-Multi-Res": {"X", "Y"},
},
},
Io: strings.NewReader("initial resp content"),
Expand All @@ -497,6 +512,9 @@ func TestProxyFactory(t *testing.T) { // skipcq: GO-R1005
if req.Headers["Accept"][0] != "application/xml" {
t.Errorf("unexpected header 'Accept' %v", req.Headers["Accept"])
}
if req.Headers["X-Multi"][0] != "A" || req.Headers["X-Multi"][1] != "B" {
t.Errorf("unexpected header 'X-Multi' %v", req.Headers["X-Multi"])
}
if _, found := req.Headers["X-To-Delete"]; found {
t.Error("unexpected header 'X-To-Delete', should have been deleted")
}
Expand Down Expand Up @@ -530,6 +548,12 @@ func TestProxyFactory(t *testing.T) { // skipcq: GO-R1005
req:params("foo", "some_new_value")
req:headers("Accept", "application/xml")
req:headers("X-To-Delete", nil)
local multi_header = luaList.new()
multi_header:set(0, "A")
multi_header:set(1, "B")
req:headerList("X-Multi", multi_header)
req:url(req:url() .. "&more=true")
req:body(req:body() .. " foo" .. req:headers("unknown"))`,

Expand Down Expand Up @@ -577,6 +601,12 @@ func TestProxyFactory(t *testing.T) { // skipcq: GO-R1005
resp:headers("Content-Type", "application/xml")
resp:headers("X-Not-Needed", nil)
local res_header = luaList.new()
res_header:set(0, "X")
res_header:set(1, "Y")
resp:headerList("X-Multi-Res", res_header)
resp:statusCode(200)
resp:body(resp:body() .. " bar" .. resp:headers("unknown"))`,
},
Expand Down Expand Up @@ -617,6 +647,10 @@ func TestProxyFactory(t *testing.T) { // skipcq: GO-R1005
t.Errorf("unexpected Content-Type %v", resp.Metadata.Headers["Content-Type"])
return
}
if resp.Metadata.Headers["X-Multi-Res"][0] != "X" || resp.Metadata.Headers["X-Multi-Res"][1] != "Y" {
t.Errorf("unexpected X-Multi-Res %v", resp.Metadata.Headers["X-Multi-Res"])
return
}
if v, ok := resp.Data["foo"].(string); !ok || v != "some_new_value" {
t.Errorf("unexpected response data %v, %T", resp.Data["foo"], resp.Data["foo"])
return
Expand Down
43 changes: 43 additions & 0 deletions proxy/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package proxy
import (
"bytes"
"errors"
"fmt"
"io"
"net/textproto"
"net/url"

"github.com/krakendio/binder"
lua "github.com/krakendio/krakend-lua/v2"
"github.com/luraproject/lura/v2/proxy"
glua "github.com/yuin/gopher-lua"
)
Expand All @@ -28,6 +30,7 @@ func registerRequestTable(req *proxy.Request, b *binder.Binder) {
t.Dynamic("url", r.url)
t.Dynamic("params", r.params)
t.Dynamic("headers", r.headers)
t.Dynamic("headerList", r.headerList)
t.Dynamic("body", r.body)
}

Expand Down Expand Up @@ -149,6 +152,46 @@ func (*ProxyRequest) headers(c *binder.Context) error {
return nil
}

func (*ProxyRequest) headerList(c *binder.Context) error {
req, ok := c.Arg(1).Data().(*ProxyRequest)
if !ok {
return errRequestExpected
}
switch c.Top() {
case 1:
return errNeedsArguments
case 2:
key := textproto.CanonicalMIMEHeaderKey(c.Arg(2).String())

headers := req.Headers[key]
d := make([]interface{}, len(headers))
for i := range headers {
d[i] = headers[i]
}
c.Push().Data(&lua.List{Data: d}, "luaList")
case 3:
key := textproto.CanonicalMIMEHeaderKey(c.Arg(2).String())

v, isUserData := c.Arg(3).Any().(*glua.LUserData)
if !isUserData {
return errInvalidLuaList
}

list, isList := v.Value.(*lua.List)
if !isList {
return errInvalidLuaList
}

d := make([]string, len(list.Data))
for i := range list.Data {
d[i] = fmt.Sprintf("%s", list.Data[i])
}
req.Headers[key] = d
}

return nil
}

func (*ProxyRequest) body(c *binder.Context) error {
req, ok := c.Arg(1).Data().(*ProxyRequest)
if !ok {
Expand Down
42 changes: 42 additions & 0 deletions proxy/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package proxy
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"

Expand Down Expand Up @@ -32,6 +33,7 @@ func registerResponseTable(resp *proxy.Response, b *binder.Binder) {
t.Dynamic("statusCode", r.statusCode)
t.Dynamic("data", r.data)
t.Dynamic("headers", r.headers)
t.Dynamic("headerList", r.headerList)
t.Dynamic("body", r.body)
}

Expand Down Expand Up @@ -100,6 +102,46 @@ func (*ProxyResponse) headers(c *binder.Context) error {
return nil
}

func (*ProxyResponse) headerList(c *binder.Context) error {
resp, ok := c.Arg(1).Data().(*ProxyResponse)
if !ok {
return errRequestExpected
}
switch c.Top() {
case 1:
return errNeedsArguments
case 2:
key := http.CanonicalHeaderKey(c.Arg(2).String())

headers := resp.Metadata.Headers[key]
d := make([]interface{}, len(headers))
for i := range headers {
d[i] = headers[i]
}
c.Push().Data(&lua.List{Data: d}, "luaList")
case 3:
key := http.CanonicalHeaderKey(c.Arg(2).String())

v, isUserData := c.Arg(3).Any().(*glua.LUserData)
if !isUserData {
return errInvalidLuaList
}

list, isList := v.Value.(*lua.List)
if !isList {
return errInvalidLuaList
}

d := make([]string, len(list.Data))
for i := range list.Data {
d[i] = fmt.Sprintf("%s", list.Data[i])
}
resp.Metadata.Headers[key] = d
}

return nil
}

func (*ProxyResponse) body(c *binder.Context) error {
resp, ok := c.Arg(1).Data().(*ProxyResponse)
if !ok {
Expand Down
47 changes: 47 additions & 0 deletions router/gin/lua.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package gin
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"net/textproto"
"net/url"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -92,6 +94,9 @@ func process(c *gin.Context, cfg *lua.Config) error {
defer b.GetBinder().Close()

decorator.RegisterErrors(b.GetBinder())
decorator.RegisterNil(b.GetBinder())
decorator.RegisterLuaTable(b.GetBinder())
decorator.RegisterLuaList(b.GetBinder())
registerCtxTable(c, b.GetBinder())

if err := b.WithConfig(cfg); err != nil {
Expand All @@ -117,6 +122,7 @@ func registerCtxTable(c *gin.Context, b *binder.Binder) {
t.Dynamic("query", r.query)
t.Dynamic("params", r.params)
t.Dynamic("headers", r.requestHeaders)
t.Dynamic("headerList", r.headerList)
t.Dynamic("body", r.requestBody)
}

Expand Down Expand Up @@ -235,6 +241,46 @@ func (*ginContext) requestHeaders(c *binder.Context) error {
return nil
}

func (*ginContext) headerList(c *binder.Context) error {
req, ok := c.Arg(1).Data().(*ginContext)
if !ok {
return errContextExpected
}
switch c.Top() {
case 1:
return errNeedsArguments
case 2:
key := textproto.CanonicalMIMEHeaderKey(c.Arg(2).String())

headers := req.Request.Header.Values(key)
d := make([]interface{}, len(headers))
for i := range headers {
d[i] = headers[i]
}
c.Push().Data(&lua.List{Data: d}, "luaList")
case 3:
key := textproto.CanonicalMIMEHeaderKey(c.Arg(2).String())

v, isUserData := c.Arg(3).Any().(*glua.LUserData)
if !isUserData {
return errInvalidLuaList
}

list, isList := v.Value.(*lua.List)
if !isList {
return errInvalidLuaList
}

d := make([]string, len(list.Data))
for i := range list.Data {
d[i] = fmt.Sprintf("%s", list.Data[i])
}
req.Request.Header[key] = d
}

return nil
}

func (*ginContext) requestBody(c *binder.Context) error {
req, ok := c.Arg(1).Data().(*ginContext)
if !ok {
Expand All @@ -260,4 +306,5 @@ func (*ginContext) requestBody(c *binder.Context) error {
var (
errNeedsArguments = errors.New("need arguments")
errContextExpected = errors.New("ginContext expected")
errInvalidLuaList = errors.New("invalid header value, must be a luaList")
)
7 changes: 7 additions & 0 deletions router/gin/lua_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func TestHandlerFactory(t *testing.T) {
req:headers("Accept", "application/xml")
req:headers("X-To-Delete", nil)
req:headers("X-TO-DELETE-LOWER", nil)
local multi = luaList.new()
multi:set(0, "A")
multi:set(1, "B")
req:headerList("X-Multi", multi)
req:url(req:url() .. "&more=true")
req:host(req:host() .. ".newtld")
req:query("extra", "foo")
Expand All @@ -48,6 +52,9 @@ func TestHandlerFactory(t *testing.T) {
if accept := c.Request.Header.Get("Accept"); accept != "application/xml" {
t.Errorf("unexpected accept header: %s", accept)
}
if multi := c.Request.Header.Values("X-Multi"); multi[0] != "A" || multi[1] != "B" {
t.Errorf("unexpected X-Multi header: %v", multi)
}
if toDelete := c.Request.Header.Get("X-To-Delete"); len(toDelete) > 0 {
t.Error("unexpected header 'X-To-Delete', should have been deleted")
}
Expand Down
Loading

0 comments on commit 6c699f8

Please sign in to comment.