package notify import ( "context" "errors" "time" ) type BulkOpenRequest struct { BulkID string DataID uint64 Range BulkRange Metadata BulkMetadata ReadTimeout time.Duration WriteTimeout time.Duration Dedicated bool AttachToken string ChunkSize int WindowBytes int MaxInFlight int } type BulkOpenResponse struct { BulkID string DataID uint64 Accepted bool Dedicated bool AttachToken string TransportGeneration uint64 Error string } type BulkCloseRequest struct { BulkID string Full bool } type BulkCloseResponse struct { BulkID string Accepted bool Error string } type BulkResetRequest struct { BulkID string DataID uint64 Error string } type BulkResetResponse struct { BulkID string Accepted bool Error string } type BulkReleaseRequest struct { BulkID string DataID uint64 Bytes int64 Chunks int } func bindClientBulkControl(c *ClientCommon) { if c == nil { return } c.SetLink(BulkOpenSignalKey, func(msg *Message) { c.handleInboundBulkOpen(msg) }) c.SetLink(BulkCloseSignalKey, func(msg *Message) { c.handleInboundBulkClose(msg) }) c.SetLink(BulkResetSignalKey, func(msg *Message) { c.handleInboundBulkReset(msg) }) c.SetLink(BulkReleaseSignalKey, func(msg *Message) { c.handleInboundBulkRelease(msg) }) } func bindServerBulkControl(s *ServerCommon) { if s == nil { return } s.SetLink(BulkOpenSignalKey, func(msg *Message) { s.handleInboundBulkOpen(msg) }) s.SetLink(BulkCloseSignalKey, func(msg *Message) { s.handleInboundBulkClose(msg) }) s.SetLink(BulkResetSignalKey, func(msg *Message) { s.handleInboundBulkReset(msg) }) s.SetLink(BulkReleaseSignalKey, func(msg *Message) { s.handleInboundBulkRelease(msg) }) } func (c *ClientCommon) handleInboundBulkOpen(msg *Message) { req, err := decodeBulkOpenRequest(msg) resp := BulkOpenResponse{BulkID: req.BulkID, DataID: req.DataID, Dedicated: req.Dedicated} if err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } if req.Dedicated { if err := clientDedicatedBulkSupportError(c); err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } } runtime := c.getBulkRuntime() if runtime == nil { resp.Error = errBulkRuntimeNil.Error() replyBulkControlIfNeeded(msg, resp) return } scope := clientFileScope() if req.DataID == 0 { req.DataID = runtime.nextDataID() resp.DataID = req.DataID } if req.Dedicated && req.AttachToken == "" { req.AttachToken = newBulkAttachToken() } resp.AttachToken = req.AttachToken bulk := newBulkHandle(c.clientStopContextSnapshot(), runtime, scope, req, c.currentClientSessionEpoch(), nil, nil, 0, clientBulkCloseSender(c), clientBulkResetSender(c), clientBulkDataSender(c, c.currentClientSessionEpoch()), clientBulkWriteSender(c, c.currentClientSessionEpoch()), clientBulkReleaseSender(c)) bulk.setClientSnapshotOwner(c) if err := runtime.register(scope, bulk); err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } handler := runtime.handlerSnapshot() if handler == nil { bulk.markReset(errBulkHandlerNotConfigured) resp.Error = errBulkHandlerNotConfigured.Error() replyBulkControlIfNeeded(msg, resp) return } if req.Dedicated { if err := c.attachDedicatedBulkSidecar(context.Background(), bulk); err != nil { bulk.markReset(err) resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } } info := BulkAcceptInfo{ ID: bulk.ID(), Range: bulk.Range(), Metadata: bulk.Metadata(), Dedicated: bulk.Dedicated(), TransportGeneration: bulk.TransportGeneration(), Bulk: bulk, } if err := handler(info); err != nil { bulk.markReset(err) resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } resp.Accepted = true resp.DataID = bulk.dataIDSnapshot() resp.TransportGeneration = bulk.TransportGeneration() replyBulkControlIfNeeded(msg, resp) } func (s *ServerCommon) handleInboundBulkOpen(msg *Message) { req, err := decodeBulkOpenRequest(msg) resp := BulkOpenResponse{BulkID: req.BulkID, DataID: req.DataID, Dedicated: req.Dedicated} if err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } runtime := s.getBulkRuntime() if runtime == nil { resp.Error = errBulkRuntimeNil.Error() replyBulkControlIfNeeded(msg, resp) return } logical := messageLogicalConnSnapshot(msg) if logical == nil { resp.Error = errBulkLogicalConnNil.Error() replyBulkControlIfNeeded(msg, resp) return } transport := messageTransportConnSnapshot(msg) if req.Dedicated { if err := logicalDedicatedBulkSupportError(logical); err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } if transport != nil { if err := transportDedicatedBulkSupportError(transport); err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } } } scope := serverFileScope(logical) if req.DataID == 0 { req.DataID = runtime.nextDataID() resp.DataID = req.DataID } if req.Dedicated && req.AttachToken == "" { req.AttachToken = newBulkAttachToken() } resp.AttachToken = req.AttachToken bulk := newBulkHandle(logical.stopContextSnapshot(), runtime, scope, req, 0, logical, transport, bulkTransportGeneration(logical, transport), serverBulkCloseSender(s, logical, transport), serverBulkResetSender(s, logical, transport), serverBulkDataSender(s, transport), serverBulkWriteSender(s, logical, transport), serverBulkReleaseSender(s, logical, transport)) if err := runtime.register(scope, bulk); err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } handler := runtime.handlerSnapshot() if handler == nil { bulk.markReset(errBulkHandlerNotConfigured) resp.Error = errBulkHandlerNotConfigured.Error() replyBulkControlIfNeeded(msg, resp) return } info := BulkAcceptInfo{ ID: bulk.ID(), Range: bulk.Range(), Metadata: bulk.Metadata(), Dedicated: bulk.Dedicated(), LogicalConn: logical, TransportConn: transport, TransportGeneration: bulk.TransportGeneration(), Bulk: bulk, } if err := handler(info); err != nil { bulk.markReset(err) resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } resp.Accepted = true resp.DataID = bulk.dataIDSnapshot() resp.TransportGeneration = bulk.TransportGeneration() replyBulkControlIfNeeded(msg, resp) } func (c *ClientCommon) handleInboundBulkClose(msg *Message) { req, err := decodeBulkCloseRequest(msg) resp := BulkCloseResponse{BulkID: req.BulkID} if err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } runtime := c.getBulkRuntime() if runtime == nil { resp.Error = errBulkRuntimeNil.Error() replyBulkControlIfNeeded(msg, resp) return } bulk, ok := runtime.lookup(clientFileScope(), req.BulkID) if !ok { resp.Error = errBulkNotFound.Error() replyBulkControlIfNeeded(msg, resp) return } if req.Full { bulk.markPeerClosed() } else { bulk.markRemoteClosed() } resp.Accepted = true replyBulkControlIfNeeded(msg, resp) } func (s *ServerCommon) handleInboundBulkClose(msg *Message) { req, err := decodeBulkCloseRequest(msg) resp := BulkCloseResponse{BulkID: req.BulkID} if err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } runtime := s.getBulkRuntime() if runtime == nil { resp.Error = errBulkRuntimeNil.Error() replyBulkControlIfNeeded(msg, resp) return } logical := messageLogicalConnSnapshot(msg) scope := serverFileScope(logical) bulk, ok := runtime.lookup(scope, req.BulkID) if !ok { resp.Error = errBulkNotFound.Error() replyBulkControlIfNeeded(msg, resp) return } if req.Full { bulk.markPeerClosed() } else { bulk.markRemoteClosed() } resp.Accepted = true replyBulkControlIfNeeded(msg, resp) } func (c *ClientCommon) handleInboundBulkReset(msg *Message) { req, err := decodeBulkResetRequest(msg) resp := BulkResetResponse{BulkID: req.BulkID} if err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } runtime := c.getBulkRuntime() if runtime == nil { resp.Error = errBulkRuntimeNil.Error() replyBulkControlIfNeeded(msg, resp) return } bulk, ok := runtime.lookup(clientFileScope(), req.BulkID) if !ok && req.DataID != 0 { bulk, ok = runtime.lookupByDataID(clientFileScope(), req.DataID) } if !ok { resp.Error = errBulkNotFound.Error() replyBulkControlIfNeeded(msg, resp) return } if resp.BulkID == "" { resp.BulkID = bulk.ID() } bulk.markReset(bulkResetError(bulkRemoteResetError(req.Error))) resp.Accepted = true replyBulkControlIfNeeded(msg, resp) } func (c *ClientCommon) handleInboundBulkRelease(msg *Message) { req, err := decodeBulkReleaseRequest(msg) if err != nil { return } runtime := c.getBulkRuntime() if runtime == nil { return } bulk, ok := runtime.lookup(clientFileScope(), req.BulkID) if !ok && req.DataID != 0 { bulk, ok = runtime.lookupByDataID(clientFileScope(), req.DataID) } if !ok { return } bulk.releaseOutboundWindow(req.Bytes, req.Chunks) } func (s *ServerCommon) handleInboundBulkReset(msg *Message) { req, err := decodeBulkResetRequest(msg) resp := BulkResetResponse{BulkID: req.BulkID} if err != nil { resp.Error = err.Error() replyBulkControlIfNeeded(msg, resp) return } runtime := s.getBulkRuntime() if runtime == nil { resp.Error = errBulkRuntimeNil.Error() replyBulkControlIfNeeded(msg, resp) return } logical := messageLogicalConnSnapshot(msg) scope := serverFileScope(logical) bulk, ok := runtime.lookup(scope, req.BulkID) if !ok && req.DataID != 0 { bulk, ok = runtime.lookupByDataID(scope, req.DataID) } if !ok { resp.Error = errBulkNotFound.Error() replyBulkControlIfNeeded(msg, resp) return } if resp.BulkID == "" { resp.BulkID = bulk.ID() } bulk.markReset(bulkResetError(bulkRemoteResetError(req.Error))) resp.Accepted = true replyBulkControlIfNeeded(msg, resp) } func (s *ServerCommon) handleInboundBulkRelease(msg *Message) { req, err := decodeBulkReleaseRequest(msg) if err != nil { return } runtime := s.getBulkRuntime() if runtime == nil { return } logical := messageLogicalConnSnapshot(msg) scope := serverFileScope(logical) bulk, ok := runtime.lookup(scope, req.BulkID) if !ok && req.DataID != 0 { bulk, ok = runtime.lookupByDataID(scope, req.DataID) } if !ok { return } bulk.releaseOutboundWindow(req.Bytes, req.Chunks) } func replyBulkControlIfNeeded(msg *Message, value interface{}) { if msg == nil || !requiresSignalReplyWait(msg.TransferMsg) { return } _ = msg.ReplyObj(value) } func sendBulkOpenClient(ctx context.Context, c Client, req BulkOpenRequest) (BulkOpenResponse, error) { if c == nil { return BulkOpenResponse{}, errBulkClientNil } msg, err := c.SendObjCtx(ctx, BulkOpenSignalKey, req) if err != nil { return BulkOpenResponse{}, err } return decodeBulkOpenResponse(msg) } func sendBulkOpenServerLogical(ctx context.Context, s Server, logical *LogicalConn, req BulkOpenRequest) (BulkOpenResponse, error) { if s == nil { return BulkOpenResponse{}, errBulkServerNil } if logical == nil { return BulkOpenResponse{}, errBulkLogicalConnNil } msg, err := s.SendObjCtxLogical(ctx, logical, BulkOpenSignalKey, req) if err != nil { return BulkOpenResponse{}, err } return decodeBulkOpenResponse(msg) } func sendBulkOpenServerTransport(ctx context.Context, s Server, transport *TransportConn, req BulkOpenRequest) (BulkOpenResponse, error) { if s == nil { return BulkOpenResponse{}, errBulkServerNil } if transport == nil { return BulkOpenResponse{}, errBulkTransportNil } msg, err := s.SendObjCtxTransport(ctx, transport, BulkOpenSignalKey, req) if err != nil { return BulkOpenResponse{}, err } return decodeBulkOpenResponse(msg) } func sendBulkCloseClient(ctx context.Context, c Client, req BulkCloseRequest) (BulkCloseResponse, error) { if c == nil { return BulkCloseResponse{}, errBulkClientNil } msg, err := c.SendObjCtx(ctx, BulkCloseSignalKey, req) if err != nil { return BulkCloseResponse{}, err } return decodeBulkCloseResponse(msg) } func sendBulkCloseServerLogical(ctx context.Context, s Server, logical *LogicalConn, req BulkCloseRequest) (BulkCloseResponse, error) { if s == nil { return BulkCloseResponse{}, errBulkServerNil } if logical == nil { return BulkCloseResponse{}, errBulkLogicalConnNil } msg, err := s.SendObjCtxLogical(ctx, logical, BulkCloseSignalKey, req) if err != nil { return BulkCloseResponse{}, err } return decodeBulkCloseResponse(msg) } func sendBulkCloseServerTransport(ctx context.Context, s Server, transport *TransportConn, req BulkCloseRequest) (BulkCloseResponse, error) { if s == nil { return BulkCloseResponse{}, errBulkServerNil } if transport == nil { return BulkCloseResponse{}, errBulkTransportNil } msg, err := s.SendObjCtxTransport(ctx, transport, BulkCloseSignalKey, req) if err != nil { return BulkCloseResponse{}, err } return decodeBulkCloseResponse(msg) } func sendBulkResetClient(ctx context.Context, c Client, req BulkResetRequest) (BulkResetResponse, error) { if c == nil { return BulkResetResponse{}, errBulkClientNil } msg, err := c.SendObjCtx(ctx, BulkResetSignalKey, req) if err != nil { return BulkResetResponse{}, err } return decodeBulkResetResponse(msg) } func sendBulkResetServerLogical(ctx context.Context, s Server, logical *LogicalConn, req BulkResetRequest) (BulkResetResponse, error) { if s == nil { return BulkResetResponse{}, errBulkServerNil } if logical == nil { return BulkResetResponse{}, errBulkLogicalConnNil } msg, err := s.SendObjCtxLogical(ctx, logical, BulkResetSignalKey, req) if err != nil { return BulkResetResponse{}, err } return decodeBulkResetResponse(msg) } func sendBulkResetServerTransport(ctx context.Context, s Server, transport *TransportConn, req BulkResetRequest) (BulkResetResponse, error) { if s == nil { return BulkResetResponse{}, errBulkServerNil } if transport == nil { return BulkResetResponse{}, errBulkTransportNil } msg, err := s.SendObjCtxTransport(ctx, transport, BulkResetSignalKey, req) if err != nil { return BulkResetResponse{}, err } return decodeBulkResetResponse(msg) } func sendBulkReleaseClient(c Client, req BulkReleaseRequest) error { if c == nil { return errBulkClientNil } return c.SendObj(BulkReleaseSignalKey, req) } func sendBulkReleaseServerLogical(s Server, logical *LogicalConn, req BulkReleaseRequest) error { if s == nil { return errBulkServerNil } if logical == nil { return errBulkLogicalConnNil } return s.SendObjLogical(logical, BulkReleaseSignalKey, req) } func sendBulkReleaseServerTransport(s Server, transport *TransportConn, req BulkReleaseRequest) error { if s == nil { return errBulkServerNil } if transport == nil { return errBulkTransportNil } return s.SendObjTransport(transport, BulkReleaseSignalKey, req) } func decodeBulkOpenRequest(msg *Message) (BulkOpenRequest, error) { var req BulkOpenRequest if msg == nil { return BulkOpenRequest{}, errBulkIDEmpty } if err := msg.Value.Orm(&req); err != nil { return BulkOpenRequest{}, err } req = normalizeBulkOpenRequest(req) if req.BulkID == "" { return BulkOpenRequest{}, errBulkIDEmpty } if !validBulkRange(req.Range) { return BulkOpenRequest{}, errBulkRangeInvalid } return req, nil } func decodeBulkCloseRequest(msg *Message) (BulkCloseRequest, error) { var req BulkCloseRequest if msg == nil { return BulkCloseRequest{}, errBulkIDEmpty } if err := msg.Value.Orm(&req); err != nil { return BulkCloseRequest{}, err } if req.BulkID == "" { return BulkCloseRequest{}, errBulkIDEmpty } return req, nil } func decodeBulkResetRequest(msg *Message) (BulkResetRequest, error) { var req BulkResetRequest if msg == nil { return BulkResetRequest{}, errBulkIDEmpty } if err := msg.Value.Orm(&req); err != nil { return BulkResetRequest{}, err } if req.BulkID == "" && req.DataID == 0 { return BulkResetRequest{}, errBulkIDEmpty } return req, nil } func decodeBulkReleaseRequest(msg *Message) (BulkReleaseRequest, error) { var req BulkReleaseRequest if msg == nil { return BulkReleaseRequest{}, errBulkIDEmpty } if err := msg.Value.Orm(&req); err != nil { return BulkReleaseRequest{}, err } if req.BulkID == "" && req.DataID == 0 { return BulkReleaseRequest{}, errBulkIDEmpty } if req.Bytes < 0 || req.Chunks < 0 { return BulkReleaseRequest{}, errBulkRangeInvalid } return req, nil } func decodeBulkOpenResponse(msg Message) (BulkOpenResponse, error) { var resp BulkOpenResponse if err := msg.Value.Orm(&resp); err != nil { return BulkOpenResponse{}, err } return resp, bulkControlResultError("open", resp.Accepted, resp.Error, nil) } func decodeBulkCloseResponse(msg Message) (BulkCloseResponse, error) { var resp BulkCloseResponse if err := msg.Value.Orm(&resp); err != nil { return BulkCloseResponse{}, err } return resp, bulkControlResultError("close", resp.Accepted, resp.Error, nil) } func decodeBulkResetResponse(msg Message) (BulkResetResponse, error) { var resp BulkResetResponse if err := msg.Value.Orm(&resp); err != nil { return BulkResetResponse{}, err } return resp, bulkControlResultError("reset", resp.Accepted, resp.Error, nil) } func bulkControlResultError(op string, accepted bool, message string, callErr error) error { if callErr != nil { return callErr } if message != "" { return bulkControlMessageError(message) } if accepted { return nil } if op == "open" { return errBulkRejected } return errors.New("bulk " + op + " rejected") } func bulkControlMessageError(message string) error { switch message { case errBulkNotFound.Error(): return errBulkNotFound case errBulkAlreadyExists.Error(): return errBulkAlreadyExists case errBulkHandlerNotConfigured.Error(): return errBulkHandlerNotConfigured case errBulkLogicalConnNil.Error(): return errBulkLogicalConnNil case errBulkTransportNil.Error(): return errBulkTransportNil case errBulkRuntimeNil.Error(): return errBulkRuntimeNil case errBulkIDEmpty.Error(): return errBulkIDEmpty case errBulkRangeInvalid.Error(): return errBulkRangeInvalid case errBulkDataIDEmpty.Error(): return errBulkDataIDEmpty default: return errors.New(message) } } func bulkRemoteResetError(message string) error { if message == "" { return errBulkReset } return errors.New(message) } func bulkTransportGeneration(logical *LogicalConn, transport *TransportConn) uint64 { return streamTransportGeneration(logical, transport) }