Files
gost-x/internal/ctx/value.go
T
Leonov Vladislav 1c42c638a6 recorder: emit periodic session snapshots and byte deltas
Handlers write one recorder entry when a session ends, so long-lived
SOCKS5 tunnels, streaming HTTP responses and UDP associations stay
invisible while they run and report their traffic as one late record.

With recorder.period set, a session now emits interim records on the
interval while it is active and one final record when it ends. Each
record carries the cumulative counters alongside the increments since the
previous one, so a consumer sums records instead of replacing them, plus
sessionID, recordIndex and phase. A session that ends inside one period
still writes a single record, as it does today.

One goroutine per handler samples every live session on the tick and
writes its record straight to the recorder. There is no queue and no
retry loop, and none is needed: a session moves its cursor past a record
only once the recorder has taken it, so a refused write costs no bytes.
They stay in the delta and go out with the next record. Holding on to
what the sink cannot take right now is the sink's job.

Interim and Finish take a context and return an error, so a handler
learns what became of every record it asked for. Start writes nothing, so
it takes neither. Close stops the sampling and reports the sessions its
owner had not finished; nothing is buffered, so there is nothing to
drain.

With recorder.period unset or non-positive the previous behaviour is
kept: one record per session, written inline, in the old shape. An object
its handler never stamped, and one whose record mode is off, is dropped
in both modes.
2026-09-15 15:05:02 +03:00

78 lines
2.5 KiB
Go

// Package ctx provides typed context value helpers for passing per-request
// data (buffer, logger, metadata, recorder objects) through the handler chain.
package ctx
import (
"bytes"
"context"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
xrecorder "github.com/go-gost/x/recorder"
)
type bufferKey struct{}
// ContextWithBuffer returns a copy of ctx with the given buffer stored.
// The buffer is used by dialers and connectors to reuse a *bytes.Buffer
// for protocol handshake data (e.g. TLS ClientHello, HTTP requests).
func ContextWithBuffer(ctx context.Context, buffer *bytes.Buffer) context.Context {
return context.WithValue(ctx, bufferKey{}, buffer)
}
// BufferFromContext returns the *bytes.Buffer stored in ctx, or nil.
func BufferFromContext(ctx context.Context) *bytes.Buffer {
v, _ := ctx.Value(bufferKey{}).(*bytes.Buffer)
return v
}
type loggerKey struct{}
// ContextWithLogger returns a copy of ctx with the given logger stored.
func ContextWithLogger(ctx context.Context, log logger.Logger) context.Context {
return context.WithValue(ctx, loggerKey{}, log)
}
// LoggerFromContext returns the logger stored in ctx, or nil.
func LoggerFromContext(ctx context.Context) logger.Logger {
v, _ := ctx.Value(loggerKey{}).(logger.Logger)
return v
}
type mdKey struct{}
// ContextWithMetadata returns a copy of ctx with the given metadata stored.
func ContextWithMetadata(ctx context.Context, md metadata.Metadata) context.Context {
return context.WithValue(ctx, mdKey{}, md)
}
// MetadataFromContext returns the metadata stored in ctx, or nil.
func MetadataFromContext(ctx context.Context) metadata.Metadata {
v, _ := ctx.Value(mdKey{}).(metadata.Metadata)
return v
}
type recorderObjectCtxKey struct{}
// ContextWithRecorderObject returns a copy of ctx with the given recorder object stored.
func ContextWithRecorderObject(ctx context.Context, ro *xrecorder.HandlerRecorderObject) context.Context {
return context.WithValue(ctx, recorderObjectCtxKey{}, ro)
}
// RecorderObjectFromContext returns the recorder object stored in ctx, or nil.
func RecorderObjectFromContext(ctx context.Context) *xrecorder.HandlerRecorderObject {
v, _ := ctx.Value(recorderObjectCtxKey{}).(*xrecorder.HandlerRecorderObject)
return v
}
type sessionCtxKey struct{}
func ContextWithSession(ctx context.Context, s *xrecorder.Session) context.Context {
return context.WithValue(ctx, sessionCtxKey{}, s)
}
func SessionFromContext(ctx context.Context) *xrecorder.Session {
v, _ := ctx.Value(sessionCtxKey{}).(*xrecorder.Session)
return v
}