go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/grpc/prpc/options.go (about) 1 // Copyright 2016 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package prpc 16 17 import ( 18 "fmt" 19 "strings" 20 "time" 21 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/codes" 24 "google.golang.org/grpc/metadata" 25 26 "go.chromium.org/luci/common/retry" 27 ) 28 29 // Options controls how RPC requests are sent. 30 type Options struct { 31 // Retry controls a strategy for retrying transient RPC errors and deadlines. 32 // 33 // Each call to this factory should return a new retry.Iterator to use for 34 // controlling retry loop of a single RPC call. 35 Retry retry.Factory 36 37 // UserAgent is the value of User-Agent HTTP header. 38 // 39 // If empty, DefaultUserAgent is used. 40 UserAgent string 41 42 // Insecure can be set to true to use HTTP instead of HTTPS. 43 // 44 // Useful for local tests. 45 Insecure bool 46 47 // PerRPCTimeout, if > 0, is a timeout that is applied to each call attempt. 48 // 49 // If the context passed to the call has a shorter deadline, this timeout will 50 // not be applied. Otherwise, if this timeout is hit, the RPC call attempt 51 // will be considered as failed transiently and it may be retried just like 52 // any other transient error per Retry policy. 53 PerRPCTimeout time.Duration 54 55 // AcceptContentSubtype defines acceptable Content-Type of responses. 56 // 57 // Valid values are "binary" and "json". Empty value defaults to "binary". 58 // It can be overridden on per-call basis via CallAcceptContentSubtype(). 59 AcceptContentSubtype string 60 61 // Debug is a flag indicate if we want to print more logs for debug purpose. 62 // 63 // Right now we use this option to print the raw requests when certain 64 // failures occur. 65 Debug bool 66 67 // These can be set only using *prpc.CallOption or some grpc.CallOption. 68 69 resHeaderMetadata *metadata.MD // destination for response HTTP headers. 70 resTrailerMetadata *metadata.MD // destination for response HTTP trailers. 71 expectedCodes []codes.Code // list of non-OK grpc codes NOT to log 72 73 // These are used internally. 74 75 host string // a hostname of a service being called 76 serviceName string // a service being called 77 methodName string // a method being called 78 inFormat Format // encoding of the request 79 outFormat Format // encoding of the response 80 } 81 82 // DefaultOptions are used if no options are specified in Client. 83 func DefaultOptions() *Options { 84 return &Options{ 85 Retry: func() retry.Iterator { 86 return &retry.ExponentialBackoff{ 87 Limited: retry.Limited{ 88 Delay: time.Second, 89 Retries: 5, 90 }, 91 } 92 }, 93 } 94 } 95 96 func (o *Options) apply(callOptions []grpc.CallOption) { 97 for _, co := range callOptions { 98 switch val := co.(type) { 99 case grpc.HeaderCallOption: 100 o.resHeaderMetadata = val.HeaderAddr 101 case grpc.TrailerCallOption: 102 o.resTrailerMetadata = val.TrailerAddr 103 case *CallOption: 104 val.apply(o) 105 default: 106 panic(fmt.Sprintf("unsupported call option %T is used with pRPC client", co)) 107 } 108 } 109 } 110 111 func (o *Options) resetResponseMetadata() { 112 if o.resHeaderMetadata != nil { 113 *o.resHeaderMetadata = nil 114 } 115 if o.resTrailerMetadata != nil { 116 *o.resTrailerMetadata = nil 117 } 118 } 119 120 // CallOption mutates Options. 121 type CallOption struct { 122 grpc.CallOption 123 // apply mutates options. 124 apply func(*Options) 125 } 126 127 // ExpectedCode can be used to indicate that given non-OK codes may appear 128 // during normal successful call flow, and thus they must not be logged as 129 // erroneous. 130 // 131 // Only affects local logging, nothing else. 132 func ExpectedCode(codes ...codes.Code) *CallOption { 133 return &CallOption{ 134 grpc.EmptyCallOption{}, 135 func(o *Options) { 136 o.expectedCodes = append(o.expectedCodes, codes...) 137 }, 138 } 139 } 140 141 // CallAcceptContentSubtype returns a CallOption that sets Content-Type. 142 // For example, if content-subtype is "json", the Content-Type over the wire 143 // will be "application/json". 144 // Unlike that of the grpc.CallContentSubtype, sets Content-Type only for 145 // response, not for the request. 146 func CallAcceptContentSubtype(contentSubtype string) *CallOption { 147 return &CallOption{ 148 grpc.EmptyCallOption{}, 149 func(o *Options) { 150 o.AcceptContentSubtype = strings.ToLower(contentSubtype) 151 }, 152 } 153 }