network/context.go

138 lines
3.7 KiB
Go
Raw Normal View History

2022-10-11 17:36:09 +08:00
package network
import (
2023-04-05 16:15:59 +08:00
"context"
2022-10-11 17:36:09 +08:00
"git.hpds.cc/Component/network/hpds_err"
"git.hpds.cc/Component/network/log"
"sync"
"time"
"git.hpds.cc/Component/network/frame"
)
2023-04-05 16:15:59 +08:00
var ctxPool sync.Pool
2022-10-11 17:36:09 +08:00
// Context for Network Server.
type Context struct {
2023-04-05 16:15:59 +08:00
// DataStream is the stream used for reading and writing frames.
DataStream DataStream
2022-10-11 17:36:09 +08:00
// Frame receives from client.
Frame frame.Frame
2023-04-05 16:15:59 +08:00
// mu is used to protect Keys from concurrent read and write operations.
2022-10-11 17:36:09 +08:00
mu sync.RWMutex
2023-04-05 16:15:59 +08:00
// Keys stores the key/value pairs in context, It is Lazy initialized.
Keys map[string]any
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
// Set is used to store a new key/value pair exclusively for this context.
// It also lazy initializes c.Keys if it was not used previously.
func (c *Context) Set(key string, value any) {
2022-10-11 17:36:09 +08:00
c.mu.Lock()
2023-04-05 16:15:59 +08:00
defer c.mu.Unlock()
2022-10-11 17:36:09 +08:00
if c.Keys == nil {
2023-04-05 16:15:59 +08:00
c.Keys = make(map[string]any)
2022-10-11 17:36:09 +08:00
}
c.Keys[key] = value
}
2023-04-05 16:15:59 +08:00
// Get returns the value for the given key, ie: (value, true).
// If the value does not exist it returns (nil, false)
func (c *Context) Get(key string) (any, bool) {
2022-10-11 17:36:09 +08:00
c.mu.RLock()
2023-04-05 16:15:59 +08:00
defer c.mu.RUnlock()
2022-10-11 17:36:09 +08:00
2023-04-05 16:15:59 +08:00
value, ok := c.Keys[key]
return value, ok
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
var _ context.Context = &Context{}
2022-10-11 17:36:09 +08:00
2023-04-05 16:15:59 +08:00
// Done returns nil (chan which will wait forever) when c.Stream.Context() has no Context.
func (c *Context) Done() <-chan struct{} { return c.DataStream.Context().Done() }
2022-10-11 17:36:09 +08:00
2023-04-05 16:15:59 +08:00
// Deadline returns that there is no deadline (ok==false) when c.Stream has no Context.
func (c *Context) Deadline() (deadline time.Time, ok bool) { return c.DataStream.Context().Deadline() }
2022-10-11 17:36:09 +08:00
2023-04-05 16:15:59 +08:00
// Err returns nil when c.Request has no Context.
func (c *Context) Err() error { return c.DataStream.Context().Err() }
2022-10-11 17:36:09 +08:00
2023-04-05 16:15:59 +08:00
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
func (c *Context) Value(key any) any {
if keyAsString, ok := key.(string); ok {
if val, exists := c.Keys[keyAsString]; exists {
return val
}
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
// There always returns nil, because quic.Stream.Context is not be allowed modify.
return c.DataStream.Context().Value(key)
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
// newContext returns a yomo context,
// The context implements standard library `context.Context` interface,
// The lifecycle of Context is equal to stream's that be passed in.
func newContext(dataStream DataStream) (c *Context) {
v := ctxPool.Get()
if v == nil {
c = new(Context)
} else {
c = v.(*Context)
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
log.Infof("stream_id: %s; stream_name: %s; stream_type: %s;", dataStream.ID(),
dataStream.Name(), dataStream.StreamType().String(),
)
c.DataStream = dataStream
2022-10-11 17:36:09 +08:00
return
}
2023-04-05 16:15:59 +08:00
// WithFrame sets a frame to context.
//
// TODO: delete frame from context due to different lifecycle between stream and stream.
func (c *Context) WithFrame(f frame.Frame) {
c.Frame = f
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
// CloseWithError close dataStream in se error,
// It tells controlStream which dataStream should be closed and close dataStream with
// returning error message to client side stream.
//
// TODO: ycode is not be transmitted.
func (c *Context) CloseWithError(hCode hpds_err.ErrorCode, errString string) {
log.Warnf("Stream Close With error", "err_code", hCode.String(), "error", errString)
err := c.DataStream.CloseWithError(errString)
if err == nil {
return
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
log.Errorf("Close DataStream error", err)
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
// Clean cleans the Context,
// Context is not available after called Clean,
//
// Warining: do not use any Context api after Clean, It maybe cause an error.
func (c *Context) Clean() {
c.reset()
ctxPool.Put(c)
2022-10-11 17:36:09 +08:00
}
2023-04-05 16:15:59 +08:00
func (c *Context) reset() {
c.DataStream = nil
c.Frame = nil
for k := range c.Keys {
delete(c.Keys, k)
2022-10-11 17:36:09 +08:00
}
}
2023-04-05 16:15:59 +08:00
// StreamId gets dataStream ID.
func (c *Context) StreamId() string {
return c.DataStream.ID()
2022-10-11 17:36:09 +08:00
}