diff --git a/pkg/client/accounting.go b/pkg/client/accounting.go index 580b225..db1f8a4 100644 --- a/pkg/client/accounting.go +++ b/pkg/client/accounting.go @@ -43,6 +43,11 @@ func (c *clientImpl) GetBalance(ctx context.Context, owner *owner.ID, opts ...Ca return nil, fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) diff --git a/pkg/client/container.go b/pkg/client/container.go index b021813..02c7993 100644 --- a/pkg/client/container.go +++ b/pkg/client/container.go @@ -130,6 +130,11 @@ func (c *clientImpl) PutContainer(ctx context.Context, cnr *container.Container, return nil, err } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) @@ -166,6 +171,11 @@ func (c *clientImpl) GetContainer(ctx context.Context, id *cid.ID, opts ...CallO return nil, fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) @@ -238,6 +248,11 @@ func (c *clientImpl) ListContainers(ctx context.Context, ownerID *owner.ID, opts return nil, fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) @@ -292,6 +307,11 @@ func (c *clientImpl) DeleteContainer(ctx context.Context, id *cid.ID, opts ...Ca return fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return err + } + if err := v2signature.VerifyServiceMessage(resp); err != nil { return fmt.Errorf("can't verify response message: %w", err) } @@ -324,6 +344,11 @@ func (c *clientImpl) GetEACL(ctx context.Context, id *cid.ID, opts ...CallOption return nil, fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) @@ -388,6 +413,11 @@ func (c *clientImpl) SetEACL(ctx context.Context, eacl *eacl.Table, opts ...Call return fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return fmt.Errorf("can't verify response message: %w", err) @@ -433,6 +463,11 @@ func (c *clientImpl) AnnounceContainerUsedSpace( return fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return fmt.Errorf("can't verify response message: %w", err) diff --git a/pkg/client/netmap.go b/pkg/client/netmap.go index be551f0..6f3291c 100644 --- a/pkg/client/netmap.go +++ b/pkg/client/netmap.go @@ -67,6 +67,11 @@ func (c *clientImpl) EndpointInfo(ctx context.Context, opts ...CallOption) (*End return nil, fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) @@ -105,6 +110,11 @@ func (c *clientImpl) NetworkInfo(ctx context.Context, opts ...CallOption) (*netm return nil, fmt.Errorf("v2 NetworkInfo RPC failure: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("response message verification failed: %w", err) diff --git a/pkg/client/object.go b/pkg/client/object.go index 6fd25ad..bc0f2e5 100644 --- a/pkg/client/object.go +++ b/pkg/client/object.go @@ -313,6 +313,11 @@ func (c *clientImpl) PutObject(ctx context.Context, p *PutObjectParams, opts ... return nil, fmt.Errorf("closing the stream failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("response verification failed: %w", err) @@ -417,6 +422,11 @@ func (c *clientImpl) DeleteObject(ctx context.Context, p *DeleteObjectParams, op return fmt.Errorf("sending the request failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return fmt.Errorf("response verification failed: %w", err) @@ -623,6 +633,11 @@ loop: return nil, fmt.Errorf("reading the response failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("response verification failed: %w", err) @@ -781,6 +796,11 @@ func (c *clientImpl) GetObjectHeader(ctx context.Context, p *ObjectHeaderParams, return nil, fmt.Errorf("sending the request failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("response verification failed: %w", err) @@ -979,6 +999,11 @@ func (c *clientImpl) ObjectPayloadRangeData(ctx context.Context, p *RangeDataPar return nil, fmt.Errorf("reading the response failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("could not verify %T: %w", resp, err) @@ -1128,6 +1153,11 @@ func (c *clientImpl) objectPayloadRangeHash(ctx context.Context, p *RangeChecksu return nil, fmt.Errorf("sending the request failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("response verification failed: %w", err) @@ -1276,6 +1306,11 @@ func (c *clientImpl) SearchObject(ctx context.Context, p *SearchObjectParams, op return nil, fmt.Errorf("reading the response failed: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOpts, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("could not verify %T: %w", resp, err) diff --git a/pkg/client/opts.go b/pkg/client/opts.go index 8a998ed..af9cedf 100644 --- a/pkg/client/opts.go +++ b/pkg/client/opts.go @@ -33,6 +33,8 @@ type ( key *ecdsa.PrivateKey rawOpts []client.Option + + cbRespInfo func(ResponseMetaInfo) error } v2SessionReqInfo struct { diff --git a/pkg/client/reputation.go b/pkg/client/reputation.go index ec55218..ff82496 100644 --- a/pkg/client/reputation.go +++ b/pkg/client/reputation.go @@ -77,6 +77,11 @@ func (c *clientImpl) AnnounceLocalTrust(ctx context.Context, prm AnnounceLocalTr return nil, err } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) @@ -152,6 +157,11 @@ func (c *clientImpl) AnnounceIntermediateTrust(ctx context.Context, prm Announce return nil, err } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err) diff --git a/pkg/client/response.go b/pkg/client/response.go new file mode 100644 index 0000000..6948844 --- /dev/null +++ b/pkg/client/response.go @@ -0,0 +1,37 @@ +package client + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/session" +) + +// ResponseMetaInfo groups meta information about any NeoFS API response. +type ResponseMetaInfo struct { + key []byte +} + +// ResponderKey returns responder's public key in a binary format. +// +// Result must not be mutated. +func (x ResponseMetaInfo) ResponderKey() []byte { + return x.key +} + +// WithResponseInfoHandler allows to specify handler of response meta information for the all Client operations. +// The handler is called right after the response is received. Client returns handler's error immediately. +func WithResponseInfoHandler(f func(ResponseMetaInfo) error) Option { + return func(opts *clientOptions) { + opts.cbRespInfo = f + } +} + +func (c *clientImpl) handleResponseInfoV2(opts *callOptions, resp interface { + GetVerificationHeader() *session.ResponseVerificationHeader +}) error { + if c.opts.cbRespInfo == nil { + return nil + } + + return c.opts.cbRespInfo(ResponseMetaInfo{ + key: resp.GetVerificationHeader().GetBodySignature().GetKey(), + }) +} diff --git a/pkg/client/session.go b/pkg/client/session.go index 24bae6a..46ec399 100644 --- a/pkg/client/session.go +++ b/pkg/client/session.go @@ -55,6 +55,11 @@ func (c *clientImpl) CreateSession(ctx context.Context, expiration uint64, opts return nil, fmt.Errorf("transport error: %w", err) } + // handle response meta info + if err := c.handleResponseInfoV2(callOptions, resp); err != nil { + return nil, err + } + err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, fmt.Errorf("can't verify response message: %w", err)