committed by
Ralf Haferkamp
parent
e270cdbfd2
commit
b5b15f29de
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, ",")
|
||||
|
||||
Reference in New Issue
Block a user