blob: f9c619046b179a353b37bd6723e03a453d930376 [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"
"strings"
"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 retry.Factory // RPC retrial.
// UserAgent is the value of User-Agent HTTP header.
// If empty, DefaultUserAgent is used.
UserAgent string
Insecure bool // if true, use HTTP instead of HTTPS.
// PerRPCTimeout, if > 0, is a timeout that is applied to each RPC. If the
// client Context has a shorter deadline, this timeout will not be applied.
// Otherwise, if this timeout is hit, the RPC round will be considered
// transient.
PerRPCTimeout time.Duration
// AcceptContentSubtype defines Content-Type over the wire for accepting
// responses.
// Valid values are "binary" and "json". Empty value defaults to "binary".
// It can be overridden on per-call basis via CallAcceptContentSubtype().
AcceptContentSubtype string
// 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
inFormat Format // encoding of the request
outFormat Format // encoding of 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 *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...)
},
}
}
// CallAcceptContentSubtype returns a CallOption that sets Content-Type.
// For example, if content-subtype is "json", the Content-Type over the wire
// will be "application/json".
// Unlike that of the grpc.CallContentSubtype, sets Content-Type only for
// response, not for the request.
func CallAcceptContentSubtype(contentSubtype string) *CallOption {
return &CallOption{
grpc.EmptyCallOption{},
func(o *Options) {
o.AcceptContentSubtype = strings.ToLower(contentSubtype)
},
}
}