mirror of https://github.com/grpc/grpc-java.git
Tightening up error message for GO_AWAY.
The attempt here is to identify all causes of GO_AWAY and to ensure there is a reasonable description to help understand the cause. Fixes #163
This commit is contained in:
parent
456216b364
commit
dfcfb7bca1
|
|
@ -121,5 +121,88 @@ public final class HttpUtil {
|
||||||
return Status.UNKNOWN;
|
return Status.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All error codes identified by the HTTP/2 spec.
|
||||||
|
*/
|
||||||
|
public enum Http2Error {
|
||||||
|
NO_ERROR(0x0, Status.INTERNAL),
|
||||||
|
PROTOCOL_ERROR(0x1, Status.INTERNAL),
|
||||||
|
INTERNAL_ERROR(0x2, Status.INTERNAL),
|
||||||
|
FLOW_CONTROL_ERROR(0x3, Status.INTERNAL),
|
||||||
|
SETTINGS_TIMEOUT(0x4, Status.INTERNAL),
|
||||||
|
STREAM_CLOSED(0x5, Status.INTERNAL),
|
||||||
|
FRAME_SIZE_ERROR(0x6, Status.INTERNAL),
|
||||||
|
REFUSED_STREAM(0x7, Status.UNAVAILABLE),
|
||||||
|
CANCEL(0x8, Status.CANCELLED),
|
||||||
|
COMPRESSION_ERROR(0x9, Status.INTERNAL),
|
||||||
|
CONNECT_ERROR(0xA, Status.INTERNAL),
|
||||||
|
ENHANCE_YOUR_CALM(0xB, Status.RESOURCE_EXHAUSTED.withDescription("Bandwidth exhausted")),
|
||||||
|
INADEQUATE_SECURITY(0xC, Status.PERMISSION_DENIED.withDescription("Permission denied as "
|
||||||
|
+ "protocol is not secure enough to call")),
|
||||||
|
HTTP_1_1_REQUIRED(0xD, Status.UNKNOWN);
|
||||||
|
|
||||||
|
// Populate a mapping of code to enum value for quick look-up.
|
||||||
|
private static final Http2Error[] codeMap;
|
||||||
|
static {
|
||||||
|
Http2Error[] errors = Http2Error.values();
|
||||||
|
int size = (int) errors[errors.length - 1].code() + 1;
|
||||||
|
codeMap = new Http2Error[size];
|
||||||
|
for (Http2Error error : errors) {
|
||||||
|
int index = (int) error.code();
|
||||||
|
codeMap[index] = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final Status status;
|
||||||
|
|
||||||
|
private Http2Error(int code, Status status) {
|
||||||
|
this.code = code;
|
||||||
|
this.status = status.augmentDescription("HTTP/2 error code: " + this.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the code for this error used on the wire.
|
||||||
|
*/
|
||||||
|
public long code() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Status} associated with this HTTP/2 code.
|
||||||
|
*/
|
||||||
|
public Status status() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the HTTP/2 error code enum value for the specified code.
|
||||||
|
*
|
||||||
|
* @param code an HTTP/2 error code value.
|
||||||
|
* @return the HTTP/2 error code enum or {@code null} if not found.
|
||||||
|
*/
|
||||||
|
public static Http2Error forCode(long code) {
|
||||||
|
if (code >= codeMap.length || code < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return codeMap[(int) code];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the {@link Status} from the given HTTP/2 error code.
|
||||||
|
*
|
||||||
|
* @param errorCode the HTTP/2 error code.
|
||||||
|
* @return a {@link Status} representing the given error.
|
||||||
|
*/
|
||||||
|
public static Status statusForCode(int code) {
|
||||||
|
Http2Error error = forCode(code);
|
||||||
|
if (error == null) {
|
||||||
|
return Status.UNKNOWN.withDescription("Unrecognized HTTP/2 error: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.status();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private HttpUtil() {}
|
private HttpUtil() {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,13 @@
|
||||||
|
|
||||||
package io.grpc.transport.netty;
|
package io.grpc.transport.netty;
|
||||||
|
|
||||||
|
import static io.netty.util.CharsetUtil.UTF_8;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import io.grpc.Metadata;
|
import io.grpc.Metadata;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
|
import io.grpc.transport.HttpUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
|
@ -82,6 +85,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
private final Deque<PendingStream> pendingStreams = new ArrayDeque<PendingStream>();
|
private final Deque<PendingStream> pendingStreams = new ArrayDeque<PendingStream>();
|
||||||
private final Http2LocalFlowController inboundFlow;
|
private final Http2LocalFlowController inboundFlow;
|
||||||
private Throwable connectionError;
|
private Throwable connectionError;
|
||||||
|
private Status goAwayStatus;
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
|
|
||||||
public NettyClientHandler(Http2Connection connection,
|
public NettyClientHandler(Http2Connection connection,
|
||||||
|
|
@ -185,6 +189,23 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
stream.transportReportStatus(Status.UNKNOWN, false, new Metadata.Trailers());
|
stream.transportReportStatus(Status.UNKNOWN, false, new Metadata.Trailers());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onGoAwayRead(long errorCode, ByteBuf debugData) {
|
||||||
|
Status status = HttpUtil.Http2Error.statusForCode((int) errorCode);
|
||||||
|
if (debugData.isReadable()) {
|
||||||
|
// If a debug message was provided, use it.
|
||||||
|
String msg = debugData.toString(UTF_8);
|
||||||
|
status = status.withDescription(msg);
|
||||||
|
}
|
||||||
|
goAwayStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
|
||||||
|
goAwayStatus(Status.UNAVAILABLE.withDescription("Network channel closed by the client"));
|
||||||
|
|
||||||
|
super.close(ctx, promise);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for the Channel shutting down.
|
* Handler for the Channel shutting down.
|
||||||
*/
|
*/
|
||||||
|
|
@ -192,7 +213,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
try {
|
try {
|
||||||
// Fail any streams that are awaiting creation.
|
// Fail any streams that are awaiting creation.
|
||||||
Status goAwayStatus = goAwayStatus().withDescription("network channel closed");
|
goAwayStatus(goAwayStatus().augmentDescription("Network channel closed"));
|
||||||
failPendingStreams(goAwayStatus);
|
failPendingStreams(goAwayStatus);
|
||||||
|
|
||||||
// Report status to the application layer for any open streams
|
// Report status to the application layer for any open streams
|
||||||
|
|
@ -210,6 +231,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
Http2Exception http2Ex) {
|
Http2Exception http2Ex) {
|
||||||
// Save the error.
|
// Save the error.
|
||||||
connectionError = cause;
|
connectionError = cause;
|
||||||
|
goAwayStatus(Status.fromThrowable(connectionError));
|
||||||
|
|
||||||
super.onConnectionError(ctx, cause, http2Ex);
|
super.onConnectionError(ctx, cause, http2Ex);
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +339,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
*/
|
*/
|
||||||
private void createPendingStreams() {
|
private void createPendingStreams() {
|
||||||
Http2Connection connection = connection();
|
Http2Connection connection = connection();
|
||||||
Http2Connection.Endpoint local = connection.local();
|
Http2Connection.Endpoint<Http2LocalFlowController> local = connection.local();
|
||||||
Status goAwayStatus = goAwayStatus();
|
Status goAwayStatus = goAwayStatus();
|
||||||
while (!pendingStreams.isEmpty()) {
|
while (!pendingStreams.isEmpty()) {
|
||||||
final int streamId = local.nextStreamId();
|
final int streamId = local.nextStreamId();
|
||||||
|
|
@ -362,12 +384,16 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
* Returns the appropriate status used to represent the cause for GOAWAY.
|
* Returns the appropriate status used to represent the cause for GOAWAY.
|
||||||
*/
|
*/
|
||||||
private Status goAwayStatus() {
|
private Status goAwayStatus() {
|
||||||
if (connectionError != null) {
|
if (goAwayStatus != null) {
|
||||||
return Status.fromThrowable(connectionError);
|
return goAwayStatus;
|
||||||
}
|
}
|
||||||
return Status.UNAVAILABLE;
|
return Status.UNAVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void goAwayStatus(Status status) {
|
||||||
|
goAwayStatus = goAwayStatus == null ? status : goAwayStatus;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the successful creation of a new stream.
|
* Handles the successful creation of a new stream.
|
||||||
*/
|
*/
|
||||||
|
|
@ -452,5 +478,11 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
||||||
throws Http2Exception {
|
throws Http2Exception {
|
||||||
handler.onRstStreamRead(streamId);
|
handler.onRstStreamRead(streamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
|
ByteBuf debugData) throws Http2Exception {
|
||||||
|
handler.onGoAwayRead(errorCode, debugData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue