bump reva

fixes: #1747
This commit is contained in:
Ralf Haferkamp
2025-10-30 17:17:27 +01:00
committed by Ralf Haferkamp
parent e270cdbfd2
commit b5b15f29de
23 changed files with 366 additions and 162 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io
go get github.com/nats-io/nats.go@latest
# To get a specific version:
go get github.com/nats-io/nats.go@v1.46.1
go get github.com/nats-io/nats.go@v1.47.0
# Note that the latest major version for NATS Server is v2:
go get github.com/nats-io/nats-server/v2@latest
+6 -7
View File
@@ -471,6 +471,7 @@ type (
r io.ReadCloser
err error
ctx context.Context
cancel context.CancelFunc
digest hash.Hash
}
)
@@ -867,7 +868,7 @@ func (obs *obs) Get(ctx context.Context, name string, opts ...GetObjectOpt) (Obj
return lobs.Get(ctx, info.ObjectMeta.Opts.Link.Name)
}
result := &objResult{info: info, ctx: ctx}
result := &objResult{info: info, ctx: ctx, cancel: cancel}
if info.Size == 0 {
return result, nil
}
@@ -934,15 +935,10 @@ func (obs *obs) Get(ctx context.Context, name string, opts ...GetObjectOpt) (Obj
nats.Context(ctx),
nats.BindStream(streamName),
}
sub, err := obs.pushJS.Subscribe(chunkSubj, processChunk, subscribeOpts...)
_, err = obs.pushJS.Subscribe(chunkSubj, processChunk, subscribeOpts...)
if err != nil {
return nil, err
}
sub.SetClosedHandler(func(subject string) {
if cancel != nil {
cancel()
}
})
return result, nil
}
@@ -1500,6 +1496,9 @@ func (o *objResult) Read(p []byte) (n int, err error) {
func (o *objResult) Close() error {
o.Lock()
defer o.Unlock()
if o.cancel != nil {
o.cancel()
}
if o.r == nil {
return nil
}
+18 -10
View File
@@ -33,12 +33,11 @@ type (
// MessagesContext supports iterating over a messages on a stream.
// It is returned by [Consumer.Messages] method.
MessagesContext interface {
// Next retrieves next message on a stream. It will block until the next
// message is available. If the context is canceled, Next will return
// ErrMsgIteratorClosed error. An optional timeout or context can be
// provided using NextOpt options. If none are provided, Next will block
// indefinitely until a message is available, iterator is closed or a
// heartbeat error occurs.
// Next retrieves next message on a stream. If MessagesContext is closed
// (either stopped or drained), Next will return ErrMsgIteratorClosed
// error. An optional timeout or context can be provided using NextOpt
// options. If none are provided, Next will block indefinitely until a
// message is available, iterator is closed or a heartbeat error occurs.
Next(opts ...NextOpt) (Msg, error)
// Stop unsubscribes from the stream and cancels subscription. Calling
@@ -137,6 +136,7 @@ type (
consumer *pullConsumer
subscription *nats.Subscription
msgs chan *nats.Msg
msgsClosed atomic.Uint32
errs chan error
pending pendingMsgs
hbMonitor *hbMonitor
@@ -552,7 +552,7 @@ func (p *pullConsumer) Messages(opts ...PullMessagesOpt) (MessagesContext, error
// in Next
p.subs.Delete(sid)
}
close(msgs)
sub.closeMsgs()
}
}(sub.id))
@@ -588,9 +588,11 @@ var (
errDisconnected = errors.New("disconnected")
)
// Next retrieves next message on a stream. It will block until the next
// message is available. If the context is canceled, Next will return
// ErrMsgIteratorClosed error.
// Next retrieves next message on a stream. If MessagesContext is closed
// (either stopped or drained), Next will return ErrMsgIteratorClosed
// error. An optional timeout or context can be provided using NextOpt
// options. If none are provided, Next will block indefinitely until a
// message is available, iterator is closed or a heartbeat error occurs.
func (s *pullSubscription) Next(opts ...NextOpt) (Msg, error) {
var nextOpts nextOpts
for _, opt := range opts {
@@ -1057,6 +1059,12 @@ func (s *pullSubscription) pullMessages(subject string) {
}
}
func (s *pullSubscription) closeMsgs() {
if s.msgsClosed.CompareAndSwap(0, 1) {
close(s.msgs)
}
}
func (s *pullSubscription) scheduleHeartbeatCheck(dur time.Duration) *hbMonitor {
if dur == 0 {
return nil
+89 -35
View File
@@ -48,7 +48,7 @@ import (
// Default Constants
const (
Version = "1.46.1"
Version = "1.47.0"
DefaultURL = "nats://127.0.0.1:4222"
DefaultPort = 4222
DefaultMaxReconnect = 60
@@ -151,6 +151,7 @@ var (
ErrMaxAccountConnectionsExceeded = errors.New("nats: maximum account active connections exceeded")
ErrConnectionNotTLS = errors.New("nats: connection is not tls")
ErrMaxSubscriptionsExceeded = errors.New("nats: server maximum subscriptions exceeded")
ErrWebSocketHeadersAlreadySet = errors.New("nats: websocket connection headers already set")
)
// GetDefaultOptions returns default configuration options for the client.
@@ -250,6 +251,9 @@ type UserInfoCB func() (string, string)
// whole list of URLs and failed to reconnect.
type ReconnectDelayHandler func(attempts int) time.Duration
// WebSocketHeadersHandler is an optional callback handler for generating token used for WebSocket connections.
type WebSocketHeadersHandler func() (http.Header, error)
// asyncCB is used to preserve order for async callbacks.
type asyncCB struct {
f func()
@@ -524,6 +528,12 @@ type Options struct {
// from SubscribeSync if the server returns a permissions error for a subscription.
// Defaults to false.
PermissionErrOnSubscribe bool
// WebSocketConnectionHeaders is an optional http request headers to be sent with the WebSocket request.
WebSocketConnectionHeaders http.Header
// WebSocketConnectionHeadersHandler is an optional callback handler for generating token used for WebSocket connections.
WebSocketConnectionHeadersHandler WebSocketHeadersHandler
}
const (
@@ -1472,6 +1482,36 @@ func TLSHandshakeFirst() Option {
}
}
// WebSocketConnectionHeaders sets a fixed set of HTTP headers that will be
// sent during the WebSocket connection handshake.
// This option is mutually exclusive with WebSocketConnectionHeadersHandler;
// if a headers handler has already been configured, it returns
// ErrWebSocketHeadersAlreadySet.
func WebSocketConnectionHeaders(headers http.Header) Option {
return func(o *Options) error {
if o.WebSocketConnectionHeadersHandler != nil {
return ErrWebSocketHeadersAlreadySet
}
o.WebSocketConnectionHeaders = headers
return nil
}
}
// WebSocketConnectionHeadersHandler registers a callback used to supply HTTP
// headers for the WebSocket connection handshake.
// This option is mutually exclusive with WebSocketConnectionHeaders; if
// non-empty static headers have already been configured, it returns
// ErrWebSocketHeadersAlreadySet.
func WebSocketConnectionHeadersHandler(cb WebSocketHeadersHandler) Option {
return func(o *Options) error {
if len(o.WebSocketConnectionHeaders) != 0 {
return ErrWebSocketHeadersAlreadySet
}
o.WebSocketConnectionHeadersHandler = cb
return nil
}
}
// Handler processing
// SetDisconnectHandler will set the disconnect event handler.
@@ -1671,8 +1711,10 @@ func (o Options) Connect() (*Conn, error) {
return nil, err
}
if connectionEstablished && nc.Opts.ConnectedCB != nil {
nc.ach.push(func() { nc.Opts.ConnectedCB(nc) })
if connectionEstablished {
if connectedCB := nc.Opts.ConnectedCB; connectedCB != nil {
nc.ach.push(func() { connectedCB(nc) })
}
}
return nc, nil
@@ -2747,8 +2789,10 @@ func (nc *Conn) sendConnect() error {
// Construct the CONNECT protocol string
cProto, err := nc.connectProto()
if err != nil {
if !nc.initc && nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) })
if !nc.initc {
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, nil, err) })
}
}
return err
}
@@ -2764,8 +2808,10 @@ func (nc *Conn) sendConnect() error {
// reading byte-by-byte here is ok.
proto, err := nc.readProto()
if err != nil {
if !nc.initc && nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) })
if !nc.initc {
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, nil, err) })
}
}
return err
}
@@ -2775,8 +2821,10 @@ func (nc *Conn) sendConnect() error {
// Read the rest now...
proto, err = nc.readProto()
if err != nil {
if !nc.initc && nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) })
if !nc.initc {
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, nil, err) })
}
}
return err
}
@@ -2884,10 +2932,10 @@ func (nc *Conn) doReconnect(err error, forceReconnect bool) {
// Perform appropriate callback if needed for a disconnect.
// DisconnectedErrCB has priority over deprecated DisconnectedCB
if !nc.initc {
if nc.Opts.DisconnectedErrCB != nil {
nc.ach.push(func() { nc.Opts.DisconnectedErrCB(nc, err) })
} else if nc.Opts.DisconnectedCB != nil {
nc.ach.push(func() { nc.Opts.DisconnectedCB(nc) })
if disconnectedErrCB := nc.Opts.DisconnectedErrCB; disconnectedErrCB != nil {
nc.ach.push(func() { disconnectedErrCB(nc, err) })
} else if disconnectedCB := nc.Opts.DisconnectedCB; disconnectedCB != nil {
nc.ach.push(func() { disconnectedCB(nc) })
}
} else if nc.Opts.RetryOnFailedConnect && nc.initc && err != nil {
// For initial connection failure with RetryOnFailedConnect,
@@ -2996,8 +3044,8 @@ func (nc *Conn) doReconnect(err error, forceReconnect bool) {
// Continue to hold the lock
if err != nil {
// Perform appropriate callback for a failed connection attempt.
if nc.Opts.ReconnectErrCB != nil {
nc.ach.push(func() { nc.Opts.ReconnectErrCB(nc, err) })
if reconnectErrCB := nc.Opts.ReconnectErrCB; reconnectErrCB != nil {
nc.ach.push(func() { reconnectErrCB(nc, err) })
}
nc.err = nil
continue
@@ -3047,10 +3095,10 @@ func (nc *Conn) doReconnect(err error, forceReconnect bool) {
// Queue up the correct callback. If we are in initial connect state
// (using retry on failed connect), we will call the ConnectedCB,
// otherwise the ReconnectedCB.
if nc.Opts.ReconnectedCB != nil && !nc.initc {
nc.ach.push(func() { nc.Opts.ReconnectedCB(nc) })
} else if nc.Opts.ConnectedCB != nil && nc.initc {
nc.ach.push(func() { nc.Opts.ConnectedCB(nc) })
if reconnectedCB := nc.Opts.ReconnectedCB; reconnectedCB != nil && !nc.initc {
nc.ach.push(func() { reconnectedCB(nc) })
} else if connectedCB := nc.Opts.ConnectedCB; connectedCB != nil && nc.initc {
nc.ach.push(func() { connectedCB(nc) })
}
// If we are here with a retry on failed connect, indicate that the
@@ -3364,8 +3412,8 @@ func (nc *Conn) processMsg(data []byte) {
// We will pass the message through but send async error.
nc.mu.Lock()
nc.err = ErrBadHeaderMsg
if nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, sub, ErrBadHeaderMsg) })
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, sub, ErrBadHeaderMsg) })
}
nc.mu.Unlock()
}
@@ -3542,8 +3590,8 @@ slowConsumer:
// is already experiencing client-side slow consumer situation.
nc.mu.Lock()
nc.err = ErrSlowConsumer
if nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, sub, ErrSlowConsumer) })
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, sub, ErrSlowConsumer) })
}
nc.mu.Unlock()
} else {
@@ -3586,8 +3634,8 @@ func (nc *Conn) processTransientError(err error) {
}
}
}
if nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) })
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, nil, err) })
}
nc.mu.Unlock()
}
@@ -3599,8 +3647,10 @@ func (nc *Conn) processTransientError(err error) {
// Connection lock is held on entry
func (nc *Conn) processAuthError(err error) bool {
nc.err = err
if !nc.initc && nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) })
if !nc.initc {
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, nil, err) })
}
}
// We should give up if we tried twice on this server and got the
// same error. This behavior can be modified using IgnoreAuthErrorAbort.
@@ -3645,8 +3695,8 @@ func (nc *Conn) flusher() {
if nc.err == nil {
nc.err = err
}
if nc.Opts.AsyncErrorCB != nil {
nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) })
if asyncErrorCB := nc.Opts.AsyncErrorCB; asyncErrorCB != nil {
nc.ach.push(func() { asyncErrorCB(nc, nil, err) })
}
}
}
@@ -3760,12 +3810,16 @@ func (nc *Conn) processInfo(info string) error {
if !nc.Opts.NoRandomize {
nc.shufflePool(1)
}
if !nc.initc && nc.Opts.DiscoveredServersCB != nil {
nc.ach.push(func() { nc.Opts.DiscoveredServersCB(nc) })
if !nc.initc {
if discoveredServersCB := nc.Opts.DiscoveredServersCB; discoveredServersCB != nil {
nc.ach.push(func() { discoveredServersCB(nc) })
}
}
}
if !nc.initc && ncInfo.LameDuckMode && nc.Opts.LameDuckModeHandler != nil {
nc.ach.push(func() { nc.Opts.LameDuckModeHandler(nc) })
if !nc.initc && ncInfo.LameDuckMode {
if lameDuckModeHandler := nc.Opts.LameDuckModeHandler; lameDuckModeHandler != nil {
nc.ach.push(func() { lameDuckModeHandler(nc) })
}
}
return nil
}
@@ -5607,8 +5661,8 @@ func (nc *Conn) close(status Status, doCBs bool, err error) {
nc.ach.push(func() { disconnectedCB(nc) })
}
}
if nc.Opts.ClosedCB != nil {
nc.ach.push(func() { nc.Opts.ClosedCB(nc) })
if closedCB := nc.Opts.ClosedCB; closedCB != nil {
nc.ach.push(func() { closedCB(nc) })
}
}
// If this is terminal, then we have to notify the asyncCB handler that
+22
View File
@@ -610,6 +610,9 @@ func (nc *Conn) wsInitHandshake(u *url.URL) error {
if compress {
req.Header.Add("Sec-WebSocket-Extensions", wsPMCReqHeaderValue)
}
if err := nc.wsUpdateConnectionHeaders(req); err != nil {
return err
}
if err := req.Write(nc.conn); err != nil {
return err
}
@@ -728,6 +731,25 @@ func (nc *Conn) wsEnqueueControlMsg(needsLock bool, frameType wsOpCode, payload
nc.bw.flush()
}
func (nc *Conn) wsUpdateConnectionHeaders(req *http.Request) error {
var headers http.Header
var err error
if nc.Opts.WebSocketConnectionHeadersHandler != nil {
headers, err = nc.Opts.WebSocketConnectionHeadersHandler()
if err != nil {
return err
}
} else {
headers = nc.Opts.WebSocketConnectionHeaders
}
for key, values := range headers {
for _, val := range values {
req.Header.Add(key, val)
}
}
return nil
}
func wsPMCExtensionSupport(header http.Header) (bool, bool) {
for _, extensionList := range header["Sec-Websocket-Extensions"] {
extensions := strings.Split(extensionList, ",")