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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,13 @@
|
|||
|
||||
package io.grpc.transport.netty;
|
||||
|
||||
import static io.netty.util.CharsetUtil.UTF_8;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.transport.HttpUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
|
|
@ -82,6 +85,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
private final Deque<PendingStream> pendingStreams = new ArrayDeque<PendingStream>();
|
||||
private final Http2LocalFlowController inboundFlow;
|
||||
private Throwable connectionError;
|
||||
private Status goAwayStatus;
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
public NettyClientHandler(Http2Connection connection,
|
||||
|
|
@ -185,6 +189,23 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
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.
|
||||
*/
|
||||
|
|
@ -192,7 +213,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
// Fail any streams that are awaiting creation.
|
||||
Status goAwayStatus = goAwayStatus().withDescription("network channel closed");
|
||||
goAwayStatus(goAwayStatus().augmentDescription("Network channel closed"));
|
||||
failPendingStreams(goAwayStatus);
|
||||
|
||||
// Report status to the application layer for any open streams
|
||||
|
|
@ -210,6 +231,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
Http2Exception http2Ex) {
|
||||
// Save the error.
|
||||
connectionError = cause;
|
||||
goAwayStatus(Status.fromThrowable(connectionError));
|
||||
|
||||
super.onConnectionError(ctx, cause, http2Ex);
|
||||
}
|
||||
|
|
@ -317,7 +339,7 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
*/
|
||||
private void createPendingStreams() {
|
||||
Http2Connection connection = connection();
|
||||
Http2Connection.Endpoint local = connection.local();
|
||||
Http2Connection.Endpoint<Http2LocalFlowController> local = connection.local();
|
||||
Status goAwayStatus = goAwayStatus();
|
||||
while (!pendingStreams.isEmpty()) {
|
||||
final int streamId = local.nextStreamId();
|
||||
|
|
@ -362,12 +384,16 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
* Returns the appropriate status used to represent the cause for GOAWAY.
|
||||
*/
|
||||
private Status goAwayStatus() {
|
||||
if (connectionError != null) {
|
||||
return Status.fromThrowable(connectionError);
|
||||
if (goAwayStatus != null) {
|
||||
return goAwayStatus;
|
||||
}
|
||||
return Status.UNAVAILABLE;
|
||||
}
|
||||
|
||||
private void goAwayStatus(Status status) {
|
||||
goAwayStatus = goAwayStatus == null ? status : goAwayStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the successful creation of a new stream.
|
||||
*/
|
||||
|
|
@ -452,5 +478,11 @@ class NettyClientHandler extends Http2ConnectionHandler {
|
|||
throws Http2Exception {
|
||||
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