From c8946e5e7606a30ec39a5b9a2ee1c2806ec0d19d Mon Sep 17 00:00:00 2001 From: Peter Grlica Date: Wed, 16 Feb 2022 11:41:15 +0100 Subject: [PATCH] Fix BufferedReader, updated tests --- automation/automation/apigw_body_handler.go | 6 -- pkg/apigw/filter/processer_test.go | 7 +- pkg/expr/expr_types.go | 4 + pkg/http/request.go | 83 ++++++++++++++------- pkg/http/request_test.go | 19 +++-- 5 files changed, 75 insertions(+), 44 deletions(-) diff --git a/automation/automation/apigw_body_handler.go b/automation/automation/apigw_body_handler.go index c06f10b43..0a59ed5ac 100644 --- a/automation/automation/apigw_body_handler.go +++ b/automation/automation/apigw_body_handler.go @@ -2,7 +2,6 @@ package automation import ( "context" - "fmt" "io" ) @@ -24,11 +23,6 @@ func ApigwBodyHandler(reg queueHandlerRegistry) *apigwBodyHandler { func (h apigwBodyHandler) read(ctx context.Context, args *apigwBodyReadArgs) (res *apigwBodyReadResults, err error) { res = &apigwBodyReadResults{} - if !args.hasRequest { - err = fmt.Errorf("could not read body, contents missing") - return - } - bb, err := io.ReadAll(args.Request.Body) if err != nil { diff --git a/pkg/apigw/filter/processer_test.go b/pkg/apigw/filter/processer_test.go index fed3dfd29..1a6f6ef95 100644 --- a/pkg/apigw/filter/processer_test.go +++ b/pkg/apigw/filter/processer_test.go @@ -139,16 +139,17 @@ func Test_processerPayload(t *testing.T) { }, exp: "{\"count\":2,\"results\":[{\"fullname\":\"Johnny Mnemonic\"},{\"fullname\":\"Johnny Knoxville\"}]}\n", params: prepareFuncPayload(t, ` - const b = JSON.parse(readRequestBody(input.Get('request'))); + const readOnce = JSON.parse(readRequestBody(input.Get('request'))); + const readTwice = JSON.parse(readRequestBody(input.Get('request'))); return { "results": - b.map(function({ name, surname }) { + readTwice.map(function({ name, surname }) { return { "fullname": name[0].toUpperCase() + name.substring(1) + " " + surname[0].toUpperCase() + surname.substring(1) } }), - "count": b.length + "count": readTwice.length }; `), }, diff --git a/pkg/expr/expr_types.go b/pkg/expr/expr_types.go index 8802def14..7d9e939dc 100644 --- a/pkg/expr/expr_types.go +++ b/pkg/expr/expr_types.go @@ -676,6 +676,10 @@ func CastToHttpRequest(val interface{}) (out *h.Request, err error) { func CastToUrl(val interface{}) (out *url.URL, err error) { switch val := UntypedValue(val).(type) { + case []byte: + return url.Parse(string(val)) + case string: + return url.Parse(val) case *url.URL: return val, nil case nil: diff --git a/pkg/http/request.go b/pkg/http/request.go index 37cdf6d59..f4cfbebae 100644 --- a/pkg/http/request.go +++ b/pkg/http/request.go @@ -1,11 +1,14 @@ package http import ( + "bytes" "encoding/json" "io" "mime/multipart" "net/http" "net/url" + + "github.com/pkg/errors" ) type ( @@ -14,24 +17,16 @@ type ( Body io.Reader } + // The BufferedReader behaves exactly like a bytes.Reader, with the exception + // when the last block is read, it automatically rewinds the internal pointer to the start, + // so effectively, the content can be read again without calling Seek() externally. BufferedReader struct { - buffer []byte + s []byte + i int64 // current reading index + prevRune int // index of previous rune; or < 0 } ) -// NewBufferedReader creates a new reader from readcloser -func NewBufferedReader(r io.ReadCloser) (b *BufferedReader, err error) { - var bb []byte - - if bb, err = io.ReadAll(r); err != nil { - return - } - - b = &BufferedReader{bb} - return -} - -// NewRequest creates a new Request with the buffered ready body func NewRequest(r *http.Request) (rr *Request, err error) { rs, err := NewBufferedReader(r.Body) @@ -43,27 +38,61 @@ func NewRequest(r *http.Request) (rr *Request, err error) { return } -func (bb *BufferedReader) Read(p []byte) (n int, err error) { - if len(bb.buffer) <= n { - err = io.EOF +// NewBufferedReader copies original data to the +// BufferedReader +func NewBufferedReader(rr io.Reader) (bb *BufferedReader, err error) { + var ( + buf = &bytes.Buffer{} + ) + + bb = &BufferedReader{} + + _, err = io.Copy(buf, rr) + + if err != nil { return } - if c := cap(p); c > 0 { - for n < c { - if len(bb.buffer) <= n { - err = io.EOF - break - } + return &BufferedReader{ + s: buf.Bytes(), + i: 0, + prevRune: -1, + }, nil +} - p[n] = bb.buffer[n] - n++ - } +func (r *BufferedReader) Read(b []byte) (n int, err error) { + if r.i >= int64(len(r.s)) { + n = 0 + err = io.EOF + r.Seek(0, io.SeekStart) + return } - + r.prevRune = -1 + n = copy(b, r.s[r.i:]) + r.i += int64(n) return } +func (r *BufferedReader) Seek(offset int64, whence int) (int64, error) { + r.prevRune = -1 + var abs int64 + switch whence { + case io.SeekStart: + abs = offset + case io.SeekCurrent: + abs = r.i + offset + case io.SeekEnd: + abs = int64(len(r.s)) + offset + default: + return 0, errors.New("bytes.Reader.Seek: invalid whence") + } + if abs < 0 { + return 0, errors.New("bytes.Reader.Seek: negative position") + } + r.i = abs + return abs, nil +} + func (bb *Request) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { Method string diff --git a/pkg/http/request_test.go b/pkg/http/request_test.go index 952fd764e..0a022d8e2 100644 --- a/pkg/http/request_test.go +++ b/pkg/http/request_test.go @@ -10,25 +10,28 @@ import ( ) func Test_requestReadMultiple(t *testing.T) { - var req = require.New(t) - r, _ := h.NewRequest("POST", "/foo", strings.NewReader(`foo body`)) + var ( + req = require.New(t) + tt = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi placerat suscipit finibus. Morbi luctus et lorem sed euismod. Donec bibendum lorem non justo pretium, a sagittis augue mollis. In varius libero id purus convallis pretium. Vestibulum ac mauris aliquet, pulvinar massa eu, rhoncus ipsum. Cras sit amet euismod metus, in tincidunt sapien. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed scelerisque vulputate imperdiet. Nulla orci magna, fringilla sit amet tempor vel, tempus pulvinar urna.` + ) - rs, err := NewBufferedReader(r.Body) + r, _ := h.NewRequest("POST", "/foo", strings.NewReader(tt)) + rs, err := NewRequest(r) req.NoError(err) - req.Equal(`foo body`, must(io.ReadAll(rs))) - req.Equal(`foo body`, must(io.ReadAll(rs))) + req.Equal(tt, must(io.ReadAll(rs.Body))) + req.Equal(tt, must(io.ReadAll(rs.Body))) } func Test_requestReadMultipleNoBody(t *testing.T) { var req = require.New(t) r, _ := h.NewRequest("POST", "/foo", h.NoBody) - rs, err := NewBufferedReader(r.Body) + rs, err := NewRequest(r) req.NoError(err) - req.Equal(``, must(io.ReadAll(rs))) - req.Equal(``, must(io.ReadAll(rs))) + req.Equal(``, must(io.ReadAll(rs.Body))) + req.Equal(``, must(io.ReadAll(rs.Body))) } func must(b []byte, e error) string {