下面列出了怎么用io.grpc.InternalMetadata的API类实例代码及写法,或者点击链接到github查看源代码。
private void reportHeaders(List<Map.Entry<String, String>> headers, boolean endOfStream) {
// TODO(ericgribkoff): create new utility methods to eliminate all these conversions
List<String> headerList = new ArrayList<>();
for (Map.Entry<String, String> entry : headers) {
headerList.add(entry.getKey());
headerList.add(entry.getValue());
}
byte[][] headerValues = new byte[headerList.size()][];
for (int i = 0; i < headerList.size(); i += 2) {
headerValues[i] = headerList.get(i).getBytes(Charset.forName("UTF-8"));
headerValues[i + 1] = headerList.get(i + 1).getBytes(Charset.forName("UTF-8"));
}
Metadata metadata =
InternalMetadata.newMetadata(TransportFrameUtil.toRawSerializedHeaders(headerValues));
synchronized (state.lock) {
// There's no pending onReadCompleted callback so we can report trailers now.
state.transportHeadersReceived(metadata, endOfStream);
}
}
private void respondWithHttpError(
ChannelHandlerContext ctx, int streamId, int code, Status.Code statusCode, String msg) {
Metadata metadata = new Metadata();
metadata.put(InternalStatus.CODE_KEY, statusCode.toStatus());
metadata.put(InternalStatus.MESSAGE_KEY, msg);
byte[][] serialized = InternalMetadata.serialize(metadata);
Http2Headers headers = new DefaultHttp2Headers(true, serialized.length / 2)
.status("" + code)
.set(CONTENT_TYPE_HEADER, "text/plain; encoding=utf-8");
for (int i = 0; i < serialized.length; i += 2) {
headers.add(new AsciiString(serialized[i], false), new AsciiString(serialized[i + 1], false));
}
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
ByteBuf msgBuf = ByteBufUtil.writeUtf8(ctx.alloc(), msg);
encoder().writeData(ctx, streamId, msgBuf, 0, true, ctx.newPromise());
}
@Test
public void dupBinHeadersWithComma() {
byte[][] http2Headers = new byte[][] {
BINARY_BYTES.name().getBytes(US_ASCII),
"BaS,e6,,4+,padding==".getBytes(US_ASCII),
BINARY_BYTES.name().getBytes(US_ASCII),
"more".getBytes(US_ASCII),
BINARY_BYTES.name().getBytes(US_ASCII),
"".getBytes(US_ASCII)};
byte[][] rawSerialized = TransportFrameUtil.toRawSerializedHeaders(http2Headers);
Metadata recoveredHeaders = InternalMetadata.newMetadata(rawSerialized);
byte[][] values = Iterables.toArray(recoveredHeaders.getAll(BINARY_BYTES), byte[].class);
assertTrue(Arrays.deepEquals(
new byte[][] {
BaseEncoding.base64().decode("BaS"),
BaseEncoding.base64().decode("e6"),
BaseEncoding.base64().decode(""),
BaseEncoding.base64().decode("4+"),
BaseEncoding.base64().decode("padding"),
BaseEncoding.base64().decode("more"),
BaseEncoding.base64().decode("")},
values));
}
private void reportHeaders(List<Map.Entry<String, String>> headers, boolean endOfStream) {
// TODO(ericgribkoff): create new utility methods to eliminate all these conversions
List<String> headerList = new ArrayList<>();
for (Map.Entry<String, String> entry : headers) {
headerList.add(entry.getKey());
headerList.add(entry.getValue());
}
byte[][] headerValues = new byte[headerList.size()][];
for (int i = 0; i < headerList.size(); i += 2) {
headerValues[i] = headerList.get(i).getBytes(Charset.forName("UTF-8"));
headerValues[i + 1] = headerList.get(i + 1).getBytes(Charset.forName("UTF-8"));
}
Metadata metadata =
InternalMetadata.newMetadata(TransportFrameUtil.toRawSerializedHeaders(headerValues));
synchronized (state.lock) {
// There's no pending onReadCompleted callback so we can report trailers now.
state.transportHeadersReceived(metadata, endOfStream);
}
}
private void respondWithHttpError(
ChannelHandlerContext ctx, int streamId, int code, Status.Code statusCode, String msg) {
Metadata metadata = new Metadata();
metadata.put(InternalStatus.CODE_KEY, statusCode.toStatus());
metadata.put(InternalStatus.MESSAGE_KEY, msg);
byte[][] serialized = InternalMetadata.serialize(metadata);
Http2Headers headers = new DefaultHttp2Headers(true, serialized.length / 2)
.status("" + code)
.set(CONTENT_TYPE_HEADER, "text/plain; encoding=utf-8");
for (int i = 0; i < serialized.length; i += 2) {
headers.add(new AsciiString(serialized[i], false), new AsciiString(serialized[i + 1], false));
}
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
ByteBuf msgBuf = ByteBufUtil.writeUtf8(ctx.alloc(), msg);
encoder().writeData(ctx, streamId, msgBuf, 0, true, ctx.newPromise());
}
@Test
public void dupBinHeadersWithComma() {
byte[][] http2Headers = new byte[][] {
BINARY_BYTES.name().getBytes(US_ASCII),
"BaS,e6,,4+,padding==".getBytes(US_ASCII),
BINARY_BYTES.name().getBytes(US_ASCII),
"more".getBytes(US_ASCII),
BINARY_BYTES.name().getBytes(US_ASCII),
"".getBytes(US_ASCII)};
byte[][] rawSerialized = TransportFrameUtil.toRawSerializedHeaders(http2Headers);
Metadata recoveredHeaders = InternalMetadata.newMetadata(rawSerialized);
byte[][] values = Iterables.toArray(recoveredHeaders.getAll(BINARY_BYTES), byte[].class);
assertTrue(Arrays.deepEquals(
new byte[][] {
BaseEncoding.base64().decode("BaS"),
BaseEncoding.base64().decode("e6"),
BaseEncoding.base64().decode(""),
BaseEncoding.base64().decode("4+"),
BaseEncoding.base64().decode("padding"),
BaseEncoding.base64().decode("more"),
BaseEncoding.base64().decode("")},
values));
}
public static Metadata convertHeaders(Http2Headers http2Headers) {
if (http2Headers instanceof GrpcHttp2InboundHeaders) {
GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
}
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
public static Metadata convertTrailers(Http2Headers http2Headers) {
if (http2Headers instanceof GrpcHttp2InboundHeaders) {
GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
}
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
@VisibleForTesting
static MaybeTruncated<io.grpc.binarylog.v1.Metadata.Builder> createMetadataProto(
Metadata metadata, int maxHeaderBytes) {
checkNotNull(metadata, "metadata");
checkArgument(maxHeaderBytes >= 0, "maxHeaderBytes must be non negative");
io.grpc.binarylog.v1.Metadata.Builder metaBuilder = io.grpc.binarylog.v1.Metadata.newBuilder();
// This code is tightly coupled with Metadata's implementation
byte[][] serialized = InternalMetadata.serialize(metadata);
boolean truncated = false;
if (serialized != null) {
int curBytes = 0;
for (int i = 0; i < serialized.length; i += 2) {
String key = new String(serialized[i], Charsets.UTF_8);
byte[] value = serialized[i + 1];
if (NEVER_INCLUDED_METADATA.contains(key)) {
continue;
}
boolean forceInclude = ALWAYS_INCLUDED_METADATA.contains(key);
int bytesAfterAdd = curBytes + key.length() + value.length;
if (!forceInclude && bytesAfterAdd > maxHeaderBytes) {
truncated = true;
continue;
}
metaBuilder.addEntryBuilder()
.setKey(key)
.setValue(ByteString.copyFrom(value));
if (!forceInclude) {
// force included keys do not count towards the size limit
curBytes = bytesAfterAdd;
}
}
}
return new MaybeTruncated<io.grpc.binarylog.v1.Metadata.Builder>(metaBuilder, truncated);
}
private static int metadataSize(Metadata metadata) {
byte[][] serialized = InternalMetadata.serialize(metadata);
if (serialized == null) {
return 0;
}
// Calculate based on SETTINGS_MAX_HEADER_LIST_SIZE in RFC 7540 §6.5.2. We could use something
// different, but it's "sane."
long size = 0;
for (int i = 0; i < serialized.length; i += 2) {
size += 32 + serialized[i].length + serialized[i + 1].length;
}
size = Math.min(size, Integer.MAX_VALUE);
return (int) size;
}
/**
* RFC 7231 says status codes are 3 digits long.
*
* @see <a href="https://tools.ietf.org/html/rfc7231#section-6">RFC 7231</a>
*/
@Override
public Integer parseAsciiString(byte[] serialized) {
if (serialized.length >= 3) {
return (serialized[0] - '0') * 100 + (serialized[1] - '0') * 10 + (serialized[2] - '0');
}
throw new NumberFormatException(
"Malformed status code " + new String(serialized, InternalMetadata.US_ASCII));
}
/**
* Transform the given headers to a format where only spec-compliant ASCII characters are allowed.
* Binary header values are encoded by Base64 in the result. It is safe to modify the returned
* array, but not to modify any of the underlying byte arrays.
*
* @return the interleaved keys and values.
*/
@SuppressWarnings("BetaApi") // BaseEncoding is stable in Guava 20.0
public static byte[][] toHttp2Headers(Metadata headers) {
byte[][] serializedHeaders = InternalMetadata.serialize(headers);
// TODO(carl-mastrangelo): eventually remove this once all callers are updated.
if (serializedHeaders == null) {
return new byte[][]{};
}
int k = 0;
for (int i = 0; i < serializedHeaders.length; i += 2) {
byte[] key = serializedHeaders[i];
byte[] value = serializedHeaders[i + 1];
if (endsWith(key, binaryHeaderSuffixBytes)) {
// Binary header.
serializedHeaders[k] = key;
serializedHeaders[k + 1]
= InternalMetadata.BASE64_ENCODING_OMIT_PADDING.encode(value).getBytes(US_ASCII);
k += 2;
} else {
// Non-binary header.
// Filter out headers that contain non-spec-compliant ASCII characters.
// TODO(zhangkun83): only do such check in development mode since it's expensive
if (isSpecCompliantAscii(value)) {
serializedHeaders[k] = key;
serializedHeaders[k + 1] = value;
k += 2;
} else {
String keyString = new String(key, US_ASCII);
logger.warning("Metadata key=" + keyString + ", value=" + Arrays.toString(value)
+ " contains invalid ASCII characters");
}
}
}
// Fast path, everything worked out fine.
if (k == serializedHeaders.length) {
return serializedHeaders;
}
return Arrays.copyOfRange(serializedHeaders, 0, k);
}
@Test
public void testToAndFromHttp2Headers() {
Metadata headers = new Metadata();
headers.put(PLAIN_STRING, COMPLIANT_ASCII_STRING);
headers.put(BINARY_STRING, NONCOMPLIANT_ASCII_STRING);
headers.put(BINARY_STRING_WITHOUT_SUFFIX, NONCOMPLIANT_ASCII_STRING);
byte[][] http2Headers = TransportFrameUtil.toHttp2Headers(headers);
byte[][] rawSerialized = TransportFrameUtil.toRawSerializedHeaders(http2Headers);
Metadata recoveredHeaders = InternalMetadata.newMetadata(rawSerialized);
assertEquals(COMPLIANT_ASCII_STRING, recoveredHeaders.get(PLAIN_STRING));
assertEquals(NONCOMPLIANT_ASCII_STRING, recoveredHeaders.get(BINARY_STRING));
assertNull(recoveredHeaders.get(BINARY_STRING_WITHOUT_SUFFIX));
}
private void doSendHeaders(Metadata metadata) {
checkState(!sendHeadersCalled, "sendHeaders already called");
checkState(!closeCalled, "call is closed");
if (compressor == null || !messageCompression || clientAcceptEncoding == null) {
compressor = Codec.Identity.NONE;
} else {
final List<String> acceptedEncodingsList =
ACCEPT_ENCODING_SPLITTER.splitToList(clientAcceptEncoding);
if (!acceptedEncodingsList.contains(compressor.getMessageEncoding())) {
// resort to using no compression.
compressor = Codec.Identity.NONE;
}
}
messageFramer.setCompressor(ForwardingCompressor.forGrpc(compressor));
ResponseHeaders headers = defaultHeaders;
if (compressor != Codec.Identity.NONE || InternalMetadata.headerCount(metadata) > 0) {
headers = headers.withMutations(builder -> {
if (compressor != Codec.Identity.NONE) {
builder.set(GrpcHeaderNames.GRPC_ENCODING, compressor.getMessageEncoding());
}
MetadataUtil.fillHeaders(metadata, builder);
});
}
sendHeadersCalled = true;
res.write(headers);
}
public static Metadata convertHeaders(Http2Headers http2Headers) {
if (http2Headers instanceof GrpcHttp2InboundHeaders) {
GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
}
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
public static Metadata convertTrailers(Http2Headers http2Headers) {
if (http2Headers instanceof GrpcHttp2InboundHeaders) {
GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
}
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
@VisibleForTesting
static MaybeTruncated<io.grpc.binarylog.v1.Metadata.Builder> createMetadataProto(
Metadata metadata, int maxHeaderBytes) {
checkNotNull(metadata, "metadata");
checkArgument(maxHeaderBytes >= 0, "maxHeaderBytes must be non negative");
io.grpc.binarylog.v1.Metadata.Builder metaBuilder = io.grpc.binarylog.v1.Metadata.newBuilder();
// This code is tightly coupled with Metadata's implementation
byte[][] serialized = InternalMetadata.serialize(metadata);
boolean truncated = false;
if (serialized != null) {
int curBytes = 0;
for (int i = 0; i < serialized.length; i += 2) {
String key = new String(serialized[i], Charsets.UTF_8);
byte[] value = serialized[i + 1];
if (NEVER_INCLUDED_METADATA.contains(key)) {
continue;
}
boolean forceInclude = ALWAYS_INCLUDED_METADATA.contains(key);
int bytesAfterAdd = curBytes + key.length() + value.length;
if (!forceInclude && bytesAfterAdd > maxHeaderBytes) {
truncated = true;
continue;
}
metaBuilder.addEntryBuilder()
.setKey(key)
.setValue(ByteString.copyFrom(value));
if (!forceInclude) {
// force included keys do not count towards the size limit
curBytes = bytesAfterAdd;
}
}
}
return new MaybeTruncated<>(metaBuilder, truncated);
}
private static int metadataSize(Metadata metadata) {
byte[][] serialized = InternalMetadata.serialize(metadata);
if (serialized == null) {
return 0;
}
// Calculate based on SETTINGS_MAX_HEADER_LIST_SIZE in RFC 7540 §6.5.2. We could use something
// different, but it's "sane."
long size = 0;
for (int i = 0; i < serialized.length; i += 2) {
size += 32 + serialized[i].length + serialized[i + 1].length;
}
size = Math.min(size, Integer.MAX_VALUE);
return (int) size;
}
/**
* RFC 7231 says status codes are 3 digits long.
*
* @see <a href="https://tools.ietf.org/html/rfc7231#section-6">RFC 7231</a>
*/
@Override
public Integer parseAsciiString(byte[] serialized) {
if (serialized.length >= 3) {
return (serialized[0] - '0') * 100 + (serialized[1] - '0') * 10 + (serialized[2] - '0');
}
throw new NumberFormatException(
"Malformed status code " + new String(serialized, InternalMetadata.US_ASCII));
}
/**
* Transform the given headers to a format where only spec-compliant ASCII characters are allowed.
* Binary header values are encoded by Base64 in the result. It is safe to modify the returned
* array, but not to modify any of the underlying byte arrays.
*
* @return the interleaved keys and values.
*/
public static byte[][] toHttp2Headers(Metadata headers) {
byte[][] serializedHeaders = InternalMetadata.serialize(headers);
// TODO(carl-mastrangelo): eventually remove this once all callers are updated.
if (serializedHeaders == null) {
return new byte[][]{};
}
int k = 0;
for (int i = 0; i < serializedHeaders.length; i += 2) {
byte[] key = serializedHeaders[i];
byte[] value = serializedHeaders[i + 1];
if (endsWith(key, binaryHeaderSuffixBytes)) {
// Binary header.
serializedHeaders[k] = key;
serializedHeaders[k + 1]
= InternalMetadata.BASE64_ENCODING_OMIT_PADDING.encode(value).getBytes(US_ASCII);
k += 2;
} else {
// Non-binary header.
// Filter out headers that contain non-spec-compliant ASCII characters.
// TODO(zhangkun83): only do such check in development mode since it's expensive
if (isSpecCompliantAscii(value)) {
serializedHeaders[k] = key;
serializedHeaders[k + 1] = value;
k += 2;
} else {
String keyString = new String(key, US_ASCII);
logger.warning("Metadata key=" + keyString + ", value=" + Arrays.toString(value)
+ " contains invalid ASCII characters");
}
}
}
// Fast path, everything worked out fine.
if (k == serializedHeaders.length) {
return serializedHeaders;
}
return Arrays.copyOfRange(serializedHeaders, 0, k);
}
@Test
public void testToAndFromHttp2Headers() {
Metadata headers = new Metadata();
headers.put(PLAIN_STRING, COMPLIANT_ASCII_STRING);
headers.put(BINARY_STRING, NONCOMPLIANT_ASCII_STRING);
headers.put(BINARY_STRING_WITHOUT_SUFFIX, NONCOMPLIANT_ASCII_STRING);
byte[][] http2Headers = TransportFrameUtil.toHttp2Headers(headers);
byte[][] rawSerialized = TransportFrameUtil.toRawSerializedHeaders(http2Headers);
Metadata recoveredHeaders = InternalMetadata.newMetadata(rawSerialized);
assertEquals(COMPLIANT_ASCII_STRING, recoveredHeaders.get(PLAIN_STRING));
assertEquals(NONCOMPLIANT_ASCII_STRING, recoveredHeaders.get(BINARY_STRING));
assertNull(recoveredHeaders.get(BINARY_STRING_WITHOUT_SUFFIX));
}
/**
* Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when
* creating a stream. Since this serializes the headers, this method should be called in the
* application thread context.
*/
public static List<Header> createRequestHeaders(
Metadata headers, String defaultPath, String authority, String userAgent, boolean useGet) {
Preconditions.checkNotNull(headers, "headers");
Preconditions.checkNotNull(defaultPath, "defaultPath");
Preconditions.checkNotNull(authority, "authority");
// Discard any application supplied duplicates of the reserved headers
headers.discardAll(GrpcUtil.CONTENT_TYPE_KEY);
headers.discardAll(GrpcUtil.TE_HEADER);
headers.discardAll(GrpcUtil.USER_AGENT_KEY);
// 7 is the number of explicit add calls below.
List<Header> okhttpHeaders = new ArrayList<>(7 + InternalMetadata.headerCount(headers));
// Set GRPC-specific headers.
okhttpHeaders.add(SCHEME_HEADER);
if (useGet) {
okhttpHeaders.add(METHOD_GET_HEADER);
} else {
okhttpHeaders.add(METHOD_HEADER);
}
okhttpHeaders.add(new Header(Header.TARGET_AUTHORITY, authority));
String path = defaultPath;
okhttpHeaders.add(new Header(Header.TARGET_PATH, path));
okhttpHeaders.add(new Header(GrpcUtil.USER_AGENT_KEY.name(), userAgent));
// 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 = TransportFrameUtil.toHttp2Headers(headers);
for (int i = 0; i < serializedHeaders.length; i += 2) {
ByteString key = ByteString.of(serializedHeaders[i]);
String keyString = key.utf8();
if (isApplicationHeader(keyString)) {
ByteString value = ByteString.of(serializedHeaders[i + 1]);
okhttpHeaders.add(new Header(key, value));
}
}
return okhttpHeaders;
}
public static Metadata convertHeaders(List<Header> http2Headers) {
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
public static Metadata convertTrailers(List<Header> http2Headers) {
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
private static byte[] base64Encode(byte[] input) {
return InternalMetadata.BASE64_ENCODING_OMIT_PADDING.encode(input).getBytes(US_ASCII);
}
/**
* Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when
* creating a stream. Since this serializes the headers, this method should be called in the
* application thread context.
*/
public static List<Header> createRequestHeaders(
Metadata headers,
String defaultPath,
String authority,
String userAgent,
boolean useGet,
boolean usePlaintext) {
Preconditions.checkNotNull(headers, "headers");
Preconditions.checkNotNull(defaultPath, "defaultPath");
Preconditions.checkNotNull(authority, "authority");
// Discard any application supplied duplicates of the reserved headers
headers.discardAll(GrpcUtil.CONTENT_TYPE_KEY);
headers.discardAll(GrpcUtil.TE_HEADER);
headers.discardAll(GrpcUtil.USER_AGENT_KEY);
// 7 is the number of explicit add calls below.
List<Header> okhttpHeaders = new ArrayList<>(7 + InternalMetadata.headerCount(headers));
// Set GRPC-specific headers.
if (usePlaintext) {
okhttpHeaders.add(HTTP_SCHEME_HEADER);
} else {
okhttpHeaders.add(HTTPS_SCHEME_HEADER);
}
if (useGet) {
okhttpHeaders.add(METHOD_GET_HEADER);
} else {
okhttpHeaders.add(METHOD_HEADER);
}
okhttpHeaders.add(new Header(Header.TARGET_AUTHORITY, authority));
String path = defaultPath;
okhttpHeaders.add(new Header(Header.TARGET_PATH, path));
okhttpHeaders.add(new Header(GrpcUtil.USER_AGENT_KEY.name(), userAgent));
// 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 = TransportFrameUtil.toHttp2Headers(headers);
for (int i = 0; i < serializedHeaders.length; i += 2) {
ByteString key = ByteString.of(serializedHeaders[i]);
String keyString = key.utf8();
if (isApplicationHeader(keyString)) {
ByteString value = ByteString.of(serializedHeaders[i + 1]);
okhttpHeaders.add(new Header(key, value));
}
}
return okhttpHeaders;
}
public static Metadata convertHeaders(List<Header> http2Headers) {
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
public static Metadata convertTrailers(List<Header> http2Headers) {
return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
}
private static byte[] base64Encode(byte[] input) {
return InternalMetadata.BASE64_ENCODING_OMIT_PADDING.encode(input).getBytes(US_ASCII);
}