blob: 419a847d499240f468bdcd5b82d0709ea373c2b2 [file] [log] [blame]
// Copyright 2016 The LUCI 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 prpc
import (
"fmt"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"go.chromium.org/luci/common/retry"
)
// Options controls how RPC requests are sent.
type Options struct {
// Retry controls a strategy for retrying transient RPC errors and deadlines.
//
// Each call to this factory should return a new retry.Iterator to use for
// controlling retry loop of a single RPC call.
Retry retry.Factory
// UserAgent is the value of User-Agent HTTP header.
//
// If empty, DefaultUserAgent is used.
UserAgent string
// Insecure can be set to true to use HTTP instead of HTTPS.
//
// Useful for local tests.
Insecure bool
// PerRPCTimeout, if > 0, is a timeout that is applied to each call attempt.
//
// If the context passed to the call has a shorter deadline, this timeout will
// not be applied. Otherwise, if this timeout is hit, the RPC call attempt
// will be considered as failed transiently and it may be retried just like
// any other transient error per Retry policy.
PerRPCTimeout time.Duration
// RequestFormat defines how to encode requests.
//
// The default value is FormatBinary (i.e. use wirepb protobuf encoding).
//
// Can be overridden on per-call basis via RequestFormat() call option.
RequestFormat Format
// ResponseFormat defines how the server should encode responses.
//
// The default value is FormatBinary (i.e. use wirepb protobuf encoding).
//
// Can be overridden on per-call basis via ResponseFormat() call option.
ResponseFormat Format
// Debug is a flag indicate if we want to print more logs for debug purpose.
//
// Right now we use this option to print the raw requests when certain
// failures occur.
Debug bool
// These can be set only using *prpc.CallOption or some grpc.CallOption.
resHeaderMetadata *metadata.MD // destination for response HTTP headers.
resTrailerMetadata *metadata.MD // destination for response HTTP trailers.
expectedCodes []codes.Code // list of non-OK grpc codes NOT to log
// These are used internally.
host string // a hostname of a service being called
serviceName string // a service being called
methodName string // a method being called
reqCodec protoCodec // how to serialize the request
respCodec protoCodec // how to deserialize the response
}
// DefaultOptions are used if no options are specified in Client.
func DefaultOptions() *Options {
return &Options{
Retry: func() retry.Iterator {
return &retry.ExponentialBackoff{
Limited: retry.Limited{
Delay: time.Second,
Retries: 5,
},
}
},
}
}
func (o *Options) apply(callOptions []grpc.CallOption) {
for _, co := range callOptions {
switch val := co.(type) {
case grpc.HeaderCallOption:
o.resHeaderMetadata = val.HeaderAddr
case grpc.TrailerCallOption:
o.resTrailerMetadata = val.TrailerAddr
case grpc.StaticMethodCallOption:
// Noop.
case *CallOption:
val.apply(o)
default:
panic(fmt.Sprintf("unsupported call option %T is used with pRPC client", co))
}
}
}
func (o *Options) resetResponseMetadata() {
if o.resHeaderMetadata != nil {
*o.resHeaderMetadata = nil
}
if o.resTrailerMetadata != nil {
*o.resTrailerMetadata = nil
}
}
// CallOption mutates Options.
type CallOption struct {
grpc.CallOption
// apply mutates options.
apply func(*Options)
}
// ExpectedCode can be used to indicate that given non-OK codes may appear
// during normal successful call flow, and thus they must not be logged as
// erroneous.
//
// Only affects local logging, nothing else.
func ExpectedCode(codes ...codes.Code) *CallOption {
return &CallOption{
grpc.EmptyCallOption{},
func(o *Options) {
o.expectedCodes = append(o.expectedCodes, codes...)
},
}
}
// RequestFormat returns a CallOption that defines how to encode the request.
//
// This has no effect on the response encoding. Use ResponseFormat() call option
// to change it.
func RequestFormat(f Format) *CallOption {
return &CallOption{
grpc.EmptyCallOption{},
func(o *Options) {
o.RequestFormat = f
},
}
}
// ResponseFormat returns a CallOption that defines how the server should encode
// the response.
//
// This has no effect on the request encoding. Use RequestFormat() call option
// to change it.
func ResponseFormat(f Format) *CallOption {
return &CallOption{
grpc.EmptyCallOption{},
func(o *Options) {
o.ResponseFormat = f
},
}
}