рабочий вариант, но скороть 10 МБит
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
package vpnruntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
FabricServiceStreamRegistrySchemaVersion = "rap.fabric_service_stream_registry.v1"
|
||||
|
||||
FabricServiceStreamStateOpen = "open"
|
||||
FabricServiceStreamStateClosed = "closed"
|
||||
FabricServiceStreamStateReset = "reset"
|
||||
)
|
||||
|
||||
type FabricServiceStream struct {
|
||||
TunnelID string `json:"tunnel_id"`
|
||||
ServiceID string `json:"service_id"`
|
||||
StreamID uint64 `json:"stream_id"`
|
||||
TrafficClass string `json:"traffic_class"`
|
||||
Direction string `json:"direction,omitempty"`
|
||||
State string `json:"state"`
|
||||
ServiceTunnel FabricServiceTunnel `json:"service_tunnel"`
|
||||
OpenedAt time.Time `json:"opened_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type FabricServiceStreamRegistry struct {
|
||||
mu sync.RWMutex
|
||||
streams map[string]FabricServiceStream
|
||||
}
|
||||
|
||||
func NewFabricServiceStreamRegistry() *FabricServiceStreamRegistry {
|
||||
return &FabricServiceStreamRegistry{streams: map[string]FabricServiceStream{}}
|
||||
}
|
||||
|
||||
func (r *FabricServiceStreamRegistry) Register(stream FabricServiceStream) FabricServiceStream {
|
||||
if r == nil {
|
||||
return FabricServiceStream{}
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
stream.ServiceTunnel = NormalizeServiceTunnel(stream.ServiceTunnel, stream.TunnelID)
|
||||
stream.TunnelID = firstNonEmptyTunnelString(stream.TunnelID, stream.ServiceTunnel.TunnelID)
|
||||
stream.ServiceID = firstNonEmptyTunnelString(stream.ServiceID, stream.ServiceTunnel.ServiceID)
|
||||
stream.TrafficClass = normalizeFabricTrafficClass(stream.TrafficClass)
|
||||
if stream.State == "" {
|
||||
stream.State = FabricServiceStreamStateOpen
|
||||
}
|
||||
if stream.OpenedAt.IsZero() {
|
||||
stream.OpenedAt = now
|
||||
}
|
||||
stream.UpdatedAt = now
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.streams == nil {
|
||||
r.streams = map[string]FabricServiceStream{}
|
||||
}
|
||||
if existing, ok := r.streams[serviceStreamKey(stream.TunnelID, stream.StreamID)]; ok {
|
||||
if !existing.OpenedAt.IsZero() {
|
||||
stream.OpenedAt = existing.OpenedAt
|
||||
}
|
||||
}
|
||||
r.streams[serviceStreamKey(stream.TunnelID, stream.StreamID)] = stream
|
||||
return stream
|
||||
}
|
||||
|
||||
func (r *FabricServiceStreamRegistry) MarkClosed(tunnelID string, streamID uint64) {
|
||||
r.markState(tunnelID, streamID, FabricServiceStreamStateClosed)
|
||||
}
|
||||
|
||||
func (r *FabricServiceStreamRegistry) MarkReset(tunnelID string, streamID uint64) {
|
||||
r.markState(tunnelID, streamID, FabricServiceStreamStateReset)
|
||||
}
|
||||
|
||||
func (r *FabricServiceStreamRegistry) StreamsForTunnel(tunnelID string) []FabricServiceStream {
|
||||
if r == nil || tunnelID == "" {
|
||||
return nil
|
||||
}
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
out := make([]FabricServiceStream, 0)
|
||||
for _, stream := range r.streams {
|
||||
if stream.TunnelID == tunnelID {
|
||||
out = append(out, cloneFabricServiceStream(stream))
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].StreamID < out[j].StreamID })
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *FabricServiceStreamRegistry) Snapshot() map[string]any {
|
||||
if r == nil {
|
||||
return map[string]any{"schema_version": FabricServiceStreamRegistrySchemaVersion, "stream_count": 0}
|
||||
}
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
items := make([]map[string]any, 0, len(r.streams))
|
||||
openCount := 0
|
||||
for _, stream := range r.streams {
|
||||
if stream.State == FabricServiceStreamStateOpen {
|
||||
openCount++
|
||||
}
|
||||
item := map[string]any{
|
||||
"tunnel_id": stream.TunnelID,
|
||||
"service_id": stream.ServiceID,
|
||||
"stream_id": stream.StreamID,
|
||||
"traffic_class": stream.TrafficClass,
|
||||
"direction": stream.Direction,
|
||||
"state": stream.State,
|
||||
"service_tunnel": stream.ServiceTunnel.Snapshot(),
|
||||
}
|
||||
if !stream.OpenedAt.IsZero() {
|
||||
item["opened_at"] = stream.OpenedAt.Format(time.RFC3339Nano)
|
||||
}
|
||||
if !stream.UpdatedAt.IsZero() {
|
||||
item["updated_at"] = stream.UpdatedAt.Format(time.RFC3339Nano)
|
||||
}
|
||||
if len(stream.Metadata) > 0 {
|
||||
item["metadata"] = cloneStringMap(stream.Metadata)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
left, _ := items[i]["stream_id"].(uint64)
|
||||
right, _ := items[j]["stream_id"].(uint64)
|
||||
return left < right
|
||||
})
|
||||
return map[string]any{
|
||||
"schema_version": FabricServiceStreamRegistrySchemaVersion,
|
||||
"stream_count": len(items),
|
||||
"open_count": openCount,
|
||||
"streams": items,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FabricServiceStreamRegistry) markState(tunnelID string, streamID uint64, state string) {
|
||||
if r == nil || tunnelID == "" || streamID == 0 {
|
||||
return
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
key := serviceStreamKey(tunnelID, streamID)
|
||||
stream, ok := r.streams[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
stream.State = state
|
||||
stream.UpdatedAt = time.Now().UTC()
|
||||
r.streams[key] = stream
|
||||
}
|
||||
|
||||
func serviceStreamKey(tunnelID string, streamID uint64) string {
|
||||
return fmt.Sprintf("%s\x00%d", tunnelID, streamID)
|
||||
}
|
||||
|
||||
func cloneFabricServiceStream(stream FabricServiceStream) FabricServiceStream {
|
||||
stream.Metadata = cloneStringMap(stream.Metadata)
|
||||
return stream
|
||||
}
|
||||
|
||||
func serviceStreamsSnapshotItems(streams []FabricServiceStream) []map[string]any {
|
||||
if len(streams) == 0 {
|
||||
return nil
|
||||
}
|
||||
items := make([]map[string]any, 0, len(streams))
|
||||
for _, stream := range streams {
|
||||
item := map[string]any{
|
||||
"tunnel_id": stream.TunnelID,
|
||||
"service_id": stream.ServiceID,
|
||||
"stream_id": stream.StreamID,
|
||||
"traffic_class": stream.TrafficClass,
|
||||
"direction": stream.Direction,
|
||||
"state": stream.State,
|
||||
"service_tunnel": stream.ServiceTunnel.Snapshot(),
|
||||
}
|
||||
if !stream.OpenedAt.IsZero() {
|
||||
item["opened_at"] = stream.OpenedAt.Format(time.RFC3339Nano)
|
||||
}
|
||||
if !stream.UpdatedAt.IsZero() {
|
||||
item["updated_at"] = stream.UpdatedAt.Format(time.RFC3339Nano)
|
||||
}
|
||||
if len(stream.Metadata) > 0 {
|
||||
item["metadata"] = cloneStringMap(stream.Metadata)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
left, _ := items[i]["stream_id"].(uint64)
|
||||
right, _ := items[j]["stream_id"].(uint64)
|
||||
return left < right
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
func cloneStringMap(values map[string]string) map[string]string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]string, len(values))
|
||||
for key, value := range values {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user