1 #region Copyright notice and license 2 3 // Copyright 2015 gRPC authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System; 20 using System.Threading; 21 22 using Grpc.Core.Internal; 23 using Grpc.Core.Utils; 24 25 namespace Grpc.Core 26 { 27 /// <summary> 28 /// Options for calls made by client. 29 /// </summary> 30 public struct CallOptions 31 { 32 Metadata headers; 33 DateTime? deadline; 34 CancellationToken cancellationToken; 35 WriteOptions writeOptions; 36 ContextPropagationToken propagationToken; 37 CallCredentials credentials; 38 CallFlags flags; 39 40 /// <summary> 41 /// Creates a new instance of <c>CallOptions</c> struct. 42 /// </summary> 43 /// <param name="headers">Headers to be sent with the call.</param> 44 /// <param name="deadline">Deadline for the call to finish. null means no deadline.</param> 45 /// <param name="cancellationToken">Can be used to request cancellation of the call.</param> 46 /// <param name="writeOptions">Write options that will be used for this call.</param> 47 /// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param> 48 /// <param name="credentials">Credentials to use for this call.</param> CallOptionsGrpc.Core.CallOptions49 public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken), 50 WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null, CallCredentials credentials = null) 51 { 52 this.headers = headers; 53 this.deadline = deadline; 54 this.cancellationToken = cancellationToken; 55 this.writeOptions = writeOptions; 56 this.propagationToken = propagationToken; 57 this.credentials = credentials; 58 this.flags = default(CallFlags); 59 } 60 61 /// <summary> 62 /// Headers to send at the beginning of the call. 63 /// </summary> 64 public Metadata Headers 65 { 66 get { return headers; } 67 } 68 69 /// <summary> 70 /// Call deadline. 71 /// </summary> 72 public DateTime? Deadline 73 { 74 get { return deadline; } 75 } 76 77 /// <summary> 78 /// Token that can be used for cancelling the call on the client side. 79 /// Cancelling the token will request cancellation 80 /// of the remote call. Best effort will be made to deliver the cancellation 81 /// notification to the server and interaction of the call with the server side 82 /// will be terminated. Unless the call finishes before the cancellation could 83 /// happen (there is an inherent race), 84 /// the call will finish with <c>StatusCode.Cancelled</c> status. 85 /// </summary> 86 public CancellationToken CancellationToken 87 { 88 get { return cancellationToken; } 89 } 90 91 /// <summary> 92 /// Write options that will be used for this call. 93 /// </summary> 94 public WriteOptions WriteOptions 95 { 96 get { return this.writeOptions; } 97 } 98 99 /// <summary> 100 /// Token for propagating parent call context. 101 /// </summary> 102 public ContextPropagationToken PropagationToken 103 { 104 get { return this.propagationToken; } 105 } 106 107 /// <summary> 108 /// Credentials to use for this call. 109 /// </summary> 110 public CallCredentials Credentials 111 { 112 get { return this.credentials; } 113 } 114 115 /// <summary> 116 /// If <c>true</c> and and channel is in <c>ChannelState.TransientFailure</c>, the call will attempt waiting for the channel to recover 117 /// instead of failing immediately (which is the default "FailFast" semantics). 118 /// Note: experimental API that can change or be removed without any prior notice. 119 /// </summary> 120 public bool IsWaitForReady 121 { 122 get { return (this.flags & CallFlags.WaitForReady) == CallFlags.WaitForReady; } 123 } 124 125 /// <summary> 126 /// Flags to use for this call. 127 /// </summary> 128 internal CallFlags Flags 129 { 130 get { return this.flags; } 131 } 132 133 /// <summary> 134 /// Returns new instance of <see cref="CallOptions"/> with 135 /// <c>Headers</c> set to the value provided. Values of all other fields are preserved. 136 /// </summary> 137 /// <param name="headers">The headers.</param> WithHeadersGrpc.Core.CallOptions138 public CallOptions WithHeaders(Metadata headers) 139 { 140 var newOptions = this; 141 newOptions.headers = headers; 142 return newOptions; 143 } 144 145 /// <summary> 146 /// Returns new instance of <see cref="CallOptions"/> with 147 /// <c>Deadline</c> set to the value provided. Values of all other fields are preserved. 148 /// </summary> 149 /// <param name="deadline">The deadline.</param> WithDeadlineGrpc.Core.CallOptions150 public CallOptions WithDeadline(DateTime deadline) 151 { 152 var newOptions = this; 153 newOptions.deadline = deadline; 154 return newOptions; 155 } 156 157 /// <summary> 158 /// Returns new instance of <see cref="CallOptions"/> with 159 /// <c>CancellationToken</c> set to the value provided. Values of all other fields are preserved. 160 /// </summary> 161 /// <param name="cancellationToken">The cancellation token.</param> WithCancellationTokenGrpc.Core.CallOptions162 public CallOptions WithCancellationToken(CancellationToken cancellationToken) 163 { 164 var newOptions = this; 165 newOptions.cancellationToken = cancellationToken; 166 return newOptions; 167 } 168 169 /// <summary> 170 /// Returns new instance of <see cref="CallOptions"/> with 171 /// <c>WriteOptions</c> set to the value provided. Values of all other fields are preserved. 172 /// </summary> 173 /// <param name="writeOptions">The write options.</param> WithWriteOptionsGrpc.Core.CallOptions174 public CallOptions WithWriteOptions(WriteOptions writeOptions) 175 { 176 var newOptions = this; 177 newOptions.writeOptions = writeOptions; 178 return newOptions; 179 } 180 181 /// <summary> 182 /// Returns new instance of <see cref="CallOptions"/> with 183 /// <c>PropagationToken</c> set to the value provided. Values of all other fields are preserved. 184 /// </summary> 185 /// <param name="propagationToken">The context propagation token.</param> WithPropagationTokenGrpc.Core.CallOptions186 public CallOptions WithPropagationToken(ContextPropagationToken propagationToken) 187 { 188 var newOptions = this; 189 newOptions.propagationToken = propagationToken; 190 return newOptions; 191 } 192 193 /// <summary> 194 /// Returns new instance of <see cref="CallOptions"/> with 195 /// <c>Credentials</c> set to the value provided. Values of all other fields are preserved. 196 /// </summary> 197 /// <param name="credentials">The call credentials.</param> WithCredentialsGrpc.Core.CallOptions198 public CallOptions WithCredentials(CallCredentials credentials) 199 { 200 var newOptions = this; 201 newOptions.credentials = credentials; 202 return newOptions; 203 } 204 205 /// <summary> 206 /// Returns new instance of <see cref="CallOptions"/> with "WaitForReady" semantics enabled/disabled. 207 /// <see cref="IsWaitForReady"/>. 208 /// Note: experimental API that can change or be removed without any prior notice. 209 /// </summary> WithWaitForReadyGrpc.Core.CallOptions210 public CallOptions WithWaitForReady(bool waitForReady = true) 211 { 212 if (waitForReady) 213 { 214 return WithFlags(this.flags | CallFlags.WaitForReady); 215 } 216 return WithFlags(this.flags & ~CallFlags.WaitForReady); 217 } 218 219 /// <summary> 220 /// Returns new instance of <see cref="CallOptions"/> with 221 /// <c>Flags</c> set to the value provided. Values of all other fields are preserved. 222 /// </summary> 223 /// <param name="flags">The call flags.</param> WithFlagsGrpc.Core.CallOptions224 internal CallOptions WithFlags(CallFlags flags) 225 { 226 var newOptions = this; 227 newOptions.flags = flags; 228 return newOptions; 229 } 230 231 /// <summary> 232 /// Returns a new instance of <see cref="CallOptions"/> with 233 /// all previously unset values set to their defaults and deadline and cancellation 234 /// token propagated when appropriate. 235 /// </summary> NormalizeGrpc.Core.CallOptions236 internal CallOptions Normalize() 237 { 238 var newOptions = this; 239 if (propagationToken != null) 240 { 241 if (propagationToken.Options.IsPropagateDeadline) 242 { 243 GrpcPreconditions.CheckArgument(!newOptions.deadline.HasValue, 244 "Cannot propagate deadline from parent call. The deadline has already been set explicitly."); 245 newOptions.deadline = propagationToken.ParentDeadline; 246 } 247 if (propagationToken.Options.IsPropagateCancellation) 248 { 249 GrpcPreconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled, 250 "Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value."); 251 newOptions.cancellationToken = propagationToken.ParentCancellationToken; 252 } 253 } 254 255 newOptions.headers = newOptions.headers ?? Metadata.Empty; 256 newOptions.deadline = newOptions.deadline ?? DateTime.MaxValue; 257 return newOptions; 258 } 259 } 260 } 261