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 d8647f9bf36785c976fd0400abab488ead73212d Author: spacewander <[email protected]> AuthorDate: Tue May 25 14:58:51 2021 +0800 feat: extend pkg Request --- internal/http/request.go | 54 +++++++++++++++++++- internal/http/request_test.go | 113 ++++++++++++++++++++++++++++++++++++++++++ internal/plugin/plugin.go | 2 +- pkg/http/http.go | 16 +++++- 4 files changed, 180 insertions(+), 5 deletions(-) diff --git a/internal/http/request.go b/internal/http/request.go index 596b0c4..570c245 100644 --- a/internal/http/request.go +++ b/internal/http/request.go @@ -15,22 +15,72 @@ package http import ( + "net" + hrc "github.com/api7/ext-plugin-proto/go/A6/HTTPReqCall" + flatbuffers "github.com/google/flatbuffers/go" ) type Request struct { // the root of the flatbuffers HTTPReqCall Request msg r *hrc.Req + + path []byte } -func (r Request) ConfToken() uint32 { +func (r *Request) ConfToken() uint32 { return r.r.ConfToken() } -func (r Request) Id() uint32 { +func (r *Request) ID() uint32 { return r.r.Id() } +func (r *Request) SrcIP() net.IP { + return r.r.SrcIpBytes() +} + +func (r *Request) Method() string { + return r.r.Method().String() +} + +func (r *Request) Path() []byte { + if r.path == nil { + return r.r.Path() + } + return r.path +} + +func (r *Request) SetPath(path []byte) { + r.path = path +} + +func (r *Request) FetchChanges(id uint32, builder *flatbuffers.Builder) bool { + if r.path == nil { + return false + } + + var path flatbuffers.UOffsetT + if r.path != nil { + path = builder.CreateByteString(r.path) + } + + hrc.RewriteStart(builder) + if path > 0 { + hrc.RewriteAddPath(builder, path) + } + rewrite := hrc.RewriteEnd(builder) + + hrc.RespStart(builder) + hrc.RespAddId(builder, id) + hrc.RespAddActionType(builder, hrc.ActionRewrite) + hrc.RespAddAction(builder, rewrite) + res := hrc.RespEnd(builder) + builder.Finish(res) + + return true +} + func CreateRequest(buf []byte) *Request { req := &Request{ r: hrc.GetRootAsReq(buf, 0), diff --git a/internal/http/request_test.go b/internal/http/request_test.go new file mode 100644 index 0000000..667138e --- /dev/null +++ b/internal/http/request_test.go @@ -0,0 +1,113 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package http + +import ( + "net" + "testing" + + "github.com/apache/apisix-go-plugin-runner/internal/util" + "github.com/api7/ext-plugin-proto/go/A6" + hrc "github.com/api7/ext-plugin-proto/go/A6/HTTPReqCall" + flatbuffers "github.com/google/flatbuffers/go" + "github.com/stretchr/testify/assert" +) + +func getRewriteAction(t *testing.T, b *flatbuffers.Builder) *hrc.Rewrite { + buf := b.FinishedBytes() + res := hrc.GetRootAsResp(buf, 0) + tab := &flatbuffers.Table{} + if res.Action(tab) { + assert.Equal(t, hrc.ActionRewrite, res.ActionType()) + rewrite := &hrc.Rewrite{} + rewrite.Init(tab.Bytes, tab.Pos) + return rewrite + } + return nil +} + +type reqOpt struct { + srcIP []byte + method A6.Method + path string +} + +func buildReq(opt reqOpt) []byte { + builder := flatbuffers.NewBuilder(1024) + + var ip flatbuffers.UOffsetT + if len(opt.srcIP) > 0 { + ip = builder.CreateByteVector(opt.srcIP) + } + + var path flatbuffers.UOffsetT + if opt.path != "" { + path = builder.CreateString(opt.path) + } + + hrc.ReqStart(builder) + hrc.ReqAddId(builder, 233) + hrc.ReqAddConfToken(builder, 1) + if ip > 0 { + hrc.ReqAddSrcIp(builder, ip) + } + if opt.method != 0 { + hrc.ReqAddMethod(builder, opt.method) + } + if path > 0 { + hrc.ReqAddPath(builder, path) + } + r := hrc.ReqEnd(builder) + builder.Finish(r) + return builder.FinishedBytes() +} + +func TestSrcIp(t *testing.T) { + for _, ip := range []net.IP{ + net.IPv4(127, 0, 0, 1), + net.IPv4(127, 2, 3, 1), + net.ParseIP("2001:db8::68"), + net.ParseIP("::12"), + } { + out := buildReq(reqOpt{srcIP: ip}) + r := CreateRequest(out) + assert.Equal(t, ip, r.SrcIP()) + } +} + +func TestMethod(t *testing.T) { + for _, m := range []A6.Method{ + A6.MethodGET, + A6.MethodPATCH, + } { + out := buildReq(reqOpt{method: m}) + r := CreateRequest(out) + assert.Equal(t, m.String(), r.Method()) + } +} + +func TestPath(t *testing.T) { + out := buildReq(reqOpt{path: "/apisix"}) + r := CreateRequest(out) + assert.Equal(t, "/apisix", string(r.Path())) + + r.SetPath([]byte("/go")) + assert.Equal(t, "/go", string(r.Path())) + + builder := util.GetBuilder() + assert.True(t, r.FetchChanges(1, builder)) + rewrite := getRewriteAction(t, builder) + assert.Equal(t, "/go", string(rewrite.Path())) +} diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go index e4d9ef6..3ca4159 100644 --- a/internal/plugin/plugin.go +++ b/internal/plugin/plugin.go @@ -57,7 +57,7 @@ func HTTPReqCall(buf []byte) (*flatbuffers.Builder, error) { return nil, err } - id := req.Id() + id := req.ID() builder := reportAction(id, req, resp) return builder, nil } diff --git a/pkg/http/http.go b/pkg/http/http.go index de83956..ebae8ac 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -14,6 +14,8 @@ // limitations under the License. package http +import "net" + // Request represents the HTTP request received by APISIX. // We don't use net/http's Request because it doesn't suit our purpose. // Take `Request.Header` as an example: @@ -24,6 +26,16 @@ package http // So the server must parse all the headers, ...". The official API is suboptimal, which // is even worse in our case as it is not a real HTTP server. type Request interface { - // Id returns the request id - Id() uint32 + // ID returns the request id + ID() uint32 + // SrcIP returns the client's IP + SrcIP() net.IP + // Method returns the HTTP method (GET, POST, PUT, etc.) + Method() string + // Path returns the path part of the client's URI (without query string and the other parts) + // It won't be equal to the one in the Request-Line sent by the client if it has + // been rewritten by APISIX + Path() []byte + // SetPath is the setter for Path + SetPath([]byte) }
