예제 #1
0
 @Before
 public void setUp() {
   MockitoAnnotations.initMocks(this);
   when(provider.get(any(CallOptions.class))).thenReturn(transport);
   when(transport.newStream(
           any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class)))
       .thenReturn(stream);
 }
예제 #2
0
  @Test
  public void advertisedEncodingsAreSent() {
    MethodDescriptor<Void, Void> descriptor =
        MethodDescriptor.create(
            MethodType.UNARY,
            "service/method",
            new TestMarshaller<Void>(),
            new TestMarshaller<Void>());
    final ClientTransport transport = mock(ClientTransport.class);
    final ClientStream stream = mock(ClientStream.class);
    ClientTransportProvider provider =
        new ClientTransportProvider() {
          @Override
          public ListenableFuture<ClientTransport> get(CallOptions callOptions) {
            return Futures.immediateFuture(transport);
          }
        };
    when(transport.newStream(
            any(MethodDescriptor.class), any(Metadata.class), any(ClientStreamListener.class)))
        .thenReturn(stream);
    ClientCallImpl<Void, Void> call =
        new ClientCallImpl<Void, Void>(
                descriptor, executor, CallOptions.DEFAULT, provider, deadlineCancellationExecutor)
            .setDecompressorRegistry(decompressorRegistry);

    call.start(new TestClientCallListener<Void>(), new Metadata());

    ArgumentCaptor<Metadata> metadataCaptor = ArgumentCaptor.forClass(Metadata.class);
    verify(transport)
        .newStream(eq(descriptor), metadataCaptor.capture(), isA(ClientStreamListener.class));
    Metadata actual = metadataCaptor.getValue();

    Set<String> acceptedEncodings =
        ImmutableSet.copyOf(actual.getAll(GrpcUtil.MESSAGE_ACCEPT_ENCODING_KEY));
    assertEquals(decompressorRegistry.getAdvertisedMessageEncodings(), acceptedEncodings);
  }
예제 #3
0
  @Override
  public void start(final Listener<RespT> observer, Metadata headers) {
    checkState(stream == null, "Already started");
    checkNotNull(observer, "observer");
    checkNotNull(headers, "headers");

    // Create the context
    final Deadline effectiveDeadline = min(callOptions.getDeadline(), parentContext.getDeadline());
    if (effectiveDeadline != parentContext.getDeadline()) {
      context = parentContext.withDeadline(effectiveDeadline, deadlineCancellationExecutor);
    } else {
      context = parentContext.withCancellation();
    }

    if (context.isCancelled()) {
      // Context is already cancelled so no need to create a real stream, just notify the observer
      // of cancellation via callback on the executor
      stream = NoopClientStream.INSTANCE;
      callExecutor.execute(
          new ContextRunnable(context) {
            @Override
            public void runInContext() {
              observer.onClose(statusFromCancelled(context), new Metadata());
            }
          });
      return;
    }
    final String compressorName = callOptions.getCompressor();
    Compressor compressor = null;
    if (compressorName != null) {
      compressor = compressorRegistry.lookupCompressor(compressorName);
      if (compressor == null) {
        stream = NoopClientStream.INSTANCE;
        callExecutor.execute(
            new ContextRunnable(context) {
              @Override
              public void runInContext() {
                observer.onClose(
                    Status.INTERNAL.withDescription(
                        String.format("Unable to find compressor by name %s", compressorName)),
                    new Metadata());
              }
            });
        return;
      }
    } else {
      compressor = Codec.Identity.NONE;
    }

    prepareHeaders(headers, callOptions, userAgent, decompressorRegistry, compressor);

    final boolean deadlineExceeded = effectiveDeadline != null && effectiveDeadline.isExpired();
    if (!deadlineExceeded) {
      updateTimeoutHeaders(
          effectiveDeadline, callOptions.getDeadline(), parentContext.getDeadline(), headers);
      ClientTransport transport = clientTransportProvider.get(callOptions);
      stream = transport.newStream(method, headers);
    } else {
      stream = new FailingClientStream(DEADLINE_EXCEEDED);
    }

    if (callOptions.getAuthority() != null) {
      stream.setAuthority(callOptions.getAuthority());
    }
    stream.setCompressor(compressor);

    stream.start(new ClientStreamListenerImpl(observer));
    if (compressor != Codec.Identity.NONE) {
      stream.setMessageCompression(true);
    }

    // Delay any sources of cancellation after start(), because most of the transports are broken if
    // they receive cancel before start. Issue #1343 has more details

    // Propagate later Context cancellation to the remote side.
    context.addListener(this, directExecutor());
    if (contextListenerShouldBeRemoved) {
      // Race detected! ClientStreamListener.closed may have been called before
      // deadlineCancellationFuture was set, thereby preventing the future from being cancelled.
      // Go ahead and cancel again, just to be sure it was cancelled.
      context.removeListener(this);
    }
  }
  @Test
  public void shutdownBeforeTransportCreatedWithPendingStream() throws Exception {
    SocketAddress addr = mock(SocketAddress.class);
    createTransportSet(addr);

    // First transport is created immediately
    ClientTransport pick = transportSet.obtainActiveTransport();
    assertEquals(ConnectivityState.CONNECTING, transportSet.getState(false));
    verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT);
    assertNotNull(pick);
    // Fail this one
    MockClientTransportInfo transportInfo = transports.poll();
    transportInfo.listener.transportShutdown(Status.UNAVAILABLE);
    assertEquals(ConnectivityState.TRANSIENT_FAILURE, transportSet.getState(false));
    transportInfo.listener.transportTerminated();

    // Second transport will wait for back-off
    pick = transportSet.obtainActiveTransport();
    assertTrue(pick instanceof DelayedClientTransport);
    // Start a stream, which will be pending in the delayed transport
    ClientStream pendingStream =
        pick.newStream(method, headers, waitForReadyCallOptions, statsTraceCtx);
    pendingStream.start(mockStreamListener);

    // Shut down TransportSet before the transport is created. Further call to
    // obtainActiveTransport() gets failing transports
    assertEquals(ConnectivityState.TRANSIENT_FAILURE, transportSet.getState(false));
    transportSet.shutdown();
    assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false));
    pick = transportSet.obtainActiveTransport();
    assertNotNull(pick);
    assertTrue(pick instanceof FailingClientTransport);
    verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT);

    // Reconnect will eventually happen, even though TransportSet has been shut down
    fakeClock.forwardMillis(10);
    assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false));
    verify(mockTransportFactory, times(2)).newClientTransport(addr, AUTHORITY, USER_AGENT);
    // The pending stream will be started on this newly started transport after it's ready.
    // The transport is shut down by TransportSet right after the stream is created.
    transportInfo = transports.poll();
    verify(transportInfo.transport, times(0)).shutdown();
    assertEquals(0, fakeExecutor.numPendingTasks());
    transportInfo.listener.transportReady();
    assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false));
    verify(transportInfo.transport, times(0))
        .newStream(any(MethodDescriptor.class), any(Metadata.class));
    assertEquals(1, fakeExecutor.runDueTasks());
    verify(transportInfo.transport)
        .newStream(
            same(method),
            same(headers),
            same(waitForReadyCallOptions),
            any(StatsTraceContext.class));
    verify(transportInfo.transport).shutdown();
    transportInfo.listener.transportShutdown(Status.UNAVAILABLE);
    assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false));
    verify(mockTransportSetCallback, never()).onTerminated(any(TransportSet.class));
    // Terminating the transport will let TransportSet to be terminated.
    transportInfo.listener.transportTerminated();
    assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false));
    verify(mockTransportSetCallback).onTerminated(transportSet);

    // No more transports will be created.
    fakeClock.forwardMillis(10000);
    assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false));
    verifyNoMoreInteractions(mockTransportFactory);
    assertEquals(0, transports.size());
  }