You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2021/05/31 02:07:23 UTC
[apisix-go-plugin-runner] 16/22: feat: add Request.Header
This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-go-plugin-runner.git
commit 65cbe80b2aba18644d8e485dc2f3a4f7814af36b
Author: spacewander <sp...@gmail.com>
AuthorDate: Wed May 26 10:41:05 2021 +0800
feat: add Request.Header
---
internal/http/request.go | 78 ++++++++++++++++++++++++++++++++++++++++-
internal/http/request_test.go | 81 +++++++++++++++++++++++++++++++++++++++++--
pkg/http/http.go | 26 +++++++++++++-
3 files changed, 180 insertions(+), 5 deletions(-)
diff --git a/internal/http/request.go b/internal/http/request.go
index 570c245..11c9ef4 100644
--- a/internal/http/request.go
+++ b/internal/http/request.go
@@ -16,7 +16,10 @@ package http
import (
"net"
+ "net/http"
+ pkgHTTP "github.com/apache/apisix-go-plugin-runner/pkg/http"
+ "github.com/api7/ext-plugin-proto/go/A6"
hrc "github.com/api7/ext-plugin-proto/go/A6/HTTPReqCall"
flatbuffers "github.com/google/flatbuffers/go"
)
@@ -26,6 +29,9 @@ type Request struct {
r *hrc.Req
path []byte
+
+ hdr *Header
+ rawHdr http.Header
}
func (r *Request) ConfToken() uint32 {
@@ -55,8 +61,25 @@ func (r *Request) SetPath(path []byte) {
r.path = path
}
+func (r *Request) Header() pkgHTTP.Header {
+ if r.hdr == nil {
+ hdr := newHeader()
+ hh := hdr.View()
+ size := r.r.HeadersLength()
+ obj := A6.TextEntry{}
+ for i := 0; i < size; i++ {
+ if r.r.Headers(&obj, i) {
+ hh.Add(string(obj.Name()), string(obj.Value()))
+ }
+ }
+ r.hdr = hdr
+ r.rawHdr = hdr.Clone()
+ }
+ return r.hdr
+}
+
func (r *Request) FetchChanges(id uint32, builder *flatbuffers.Builder) bool {
- if r.path == nil {
+ if r.path == nil && r.hdr == nil {
return false
}
@@ -65,10 +88,49 @@ func (r *Request) FetchChanges(id uint32, builder *flatbuffers.Builder) bool {
path = builder.CreateByteString(r.path)
}
+ var hdrVec flatbuffers.UOffsetT
+ if r.hdr != nil {
+ hdrs := []flatbuffers.UOffsetT{}
+ oldHdr := r.rawHdr
+ newHdr := r.hdr.View()
+ for n := range oldHdr {
+ if _, ok := newHdr[n]; !ok {
+ // deleted
+ name := builder.CreateString(n)
+ A6.TextEntryStart(builder)
+ A6.TextEntryAddName(builder, name)
+ te := A6.TextEntryEnd(builder)
+ hdrs = append(hdrs, te)
+ }
+ }
+ for n, v := range newHdr {
+ if raw, ok := oldHdr[n]; !ok || raw[0] != v[0] {
+ // set
+ name := builder.CreateString(n)
+ value := builder.CreateString(v[0])
+ A6.TextEntryStart(builder)
+ A6.TextEntryAddName(builder, name)
+ A6.TextEntryAddValue(builder, value)
+ te := A6.TextEntryEnd(builder)
+ hdrs = append(hdrs, te)
+ }
+ }
+ size := len(hdrs)
+ hrc.RewriteStartHeadersVector(builder, size)
+ for i := size - 1; i >= 0; i-- {
+ te := hdrs[i]
+ builder.PrependUOffsetT(te)
+ }
+ hdrVec = builder.EndVector(size)
+ }
+
hrc.RewriteStart(builder)
if path > 0 {
hrc.RewriteAddPath(builder, path)
}
+ if hdrVec > 0 {
+ hrc.RewriteAddHeaders(builder, hdrVec)
+ }
rewrite := hrc.RewriteEnd(builder)
hrc.RespStart(builder)
@@ -87,3 +149,17 @@ func CreateRequest(buf []byte) *Request {
}
return req
}
+
+type Header struct {
+ http.Header
+}
+
+func newHeader() *Header {
+ return &Header{
+ Header: http.Header{},
+ }
+}
+
+func (h *Header) View() http.Header {
+ return h.Header
+}
diff --git a/internal/http/request_test.go b/internal/http/request_test.go
index 667138e..54fed1e 100644
--- a/internal/http/request_test.go
+++ b/internal/http/request_test.go
@@ -16,6 +16,7 @@ package http
import (
"net"
+ "net/http"
"testing"
"github.com/apache/apisix-go-plugin-runner/internal/util"
@@ -38,10 +39,16 @@ func getRewriteAction(t *testing.T, b *flatbuffers.Builder) *hrc.Rewrite {
return nil
}
+type pair struct {
+ name string
+ value string
+}
+
type reqOpt struct {
- srcIP []byte
- method A6.Method
- path string
+ srcIP []byte
+ method A6.Method
+ path string
+ headers []pair
}
func buildReq(opt reqOpt) []byte {
@@ -57,6 +64,28 @@ func buildReq(opt reqOpt) []byte {
path = builder.CreateString(opt.path)
}
+ hdrLen := len(opt.headers)
+ var hdrVec flatbuffers.UOffsetT
+ if hdrLen > 0 {
+ hdrs := []flatbuffers.UOffsetT{}
+ for _, v := range opt.headers {
+ name := builder.CreateString(v.name)
+ value := builder.CreateString(v.value)
+ A6.TextEntryStart(builder)
+ A6.TextEntryAddName(builder, name)
+ A6.TextEntryAddValue(builder, value)
+ te := A6.TextEntryEnd(builder)
+ hdrs = append(hdrs, te)
+ }
+ size := len(hdrs)
+ hrc.StopStartHeadersVector(builder, size)
+ for i := size - 1; i >= 0; i-- {
+ te := hdrs[i]
+ builder.PrependUOffsetT(te)
+ }
+ hdrVec = builder.EndVector(size)
+ }
+
hrc.ReqStart(builder)
hrc.ReqAddId(builder, 233)
hrc.ReqAddConfToken(builder, 1)
@@ -69,6 +98,9 @@ func buildReq(opt reqOpt) []byte {
if path > 0 {
hrc.ReqAddPath(builder, path)
}
+ if hdrVec > 0 {
+ hrc.ReqAddHeaders(builder, hdrVec)
+ }
r := hrc.ReqEnd(builder)
builder.Finish(r)
return builder.FinishedBytes()
@@ -111,3 +143,46 @@ func TestPath(t *testing.T) {
rewrite := getRewriteAction(t, builder)
assert.Equal(t, "/go", string(rewrite.Path()))
}
+
+func TestHeader(t *testing.T) {
+ out := buildReq(reqOpt{headers: []pair{
+ {"k", "v"},
+ {"cache-control", "no-cache"},
+ {"cache-control", "no-store"},
+ {"cat", "dog"},
+ }})
+ r := CreateRequest(out)
+ hdr := r.Header()
+ assert.Equal(t, "v", hdr.Get("k"))
+ assert.Equal(t, "no-cache", hdr.Get("Cache-Control"))
+ assert.Equal(t, "no-cache", hdr.Get("cache-control"))
+
+ hdr.Del("empty")
+ hdr.Del("k")
+ assert.Equal(t, "", hdr.Get("k"))
+
+ hdr.Set("cache-control", "max-age=10s")
+ assert.Equal(t, "max-age=10s", hdr.Get("Cache-Control"))
+ hdr.Del("cache-Control")
+ assert.Equal(t, "", hdr.Get("cache-control"))
+
+ hdr.Set("k", "v2")
+ hdr.Del("cat")
+
+ builder := util.GetBuilder()
+ assert.True(t, r.FetchChanges(1, builder))
+ rewrite := getRewriteAction(t, builder)
+ assert.Equal(t, 3, rewrite.HeadersLength())
+
+ exp := http.Header{}
+ exp.Set("Cache-Control", "")
+ exp.Set("cat", "")
+ exp.Set("k", "v2")
+ res := http.Header{}
+ for i := 0; i < rewrite.HeadersLength(); i++ {
+ e := &A6.TextEntry{}
+ rewrite.Headers(e, i)
+ res.Add(string(e.Name()), string(e.Value()))
+ }
+ assert.Equal(t, exp, res)
+}
diff --git a/pkg/http/http.go b/pkg/http/http.go
index ebae8ac..cbbceeb 100644
--- a/pkg/http/http.go
+++ b/pkg/http/http.go
@@ -14,7 +14,10 @@
// limitations under the License.
package http
-import "net"
+import (
+ "net"
+ "net/http"
+)
// Request represents the HTTP request received by APISIX.
// We don't use net/http's Request because it doesn't suit our purpose.
@@ -38,4 +41,25 @@ type Request interface {
Path() []byte
// SetPath is the setter for Path
SetPath([]byte)
+ // Header returns the HTTP headers
+ Header() Header
+}
+
+// Header is like http.Header, but only implements the subset of its methods
+type Header interface {
+ // Set sets the header entries associated with key to the single element value.
+ // It replaces any existing values associated with key.
+ // The key is case insensitive
+ Set(key, value string)
+ // Del deletes the values associated with key. The key is case insensitive
+ Del(key string)
+ // Get gets the first value associated with the given key.
+ // If there are no values associated with the key, Get returns "".
+ // It is case insensitive
+ Get(key string) string
+ // View returns the internal structure. It is expected for read operations. Any write operation
+ // won't be recorded
+ View() http.Header
+
+ // TODO: support Add
}