Skip to content

Commit 1754f26

Browse files
wooruivenjiangfanweixiao
authored
feat: zipper support middleware (#660)
# Description Adding `WithConnMiddleware()` and `WithFrameMiddleware()` zipper option, their type is below: ```go type ( // FrameHandler handles a frame. FrameHandler func(*Context) // FrameMiddleware is a middleware for frame handler. FrameMiddleware func(FrameHandler) FrameHandler ) type ( // ConnHandler handles a connection and route. ConnHandler func(*Connection, router.Route) // ConnMiddleware is a middleware for connection handler. ConnMiddleware func(ConnHandler) ConnHandler ) ``` --------- Co-authored-by: venjiang <venjiang@gmail.com> Co-authored-by: C.C <fanweixiao@gmail.com>
1 parent 92b21e7 commit 1754f26

10 files changed

Lines changed: 249 additions & 248 deletions

core/client_test.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,23 @@ func TestFrameRoundTrip(t *testing.T) {
4545
backflow = []byte("hello backflow frame")
4646
)
4747

48+
// test server hooks
49+
ht := &hookTester{t: t}
50+
4851
server := NewServer("zipper",
4952
WithAuth("token", "auth-token"),
5053
WithServerQuicConfig(DefalutQuicConfig),
5154
WithServerTLSConfig(nil),
5255
WithServerLogger(discardingLogger),
56+
WithConnMiddleware(ht.connMiddleware),
57+
WithFrameMiddleware(ht.frameMiddleware),
5358
)
5459
server.ConfigRouter(router.Default())
5560

56-
// test server hooks
57-
ht := &hookTester{t}
58-
server.SetStartHandlers(ht.startHandler)
59-
server.SetBeforeHandlers(ht.beforeHandler)
60-
server.SetAfterHandlers(ht.afterHandler)
61-
6261
recorder := newFrameWriterRecorder("mockID", "mockClientLocal", "mockClientRemote")
6362
server.AddDownstreamServer(recorder)
6463

65-
assert.Equal(t, server.Downstreams()["mockID"], recorder.ID())
64+
assert.Equal(t, server.Downstreams()["mockClientLocal"], recorder.ID())
6665

6766
go func() {
6867
err := server.ListenAndServe(ctx, testaddr)
@@ -191,29 +190,36 @@ func checkClientExited(client *Client, tim time.Duration) bool {
191190
}
192191

193192
type hookTester struct {
194-
t *testing.T
195-
}
196-
197-
func (a *hookTester) startHandler(ctx *Context) error {
198-
ctx.Set("start", "yes")
199-
return nil
193+
mu sync.Mutex
194+
connNames []string
195+
t *testing.T
200196
}
201197

202-
func (a *hookTester) beforeHandler(ctx *Context) error {
203-
ctx.Set("before", "ok")
204-
return nil
205-
}
198+
func (a *hookTester) connMiddleware(next ConnHandler) ConnHandler {
199+
return func(c *Connection, r router.Route) {
200+
a.mu.Lock()
201+
if a.connNames == nil {
202+
a.connNames = make([]string, 0)
203+
}
204+
a.connNames = append(a.connNames, c.Name())
205+
a.mu.Unlock()
206206

207-
func (a *hookTester) afterHandler(ctx *Context) error {
208-
v, ok := ctx.Get("start")
209-
assert.True(a.t, ok)
210-
assert.Equal(a.t, v, "yes")
207+
next(c, r)
211208

212-
v = ctx.Value("before")
213-
assert.True(a.t, ok)
214-
assert.Equal(a.t, v, "ok")
209+
a.mu.Lock()
210+
assert.Contains(a.t, a.connNames, c.Name())
211+
a.mu.Unlock()
212+
}
213+
}
215214

216-
return nil
215+
func (a *hookTester) frameMiddleware(next FrameHandler) FrameHandler {
216+
return func(c *Context) {
217+
c.Set("a", "b")
218+
next(c)
219+
v, ok := c.Get("a")
220+
assert.True(a.t, ok)
221+
assert.Equal(a.t, "b", v)
222+
}
217223
}
218224

219225
func createTestStreamFunction(name string, zipperAddr string, observedTag frame.Tag) *Client {

core/connection.go

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/quic-go/quic-go"
77
"github.com/yomorun/yomo/core/frame"
88
"github.com/yomorun/yomo/core/metadata"
9+
"golang.org/x/exp/slog"
910
)
1011

1112
// ConnectionInfo holds the information of connection.
@@ -22,77 +23,87 @@ type ConnectionInfo interface {
2223
ObserveDataTags() []frame.Tag
2324
}
2425

25-
// Connection wraps conneciton and stream to transfer frames.
26-
// Connection be used to read and write frames, and be managed by Connector.
27-
type Connection interface {
28-
Context() context.Context
29-
ConnectionInfo
30-
frame.ReadWriteCloser
31-
// CloseWithError closes the connection with an error string.
32-
CloseWithError(string) error
33-
}
34-
35-
type connection struct {
26+
// Connection wraps connection and stream for transmitting frames, it can be
27+
// used for reading and writing frames, and is managed by the Connector.
28+
type Connection struct {
3629
name string
3730
id string
3831
clientType ClientType
3932
metadata metadata.M
4033
observeDataTags []uint32
4134
conn quic.Connection
4235
fs *FrameStream
36+
Logger *slog.Logger
4337
}
4438

4539
func newConnection(
4640
name string, id string, clientType ClientType, md metadata.M, tags []uint32,
47-
conn quic.Connection, fs *FrameStream) *connection {
48-
return &connection{
41+
conn quic.Connection, fs *FrameStream, logger *slog.Logger) *Connection {
42+
43+
logger = logger.With("conn_id", id, "conn_name", name)
44+
if conn != nil {
45+
logger.Info("new client connected", "remote_addr", conn.RemoteAddr().String(), "client_type", clientType.String())
46+
}
47+
48+
return &Connection{
4949
name: name,
5050
id: id,
5151
clientType: clientType,
5252
metadata: md,
5353
observeDataTags: tags,
5454
conn: conn,
5555
fs: fs,
56+
Logger: logger,
5657
}
5758
}
5859

59-
func (c *connection) Close() error {
60+
// Close closes the connection.
61+
func (c *Connection) Close() error {
6062
return c.fs.Close()
6163
}
6264

63-
func (c *connection) Context() context.Context {
65+
// Context returns the context of the connection.
66+
func (c *Connection) Context() context.Context {
6467
return c.fs.Context()
6568
}
6669

67-
func (c *connection) ID() string {
70+
// ID returns the connection ID.
71+
func (c *Connection) ID() string {
6872
return c.id
6973
}
7074

71-
func (c *connection) Metadata() metadata.M {
75+
// Metadata returns the extra info of the application.
76+
func (c *Connection) Metadata() metadata.M {
7277
return c.metadata
7378
}
7479

75-
func (c *connection) Name() string {
80+
// Name returns the name of the connection
81+
func (c *Connection) Name() string {
7682
return c.name
7783
}
7884

79-
func (c *connection) ObserveDataTags() []uint32 {
85+
// ObserveDataTags returns the observed data tags.
86+
func (c *Connection) ObserveDataTags() []uint32 {
8087
return c.observeDataTags
8188
}
8289

83-
func (c *connection) ReadFrame() (frame.Frame, error) {
90+
// ReadFrame reads a frame from the connection.
91+
func (c *Connection) ReadFrame() (frame.Frame, error) {
8492
return c.fs.ReadFrame()
8593
}
8694

87-
func (c *connection) ClientType() ClientType {
95+
// ClientType returns the client type of the connection.
96+
func (c *Connection) ClientType() ClientType {
8897
return c.clientType
8998
}
9099

91-
func (c *connection) WriteFrame(f frame.Frame) error {
100+
// WriteFrame writes a frame to the connection.
101+
func (c *Connection) WriteFrame(f frame.Frame) error {
92102
return c.fs.WriteFrame(f)
93103
}
94104

95-
func (c *connection) CloseWithError(errString string) error {
105+
// CloseWithError closes the connection with error.
106+
func (c *Connection) CloseWithError(errString string) error {
96107
return c.conn.CloseWithError(YomoCloseErrorCode, errString)
97108
}
98109

core/connection_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/stretchr/testify/assert"
1313
"github.com/yomorun/yomo/core/frame"
1414
"github.com/yomorun/yomo/core/metadata"
15+
"golang.org/x/exp/slog"
1516
)
1617

1718
func TestConnection(t *testing.T) {
@@ -30,7 +31,7 @@ func TestConnection(t *testing.T) {
3031
// create frame connection.
3132
fs := NewFrameStream(mockStream, &byteCodec{}, &bytePacketReadWriter{})
3233

33-
connection := newConnection(name, id, styp, md, observed, nil, fs)
34+
connection := newConnection(name, id, styp, md, observed, nil, fs, slog.Default())
3435

3536
t.Run("ConnectionInfo", func(t *testing.T) {
3637
assert.Equal(t, id, connection.ID())

core/connector.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func NewConnector(ctx context.Context) *Connector {
3232
// Store stores Connection to Connector,
3333
// If the connID is the same twice, the new connection will replace the old connection.
3434
// If Connector be closed, The function will return ErrConnectorClosed.
35-
func (c *Connector) Store(connID string, conn Connection) error {
35+
func (c *Connector) Store(connID string, conn *Connection) error {
3636
select {
3737
case <-c.ctx.Done():
3838
return ErrConnectorClosed
@@ -62,7 +62,7 @@ func (c *Connector) Remove(connID string) error {
6262
// Get retrieves the Connection with the specified connID.
6363
// If the Connector does not have a connection with the given connID, return nil and false.
6464
// If Connector be closed, The function will return ErrConnectorClosed.
65-
func (c *Connector) Get(connID string) (Connection, bool, error) {
65+
func (c *Connector) Get(connID string) (*Connection, bool, error) {
6666
select {
6767
case <-c.ctx.Done():
6868
return nil, false, ErrConnectorClosed
@@ -74,29 +74,27 @@ func (c *Connector) Get(connID string) (Connection, bool, error) {
7474
return nil, false, nil
7575
}
7676

77-
connection := v.(Connection)
78-
79-
return connection, true, nil
77+
return v.(*Connection), true, nil
8078
}
8179

8280
// FindConnectionFunc is used to search for a specific connection within the Connector.
8381
type FindConnectionFunc func(ConnectionInfo) bool
8482

8583
// Find searches a stream collection using the specified find function.
8684
// If Connector be closed, The function will return ErrConnectorClosed.
87-
func (c *Connector) Find(findFunc FindConnectionFunc) ([]Connection, error) {
85+
func (c *Connector) Find(findFunc FindConnectionFunc) ([]*Connection, error) {
8886
select {
8987
case <-c.ctx.Done():
90-
return []Connection{}, ErrConnectorClosed
88+
return []*Connection{}, ErrConnectorClosed
9189
default:
9290
}
9391

94-
connections := make([]Connection, 0)
92+
connections := make([]*Connection, 0)
9593
c.connections.Range(func(key interface{}, val interface{}) bool {
96-
stream := val.(Connection)
94+
conn := val.(*Connection)
9795

98-
if findFunc(stream) {
99-
connections = append(connections, stream)
96+
if findFunc(conn) {
97+
connections = append(connections, conn)
10098
}
10199
return true
102100
})
@@ -112,10 +110,10 @@ func (c *Connector) Snapshot() map[string]string {
112110

113111
c.connections.Range(func(key interface{}, val interface{}) bool {
114112
var (
115-
streamID = key.(string)
116-
stream = val.(Connection)
113+
connID = key.(string)
114+
conn = val.(*Connection)
117115
)
118-
result[streamID] = stream.Name()
116+
result[connID] = conn.Name()
119117
return true
120118
})
121119

core/connector_test.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/stretchr/testify/assert"
88
"github.com/yomorun/yomo/core/frame"
9+
"golang.org/x/exp/slog"
910
)
1011

1112
func TestConnector(t *testing.T) {
@@ -37,10 +38,9 @@ func TestConnector(t *testing.T) {
3738
err = connector.Remove(connID)
3839
assert.NoError(t, err)
3940

40-
gotStream, ok, err := connector.Get(connID)
41+
_, ok, err := connector.Get(connID)
4142
assert.NoError(t, err)
4243
assert.False(t, ok)
43-
assert.Equal(t, gotStream, nil)
4444
})
4545

4646
t.Run("Find", func(t *testing.T) {
@@ -92,9 +92,8 @@ func TestConnector(t *testing.T) {
9292

9393
t.Run("Get", func(t *testing.T) {
9494
conn1 := mockConn("id-1", "name-1")
95-
gotStream, ok, err := connector.Get(conn1.ID())
95+
_, ok, err := connector.Get(conn1.ID())
9696
assert.False(t, ok)
97-
assert.Equal(t, gotStream, nil)
9897
assert.ErrorIs(t, err, ErrConnectorClosed)
9998
})
10099

@@ -117,6 +116,6 @@ func TestConnector(t *testing.T) {
117116

118117
// mockConn returns a connection that only includes an ID and a name.
119118
// This function is used for unit testing purposes.
120-
func mockConn(id, name string) Connection {
121-
return newConnection(name, id, ClientType(0), nil, []frame.Tag{0}, nil, nil)
119+
func mockConn(id, name string) *Connection {
120+
return newConnection(name, id, ClientType(0), nil, []frame.Tag{0}, nil, nil, slog.Default())
122121
}

0 commit comments

Comments
 (0)