1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 21 import com.google.common.base.MoreObjects; 22 import com.google.common.base.Preconditions; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collections; 26 import java.util.List; 27 import java.util.concurrent.Executor; 28 import java.util.concurrent.TimeUnit; 29 import javax.annotation.CheckReturnValue; 30 import javax.annotation.Nullable; 31 import javax.annotation.concurrent.Immutable; 32 33 /** 34 * The collection of runtime options for a new RPC call. 35 * 36 * <p>A field that is not set is {@code null}. 37 */ 38 @Immutable 39 @CheckReturnValue 40 public final class CallOptions { 41 /** 42 * A blank {@code CallOptions} that all fields are not set. 43 */ 44 public static final CallOptions DEFAULT; 45 46 static { 47 Builder b = new Builder(); 48 b.customOptions = new Object[0][2]; 49 b.streamTracerFactories = Collections.emptyList(); 50 DEFAULT = b.build(); 51 } 52 53 @Nullable 54 private final Deadline deadline; 55 56 @Nullable 57 private final Executor executor; 58 59 @Nullable 60 private final String authority; 61 62 @Nullable 63 private final CallCredentials credentials; 64 65 @Nullable 66 private final String compressorName; 67 68 private final Object[][] customOptions; 69 70 private final List<ClientStreamTracer.Factory> streamTracerFactories; 71 72 /** 73 * Opposite to fail fast. 74 */ 75 @Nullable 76 private final Boolean waitForReady; 77 78 @Nullable 79 private final Integer maxInboundMessageSize; 80 @Nullable 81 private final Integer maxOutboundMessageSize; 82 CallOptions(Builder builder)83 private CallOptions(Builder builder) { 84 this.deadline = builder.deadline; 85 this.executor = builder.executor; 86 this.authority = builder.authority; 87 this.credentials = builder.credentials; 88 this.compressorName = builder.compressorName; 89 this.customOptions = builder.customOptions; 90 this.streamTracerFactories = builder.streamTracerFactories; 91 this.waitForReady = builder.waitForReady; 92 this.maxInboundMessageSize = builder.maxInboundMessageSize; 93 this.maxOutboundMessageSize = builder.maxOutboundMessageSize; 94 } 95 96 static class Builder { 97 Deadline deadline; 98 Executor executor; 99 String authority; 100 CallCredentials credentials; 101 String compressorName; 102 Object[][] customOptions; 103 // Unmodifiable list 104 List<ClientStreamTracer.Factory> streamTracerFactories; 105 Boolean waitForReady; 106 Integer maxInboundMessageSize; 107 Integer maxOutboundMessageSize; 108 build()109 private CallOptions build() { 110 return new CallOptions(this); 111 } 112 } 113 114 /** 115 * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not 116 * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple 117 * services, even if those services are hosted on different domain names. That assumes the 118 * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is 119 * rare for a service provider to make such a guarantee. <em>At this time, there is no security 120 * verification of the overridden value, such as making sure the authority matches the server's 121 * TLS certificate.</em> 122 */ 123 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767") withAuthority(@ullable String authority)124 public CallOptions withAuthority(@Nullable String authority) { 125 Builder builder = toBuilder(this); 126 builder.authority = authority; 127 return builder.build(); 128 } 129 130 /** 131 * Returns a new {@code CallOptions} with the given call credentials. 132 */ withCallCredentials(@ullable CallCredentials credentials)133 public CallOptions withCallCredentials(@Nullable CallCredentials credentials) { 134 Builder builder = toBuilder(this); 135 builder.credentials = credentials; 136 return builder.build(); 137 } 138 139 /** 140 * Sets the compression to use for the call. The compressor must be a valid name known in the 141 * {@link CompressorRegistry}. By default, the "gzip" compressor will be available. 142 * 143 * <p>It is only safe to call this if the server supports the compression format chosen. There is 144 * no negotiation performed; if the server does not support the compression chosen, the call will 145 * fail. 146 */ withCompression(@ullable String compressorName)147 public CallOptions withCompression(@Nullable String compressorName) { 148 Builder builder = toBuilder(this); 149 builder.compressorName = compressorName; 150 return builder.build(); 151 } 152 153 /** 154 * Returns a new {@code CallOptions} with the given absolute deadline. 155 * 156 * <p>This is mostly used for propagating an existing deadline. {@link #withDeadlineAfter} is the 157 * recommended way of setting a new deadline, 158 * 159 * @param deadline the deadline or {@code null} for unsetting the deadline. 160 */ withDeadline(@ullable Deadline deadline)161 public CallOptions withDeadline(@Nullable Deadline deadline) { 162 Builder builder = toBuilder(this); 163 builder.deadline = deadline; 164 return builder.build(); 165 } 166 167 /** 168 * Returns a new {@code CallOptions} with a deadline that is after the given {@code duration} from 169 * now. 170 */ withDeadlineAfter(long duration, TimeUnit unit)171 public CallOptions withDeadlineAfter(long duration, TimeUnit unit) { 172 return withDeadline(Deadline.after(duration, unit)); 173 } 174 175 /** 176 * Returns the deadline or {@code null} if the deadline is not set. 177 */ 178 @Nullable getDeadline()179 public Deadline getDeadline() { 180 return deadline; 181 } 182 183 /** 184 * Enables <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md"> 185 * 'wait for ready'</a> for the call. Wait-for-ready queues the RPC until a connection is 186 * available. This may dramatically increase the latency of the RPC, but avoids failing 187 * "unnecessarily." The default queues the RPC until an attempt to connect has completed, but 188 * fails RPCs without sending them if unable to connect. 189 */ withWaitForReady()190 public CallOptions withWaitForReady() { 191 Builder builder = toBuilder(this); 192 builder.waitForReady = Boolean.TRUE; 193 return builder.build(); 194 } 195 196 /** 197 * Disables 'wait for ready' feature for the call. 198 * This method should be rarely used because the default is without 'wait for ready'. 199 */ withoutWaitForReady()200 public CallOptions withoutWaitForReady() { 201 Builder builder = toBuilder(this); 202 builder.waitForReady = Boolean.FALSE; 203 return builder.build(); 204 } 205 206 /** 207 * Returns the compressor's name. 208 */ 209 @Nullable getCompressor()210 public String getCompressor() { 211 return compressorName; 212 } 213 214 /** 215 * Override the HTTP/2 authority the channel claims to be connecting to. <em>This is not 216 * generally safe.</em> Overriding allows advanced users to re-use a single Channel for multiple 217 * services, even if those services are hosted on different domain names. That assumes the 218 * server is virtually hosting multiple domains and is guaranteed to continue doing so. It is 219 * rare for a service provider to make such a guarantee. <em>At this time, there is no security 220 * verification of the overridden value, such as making sure the authority matches the server's 221 * TLS certificate.</em> 222 */ 223 @Nullable 224 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1767") getAuthority()225 public String getAuthority() { 226 return authority; 227 } 228 229 /** 230 * Returns the call credentials. 231 */ 232 @Nullable getCredentials()233 public CallCredentials getCredentials() { 234 return credentials; 235 } 236 237 /** 238 * Returns a new {@code CallOptions} with {@code executor} to be used instead of the default 239 * executor specified with {@link ManagedChannelBuilder#executor}. 240 */ withExecutor(@ullable Executor executor)241 public CallOptions withExecutor(@Nullable Executor executor) { 242 Builder builder = toBuilder(this); 243 builder.executor = executor; 244 return builder.build(); 245 } 246 247 /** 248 * Returns a new {@code CallOptions} with a {@code ClientStreamTracerFactory} in addition to 249 * the existing factories. 250 * 251 * <p>This method doesn't replace existing factories, or try to de-duplicate factories. 252 */ 253 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861") withStreamTracerFactory(ClientStreamTracer.Factory factory)254 public CallOptions withStreamTracerFactory(ClientStreamTracer.Factory factory) { 255 ArrayList<ClientStreamTracer.Factory> newList = 256 new ArrayList<>(streamTracerFactories.size() + 1); 257 newList.addAll(streamTracerFactories); 258 newList.add(factory); 259 Builder builder = toBuilder(this); 260 builder.streamTracerFactories = Collections.unmodifiableList(newList); 261 return builder.build(); 262 } 263 264 /** 265 * Returns an immutable list of {@code ClientStreamTracerFactory}s. 266 */ 267 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861") getStreamTracerFactories()268 public List<ClientStreamTracer.Factory> getStreamTracerFactories() { 269 return streamTracerFactories; 270 } 271 272 /** 273 * Key for a key-value pair. Uses reference equality. 274 */ 275 public static final class Key<T> { 276 private final String debugString; 277 private final T defaultValue; 278 Key(String debugString, T defaultValue)279 private Key(String debugString, T defaultValue) { 280 this.debugString = debugString; 281 this.defaultValue = defaultValue; 282 } 283 284 /** 285 * Returns the user supplied default value for this key. 286 */ getDefault()287 public T getDefault() { 288 return defaultValue; 289 } 290 291 @Override toString()292 public String toString() { 293 return debugString; 294 } 295 296 /** 297 * Factory method for creating instances of {@link Key}. 298 * 299 * @param debugString a string used to describe this key, used for debugging. 300 * @param defaultValue default value to return when value for key not set 301 * @param <T> Key type 302 * @return Key object 303 * @deprecated Use {@link #create} or {@link #createWithDefault} instead. This method will 304 * be removed. 305 */ 306 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869") 307 @Deprecated of(String debugString, T defaultValue)308 public static <T> Key<T> of(String debugString, T defaultValue) { 309 Preconditions.checkNotNull(debugString, "debugString"); 310 return new Key<>(debugString, defaultValue); 311 } 312 313 /** 314 * Factory method for creating instances of {@link Key}. The default value of the 315 * key is {@code null}. 316 * 317 * @param debugString a debug string that describes this key. 318 * @param <T> Key type 319 * @return Key object 320 * @since 1.13.0 321 */ create(String debugString)322 public static <T> Key<T> create(String debugString) { 323 Preconditions.checkNotNull(debugString, "debugString"); 324 return new Key<>(debugString, /*defaultValue=*/ null); 325 } 326 327 /** 328 * Factory method for creating instances of {@link Key}. 329 * 330 * @param debugString a debug string that describes this key. 331 * @param defaultValue default value to return when value for key not set 332 * @param <T> Key type 333 * @return Key object 334 * @since 1.13.0 335 */ createWithDefault(String debugString, T defaultValue)336 public static <T> Key<T> createWithDefault(String debugString, T defaultValue) { 337 Preconditions.checkNotNull(debugString, "debugString"); 338 return new Key<>(debugString, defaultValue); 339 } 340 } 341 342 /** 343 * Sets a custom option. Any existing value for the key is overwritten. 344 * 345 * @param key The option key 346 * @param value The option value. 347 * @since 1.13.0 348 */ withOption(Key<T> key, T value)349 public <T> CallOptions withOption(Key<T> key, T value) { 350 Preconditions.checkNotNull(key, "key"); 351 Preconditions.checkNotNull(value, "value"); 352 353 Builder builder = toBuilder(this); 354 int existingIdx = -1; 355 for (int i = 0; i < customOptions.length; i++) { 356 if (key.equals(customOptions[i][0])) { 357 existingIdx = i; 358 break; 359 } 360 } 361 362 builder.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2]; 363 System.arraycopy(customOptions, 0, builder.customOptions, 0, customOptions.length); 364 365 if (existingIdx == -1) { 366 // Add a new option 367 builder.customOptions[customOptions.length] = new Object[] {key, value}; 368 } else { 369 // Replace an existing option 370 builder.customOptions[existingIdx] = new Object[] {key, value}; 371 } 372 373 return builder.build(); 374 } 375 376 /** 377 * Get the value for a custom option or its inherent default. 378 * @param key Key identifying option 379 */ 380 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1869") 381 @SuppressWarnings("unchecked") getOption(Key<T> key)382 public <T> T getOption(Key<T> key) { 383 Preconditions.checkNotNull(key, "key"); 384 for (int i = 0; i < customOptions.length; i++) { 385 if (key.equals(customOptions[i][0])) { 386 return (T) customOptions[i][1]; 387 } 388 } 389 return key.defaultValue; 390 } 391 392 /** 393 * Returns the executor override to use for this specific call, or {@code null} if there is no 394 * override. The executor is only for servicing this one call, so is not safe to use after 395 * {@link ClientCall.Listener#onClose}. 396 */ 397 @Nullable getExecutor()398 public Executor getExecutor() { 399 return executor; 400 } 401 402 /** 403 * Returns whether <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md"> 404 * 'wait for ready'</a> option is enabled for the call. 'Fail fast' is the default option for gRPC 405 * calls and 'wait for ready' is the opposite to it. 406 */ isWaitForReady()407 public boolean isWaitForReady() { 408 return Boolean.TRUE.equals(waitForReady); 409 } 410 getWaitForReady()411 Boolean getWaitForReady() { 412 return waitForReady; 413 } 414 415 /** 416 * Sets the maximum allowed message size acceptable from the remote peer. If unset, this will 417 * default to the value set on the {@link ManagedChannelBuilder#maxInboundMessageSize(int)}. 418 */ 419 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") withMaxInboundMessageSize(int maxSize)420 public CallOptions withMaxInboundMessageSize(int maxSize) { 421 checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize); 422 Builder builder = toBuilder(this); 423 builder.maxInboundMessageSize = maxSize; 424 return builder.build(); 425 } 426 427 /** 428 * Sets the maximum allowed message size acceptable sent to the remote peer. 429 */ 430 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") withMaxOutboundMessageSize(int maxSize)431 public CallOptions withMaxOutboundMessageSize(int maxSize) { 432 checkArgument(maxSize >= 0, "invalid maxsize %s", maxSize); 433 Builder builder = toBuilder(this); 434 builder.maxOutboundMessageSize = maxSize; 435 return builder.build(); 436 } 437 438 /** 439 * Gets the maximum allowed message size acceptable from the remote peer. 440 */ 441 @Nullable 442 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") getMaxInboundMessageSize()443 public Integer getMaxInboundMessageSize() { 444 return maxInboundMessageSize; 445 } 446 447 /** 448 * Gets the maximum allowed message size acceptable to send the remote peer. 449 */ 450 @Nullable 451 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2563") getMaxOutboundMessageSize()452 public Integer getMaxOutboundMessageSize() { 453 return maxOutboundMessageSize; 454 } 455 456 /** 457 * Copy CallOptions. 458 */ toBuilder(CallOptions other)459 private static Builder toBuilder(CallOptions other) { 460 Builder builder = new Builder(); 461 builder.deadline = other.deadline; 462 builder.executor = other.executor; 463 builder.authority = other.authority; 464 builder.credentials = other.credentials; 465 builder.compressorName = other.compressorName; 466 builder.customOptions = other.customOptions; 467 builder.streamTracerFactories = other.streamTracerFactories; 468 builder.waitForReady = other.waitForReady; 469 builder.maxInboundMessageSize = other.maxInboundMessageSize; 470 builder.maxOutboundMessageSize = other.maxOutboundMessageSize; 471 return builder; 472 } 473 474 @Override toString()475 public String toString() { 476 return MoreObjects.toStringHelper(this) 477 .add("deadline", deadline) 478 .add("authority", authority) 479 .add("callCredentials", credentials) 480 .add("executor", executor != null ? executor.getClass() : null) 481 .add("compressorName", compressorName) 482 .add("customOptions", Arrays.deepToString(customOptions)) 483 .add("waitForReady", isWaitForReady()) 484 .add("maxInboundMessageSize", maxInboundMessageSize) 485 .add("maxOutboundMessageSize", maxOutboundMessageSize) 486 .add("streamTracerFactories", streamTracerFactories) 487 .toString(); 488 } 489 } 490