mirror of https://github.com/grpc/grpc-java.git
parent
f51316b84a
commit
2bd74c5a92
|
|
@ -38,6 +38,7 @@ import static io.grpc.Status.DEADLINE_EXCEEDED;
|
||||||
import static io.grpc.internal.GrpcUtil.TIMEOUT_KEY;
|
import static io.grpc.internal.GrpcUtil.TIMEOUT_KEY;
|
||||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
|
@ -524,7 +525,8 @@ public final class ServerImpl extends io.grpc.Server implements WithLogId {
|
||||||
* Dispatches callbacks onto an application-provided executor and correctly propagates
|
* Dispatches callbacks onto an application-provided executor and correctly propagates
|
||||||
* exceptions.
|
* exceptions.
|
||||||
*/
|
*/
|
||||||
private static class JumpToApplicationThreadServerStreamListener implements ServerStreamListener {
|
@VisibleForTesting
|
||||||
|
static class JumpToApplicationThreadServerStreamListener implements ServerStreamListener {
|
||||||
private final Executor callExecutor;
|
private final Executor callExecutor;
|
||||||
private final Context.CancellableContext context;
|
private final Context.CancellableContext context;
|
||||||
private final ServerStream stream;
|
private final ServerStream stream;
|
||||||
|
|
@ -545,7 +547,8 @@ public final class ServerImpl extends io.grpc.Server implements WithLogId {
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setListener(ServerStreamListener listener) {
|
@VisibleForTesting
|
||||||
|
void setListener(ServerStreamListener listener) {
|
||||||
Preconditions.checkNotNull(listener, "listener must not be null");
|
Preconditions.checkNotNull(listener, "listener must not be null");
|
||||||
Preconditions.checkState(this.listener == null, "Listener already set");
|
Preconditions.checkState(this.listener == null, "Listener already set");
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
@ -616,7 +619,15 @@ public final class ServerImpl extends io.grpc.Server implements WithLogId {
|
||||||
callExecutor.execute(new ContextRunnable(context) {
|
callExecutor.execute(new ContextRunnable(context) {
|
||||||
@Override
|
@Override
|
||||||
public void runInContext() {
|
public void runInContext() {
|
||||||
getListener().onReady();
|
try {
|
||||||
|
getListener().onReady();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
internalClose(Status.fromThrowable(e), new Metadata());
|
||||||
|
throw e;
|
||||||
|
} catch (Error e) {
|
||||||
|
internalClose(Status.fromThrowable(e), new Metadata());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import static org.mockito.Matchers.isNotNull;
|
||||||
import static org.mockito.Matchers.notNull;
|
import static org.mockito.Matchers.notNull;
|
||||||
import static org.mockito.Matchers.same;
|
import static org.mockito.Matchers.same;
|
||||||
import static org.mockito.Mockito.atLeast;
|
import static org.mockito.Mockito.atLeast;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
|
|
@ -77,6 +78,7 @@ import io.grpc.ServerTransportFilter;
|
||||||
import io.grpc.ServiceDescriptor;
|
import io.grpc.ServiceDescriptor;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StringMarshaller;
|
import io.grpc.StringMarshaller;
|
||||||
|
import io.grpc.internal.ServerImpl.JumpToApplicationThreadServerStreamListener;
|
||||||
import io.grpc.internal.testing.StatsTestUtils;
|
import io.grpc.internal.testing.StatsTestUtils;
|
||||||
import io.grpc.internal.testing.StatsTestUtils.FakeStatsContextFactory;
|
import io.grpc.internal.testing.StatsTestUtils.FakeStatsContextFactory;
|
||||||
import io.grpc.util.MutableHandlerRegistry;
|
import io.grpc.util.MutableHandlerRegistry;
|
||||||
|
|
@ -898,6 +900,134 @@ public class ServerImplTest {
|
||||||
verifyNoMoreInteractions(fallbackRegistry);
|
verifyNoMoreInteractions(fallbackRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void messageRead_errorCancelsCall() throws Exception {
|
||||||
|
JumpToApplicationThreadServerStreamListener listener
|
||||||
|
= new JumpToApplicationThreadServerStreamListener(
|
||||||
|
executor.getScheduledExecutorService(), stream, Context.ROOT.withCancellation());
|
||||||
|
ServerStreamListener mockListener = mock(ServerStreamListener.class);
|
||||||
|
listener.setListener(mockListener);
|
||||||
|
|
||||||
|
Throwable expectedT = new AssertionError();
|
||||||
|
doThrow(expectedT).when(mockListener).messageRead(any(InputStream.class));
|
||||||
|
// Closing the InputStream is done by the delegated listener (generally ServerCallImpl)
|
||||||
|
listener.messageRead(mock(InputStream.class));
|
||||||
|
try {
|
||||||
|
executor.runDueTasks();
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
assertSame(expectedT, t);
|
||||||
|
verify(stream).close(statusCaptor.capture(), any(Metadata.class));
|
||||||
|
assertSame(expectedT, statusCaptor.getValue().getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void messageRead_runtimeExceptionCancelsCall() throws Exception {
|
||||||
|
JumpToApplicationThreadServerStreamListener listener
|
||||||
|
= new JumpToApplicationThreadServerStreamListener(
|
||||||
|
executor.getScheduledExecutorService(), stream, Context.ROOT.withCancellation());
|
||||||
|
ServerStreamListener mockListener = mock(ServerStreamListener.class);
|
||||||
|
listener.setListener(mockListener);
|
||||||
|
|
||||||
|
Throwable expectedT = new RuntimeException();
|
||||||
|
doThrow(expectedT).when(mockListener).messageRead(any(InputStream.class));
|
||||||
|
// Closing the InputStream is done by the delegated listener (generally ServerCallImpl)
|
||||||
|
listener.messageRead(mock(InputStream.class));
|
||||||
|
try {
|
||||||
|
executor.runDueTasks();
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
assertSame(expectedT, t);
|
||||||
|
verify(stream).close(statusCaptor.capture(), any(Metadata.class));
|
||||||
|
assertSame(expectedT, statusCaptor.getValue().getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void halfClosed_errorCancelsCall() {
|
||||||
|
JumpToApplicationThreadServerStreamListener listener
|
||||||
|
= new JumpToApplicationThreadServerStreamListener(
|
||||||
|
executor.getScheduledExecutorService(), stream, Context.ROOT.withCancellation());
|
||||||
|
ServerStreamListener mockListener = mock(ServerStreamListener.class);
|
||||||
|
listener.setListener(mockListener);
|
||||||
|
|
||||||
|
Throwable expectedT = new AssertionError();
|
||||||
|
doThrow(expectedT).when(mockListener).halfClosed();
|
||||||
|
listener.halfClosed();
|
||||||
|
try {
|
||||||
|
executor.runDueTasks();
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
assertSame(expectedT, t);
|
||||||
|
verify(stream).close(statusCaptor.capture(), any(Metadata.class));
|
||||||
|
assertSame(expectedT, statusCaptor.getValue().getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void halfClosed_runtimeExceptionCancelsCall() {
|
||||||
|
JumpToApplicationThreadServerStreamListener listener
|
||||||
|
= new JumpToApplicationThreadServerStreamListener(
|
||||||
|
executor.getScheduledExecutorService(), stream, Context.ROOT.withCancellation());
|
||||||
|
ServerStreamListener mockListener = mock(ServerStreamListener.class);
|
||||||
|
listener.setListener(mockListener);
|
||||||
|
|
||||||
|
Throwable expectedT = new RuntimeException();
|
||||||
|
doThrow(expectedT).when(mockListener).halfClosed();
|
||||||
|
listener.halfClosed();
|
||||||
|
try {
|
||||||
|
executor.runDueTasks();
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
assertSame(expectedT, t);
|
||||||
|
verify(stream).close(statusCaptor.capture(), any(Metadata.class));
|
||||||
|
assertSame(expectedT, statusCaptor.getValue().getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onReady_errorCancelsCall() {
|
||||||
|
JumpToApplicationThreadServerStreamListener listener
|
||||||
|
= new JumpToApplicationThreadServerStreamListener(
|
||||||
|
executor.getScheduledExecutorService(), stream, Context.ROOT.withCancellation());
|
||||||
|
ServerStreamListener mockListener = mock(ServerStreamListener.class);
|
||||||
|
listener.setListener(mockListener);
|
||||||
|
|
||||||
|
Throwable expectedT = new AssertionError();
|
||||||
|
doThrow(expectedT).when(mockListener).onReady();
|
||||||
|
listener.onReady();
|
||||||
|
try {
|
||||||
|
executor.runDueTasks();
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
assertSame(expectedT, t);
|
||||||
|
verify(stream).close(statusCaptor.capture(), any(Metadata.class));
|
||||||
|
assertSame(expectedT, statusCaptor.getValue().getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onReady_runtimeExceptionCancelsCall() {
|
||||||
|
JumpToApplicationThreadServerStreamListener listener
|
||||||
|
= new JumpToApplicationThreadServerStreamListener(
|
||||||
|
executor.getScheduledExecutorService(), stream, Context.ROOT.withCancellation());
|
||||||
|
ServerStreamListener mockListener = mock(ServerStreamListener.class);
|
||||||
|
listener.setListener(mockListener);
|
||||||
|
|
||||||
|
Throwable expectedT = new RuntimeException();
|
||||||
|
doThrow(expectedT).when(mockListener).onReady();
|
||||||
|
listener.onReady();
|
||||||
|
try {
|
||||||
|
executor.runDueTasks();
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
assertSame(expectedT, t);
|
||||||
|
verify(stream).close(statusCaptor.capture(), any(Metadata.class));
|
||||||
|
assertSame(expectedT, statusCaptor.getValue().getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createAndStartServer(List<ServerTransportFilter> filters) throws IOException {
|
private void createAndStartServer(List<ServerTransportFilter> filters) throws IOException {
|
||||||
createServer(filters);
|
createServer(filters);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue