mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-02 22:17:10 +08:00

* Return Auth service Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Compose to run with SpiceDB and Auth svc Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth gRPC API Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove Users' policies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Groups to internal Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use shared groups in Users Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove unused code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use pkg Groups in Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove Things groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Make imports consistent Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Groups networking Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Remove things groups-specific API Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Things Clients to the root Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Move Clients to Users root Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Temporarily remove tracing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix imports Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add buffer config for gRPC Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth type for Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use Auth for login Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add temporary solution for refresh token Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Tokenizer interface Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Updade tokens issuing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix token issuing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update JWT validator and refactor Tokenizer Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Rename access timeout Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Rename login to authenticate Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update Identify to use SubjectID Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add Auth to Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Use the Auth service for Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update auth schema Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix Auth for Groups Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add auth for addons (#14) Signed-off-by: Arvindh <arvindh91@gmail.com> Speparate Login and Refresh tokens Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Merge authN and authZ requests for things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add connect and disconnect Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update sharing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Fix policies addition and removal Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Update relation with roels Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Add gRPC to Things Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Assign and Unassign members to group and Listing of Group members (#15) * add auth for addons Signed-off-by: Arvindh <arvindh91@gmail.com> * add assign and unassign to group Signed-off-by: Arvindh <arvindh91@gmail.com> * add group incomplete repo implementation Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Move coap mqtt and ws policies to spicedb (#16) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Remove old policies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> NOISSUE - Things authorize to return thingID (#18) This commit modifies the authorize endpoint to the grpc endpoint to return thingID. The authorize endpoint allows adapters to get the publisher of the message. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Add Groups to users service (#17) * add assign and unassign to group Signed-off-by: Arvindh <arvindh91@gmail.com> * add group incomplete repo implementation Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users stable 1 Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users stable 2 Signed-off-by: Arvindh <arvindh91@gmail.com> * groups for users & things Signed-off-by: Arvindh <arvindh91@gmail.com> * Amend signature Signed-off-by: Arvindh <arvindh91@gmail.com> * fix merge error Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Fix es code (#21) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Fix Bugs (#20) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Test e2e (#19) * fix: connect method Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * fix: e2e Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * fix changes in sdk and e2e Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(docker): remove unnecessary port mapping Remove the port mapping for MQTT broker in the docker-compose.yml file. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Enable group listing Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(responses): update ChannelsPage struct The ChannelsPage struct in the responses.go file has been updated. The "Channels" field has been renamed to "Groups" to provide more accurate naming. This change ensures consistency and clarity in the codebase. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(things): add UpdateClientSecret method Add the UpdateClientSecret method to the things service. This method allows updating the client secret for a specific client identified by the provided token, id, and key parameters. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Use smaller buffers for gRPC Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Clean up tests (#22) Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add Connect Disconnect endpoints (#23) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Things share with users (#25) * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * add: things share with other users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Rename gRPC Services (#24) * Rename things and users auth service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * docs: add authorization docs for gRPC services Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Rename things and users grpc services Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * Remove mainflux.env package Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Listing of things, channels, groups, users (#26) * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Users (#27) * feat(groups): rename redis package to events - Renamed the `redis` package to `events` in the `internal/groups` directory. - Updated the file paths and names accordingly. - This change reflects the more accurate purpose of the package and improves code organization. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(auth): Modify identity method Change request and response of identity method Add accessToken and refreshToken to Token response Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * clean up users, remove dead code Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(users): add unit tests for user service This commit adds unit tests for the user service in the `users` package. The tests cover various scenarios and ensure the correct behavior of the service. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: List of user groups & removed repeating code in groups (#29) * removed repeating code in list groups Signed-off-by: Arvindh <arvindh91@gmail.com> * add: list of user group Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: otel handler operator name for endpoints Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Things Service (#28) * Rework things service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * add tests Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Clean Up Auth Service (#30) * clean up auth service Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> * feat(auth): remove unused import Remove the unused import of `emptypb` in `auth.pb.go`. This import is not being used in the codebase and can be safely removed. Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> --------- Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * NOISSUE - Update API docs (#31) Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove TODO comments and cleanup the code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update dependenices Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Co-authored-by: b1ackd0t <28790446+rodneyosodo@users.noreply.github.com> Co-authored-by: Arvindh <30824765+arvindh123@users.noreply.github.com>
960 lines
29 KiB
Go
960 lines
29 KiB
Go
/*
|
|
*
|
|
* Copyright 2014 gRPC authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
package grpc
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/encoding"
|
|
"google.golang.org/grpc/encoding/proto"
|
|
"google.golang.org/grpc/internal/transport"
|
|
"google.golang.org/grpc/metadata"
|
|
"google.golang.org/grpc/peer"
|
|
"google.golang.org/grpc/stats"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// Compressor defines the interface gRPC uses to compress a message.
|
|
//
|
|
// Deprecated: use package encoding.
|
|
type Compressor interface {
|
|
// Do compresses p into w.
|
|
Do(w io.Writer, p []byte) error
|
|
// Type returns the compression algorithm the Compressor uses.
|
|
Type() string
|
|
}
|
|
|
|
type gzipCompressor struct {
|
|
pool sync.Pool
|
|
}
|
|
|
|
// NewGZIPCompressor creates a Compressor based on GZIP.
|
|
//
|
|
// Deprecated: use package encoding/gzip.
|
|
func NewGZIPCompressor() Compressor {
|
|
c, _ := NewGZIPCompressorWithLevel(gzip.DefaultCompression)
|
|
return c
|
|
}
|
|
|
|
// NewGZIPCompressorWithLevel is like NewGZIPCompressor but specifies the gzip compression level instead
|
|
// of assuming DefaultCompression.
|
|
//
|
|
// The error returned will be nil if the level is valid.
|
|
//
|
|
// Deprecated: use package encoding/gzip.
|
|
func NewGZIPCompressorWithLevel(level int) (Compressor, error) {
|
|
if level < gzip.DefaultCompression || level > gzip.BestCompression {
|
|
return nil, fmt.Errorf("grpc: invalid compression level: %d", level)
|
|
}
|
|
return &gzipCompressor{
|
|
pool: sync.Pool{
|
|
New: func() any {
|
|
w, err := gzip.NewWriterLevel(io.Discard, level)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return w
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (c *gzipCompressor) Do(w io.Writer, p []byte) error {
|
|
z := c.pool.Get().(*gzip.Writer)
|
|
defer c.pool.Put(z)
|
|
z.Reset(w)
|
|
if _, err := z.Write(p); err != nil {
|
|
return err
|
|
}
|
|
return z.Close()
|
|
}
|
|
|
|
func (c *gzipCompressor) Type() string {
|
|
return "gzip"
|
|
}
|
|
|
|
// Decompressor defines the interface gRPC uses to decompress a message.
|
|
//
|
|
// Deprecated: use package encoding.
|
|
type Decompressor interface {
|
|
// Do reads the data from r and uncompress them.
|
|
Do(r io.Reader) ([]byte, error)
|
|
// Type returns the compression algorithm the Decompressor uses.
|
|
Type() string
|
|
}
|
|
|
|
type gzipDecompressor struct {
|
|
pool sync.Pool
|
|
}
|
|
|
|
// NewGZIPDecompressor creates a Decompressor based on GZIP.
|
|
//
|
|
// Deprecated: use package encoding/gzip.
|
|
func NewGZIPDecompressor() Decompressor {
|
|
return &gzipDecompressor{}
|
|
}
|
|
|
|
func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
|
|
var z *gzip.Reader
|
|
switch maybeZ := d.pool.Get().(type) {
|
|
case nil:
|
|
newZ, err := gzip.NewReader(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
z = newZ
|
|
case *gzip.Reader:
|
|
z = maybeZ
|
|
if err := z.Reset(r); err != nil {
|
|
d.pool.Put(z)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
defer func() {
|
|
z.Close()
|
|
d.pool.Put(z)
|
|
}()
|
|
return io.ReadAll(z)
|
|
}
|
|
|
|
func (d *gzipDecompressor) Type() string {
|
|
return "gzip"
|
|
}
|
|
|
|
// callInfo contains all related configuration and information about an RPC.
|
|
type callInfo struct {
|
|
compressorType string
|
|
failFast bool
|
|
maxReceiveMessageSize *int
|
|
maxSendMessageSize *int
|
|
creds credentials.PerRPCCredentials
|
|
contentSubtype string
|
|
codec baseCodec
|
|
maxRetryRPCBufferSize int
|
|
onFinish []func(err error)
|
|
}
|
|
|
|
func defaultCallInfo() *callInfo {
|
|
return &callInfo{
|
|
failFast: true,
|
|
maxRetryRPCBufferSize: 256 * 1024, // 256KB
|
|
}
|
|
}
|
|
|
|
// CallOption configures a Call before it starts or extracts information from
|
|
// a Call after it completes.
|
|
type CallOption interface {
|
|
// before is called before the call is sent to any server. If before
|
|
// returns a non-nil error, the RPC fails with that error.
|
|
before(*callInfo) error
|
|
|
|
// after is called after the call has completed. after cannot return an
|
|
// error, so any failures should be reported via output parameters.
|
|
after(*callInfo, *csAttempt)
|
|
}
|
|
|
|
// EmptyCallOption does not alter the Call configuration.
|
|
// It can be embedded in another structure to carry satellite data for use
|
|
// by interceptors.
|
|
type EmptyCallOption struct{}
|
|
|
|
func (EmptyCallOption) before(*callInfo) error { return nil }
|
|
func (EmptyCallOption) after(*callInfo, *csAttempt) {}
|
|
|
|
// Header returns a CallOptions that retrieves the header metadata
|
|
// for a unary RPC.
|
|
func Header(md *metadata.MD) CallOption {
|
|
return HeaderCallOption{HeaderAddr: md}
|
|
}
|
|
|
|
// HeaderCallOption is a CallOption for collecting response header metadata.
|
|
// The metadata field will be populated *after* the RPC completes.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type HeaderCallOption struct {
|
|
HeaderAddr *metadata.MD
|
|
}
|
|
|
|
func (o HeaderCallOption) before(c *callInfo) error { return nil }
|
|
func (o HeaderCallOption) after(c *callInfo, attempt *csAttempt) {
|
|
*o.HeaderAddr, _ = attempt.s.Header()
|
|
}
|
|
|
|
// Trailer returns a CallOptions that retrieves the trailer metadata
|
|
// for a unary RPC.
|
|
func Trailer(md *metadata.MD) CallOption {
|
|
return TrailerCallOption{TrailerAddr: md}
|
|
}
|
|
|
|
// TrailerCallOption is a CallOption for collecting response trailer metadata.
|
|
// The metadata field will be populated *after* the RPC completes.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type TrailerCallOption struct {
|
|
TrailerAddr *metadata.MD
|
|
}
|
|
|
|
func (o TrailerCallOption) before(c *callInfo) error { return nil }
|
|
func (o TrailerCallOption) after(c *callInfo, attempt *csAttempt) {
|
|
*o.TrailerAddr = attempt.s.Trailer()
|
|
}
|
|
|
|
// Peer returns a CallOption that retrieves peer information for a unary RPC.
|
|
// The peer field will be populated *after* the RPC completes.
|
|
func Peer(p *peer.Peer) CallOption {
|
|
return PeerCallOption{PeerAddr: p}
|
|
}
|
|
|
|
// PeerCallOption is a CallOption for collecting the identity of the remote
|
|
// peer. The peer field will be populated *after* the RPC completes.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type PeerCallOption struct {
|
|
PeerAddr *peer.Peer
|
|
}
|
|
|
|
func (o PeerCallOption) before(c *callInfo) error { return nil }
|
|
func (o PeerCallOption) after(c *callInfo, attempt *csAttempt) {
|
|
if x, ok := peer.FromContext(attempt.s.Context()); ok {
|
|
*o.PeerAddr = *x
|
|
}
|
|
}
|
|
|
|
// WaitForReady configures the action to take when an RPC is attempted on broken
|
|
// connections or unreachable servers. If waitForReady is false and the
|
|
// connection is in the TRANSIENT_FAILURE state, the RPC will fail
|
|
// immediately. Otherwise, the RPC client will block the call until a
|
|
// connection is available (or the call is canceled or times out) and will
|
|
// retry the call if it fails due to a transient error. gRPC will not retry if
|
|
// data was written to the wire unless the server indicates it did not process
|
|
// the data. Please refer to
|
|
// https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
|
|
//
|
|
// By default, RPCs don't "wait for ready".
|
|
func WaitForReady(waitForReady bool) CallOption {
|
|
return FailFastCallOption{FailFast: !waitForReady}
|
|
}
|
|
|
|
// FailFast is the opposite of WaitForReady.
|
|
//
|
|
// Deprecated: use WaitForReady.
|
|
func FailFast(failFast bool) CallOption {
|
|
return FailFastCallOption{FailFast: failFast}
|
|
}
|
|
|
|
// FailFastCallOption is a CallOption for indicating whether an RPC should fail
|
|
// fast or not.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type FailFastCallOption struct {
|
|
FailFast bool
|
|
}
|
|
|
|
func (o FailFastCallOption) before(c *callInfo) error {
|
|
c.failFast = o.FailFast
|
|
return nil
|
|
}
|
|
func (o FailFastCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// OnFinish returns a CallOption that configures a callback to be called when
|
|
// the call completes. The error passed to the callback is the status of the
|
|
// RPC, and may be nil. The onFinish callback provided will only be called once
|
|
// by gRPC. This is mainly used to be used by streaming interceptors, to be
|
|
// notified when the RPC completes along with information about the status of
|
|
// the RPC.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
func OnFinish(onFinish func(err error)) CallOption {
|
|
return OnFinishCallOption{
|
|
OnFinish: onFinish,
|
|
}
|
|
}
|
|
|
|
// OnFinishCallOption is CallOption that indicates a callback to be called when
|
|
// the call completes.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type OnFinishCallOption struct {
|
|
OnFinish func(error)
|
|
}
|
|
|
|
func (o OnFinishCallOption) before(c *callInfo) error {
|
|
c.onFinish = append(c.onFinish, o.OnFinish)
|
|
return nil
|
|
}
|
|
|
|
func (o OnFinishCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// MaxCallRecvMsgSize returns a CallOption which sets the maximum message size
|
|
// in bytes the client can receive. If this is not set, gRPC uses the default
|
|
// 4MB.
|
|
func MaxCallRecvMsgSize(bytes int) CallOption {
|
|
return MaxRecvMsgSizeCallOption{MaxRecvMsgSize: bytes}
|
|
}
|
|
|
|
// MaxRecvMsgSizeCallOption is a CallOption that indicates the maximum message
|
|
// size in bytes the client can receive.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type MaxRecvMsgSizeCallOption struct {
|
|
MaxRecvMsgSize int
|
|
}
|
|
|
|
func (o MaxRecvMsgSizeCallOption) before(c *callInfo) error {
|
|
c.maxReceiveMessageSize = &o.MaxRecvMsgSize
|
|
return nil
|
|
}
|
|
func (o MaxRecvMsgSizeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// MaxCallSendMsgSize returns a CallOption which sets the maximum message size
|
|
// in bytes the client can send. If this is not set, gRPC uses the default
|
|
// `math.MaxInt32`.
|
|
func MaxCallSendMsgSize(bytes int) CallOption {
|
|
return MaxSendMsgSizeCallOption{MaxSendMsgSize: bytes}
|
|
}
|
|
|
|
// MaxSendMsgSizeCallOption is a CallOption that indicates the maximum message
|
|
// size in bytes the client can send.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type MaxSendMsgSizeCallOption struct {
|
|
MaxSendMsgSize int
|
|
}
|
|
|
|
func (o MaxSendMsgSizeCallOption) before(c *callInfo) error {
|
|
c.maxSendMessageSize = &o.MaxSendMsgSize
|
|
return nil
|
|
}
|
|
func (o MaxSendMsgSizeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// PerRPCCredentials returns a CallOption that sets credentials.PerRPCCredentials
|
|
// for a call.
|
|
func PerRPCCredentials(creds credentials.PerRPCCredentials) CallOption {
|
|
return PerRPCCredsCallOption{Creds: creds}
|
|
}
|
|
|
|
// PerRPCCredsCallOption is a CallOption that indicates the per-RPC
|
|
// credentials to use for the call.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type PerRPCCredsCallOption struct {
|
|
Creds credentials.PerRPCCredentials
|
|
}
|
|
|
|
func (o PerRPCCredsCallOption) before(c *callInfo) error {
|
|
c.creds = o.Creds
|
|
return nil
|
|
}
|
|
func (o PerRPCCredsCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// UseCompressor returns a CallOption which sets the compressor used when
|
|
// sending the request. If WithCompressor is also set, UseCompressor has
|
|
// higher priority.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
func UseCompressor(name string) CallOption {
|
|
return CompressorCallOption{CompressorType: name}
|
|
}
|
|
|
|
// CompressorCallOption is a CallOption that indicates the compressor to use.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type CompressorCallOption struct {
|
|
CompressorType string
|
|
}
|
|
|
|
func (o CompressorCallOption) before(c *callInfo) error {
|
|
c.compressorType = o.CompressorType
|
|
return nil
|
|
}
|
|
func (o CompressorCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// CallContentSubtype returns a CallOption that will set the content-subtype
|
|
// for a call. For example, if content-subtype is "json", the Content-Type over
|
|
// the wire will be "application/grpc+json". The content-subtype is converted
|
|
// to lowercase before being included in Content-Type. See Content-Type on
|
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
|
// more details.
|
|
//
|
|
// If ForceCodec is not also used, the content-subtype will be used to look up
|
|
// the Codec to use in the registry controlled by RegisterCodec. See the
|
|
// documentation on RegisterCodec for details on registration. The lookup of
|
|
// content-subtype is case-insensitive. If no such Codec is found, the call
|
|
// will result in an error with code codes.Internal.
|
|
//
|
|
// If ForceCodec is also used, that Codec will be used for all request and
|
|
// response messages, with the content-subtype set to the given contentSubtype
|
|
// here for requests.
|
|
func CallContentSubtype(contentSubtype string) CallOption {
|
|
return ContentSubtypeCallOption{ContentSubtype: strings.ToLower(contentSubtype)}
|
|
}
|
|
|
|
// ContentSubtypeCallOption is a CallOption that indicates the content-subtype
|
|
// used for marshaling messages.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type ContentSubtypeCallOption struct {
|
|
ContentSubtype string
|
|
}
|
|
|
|
func (o ContentSubtypeCallOption) before(c *callInfo) error {
|
|
c.contentSubtype = o.ContentSubtype
|
|
return nil
|
|
}
|
|
func (o ContentSubtypeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// ForceCodec returns a CallOption that will set codec to be used for all
|
|
// request and response messages for a call. The result of calling Name() will
|
|
// be used as the content-subtype after converting to lowercase, unless
|
|
// CallContentSubtype is also used.
|
|
//
|
|
// See Content-Type on
|
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
|
// more details. Also see the documentation on RegisterCodec and
|
|
// CallContentSubtype for more details on the interaction between Codec and
|
|
// content-subtype.
|
|
//
|
|
// This function is provided for advanced users; prefer to use only
|
|
// CallContentSubtype to select a registered codec instead.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
func ForceCodec(codec encoding.Codec) CallOption {
|
|
return ForceCodecCallOption{Codec: codec}
|
|
}
|
|
|
|
// ForceCodecCallOption is a CallOption that indicates the codec used for
|
|
// marshaling messages.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type ForceCodecCallOption struct {
|
|
Codec encoding.Codec
|
|
}
|
|
|
|
func (o ForceCodecCallOption) before(c *callInfo) error {
|
|
c.codec = o.Codec
|
|
return nil
|
|
}
|
|
func (o ForceCodecCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// CallCustomCodec behaves like ForceCodec, but accepts a grpc.Codec instead of
|
|
// an encoding.Codec.
|
|
//
|
|
// Deprecated: use ForceCodec instead.
|
|
func CallCustomCodec(codec Codec) CallOption {
|
|
return CustomCodecCallOption{Codec: codec}
|
|
}
|
|
|
|
// CustomCodecCallOption is a CallOption that indicates the codec used for
|
|
// marshaling messages.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type CustomCodecCallOption struct {
|
|
Codec Codec
|
|
}
|
|
|
|
func (o CustomCodecCallOption) before(c *callInfo) error {
|
|
c.codec = o.Codec
|
|
return nil
|
|
}
|
|
func (o CustomCodecCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// MaxRetryRPCBufferSize returns a CallOption that limits the amount of memory
|
|
// used for buffering this RPC's requests for retry purposes.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
func MaxRetryRPCBufferSize(bytes int) CallOption {
|
|
return MaxRetryRPCBufferSizeCallOption{bytes}
|
|
}
|
|
|
|
// MaxRetryRPCBufferSizeCallOption is a CallOption indicating the amount of
|
|
// memory to be used for caching this RPC for retry purposes.
|
|
//
|
|
// # Experimental
|
|
//
|
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
|
// later release.
|
|
type MaxRetryRPCBufferSizeCallOption struct {
|
|
MaxRetryRPCBufferSize int
|
|
}
|
|
|
|
func (o MaxRetryRPCBufferSizeCallOption) before(c *callInfo) error {
|
|
c.maxRetryRPCBufferSize = o.MaxRetryRPCBufferSize
|
|
return nil
|
|
}
|
|
func (o MaxRetryRPCBufferSizeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
|
|
|
// The format of the payload: compressed or not?
|
|
type payloadFormat uint8
|
|
|
|
const (
|
|
compressionNone payloadFormat = 0 // no compression
|
|
compressionMade payloadFormat = 1 // compressed
|
|
)
|
|
|
|
// parser reads complete gRPC messages from the underlying reader.
|
|
type parser struct {
|
|
// r is the underlying reader.
|
|
// See the comment on recvMsg for the permissible
|
|
// error types.
|
|
r io.Reader
|
|
|
|
// The header of a gRPC message. Find more detail at
|
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
|
|
header [5]byte
|
|
|
|
// recvBufferPool is the pool of shared receive buffers.
|
|
recvBufferPool SharedBufferPool
|
|
}
|
|
|
|
// recvMsg reads a complete gRPC message from the stream.
|
|
//
|
|
// It returns the message and its payload (compression/encoding)
|
|
// format. The caller owns the returned msg memory.
|
|
//
|
|
// If there is an error, possible values are:
|
|
// - io.EOF, when no messages remain
|
|
// - io.ErrUnexpectedEOF
|
|
// - of type transport.ConnectionError
|
|
// - an error from the status package
|
|
//
|
|
// No other error values or types must be returned, which also means
|
|
// that the underlying io.Reader must not return an incompatible
|
|
// error.
|
|
func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byte, err error) {
|
|
if _, err := p.r.Read(p.header[:]); err != nil {
|
|
return 0, nil, err
|
|
}
|
|
|
|
pf = payloadFormat(p.header[0])
|
|
length := binary.BigEndian.Uint32(p.header[1:])
|
|
|
|
if length == 0 {
|
|
return pf, nil, nil
|
|
}
|
|
if int64(length) > int64(maxInt) {
|
|
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max length allowed on current machine (%d vs. %d)", length, maxInt)
|
|
}
|
|
if int(length) > maxReceiveMessageSize {
|
|
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize)
|
|
}
|
|
msg = p.recvBufferPool.Get(int(length))
|
|
if _, err := p.r.Read(msg); err != nil {
|
|
if err == io.EOF {
|
|
err = io.ErrUnexpectedEOF
|
|
}
|
|
return 0, nil, err
|
|
}
|
|
return pf, msg, nil
|
|
}
|
|
|
|
// encode serializes msg and returns a buffer containing the message, or an
|
|
// error if it is too large to be transmitted by grpc. If msg is nil, it
|
|
// generates an empty message.
|
|
func encode(c baseCodec, msg any) ([]byte, error) {
|
|
if msg == nil { // NOTE: typed nils will not be caught by this check
|
|
return nil, nil
|
|
}
|
|
b, err := c.Marshal(msg)
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error())
|
|
}
|
|
if uint(len(b)) > math.MaxUint32 {
|
|
return nil, status.Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", len(b))
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// compress returns the input bytes compressed by compressor or cp. If both
|
|
// compressors are nil, returns nil.
|
|
//
|
|
// TODO(dfawley): eliminate cp parameter by wrapping Compressor in an encoding.Compressor.
|
|
func compress(in []byte, cp Compressor, compressor encoding.Compressor) ([]byte, error) {
|
|
if compressor == nil && cp == nil {
|
|
return nil, nil
|
|
}
|
|
wrapErr := func(err error) error {
|
|
return status.Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error())
|
|
}
|
|
cbuf := &bytes.Buffer{}
|
|
if compressor != nil {
|
|
z, err := compressor.Compress(cbuf)
|
|
if err != nil {
|
|
return nil, wrapErr(err)
|
|
}
|
|
if _, err := z.Write(in); err != nil {
|
|
return nil, wrapErr(err)
|
|
}
|
|
if err := z.Close(); err != nil {
|
|
return nil, wrapErr(err)
|
|
}
|
|
} else {
|
|
if err := cp.Do(cbuf, in); err != nil {
|
|
return nil, wrapErr(err)
|
|
}
|
|
}
|
|
return cbuf.Bytes(), nil
|
|
}
|
|
|
|
const (
|
|
payloadLen = 1
|
|
sizeLen = 4
|
|
headerLen = payloadLen + sizeLen
|
|
)
|
|
|
|
// msgHeader returns a 5-byte header for the message being transmitted and the
|
|
// payload, which is compData if non-nil or data otherwise.
|
|
func msgHeader(data, compData []byte) (hdr []byte, payload []byte) {
|
|
hdr = make([]byte, headerLen)
|
|
if compData != nil {
|
|
hdr[0] = byte(compressionMade)
|
|
data = compData
|
|
} else {
|
|
hdr[0] = byte(compressionNone)
|
|
}
|
|
|
|
// Write length of payload into buf
|
|
binary.BigEndian.PutUint32(hdr[payloadLen:], uint32(len(data)))
|
|
return hdr, data
|
|
}
|
|
|
|
func outPayload(client bool, msg any, data, payload []byte, t time.Time) *stats.OutPayload {
|
|
return &stats.OutPayload{
|
|
Client: client,
|
|
Payload: msg,
|
|
Data: data,
|
|
Length: len(data),
|
|
WireLength: len(payload) + headerLen,
|
|
CompressedLength: len(payload),
|
|
SentTime: t,
|
|
}
|
|
}
|
|
|
|
func checkRecvPayload(pf payloadFormat, recvCompress string, haveCompressor bool) *status.Status {
|
|
switch pf {
|
|
case compressionNone:
|
|
case compressionMade:
|
|
if recvCompress == "" || recvCompress == encoding.Identity {
|
|
return status.New(codes.Internal, "grpc: compressed flag set with identity or empty encoding")
|
|
}
|
|
if !haveCompressor {
|
|
return status.Newf(codes.Unimplemented, "grpc: Decompressor is not installed for grpc-encoding %q", recvCompress)
|
|
}
|
|
default:
|
|
return status.Newf(codes.Internal, "grpc: received unexpected payload format %d", pf)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type payloadInfo struct {
|
|
compressedLength int // The compressed length got from wire.
|
|
uncompressedBytes []byte
|
|
}
|
|
|
|
func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) ([]byte, error) {
|
|
pf, buf, err := p.recvMsg(maxReceiveMessageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if payInfo != nil {
|
|
payInfo.compressedLength = len(buf)
|
|
}
|
|
|
|
if st := checkRecvPayload(pf, s.RecvCompress(), compressor != nil || dc != nil); st != nil {
|
|
return nil, st.Err()
|
|
}
|
|
|
|
var size int
|
|
if pf == compressionMade {
|
|
// To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor,
|
|
// use this decompressor as the default.
|
|
if dc != nil {
|
|
buf, err = dc.Do(bytes.NewReader(buf))
|
|
size = len(buf)
|
|
} else {
|
|
buf, size, err = decompress(compressor, buf, maxReceiveMessageSize)
|
|
}
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err)
|
|
}
|
|
if size > maxReceiveMessageSize {
|
|
// TODO: Revisit the error code. Currently keep it consistent with java
|
|
// implementation.
|
|
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize)
|
|
}
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
// Using compressor, decompress d, returning data and size.
|
|
// Optionally, if data will be over maxReceiveMessageSize, just return the size.
|
|
func decompress(compressor encoding.Compressor, d []byte, maxReceiveMessageSize int) ([]byte, int, error) {
|
|
dcReader, err := compressor.Decompress(bytes.NewReader(d))
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if sizer, ok := compressor.(interface {
|
|
DecompressedSize(compressedBytes []byte) int
|
|
}); ok {
|
|
if size := sizer.DecompressedSize(d); size >= 0 {
|
|
if size > maxReceiveMessageSize {
|
|
return nil, size, nil
|
|
}
|
|
// size is used as an estimate to size the buffer, but we
|
|
// will read more data if available.
|
|
// +MinRead so ReadFrom will not reallocate if size is correct.
|
|
buf := bytes.NewBuffer(make([]byte, 0, size+bytes.MinRead))
|
|
bytesRead, err := buf.ReadFrom(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1))
|
|
return buf.Bytes(), int(bytesRead), err
|
|
}
|
|
}
|
|
// Read from LimitReader with limit max+1. So if the underlying
|
|
// reader is over limit, the result will be bigger than max.
|
|
d, err = io.ReadAll(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1))
|
|
return d, len(d), err
|
|
}
|
|
|
|
// For the two compressor parameters, both should not be set, but if they are,
|
|
// dc takes precedence over compressor.
|
|
// TODO(dfawley): wrap the old compressor/decompressor using the new API?
|
|
func recv(p *parser, c baseCodec, s *transport.Stream, dc Decompressor, m any, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) error {
|
|
buf, err := recvAndDecompress(p, s, dc, maxReceiveMessageSize, payInfo, compressor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := c.Unmarshal(buf, m); err != nil {
|
|
return status.Errorf(codes.Internal, "grpc: failed to unmarshal the received message: %v", err)
|
|
}
|
|
if payInfo != nil {
|
|
payInfo.uncompressedBytes = buf
|
|
} else {
|
|
p.recvBufferPool.Put(&buf)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Information about RPC
|
|
type rpcInfo struct {
|
|
failfast bool
|
|
preloaderInfo *compressorInfo
|
|
}
|
|
|
|
// Information about Preloader
|
|
// Responsible for storing codec, and compressors
|
|
// If stream (s) has context s.Context which stores rpcInfo that has non nil
|
|
// pointers to codec, and compressors, then we can use preparedMsg for Async message prep
|
|
// and reuse marshalled bytes
|
|
type compressorInfo struct {
|
|
codec baseCodec
|
|
cp Compressor
|
|
comp encoding.Compressor
|
|
}
|
|
|
|
type rpcInfoContextKey struct{}
|
|
|
|
func newContextWithRPCInfo(ctx context.Context, failfast bool, codec baseCodec, cp Compressor, comp encoding.Compressor) context.Context {
|
|
return context.WithValue(ctx, rpcInfoContextKey{}, &rpcInfo{
|
|
failfast: failfast,
|
|
preloaderInfo: &compressorInfo{
|
|
codec: codec,
|
|
cp: cp,
|
|
comp: comp,
|
|
},
|
|
})
|
|
}
|
|
|
|
func rpcInfoFromContext(ctx context.Context) (s *rpcInfo, ok bool) {
|
|
s, ok = ctx.Value(rpcInfoContextKey{}).(*rpcInfo)
|
|
return
|
|
}
|
|
|
|
// Code returns the error code for err if it was produced by the rpc system.
|
|
// Otherwise, it returns codes.Unknown.
|
|
//
|
|
// Deprecated: use status.Code instead.
|
|
func Code(err error) codes.Code {
|
|
return status.Code(err)
|
|
}
|
|
|
|
// ErrorDesc returns the error description of err if it was produced by the rpc system.
|
|
// Otherwise, it returns err.Error() or empty string when err is nil.
|
|
//
|
|
// Deprecated: use status.Convert and Message method instead.
|
|
func ErrorDesc(err error) string {
|
|
return status.Convert(err).Message()
|
|
}
|
|
|
|
// Errorf returns an error containing an error code and a description;
|
|
// Errorf returns nil if c is OK.
|
|
//
|
|
// Deprecated: use status.Errorf instead.
|
|
func Errorf(c codes.Code, format string, a ...any) error {
|
|
return status.Errorf(c, format, a...)
|
|
}
|
|
|
|
var errContextCanceled = status.Error(codes.Canceled, context.Canceled.Error())
|
|
var errContextDeadline = status.Error(codes.DeadlineExceeded, context.DeadlineExceeded.Error())
|
|
|
|
// toRPCErr converts an error into an error from the status package.
|
|
func toRPCErr(err error) error {
|
|
switch err {
|
|
case nil, io.EOF:
|
|
return err
|
|
case context.DeadlineExceeded:
|
|
return errContextDeadline
|
|
case context.Canceled:
|
|
return errContextCanceled
|
|
case io.ErrUnexpectedEOF:
|
|
return status.Error(codes.Internal, err.Error())
|
|
}
|
|
|
|
switch e := err.(type) {
|
|
case transport.ConnectionError:
|
|
return status.Error(codes.Unavailable, e.Desc)
|
|
case *transport.NewStreamError:
|
|
return toRPCErr(e.Err)
|
|
}
|
|
|
|
if _, ok := status.FromError(err); ok {
|
|
return err
|
|
}
|
|
|
|
return status.Error(codes.Unknown, err.Error())
|
|
}
|
|
|
|
// setCallInfoCodec should only be called after CallOptions have been applied.
|
|
func setCallInfoCodec(c *callInfo) error {
|
|
if c.codec != nil {
|
|
// codec was already set by a CallOption; use it, but set the content
|
|
// subtype if it is not set.
|
|
if c.contentSubtype == "" {
|
|
// c.codec is a baseCodec to hide the difference between grpc.Codec and
|
|
// encoding.Codec (Name vs. String method name). We only support
|
|
// setting content subtype from encoding.Codec to avoid a behavior
|
|
// change with the deprecated version.
|
|
if ec, ok := c.codec.(encoding.Codec); ok {
|
|
c.contentSubtype = strings.ToLower(ec.Name())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if c.contentSubtype == "" {
|
|
// No codec specified in CallOptions; use proto by default.
|
|
c.codec = encoding.GetCodec(proto.Name)
|
|
return nil
|
|
}
|
|
|
|
// c.contentSubtype is already lowercased in CallContentSubtype
|
|
c.codec = encoding.GetCodec(c.contentSubtype)
|
|
if c.codec == nil {
|
|
return status.Errorf(codes.Internal, "no codec registered for content-subtype %s", c.contentSubtype)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// channelzData is used to store channelz related data for ClientConn, addrConn and Server.
|
|
// These fields cannot be embedded in the original structs (e.g. ClientConn), since to do atomic
|
|
// operation on int64 variable on 32-bit machine, user is responsible to enforce memory alignment.
|
|
// Here, by grouping those int64 fields inside a struct, we are enforcing the alignment.
|
|
type channelzData struct {
|
|
callsStarted int64
|
|
callsFailed int64
|
|
callsSucceeded int64
|
|
// lastCallStartedTime stores the timestamp that last call starts. It is of int64 type instead of
|
|
// time.Time since it's more costly to atomically update time.Time variable than int64 variable.
|
|
lastCallStartedTime int64
|
|
}
|
|
|
|
// The SupportPackageIsVersion variables are referenced from generated protocol
|
|
// buffer files to ensure compatibility with the gRPC version used. The latest
|
|
// support package version is 7.
|
|
//
|
|
// Older versions are kept for compatibility.
|
|
//
|
|
// These constants should not be referenced from any other code.
|
|
const (
|
|
SupportPackageIsVersion3 = true
|
|
SupportPackageIsVersion4 = true
|
|
SupportPackageIsVersion5 = true
|
|
SupportPackageIsVersion6 = true
|
|
SupportPackageIsVersion7 = true
|
|
)
|
|
|
|
const grpcUA = "grpc-go/" + Version
|