Skip to content

Commit e57d0a6

Browse files
feat(framing): add frame length to support large data (#191)
* feat(framing): add frame length and payload to support large data * fix(client): auto re-connect when the connection is interrupted
1 parent e098311 commit e57d0a6

9 files changed

Lines changed: 170 additions & 31 deletions

File tree

pkg/client/client.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"sync"
1212
"time"
1313

14+
"github.com/yomorun/yomo/pkg/framing"
1415
"github.com/yomorun/yomo/pkg/quic"
1516
)
1617

@@ -135,7 +136,6 @@ func (c *clientImpl) handleSignal(accepted chan bool) {
135136
break
136137
}
137138
value := buf[:n]
138-
139139
if bytes.Equal(value, quic.SignalHeartbeat) {
140140
// heartbeart
141141
c.conn.Heartbeat <- buf[0]
@@ -169,9 +169,11 @@ func (c *clientImpl) handleSignal(accepted chan bool) {
169169
}
170170

171171
// Write the data to downstream.
172-
func (c *clientImpl) Write(b []byte) (int, error) {
172+
func (c *clientImpl) Write(data []byte) (int, error) {
173173
if c.conn.Stream != nil {
174-
return c.conn.Stream.Write(b)
174+
// wrap data with framing.
175+
f := framing.NewPayloadFrame(data)
176+
return c.conn.Stream.Write(f.Bytes())
175177
} else {
176178
return 0, errors.New("not found stream")
177179
}

pkg/client/serverless.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"log"
66

7+
"github.com/yomorun/yomo/pkg/framing"
78
"github.com/yomorun/yomo/pkg/quic"
89
"github.com/yomorun/yomo/pkg/rx"
910
)
@@ -60,7 +61,10 @@ func (c *serverlessClientImpl) Pipe(f func(rxstream rx.RxStream) rx.RxStream) {
6061
log.Print("❌ Please add the encode/marshal operator in the end of your Serverless handler.")
6162
continue
6263
}
63-
_, err := c.writer.Write(buf)
64+
65+
// wrap data with framing.
66+
f := framing.NewPayloadFrame(buf)
67+
_, err := c.writer.Write(f.Bytes())
6468
if err != nil {
6569
log.Print("❌ Send data to zipper failed. ", err)
6670
}

pkg/decoder/decoder.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,14 @@ func FromStream(reader io.Reader) Observable {
123123

124124
f := func(next chan interface{}) {
125125
defer close(next)
126+
fd := NewFrameDecoder(reader)
126127
for {
127-
buf := make([]byte, 3*1024)
128-
n, err := reader.Read(buf)
129-
128+
// read next raw frame.
129+
buf, err := fd.Read(true)
130130
if err != nil {
131131
break
132132
} else {
133-
value := buf[:n]
134-
next <- value
133+
next <- buf
135134
}
136135
}
137136
}

pkg/decoder/frame.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package decoder
2+
3+
import (
4+
"bufio"
5+
"errors"
6+
"io"
7+
8+
"github.com/yomorun/yomo/pkg/framing"
9+
)
10+
11+
const (
12+
minBuffSize = 3 * 1024
13+
maxBuffSize = 16*1024*1024 + framing.FrameLengthFieldSize
14+
)
15+
16+
// FrameDecoder defines a decoder for decoding frames which have a header of length.
17+
type FrameDecoder bufio.Scanner
18+
19+
// Read reads next frame in bytes.
20+
// if rawFrame == true, it returns the raw frame without frame length field.
21+
func (p *FrameDecoder) Read(rawFrame bool) ([]byte, error) {
22+
scanner := (*bufio.Scanner)(p)
23+
if !scanner.Scan() {
24+
err := scanner.Err()
25+
if err == nil {
26+
err = io.EOF
27+
}
28+
return nil, err
29+
}
30+
31+
// read raw frame
32+
if rawFrame {
33+
raw := scanner.Bytes()[framing.FrameLengthFieldSize:]
34+
return raw, nil
35+
}
36+
37+
// read full frame includes frame length field.
38+
return scanner.Bytes(), nil
39+
}
40+
41+
func doSplit(data []byte, eof bool) (advance int, token []byte, err error) {
42+
if eof {
43+
return
44+
}
45+
if len(data) < framing.FrameLengthFieldSize {
46+
return
47+
}
48+
frameLength := framing.ReadFrameLength(data)
49+
if frameLength < 1 {
50+
err = errors.New("invalid frame length")
51+
return
52+
}
53+
frameSize := frameLength + framing.FrameLengthFieldSize
54+
if frameSize <= len(data) {
55+
return frameSize, data[:frameSize], nil
56+
}
57+
return
58+
}
59+
60+
// NewFrameDecoder creates a new frame decoder.
61+
func NewFrameDecoder(r io.Reader) *FrameDecoder {
62+
scanner := bufio.NewScanner(r)
63+
scanner.Split(doSplit)
64+
buf := make([]byte, 0, minBuffSize)
65+
scanner.Buffer(buf, maxBuffSize)
66+
return (*FrameDecoder)(scanner)
67+
}

pkg/framing/frame.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package framing
2+
3+
// Frame represents a YoMo frame.
4+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
5+
// | Frame Length |
6+
// +-----------------------------------------------+
7+
// | Frame |
8+
// | |
9+
// +-----------------------------------------------+
10+
type Frame interface {
11+
// Bytes converts the frame to bytes
12+
Bytes() []byte
13+
}
14+
15+
const FrameLengthFieldSize = 3
16+
17+
func appendFrameLength(buf []byte, len int) {
18+
for i := 0; i < FrameLengthFieldSize; i++ {
19+
offset := 8 * (FrameLengthFieldSize - i - 1)
20+
if offset > 0 {
21+
buf[i] = byte(len >> offset)
22+
} else {
23+
buf[i] = byte(len)
24+
}
25+
}
26+
}
27+
28+
// ReadFrameLength reads frame length from bytes.
29+
func ReadFrameLength(buf []byte) int {
30+
c := 0
31+
for i := 0; i < FrameLengthFieldSize; i++ {
32+
offset := 8 * (FrameLengthFieldSize - i - 1)
33+
if offset > 0 {
34+
c += int(buf[i]) << offset
35+
} else {
36+
c += int(buf[i])
37+
}
38+
}
39+
40+
if c == 0 && len(buf) > FrameLengthFieldSize {
41+
// skip the first byte and contine reading frame length from buf
42+
buf = buf[1:]
43+
return ReadFrameLength(buf)
44+
}
45+
46+
return c
47+
}

pkg/framing/payload.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package framing
2+
3+
// PayloadFrame represents a payload frame.
4+
type PayloadFrame struct {
5+
data []byte
6+
}
7+
8+
// NewPayloadFrame inits a new PayloadFrame.
9+
func NewPayloadFrame(data []byte) *PayloadFrame {
10+
return &PayloadFrame{
11+
data: data,
12+
}
13+
}
14+
15+
func (p *PayloadFrame) Bytes() []byte {
16+
len := len(p.data)
17+
buf := make([]byte, FrameLengthFieldSize)
18+
19+
appendFrameLength(buf, len)
20+
buf = append(buf, p.data...)
21+
return buf
22+
}

pkg/quic/conn.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ func (c *QuicConn) Healthcheck() {
8181
if c.OnHeartbeatExpired != nil {
8282
c.OnHeartbeatExpired()
8383
}
84-
break loop
8584
}
8685
}
8786
}()

pkg/runtime/handler.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/reactivex/rxgo/v2"
1212
"github.com/yomorun/yomo/pkg/client"
13+
"github.com/yomorun/yomo/pkg/decoder"
1314
"github.com/yomorun/yomo/pkg/quic"
1415
"github.com/yomorun/yomo/pkg/serverless"
1516
)
@@ -158,13 +159,12 @@ func (s *quicHandler) receiveDataFromZipperSenders() {
158159
}
159160

160161
go func() {
162+
fd := decoder.NewFrameDecoder(receiver)
161163
for {
162-
buf := make([]byte, 3*1024)
163-
n, err := receiver.Read(buf)
164+
buf, err := fd.Read(false)
164165
if err != nil {
165166
break
166167
} else {
167-
value := buf[:n]
168168
// send data to sinks
169169
for _, sink := range sinks {
170170
go func(sf serverless.GetSinkFunc, buf []byte) {
@@ -176,7 +176,7 @@ func (s *quicHandler) receiveDataFromZipperSenders() {
176176
cancel()
177177
}
178178
}
179-
}(sink, value)
179+
}(sink, buf)
180180
}
181181
}
182182
}

pkg/rx/rxstream_operator.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,13 @@ func FromReader(reader io.Reader) RxStream {
5757
go func() {
5858
defer close(next)
5959

60+
fd := decoder.NewFrameDecoder(reader)
6061
for {
61-
buf := make([]byte, 3*1024)
62-
n, err := reader.Read(buf)
62+
buf, err := fd.Read(false)
6363
if err != nil {
6464
break
6565
} else {
66-
value := buf[:n]
67-
next <- Of(value)
66+
next <- Of(buf)
6867
}
6968
}
7069
}()
@@ -105,16 +104,16 @@ func FromReaderWithFunc(f func() io.Reader) RxStream {
105104
if reader == nil {
106105
time.Sleep(time.Second)
107106
} else {
108-
buf := make([]byte, 3*1024)
109-
n, err := reader.Read(buf)
110-
if err != nil {
111-
fmt.Println("[FromReaderWithFunc Reader Error] ", err)
112-
} else {
113-
value := buf[:n]
114-
next <- Of(value)
107+
fd := decoder.NewFrameDecoder(reader)
108+
for {
109+
buf, err := fd.Read(false)
110+
if err != nil {
111+
break
112+
} else {
113+
next <- Of(buf)
114+
}
115115
}
116116
}
117-
118117
}
119118
}()
120119

@@ -658,13 +657,13 @@ func (s *RxStreamImpl) MergeReadWriterWithFunc(rwf serverless.GetFlowFunc, opts
658657
if rw == nil {
659658
time.Sleep(time.Second)
660659
} else {
661-
buf := make([]byte, 3*1024)
662-
n, err := rw.Read(buf)
663-
664-
if err != nil {
660+
fd := decoder.NewFrameDecoder(rw)
661+
buf, err := fd.Read(false)
662+
if err != nil && err != io.EOF {
665663
cancel()
664+
break
666665
} else {
667-
response <- buf[:n]
666+
response <- buf
668667
}
669668
}
670669
}

0 commit comments

Comments
 (0)