feat(notify): 重构通信内核并补齐 stream/bulk/record/transfer 能力
- 引入 LogicalConn/TransportConn 分层,ClientConn 保留兼容适配层 - 新增 Stream、Bulk、RecordStream 三条数据面能力及对应控制路径 - 完成 transfer/file 传输内核与状态快照、诊断能力 - 补齐 reconnect、inbound dispatcher、modern psk 等基础模块 - 增加大规模回归、并发与基准测试覆盖 - 更新依赖库
This commit is contained in:
+243
@@ -0,0 +1,243 @@
|
||||
package notify
|
||||
|
||||
import "time"
|
||||
|
||||
type FileEvent struct {
|
||||
NetType NetType
|
||||
LogicalConn *LogicalConn
|
||||
// Deprecated: ClientConn aliases LogicalConn for compatibility.
|
||||
ClientConn *ClientConn
|
||||
TransportConn *TransportConn
|
||||
ServerConn Client
|
||||
Kind EnvelopeKind
|
||||
Packet FilePacket
|
||||
Path string
|
||||
Received int64
|
||||
Total int64
|
||||
Percent float64
|
||||
Done bool
|
||||
StartedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Duration time.Duration
|
||||
RateBPS float64
|
||||
StepDuration time.Duration
|
||||
InstantRateBPS float64
|
||||
Err error
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func normalizeFileEventTime(now time.Time) time.Time {
|
||||
if now.IsZero() {
|
||||
return time.Now()
|
||||
}
|
||||
return now
|
||||
}
|
||||
|
||||
func hydrateServerFileEventPeerFields(event FileEvent) FileEvent {
|
||||
if event.LogicalConn == nil {
|
||||
event.LogicalConn = logicalConnFromClient(event.ClientConn)
|
||||
}
|
||||
if event.ClientConn == nil {
|
||||
event.ClientConn = event.LogicalConn.compatClientConn()
|
||||
}
|
||||
if event.TransportConn == nil && event.LogicalConn != nil {
|
||||
event.TransportConn = event.LogicalConn.CurrentTransportConn()
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func fileEventLogicalConnSnapshot(event FileEvent) *LogicalConn {
|
||||
if event.LogicalConn != nil {
|
||||
return event.LogicalConn
|
||||
}
|
||||
return logicalConnFromClient(event.ClientConn)
|
||||
}
|
||||
|
||||
func fileEventTransportConnSnapshot(event FileEvent) *TransportConn {
|
||||
if event.TransportConn != nil {
|
||||
return event.TransportConn
|
||||
}
|
||||
logical := fileEventLogicalConnSnapshot(event)
|
||||
if logical == nil {
|
||||
return nil
|
||||
}
|
||||
return logical.CurrentTransportConn()
|
||||
}
|
||||
|
||||
type fileEventTimeline struct {
|
||||
startedAt time.Time
|
||||
updatedAt time.Time
|
||||
previousUpdatedAt time.Time
|
||||
previousProgress int64
|
||||
}
|
||||
|
||||
func fillFileEventProgress(event *FileEvent) {
|
||||
if event == nil {
|
||||
return
|
||||
}
|
||||
event.Total = event.Packet.Size
|
||||
if event.Received < 0 {
|
||||
event.Received = 0
|
||||
}
|
||||
if event.Total > 0 && event.Received > event.Total {
|
||||
event.Received = event.Total
|
||||
}
|
||||
switch event.Kind {
|
||||
case EnvelopeFileEnd:
|
||||
event.Done = event.Err == nil
|
||||
if event.Done && event.Total > 0 {
|
||||
event.Received = event.Total
|
||||
}
|
||||
case EnvelopeFileAbort:
|
||||
event.Done = false
|
||||
}
|
||||
if event.Total <= 0 {
|
||||
if event.Done {
|
||||
event.Percent = 100
|
||||
}
|
||||
if !event.StartedAt.IsZero() && !event.UpdatedAt.IsZero() && !event.UpdatedAt.Before(event.StartedAt) {
|
||||
event.Duration = event.UpdatedAt.Sub(event.StartedAt)
|
||||
}
|
||||
return
|
||||
}
|
||||
event.Percent = float64(event.Received) * 100 / float64(event.Total)
|
||||
if event.Percent < 0 {
|
||||
event.Percent = 0
|
||||
}
|
||||
if event.Percent > 100 {
|
||||
event.Percent = 100
|
||||
}
|
||||
if !event.StartedAt.IsZero() && !event.UpdatedAt.IsZero() && !event.UpdatedAt.Before(event.StartedAt) {
|
||||
event.Duration = event.UpdatedAt.Sub(event.StartedAt)
|
||||
}
|
||||
if event.Duration > 0 && event.Received > 0 {
|
||||
event.RateBPS = float64(event.Received) / event.Duration.Seconds()
|
||||
}
|
||||
}
|
||||
|
||||
func fillFileEventTimeline(event *FileEvent, timeline fileEventTimeline) {
|
||||
if event == nil {
|
||||
return
|
||||
}
|
||||
event.StartedAt = timeline.startedAt
|
||||
event.UpdatedAt = timeline.updatedAt
|
||||
if !timeline.previousUpdatedAt.IsZero() && !timeline.updatedAt.Before(timeline.previousUpdatedAt) {
|
||||
event.StepDuration = timeline.updatedAt.Sub(timeline.previousUpdatedAt)
|
||||
}
|
||||
if delta := event.Received - timeline.previousProgress; delta > 0 && event.StepDuration > 0 {
|
||||
event.InstantRateBPS = float64(delta) / event.StepDuration.Seconds()
|
||||
}
|
||||
}
|
||||
|
||||
func fillFileEventTiming(event *FileEvent, session *fileReceiveSession) {
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
fillFileEventTimeline(event, fileEventTimeline{
|
||||
startedAt: session.startedAt,
|
||||
updatedAt: session.updatedAt,
|
||||
previousUpdatedAt: session.previousUpdatedAt,
|
||||
previousProgress: session.previousReceived,
|
||||
})
|
||||
}
|
||||
|
||||
func fillFileSendEventTiming(event *FileEvent, session *fileSendSession) {
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
fillFileEventTimeline(event, fileEventTimeline{
|
||||
startedAt: session.startedAt,
|
||||
updatedAt: session.updatedAt,
|
||||
previousUpdatedAt: session.previousUpdatedAt,
|
||||
previousProgress: session.previousSent,
|
||||
})
|
||||
}
|
||||
|
||||
func normalizeFileEventCallback(fn func(FileEvent)) func(FileEvent) {
|
||||
if fn == nil {
|
||||
return func(FileEvent) {}
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
func (c *ClientCommon) setFileEventObserver(fn func(FileEvent)) {
|
||||
c.mu.Lock()
|
||||
c.fileEventObserver = normalizeFileEventCallback(fn)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *ServerCommon) setFileEventObserver(fn func(FileEvent)) {
|
||||
s.mu.Lock()
|
||||
s.fileEventObserver = normalizeFileEventCallback(fn)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *ClientCommon) observeFileEvent(event FileEvent) {
|
||||
c.mu.Lock()
|
||||
observer := c.fileEventObserver
|
||||
c.mu.Unlock()
|
||||
normalizeFileEventCallback(observer)(event)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) observeFileEvent(event FileEvent) {
|
||||
s.mu.RLock()
|
||||
observer := s.fileEventObserver
|
||||
s.mu.RUnlock()
|
||||
normalizeFileEventCallback(observer)(hydrateServerFileEventPeerFields(event))
|
||||
}
|
||||
|
||||
func (c *ClientCommon) publishReceivedFileEvent(event FileEvent) {
|
||||
c.getFileTransferState().observe(fileTransferDirectionReceive, event)
|
||||
c.observeFileEvent(event)
|
||||
c.logFileEvent("client", event)
|
||||
c.emitFileEvent(event)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) publishReceivedFileEventMonitorOnly(event FileEvent) {
|
||||
c.getFileTransferState().observeMonitorOnly(fileTransferDirectionReceive, event)
|
||||
c.observeFileEvent(event)
|
||||
c.logFileEvent("client", event)
|
||||
c.emitFileEvent(event)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) publishReceivedFileEvent(event FileEvent) {
|
||||
event = hydrateServerFileEventPeerFields(event)
|
||||
s.getFileTransferState().observe(fileTransferDirectionReceive, event)
|
||||
s.observeFileEvent(event)
|
||||
s.logFileEvent("server", event)
|
||||
s.emitFileEvent(event)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) publishReceivedFileEventMonitorOnly(event FileEvent) {
|
||||
event = hydrateServerFileEventPeerFields(event)
|
||||
s.getFileTransferState().observeMonitorOnly(fileTransferDirectionReceive, event)
|
||||
s.observeFileEvent(event)
|
||||
s.logFileEvent("server", event)
|
||||
s.emitFileEvent(event)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) publishSendFileEvent(event FileEvent) {
|
||||
c.getFileTransferState().observe(fileTransferDirectionSend, event)
|
||||
c.observeFileEvent(event)
|
||||
c.logFileEvent("client-send", event)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) publishSendFileEventMonitorOnly(event FileEvent) {
|
||||
c.getFileTransferState().observeMonitorOnly(fileTransferDirectionSend, event)
|
||||
c.observeFileEvent(event)
|
||||
c.logFileEvent("client-send", event)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) publishSendFileEvent(event FileEvent) {
|
||||
event = hydrateServerFileEventPeerFields(event)
|
||||
s.getFileTransferState().observe(fileTransferDirectionSend, event)
|
||||
s.observeFileEvent(event)
|
||||
s.logFileEvent("server-send", event)
|
||||
}
|
||||
|
||||
func (s *ServerCommon) publishSendFileEventMonitorOnly(event FileEvent) {
|
||||
event = hydrateServerFileEventPeerFields(event)
|
||||
s.getFileTransferState().observeMonitorOnly(fileTransferDirectionSend, event)
|
||||
s.observeFileEvent(event)
|
||||
s.logFileEvent("server-send", event)
|
||||
}
|
||||
Reference in New Issue
Block a user