mirror of https://github.com/grpc/grpc-java.git
GRPC Java clients will send the "te: trailers" header, and the server will
check for it, so that we can detect intermediate proxies that do not support trailers. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=81084983
This commit is contained in:
parent
4332c2f56e
commit
8375cd00e8
|
@ -26,6 +26,16 @@ public final class HttpUtil {
|
|||
*/
|
||||
public static final String HTTP_METHOD = "POST";
|
||||
|
||||
/**
|
||||
* The TE header name. Defined here since it is not explicitly defined by the HTTP/2 spec.
|
||||
*/
|
||||
public static final Metadata.Key<String> TE = Metadata.Key.of("te", Metadata.STRING_MARSHALLER);
|
||||
|
||||
/**
|
||||
* The TE (transport encoding) header for requests over HTTP/2
|
||||
*/
|
||||
public static final String TE_TRAILERS = "trailers";
|
||||
|
||||
/**
|
||||
* Maps HTTP error response status codes to transport codes.
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.google.net.stubby.transport.netty;
|
|||
import static com.google.net.stubby.transport.netty.Utils.CONTENT_TYPE_GRPC;
|
||||
import static com.google.net.stubby.transport.netty.Utils.CONTENT_TYPE_HEADER;
|
||||
import static com.google.net.stubby.transport.netty.Utils.HTTP_METHOD;
|
||||
import static com.google.net.stubby.transport.netty.Utils.TE_HEADER;
|
||||
import static com.google.net.stubby.transport.netty.Utils.TE_TRAILERS;
|
||||
import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.toByteBuf;
|
||||
import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
|
||||
|
@ -18,6 +20,7 @@ import io.netty.channel.ChannelFuture;
|
|||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.handler.codec.http2.Http2Connection;
|
||||
import io.netty.handler.codec.http2.Http2ConnectionHandler;
|
||||
import io.netty.handler.codec.http2.Http2Error;
|
||||
|
@ -50,6 +53,7 @@ class NettyServerHandler extends Http2ConnectionHandler {
|
|||
private final ServerTransportListener transportListener;
|
||||
private Throwable connectionError;
|
||||
private ChannelHandlerContext ctx;
|
||||
private boolean teWarningLogged;
|
||||
|
||||
NettyServerHandler(ServerTransportListener transportListener,
|
||||
Http2Connection connection,
|
||||
|
@ -92,6 +96,13 @@ class NettyServerHandler extends Http2ConnectionHandler {
|
|||
|
||||
private void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers)
|
||||
throws Http2Exception {
|
||||
if (!teWarningLogged && !TE_TRAILERS.equals(headers.get(TE_HEADER))) {
|
||||
logger.warning(String.format("Expected header TE: %s, but %s is received. This means "
|
||||
+ "some intermediate proxy may not support trailers",
|
||||
TE_TRAILERS, headers.get(TE_HEADER)));
|
||||
teWarningLogged = true;
|
||||
}
|
||||
|
||||
try {
|
||||
NettyServerStream stream = new NettyServerStream(ctx.channel(), streamId, this);
|
||||
// The Http2Stream object was put by AbstractHttp2ConnectionHandler before calling this
|
||||
|
@ -268,11 +279,7 @@ class NettyServerHandler extends Http2ConnectionHandler {
|
|||
throw new Http2StreamException(streamId, Http2Error.REFUSED_STREAM,
|
||||
String.format("Method '%s' is not supported", headers.method()));
|
||||
}
|
||||
if (!CONTENT_TYPE_GRPC.equals(headers.get(CONTENT_TYPE_HEADER))) {
|
||||
throw new Http2StreamException(streamId, Http2Error.REFUSED_STREAM, String.format(
|
||||
"Header '%s'='%s', while '%s' is expected", CONTENT_TYPE_HEADER,
|
||||
headers.get(CONTENT_TYPE_HEADER), CONTENT_TYPE_GRPC));
|
||||
}
|
||||
checkHeader(streamId, headers, CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC);
|
||||
String methodName = TransportFrameUtil.getFullMethodNameFromPath(headers.path().toString());
|
||||
if (methodName == null) {
|
||||
throw new Http2StreamException(streamId, Http2Error.REFUSED_STREAM,
|
||||
|
@ -281,6 +288,14 @@ class NettyServerHandler extends Http2ConnectionHandler {
|
|||
return methodName;
|
||||
}
|
||||
|
||||
private static void checkHeader(int streamId, Http2Headers headers,
|
||||
AsciiString header, AsciiString expectedValue) throws Http2StreamException {
|
||||
if (!expectedValue.equals(headers.get(header))) {
|
||||
throw new Http2StreamException(streamId, Http2Error.REFUSED_STREAM, String.format(
|
||||
"Header '%s'='%s', while '%s' is expected", header, headers.get(header), expectedValue));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server stream associated to the given HTTP/2 stream object
|
||||
*/
|
||||
|
|
|
@ -33,6 +33,8 @@ class Utils {
|
|||
new AsciiString(HttpUtil.CONTENT_TYPE.name());
|
||||
public static final AsciiString CONTENT_TYPE_GRPC =
|
||||
new AsciiString(HttpUtil.CONTENT_TYPE_GRPC);
|
||||
public static final AsciiString TE_HEADER = new AsciiString(HttpUtil.TE.name());
|
||||
public static final AsciiString TE_TRAILERS = new AsciiString(HttpUtil.TE_TRAILERS);
|
||||
|
||||
public static final Resource<EventLoopGroup> DEFAULT_CHANNEL_EVENT_LOOP_GROUP =
|
||||
new DefaultEventLoopGroupResource("grpc-default-channel-ELG");
|
||||
|
@ -93,7 +95,8 @@ class Utils {
|
|||
.path(defaultPath)
|
||||
.method(HTTP_METHOD)
|
||||
.scheme(ssl ? HTTPS : HTTP)
|
||||
.set(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC);
|
||||
.set(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC)
|
||||
.set(TE_HEADER, TE_TRAILERS);
|
||||
|
||||
// Override the default authority and path if provided by the headers.
|
||||
if (headers.getAuthority() != null) {
|
||||
|
|
|
@ -6,6 +6,8 @@ import static com.google.net.stubby.transport.netty.Utils.CONTENT_TYPE_HEADER;
|
|||
import static com.google.net.stubby.transport.netty.Utils.HTTPS;
|
||||
import static com.google.net.stubby.transport.netty.Utils.HTTP_METHOD;
|
||||
import static com.google.net.stubby.transport.netty.Utils.STATUS_OK;
|
||||
import static com.google.net.stubby.transport.netty.Utils.TE_HEADER;
|
||||
import static com.google.net.stubby.transport.netty.Utils.TE_TRAILERS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
|
@ -82,7 +84,8 @@ public class NettyClientHandlerTest extends NettyHandlerTestBase {
|
|||
.path(as("/fakemethod"))
|
||||
.method(HTTP_METHOD)
|
||||
.add(as("auth"), as("sometoken"))
|
||||
.add(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC);
|
||||
.add(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC)
|
||||
.add(TE_HEADER, TE_TRAILERS);
|
||||
|
||||
// Simulate activation of the handler to force writing of the initial settings
|
||||
handler.handlerAdded(ctx);
|
||||
|
@ -121,6 +124,7 @@ public class NettyClientHandlerTest extends NettyHandlerTestBase {
|
|||
assertEquals(HTTP_METHOD, headers.method());
|
||||
assertEquals("www.fake.com", headers.authority().toString());
|
||||
assertEquals(CONTENT_TYPE_GRPC, headers.get(CONTENT_TYPE_HEADER));
|
||||
assertEquals(TE_TRAILERS, headers.get(TE_HEADER));
|
||||
assertEquals("/fakemethod", headers.path().toString());
|
||||
assertEquals("sometoken", headers.get(as("auth")).toString());
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import static com.google.common.base.Charsets.UTF_8;
|
|||
import static com.google.net.stubby.transport.netty.Utils.CONTENT_TYPE_GRPC;
|
||||
import static com.google.net.stubby.transport.netty.Utils.CONTENT_TYPE_HEADER;
|
||||
import static com.google.net.stubby.transport.netty.Utils.HTTP_METHOD;
|
||||
import static com.google.net.stubby.transport.netty.Utils.TE_HEADER;
|
||||
import static com.google.net.stubby.transport.netty.Utils.TE_TRAILERS;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.toByteBuf;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.protocolError;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
@ -224,6 +226,7 @@ public class NettyServerHandlerTest extends NettyHandlerTestBase {
|
|||
Http2Headers headers = new DefaultHttp2Headers()
|
||||
.method(HTTP_METHOD)
|
||||
.set(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC)
|
||||
.set(TE_HEADER, TE_TRAILERS)
|
||||
.path(new AsciiString("/foo.bar"));
|
||||
ByteBuf headersFrame = headersFrame(STREAM_ID, headers);
|
||||
handler.channelRead(ctx, headersFrame);
|
||||
|
|
|
@ -20,6 +20,7 @@ public class Headers {
|
|||
public static final Header METHOD_HEADER = new Header(Header.TARGET_METHOD, HttpUtil.HTTP_METHOD);
|
||||
public static final Header CONTENT_TYPE_HEADER =
|
||||
new Header(HttpUtil.CONTENT_TYPE.name(), HttpUtil.CONTENT_TYPE_GRPC);
|
||||
public static final Header TE_HEADER = new Header(HttpUtil.TE.name(), HttpUtil.TE_TRAILERS);
|
||||
|
||||
/**
|
||||
* Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when
|
||||
|
@ -44,6 +45,7 @@ public class Headers {
|
|||
|
||||
// All non-pseudo headers must come after pseudo headers.
|
||||
okhttpHeaders.add(CONTENT_TYPE_HEADER);
|
||||
okhttpHeaders.add(TE_HEADER);
|
||||
|
||||
// Now add any application-provided headers.
|
||||
byte[][] serializedHeaders = headers.serialize();
|
||||
|
|
Loading…
Reference in New Issue