mirror of https://github.com/grpc/grpc-java.git
Encode binary headers with Base64 on the wire, and requires all binary headers
have "-bin" suffix in their names. Split Metadata.Marshaller into BinaryMarshaller and AsciiMarshaller. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=81306135
This commit is contained in:
parent
63fc64761a
commit
2b116ef2cd
|
|
@ -15,7 +15,7 @@ import javax.inject.Provider;
|
||||||
/** Client interceptor that authenticates all calls with OAuth2. */
|
/** Client interceptor that authenticates all calls with OAuth2. */
|
||||||
public class OAuth2ChannelInterceptor implements ClientInterceptor {
|
public class OAuth2ChannelInterceptor implements ClientInterceptor {
|
||||||
private static final Metadata.Key<String> AUTHORIZATION =
|
private static final Metadata.Key<String> AUTHORIZATION =
|
||||||
Metadata.Key.of("Authorization", Metadata.STRING_MARSHALLER);
|
Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER);
|
||||||
|
|
||||||
private final OAuth2AccessTokenProvider accessTokenProvider;
|
private final OAuth2AccessTokenProvider accessTokenProvider;
|
||||||
private final Provider<String> authorizationHeaderProvider
|
private final Provider<String> authorizationHeaderProvider
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
package com.google.net.stubby;
|
package com.google.net.stubby;
|
||||||
|
|
||||||
import static com.google.common.base.Charsets.US_ASCII;
|
import static com.google.common.base.Charsets.US_ASCII;
|
||||||
import static com.google.common.base.Charsets.UTF_8;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Iterators;
|
|
||||||
import com.google.common.collect.LinkedListMultimap;
|
import com.google.common.collect.LinkedListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
@ -28,6 +26,11 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
public abstract class Metadata {
|
public abstract class Metadata {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All binary headers should have this suffix in their names. Vice versa.
|
||||||
|
*/
|
||||||
|
public static final String BINARY_HEADER_SUFFIX = "-bin";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interleave keys and values into a single iterator.
|
* Interleave keys and values into a single iterator.
|
||||||
*/
|
*/
|
||||||
|
|
@ -60,65 +63,38 @@ public abstract class Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple metadata marshaller that encodes strings as either UTF-8 or ASCII bytes.
|
* Simple metadata marshaller that encodes strings as is.
|
||||||
|
*
|
||||||
|
* <p>This should be used with ASCII strings that only contain printable characters and space.
|
||||||
|
* Otherwise the output may be considered invalid and discarded by the transport.
|
||||||
*/
|
*/
|
||||||
public static final Marshaller<String> STRING_MARSHALLER =
|
public static final AsciiMarshaller<String> ASCII_STRING_MARSHALLER =
|
||||||
new Marshaller<String>() {
|
new AsciiMarshaller<String>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes(String value) {
|
public String toAsciiString(String value) {
|
||||||
return value.getBytes(UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toAscii(String value) {
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String parseBytes(byte[] serialized) {
|
public String parseAsciiString(String serialized) {
|
||||||
return new String(serialized, UTF_8);
|
return serialized;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String parseAscii(String ascii) {
|
|
||||||
return ascii;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple metadata marshaller that encodes an integer as a signed decimal string or as big endian
|
* Simple metadata marshaller that encodes an integer as a signed decimal string.
|
||||||
* binary with four bytes.
|
|
||||||
*/
|
*/
|
||||||
public static final Marshaller<Integer> INTEGER_MARSHALLER = new Marshaller<Integer>() {
|
public static final AsciiMarshaller<Integer> INTEGER_MARSHALLER = new AsciiMarshaller<Integer>() {
|
||||||
@Override
|
|
||||||
public byte[] toBytes(Integer value) {
|
|
||||||
return new byte[] {
|
|
||||||
(byte) (value >>> 24),
|
|
||||||
(byte) (value >>> 16),
|
|
||||||
(byte) (value >>> 8),
|
|
||||||
(byte) (value >>> 0)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toAscii(Integer value) {
|
public String toAsciiString(Integer value) {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer parseBytes(byte[] serialized) {
|
public Integer parseAsciiString(String serialized) {
|
||||||
if (serialized.length != 4) {
|
return Integer.parseInt(serialized);
|
||||||
throw new IllegalArgumentException("Can only deserialize 4 bytes into an integer");
|
|
||||||
}
|
|
||||||
return (serialized[0] << 24)
|
|
||||||
| (serialized[1] << 16)
|
|
||||||
| (serialized[2] << 8)
|
|
||||||
| serialized[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer parseAscii(String ascii) {
|
|
||||||
return Integer.valueOf(ascii);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -138,17 +114,6 @@ public abstract class Metadata {
|
||||||
this.serializable = false;
|
this.serializable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor called by the transport layer when it receives ASCII metadata.
|
|
||||||
*/
|
|
||||||
private Metadata(String... asciiValues) {
|
|
||||||
store = LinkedListMultimap.create();
|
|
||||||
for (int i = 0; i < asciiValues.length; i++) {
|
|
||||||
store.put(asciiValues[i], new MetadataEntry(asciiValues[++i]));
|
|
||||||
}
|
|
||||||
this.serializable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor called by the application layer when it wants to send metadata.
|
* Constructor called by the application layer when it wants to send metadata.
|
||||||
*/
|
*/
|
||||||
|
|
@ -227,7 +192,13 @@ public abstract class Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize all the metadata entries
|
* Serialize all the metadata entries.
|
||||||
|
*
|
||||||
|
* <p>It produces serialized names and values interleaved. result[i*2] are names, while
|
||||||
|
* result[i*2+1] are values.
|
||||||
|
*
|
||||||
|
* <p>Names are ASCII string bytes. If the name ends with "-bin", the value can be raw binary.
|
||||||
|
* Otherwise, the value must be printable ASCII characters or space.
|
||||||
*/
|
*/
|
||||||
public byte[][] serialize() {
|
public byte[][] serialize() {
|
||||||
Preconditions.checkState(serializable, "Can't serialize raw metadata");
|
Preconditions.checkState(serializable, "Can't serialize raw metadata");
|
||||||
|
|
@ -240,20 +211,6 @@ public abstract class Metadata {
|
||||||
return serialized;
|
return serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize all the metadata entries
|
|
||||||
*/
|
|
||||||
public String[] serializeAscii() {
|
|
||||||
Preconditions.checkState(serializable, "Can't serialize received metadata");
|
|
||||||
String[] serialized = new String[store.size() * 2];
|
|
||||||
int i = 0;
|
|
||||||
for (Map.Entry<String, MetadataEntry> entry : store.entries()) {
|
|
||||||
serialized[i++] = entry.getValue().key.name();
|
|
||||||
serialized[i++] = entry.getValue().getSerializedAscii();
|
|
||||||
}
|
|
||||||
return serialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a simple merge of two sets of metadata.
|
* Perform a simple merge of two sets of metadata.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -301,20 +258,6 @@ public abstract class Metadata {
|
||||||
super(headers);
|
super(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the transport layer to create headers from their ASCII serialized values.
|
|
||||||
*/
|
|
||||||
public Headers(String... asciiValues) {
|
|
||||||
super(asciiValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the transport layer to create headers from their ASCII serialized values.
|
|
||||||
*/
|
|
||||||
public Headers(Iterable<Map.Entry<String, String>> mapEntries) {
|
|
||||||
super(Iterators.toArray(fromMapEntries(mapEntries), String.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the application layer to construct headers prior to passing them to the
|
* Called by the application layer to construct headers prior to passing them to the
|
||||||
* transport for serialization.
|
* transport for serialization.
|
||||||
|
|
@ -377,20 +320,6 @@ public abstract class Metadata {
|
||||||
super(headers);
|
super(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the transport layer to create trailers from their ASCII serialized values.
|
|
||||||
*/
|
|
||||||
public Trailers(String... asciiValues) {
|
|
||||||
super(asciiValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the transport layer to create headers from their ASCII serialized values.
|
|
||||||
*/
|
|
||||||
public Trailers(Iterable<Map.Entry<String, String>> mapEntries) {
|
|
||||||
super(Iterators.toArray(fromMapEntries(mapEntries), String.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the application layer to construct trailers prior to passing them to the
|
* Called by the application layer to construct trailers prior to passing them to the
|
||||||
* transport for serialization.
|
* transport for serialization.
|
||||||
|
|
@ -401,57 +330,75 @@ public abstract class Metadata {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marshaller for metadata values.
|
* Marshaller for metadata values that are serialized into raw binary.
|
||||||
*/
|
*/
|
||||||
public static interface Marshaller<T> {
|
public static interface BinaryMarshaller<T> {
|
||||||
/**
|
/**
|
||||||
* Serialize a metadata value to bytes.
|
* Serialize a metadata value to bytes.
|
||||||
* @param value to serialize
|
* @param value to serialize
|
||||||
* @return serialized version of value, or null if value cannot be transmitted.
|
* @return serialized version of value
|
||||||
*/
|
*/
|
||||||
public byte[] toBytes(T value);
|
public byte[] toBytes(T value);
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a metadata value to an ASCII string
|
|
||||||
* @param value to serialize
|
|
||||||
* @return serialized ascii version of value, or null if value cannot be transmitted.
|
|
||||||
*/
|
|
||||||
public String toAscii(T value);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a serialized metadata value from bytes.
|
* Parse a serialized metadata value from bytes.
|
||||||
* @param serialized value of metadata to parse
|
* @param serialized value of metadata to parse
|
||||||
* @return a parsed instance of type T
|
* @return a parsed instance of type T
|
||||||
*/
|
*/
|
||||||
public T parseBytes(byte[] serialized);
|
public T parseBytes(byte[] serialized);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a serialized metadata value from an ascii string.
|
* Marshaller for metadata values that are serialized into ASCII strings that contain only
|
||||||
* @param ascii string value of metadata to parse
|
* printable characters and space.
|
||||||
|
*/
|
||||||
|
public static interface AsciiMarshaller<T> {
|
||||||
|
/**
|
||||||
|
* Serialize a metadata value to a ASCII string that contains only printable characters and
|
||||||
|
* space.
|
||||||
|
*
|
||||||
|
* @param value to serialize
|
||||||
|
* @return serialized version of value, or null if value cannot be transmitted.
|
||||||
|
*/
|
||||||
|
public String toAsciiString(T value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a serialized metadata value from an ASCII string.
|
||||||
|
* @param serialized value of metadata to parse
|
||||||
* @return a parsed instance of type T
|
* @return a parsed instance of type T
|
||||||
*/
|
*/
|
||||||
public T parseAscii(String ascii);
|
public T parseAsciiString(String serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key for metadata entries. Allows for parsing and serialization of metadata.
|
* Key for metadata entries. Allows for parsing and serialization of metadata.
|
||||||
*/
|
*/
|
||||||
public static class Key<T> {
|
public abstract static class Key<T> {
|
||||||
public static <T> Key<T> of(String name, Marshaller<T> marshaller) {
|
|
||||||
return new Key<T>(name, marshaller);
|
/**
|
||||||
|
* Creates a key for a binary header.
|
||||||
|
*
|
||||||
|
* @param name must end with {@link BINARY_HEADER_SUFFIX}
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> of(String name, BinaryMarshaller<T> marshaller) {
|
||||||
|
return new BinaryKey<T>(name, marshaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a key for a ASCII header.
|
||||||
|
*
|
||||||
|
* @param name must not end with {@link BINARY_HEADER_SUFFIX}
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> of(String name, AsciiMarshaller<T> marshaller) {
|
||||||
|
return new AsciiKey<T>(name, marshaller);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final byte[] asciiName;
|
private final byte[] asciiName;
|
||||||
private final Marshaller<T> marshaller;
|
|
||||||
|
|
||||||
/**
|
private Key(String name) {
|
||||||
* Keys have a name and a marshaller used for serialization.
|
|
||||||
*/
|
|
||||||
private Key(String name, Marshaller<T> marshaller) {
|
|
||||||
this.name = Preconditions.checkNotNull(name, "name").toLowerCase().intern();
|
this.name = Preconditions.checkNotNull(name, "name").toLowerCase().intern();
|
||||||
this.asciiName = this.name.getBytes(US_ASCII);
|
this.asciiName = this.name.getBytes(US_ASCII);
|
||||||
this.marshaller = Preconditions.checkNotNull(marshaller);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|
@ -463,10 +410,6 @@ public abstract class Metadata {
|
||||||
return asciiName;
|
return asciiName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Marshaller<T> getMarshaller() {
|
|
||||||
return marshaller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
@ -484,6 +427,68 @@ public abstract class Metadata {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Key{name='" + name + "'}";
|
return "Key{name='" + name + "'}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a metadata value to bytes.
|
||||||
|
* @param value to serialize
|
||||||
|
* @return serialized version of value
|
||||||
|
*/
|
||||||
|
abstract byte[] toBytes(T value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a serialized metadata value from bytes.
|
||||||
|
* @param serialized value of metadata to parse
|
||||||
|
* @return a parsed instance of type T
|
||||||
|
*/
|
||||||
|
abstract T parseBytes(byte[] serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BinaryKey<T> extends Key<T> {
|
||||||
|
private final BinaryMarshaller<T> marshaller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys have a name and a binary marshaller used for serialization.
|
||||||
|
*/
|
||||||
|
private BinaryKey(String name, BinaryMarshaller<T> marshaller) {
|
||||||
|
super(name);
|
||||||
|
Preconditions.checkArgument(name.endsWith(BINARY_HEADER_SUFFIX),
|
||||||
|
"Binary header is named " + name + ". It must end with " + BINARY_HEADER_SUFFIX);
|
||||||
|
this.marshaller = Preconditions.checkNotNull(marshaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
byte[] toBytes(T value) {
|
||||||
|
return marshaller.toBytes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
T parseBytes(byte[] serialized) {
|
||||||
|
return marshaller.parseBytes(serialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AsciiKey<T> extends Key<T> {
|
||||||
|
private final AsciiMarshaller<T> marshaller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys have a name and an ASCII marshaller used for serialization.
|
||||||
|
*/
|
||||||
|
private AsciiKey(String name, AsciiMarshaller<T> marshaller) {
|
||||||
|
super(name);
|
||||||
|
Preconditions.checkArgument(!name.endsWith(BINARY_HEADER_SUFFIX),
|
||||||
|
"ASCII header is named " + name + ". It must not end with " + BINARY_HEADER_SUFFIX);
|
||||||
|
this.marshaller = Preconditions.checkNotNull(marshaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
byte[] toBytes(T value) {
|
||||||
|
return marshaller.toAsciiString(value).getBytes(US_ASCII);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
T parseBytes(byte[] serialized) {
|
||||||
|
return marshaller.parseAsciiString(new String(serialized, US_ASCII));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MetadataEntry {
|
private static class MetadataEntry {
|
||||||
|
|
@ -492,7 +497,6 @@ public abstract class Metadata {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
Key key;
|
Key key;
|
||||||
byte[] serializedBinary;
|
byte[] serializedBinary;
|
||||||
String serializedAscii;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor used when application layer adds a parsed value.
|
* Constructor used when application layer adds a parsed value.
|
||||||
|
|
@ -510,29 +514,20 @@ public abstract class Metadata {
|
||||||
this.serializedBinary = serialized;
|
this.serializedBinary = serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor used when reading a value from the transport.
|
|
||||||
*/
|
|
||||||
private MetadataEntry(String serializedAscii) {
|
|
||||||
this.serializedAscii = Preconditions.checkNotNull(serializedAscii);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T getParsed(Key<T> key) {
|
public <T> T getParsed(Key<T> key) {
|
||||||
T value = (T) parsed;
|
T value = (T) parsed;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (this.key != key) {
|
if (this.key != key) {
|
||||||
// Keys don't match so serialize using the old key
|
// Keys don't match so serialize using the old key
|
||||||
serializedBinary = this.key.getMarshaller().toBytes(value);
|
serializedBinary = this.key.toBytes(value);
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.key = key;
|
this.key = key;
|
||||||
if (serializedBinary != null) {
|
if (serializedBinary != null) {
|
||||||
value = key.getMarshaller().parseBytes(serializedBinary);
|
value = key.parseBytes(serializedBinary);
|
||||||
} else if (serializedAscii != null) {
|
|
||||||
value = key.getMarshaller().parseAscii(serializedAscii);
|
|
||||||
}
|
}
|
||||||
parsed = value;
|
parsed = value;
|
||||||
return value;
|
return value;
|
||||||
|
|
@ -542,16 +537,7 @@ public abstract class Metadata {
|
||||||
public byte[] getSerialized() {
|
public byte[] getSerialized() {
|
||||||
return serializedBinary =
|
return serializedBinary =
|
||||||
serializedBinary == null
|
serializedBinary == null
|
||||||
? key.getMarshaller().toBytes(parsed) :
|
? key.toBytes(parsed) : serializedBinary;
|
||||||
serializedBinary;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public String getSerializedAscii() {
|
|
||||||
return serializedAscii =
|
|
||||||
serializedAscii == null
|
|
||||||
? key.getMarshaller().toAscii(parsed) :
|
|
||||||
serializedAscii;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package com.google.net.stubby;
|
package com.google.net.stubby;
|
||||||
|
|
||||||
import static com.google.common.base.Charsets.US_ASCII;
|
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
@ -210,7 +208,7 @@ public final class Status {
|
||||||
* Key to bind status message to trailers.
|
* Key to bind status message to trailers.
|
||||||
*/
|
*/
|
||||||
public static final Metadata.Key<String> MESSAGE_KEY
|
public static final Metadata.Key<String> MESSAGE_KEY
|
||||||
= Metadata.Key.of("grpc-message", Metadata.STRING_MARSHALLER);
|
= Metadata.Key.of("grpc-message", Metadata.ASCII_STRING_MARSHALLER);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract an error {@link Status} from the causal chain of a {@link Throwable}.
|
* Extract an error {@link Status} from the causal chain of a {@link Throwable}.
|
||||||
|
|
@ -354,25 +352,15 @@ public final class Status {
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StatusCodeMarshaller implements Metadata.Marshaller<Status> {
|
private static class StatusCodeMarshaller implements Metadata.AsciiMarshaller<Status> {
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes(Status status) {
|
public String toAsciiString(Status status) {
|
||||||
return toAscii(status).getBytes(US_ASCII);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toAscii(Status status) {
|
|
||||||
return status.getCode().valueAscii();
|
return status.getCode().valueAscii();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Status parseBytes(byte[] serialized) {
|
public Status parseAsciiString(String serialized) {
|
||||||
return parseAscii(new String(serialized, US_ASCII));
|
return fromCodeValue(Integer.valueOf(serialized));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status parseAscii(String ascii) {
|
|
||||||
return fromCodeValue(Integer.valueOf(ascii));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.google.net.stubby.proto;
|
package com.google.net.stubby.proto;
|
||||||
|
|
||||||
import com.google.common.io.BaseEncoding;
|
|
||||||
import com.google.net.stubby.Marshaller;
|
import com.google.net.stubby.Marshaller;
|
||||||
import com.google.net.stubby.Metadata;
|
import com.google.net.stubby.Metadata;
|
||||||
import com.google.net.stubby.Status;
|
import com.google.net.stubby.Status;
|
||||||
|
|
@ -39,18 +38,14 @@ public class ProtoUtils {
|
||||||
* Produce a metadata key for a generated protobuf type.
|
* Produce a metadata key for a generated protobuf type.
|
||||||
*/
|
*/
|
||||||
public static <T extends GeneratedMessage> Metadata.Key<T> keyForProto(final T instance) {
|
public static <T extends GeneratedMessage> Metadata.Key<T> keyForProto(final T instance) {
|
||||||
return Metadata.Key.of(instance.getDescriptorForType().getFullName(),
|
return Metadata.Key.of(
|
||||||
new Metadata.Marshaller<T>() {
|
instance.getDescriptorForType().getFullName() + Metadata.BINARY_HEADER_SUFFIX,
|
||||||
|
new Metadata.BinaryMarshaller<T>() {
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes(T value) {
|
public byte[] toBytes(T value) {
|
||||||
return value.toByteArray();
|
return value.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toAscii(T value) {
|
|
||||||
return BaseEncoding.base64().encode(value.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public T parseBytes(byte[] serialized) {
|
public T parseBytes(byte[] serialized) {
|
||||||
|
|
@ -60,11 +55,6 @@ public class ProtoUtils {
|
||||||
throw new IllegalArgumentException(ipbe);
|
throw new IllegalArgumentException(ipbe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public T parseAscii(String ascii) {
|
|
||||||
return parseBytes(BaseEncoding.base64().decode(ascii));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,16 @@ public abstract class Http2ClientStream extends AbstractClientStream<Integer> {
|
||||||
/**
|
/**
|
||||||
* Metadata marshaller for HTTP status lines.
|
* Metadata marshaller for HTTP status lines.
|
||||||
*/
|
*/
|
||||||
private static final Metadata.Marshaller<Integer> HTTP_STATUS_LINE_MARSHALLER =
|
private static final Metadata.AsciiMarshaller<Integer> HTTP_STATUS_LINE_MARSHALLER =
|
||||||
new Metadata.Marshaller<Integer>() {
|
new Metadata.AsciiMarshaller<Integer>() {
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes(Integer value) {
|
public String toAsciiString(Integer value) {
|
||||||
return value.toString().getBytes(Charsets.US_ASCII);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toAscii(Integer value) {
|
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer parseBytes(byte[] serialized) {
|
public Integer parseAsciiString(String serialized) {
|
||||||
return parseAscii(new String(serialized, Charsets.US_ASCII));
|
return Integer.parseInt(serialized.split(" ", 2)[0]);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer parseAscii(String ascii) {
|
|
||||||
return Integer.parseInt(ascii.split(" ", 2)[0]);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public final class HttpUtil {
|
||||||
* spec.
|
* spec.
|
||||||
*/
|
*/
|
||||||
public static final Metadata.Key<String> CONTENT_TYPE =
|
public static final Metadata.Key<String> CONTENT_TYPE =
|
||||||
Metadata.Key.of("content-type", Metadata.STRING_MARSHALLER);
|
Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content-Type used for GRPC-over-HTTP/2.
|
* Content-Type used for GRPC-over-HTTP/2.
|
||||||
|
|
@ -29,7 +29,8 @@ public final class HttpUtil {
|
||||||
/**
|
/**
|
||||||
* The TE header name. Defined here since it is not explicitly defined by the HTTP/2 spec.
|
* 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);
|
public static final Metadata.Key<String> TE = Metadata.Key.of("te",
|
||||||
|
Metadata.ASCII_STRING_MARSHALLER);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TE (transport encoding) header for requests over HTTP/2
|
* The TE (transport encoding) header for requests over HTTP/2
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
package com.google.net.stubby.transport;
|
package com.google.net.stubby.transport;
|
||||||
|
|
||||||
|
import static com.google.common.base.Charsets.US_ASCII;
|
||||||
|
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import com.google.net.stubby.Metadata;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -10,6 +19,12 @@ import javax.annotation.Nullable;
|
||||||
*/
|
*/
|
||||||
public final class TransportFrameUtil {
|
public final class TransportFrameUtil {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(TransportFrameUtil.class.getName());
|
||||||
|
|
||||||
|
private static final byte[] binaryHeaderSuffixBytes =
|
||||||
|
Metadata.BINARY_HEADER_SUFFIX.getBytes(US_ASCII);
|
||||||
|
|
||||||
|
|
||||||
// Compression modes (lowest order 3 bits of frame flags)
|
// Compression modes (lowest order 3 bits of frame flags)
|
||||||
public static final byte NO_COMPRESS_FLAG = 0x0;
|
public static final byte NO_COMPRESS_FLAG = 0x0;
|
||||||
public static final byte FLATE_FLAG = 0x1;
|
public static final byte FLATE_FLAG = 0x1;
|
||||||
|
|
@ -57,5 +72,88 @@ public final class TransportFrameUtil {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @return the interleaved keys and values.
|
||||||
|
*/
|
||||||
|
public static byte[][] toHttp2Headers(Metadata headers) {
|
||||||
|
byte[][] serializedHeaders = headers.serialize();
|
||||||
|
ArrayList<byte[]> result = new ArrayList<byte[]>();
|
||||||
|
for (int i = 0; i < serializedHeaders.length; i += 2) {
|
||||||
|
byte[] key = serializedHeaders[i];
|
||||||
|
byte[] value = serializedHeaders[i + 1];
|
||||||
|
if (endsWith(key, binaryHeaderSuffixBytes)) {
|
||||||
|
// Binary header.
|
||||||
|
result.add(key);
|
||||||
|
result.add(BaseEncoding.base64().encode(value).getBytes(US_ASCII));
|
||||||
|
} else {
|
||||||
|
// Non-binary header.
|
||||||
|
// Filter out headers that contain non-spec-compliant ASCII characters.
|
||||||
|
// TODO(user): only do such check in development mode since it's expensive
|
||||||
|
if (isSpecCompliantAscii(value)) {
|
||||||
|
result.add(key);
|
||||||
|
result.add(value);
|
||||||
|
} else {
|
||||||
|
String keyString = new String(key, US_ASCII);
|
||||||
|
logger.warning("Metadata key=" + keyString + ", value=" + Arrays.toString(value)
|
||||||
|
+ " contains invalid ASCII characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toArray(new byte[result.size()][]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform HTTP/2-compliant headers to the raw serialized format which can be deserialized by
|
||||||
|
* metadata marshallers. It decodes the Base64-encoded binary headers.
|
||||||
|
*/
|
||||||
|
public static byte[][] toRawSerializedHeaders(byte[][] http2Headers) {
|
||||||
|
byte[][] result = new byte[http2Headers.length][];
|
||||||
|
for (int i = 0; i < http2Headers.length; i += 2) {
|
||||||
|
byte[] key = http2Headers[i];
|
||||||
|
byte[] value = http2Headers[i + 1];
|
||||||
|
result[i] = key;
|
||||||
|
if (endsWith(key, binaryHeaderSuffixBytes)) {
|
||||||
|
// Binary header
|
||||||
|
result[i + 1] = BaseEncoding.base64().decode(new String(value, US_ASCII));
|
||||||
|
} else {
|
||||||
|
// Non-binary header
|
||||||
|
result[i + 1] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if <b>subject</b> ends with <b>suffix</b>.
|
||||||
|
*/
|
||||||
|
private static boolean endsWith(byte[] subject, byte[] suffix) {
|
||||||
|
int start = subject.length - suffix.length;
|
||||||
|
if (start < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = start; i < subject.length; i++) {
|
||||||
|
if (subject[i] != suffix[i - start]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if <b>subject</b> contains only bytes that are spec-compliant ASCII characters and
|
||||||
|
* space.
|
||||||
|
*/
|
||||||
|
private static boolean isSpecCompliantAscii(byte[] subject) {
|
||||||
|
for (byte b : subject) {
|
||||||
|
if (b < 32 || b > 126) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private TransportFrameUtil() {}
|
private TransportFrameUtil() {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ public class ClientInterceptorsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addOutboundHeaders() {
|
public void addOutboundHeaders() {
|
||||||
final Metadata.Key<String> credKey = Metadata.Key.of("Cred", Metadata.STRING_MARSHALLER);
|
final Metadata.Key<String> credKey = Metadata.Key.of("Cred", Metadata.ASCII_STRING_MARSHALLER);
|
||||||
ClientInterceptor interceptor = new ClientInterceptor() {
|
ClientInterceptor interceptor = new ClientInterceptor() {
|
||||||
@Override
|
@Override
|
||||||
public <ReqT, RespT> Call<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
|
public <ReqT, RespT> Call<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
@ -22,31 +20,22 @@ import java.util.Iterator;
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class MetadataTest {
|
public class MetadataTest {
|
||||||
|
|
||||||
private static final Metadata.Marshaller<Fish> FISH_MARSHALLER = new Metadata.Marshaller<Fish>() {
|
private static final Metadata.BinaryMarshaller<Fish> FISH_MARSHALLER =
|
||||||
|
new Metadata.BinaryMarshaller<Fish>() {
|
||||||
@Override
|
@Override
|
||||||
public byte[] toBytes(Fish fish) {
|
public byte[] toBytes(Fish fish) {
|
||||||
return fish.name.getBytes(UTF_8);
|
return fish.name.getBytes(UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toAscii(Fish value) {
|
|
||||||
return value.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fish parseBytes(byte[] serialized) {
|
public Fish parseBytes(byte[] serialized) {
|
||||||
return new Fish(new String(serialized, UTF_8));
|
return new Fish(new String(serialized, UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Fish parseAscii(String ascii) {
|
|
||||||
return new Fish(ascii);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String LANCE = "lance";
|
private static final String LANCE = "lance";
|
||||||
private static final byte[] LANCE_BYTES = LANCE.getBytes(StandardCharsets.US_ASCII);
|
private static final byte[] LANCE_BYTES = LANCE.getBytes(StandardCharsets.US_ASCII);
|
||||||
private static final Metadata.Key<Fish> KEY = Metadata.Key.of("test", FISH_MARSHALLER);
|
private static final Metadata.Key<Fish> KEY = Metadata.Key.of("test-bin", FISH_MARSHALLER);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteParsed() {
|
public void testWriteParsed() {
|
||||||
|
|
@ -61,7 +50,7 @@ public class MetadataTest {
|
||||||
assertFalse(fishes.hasNext());
|
assertFalse(fishes.hasNext());
|
||||||
byte[][] serialized = metadata.serialize();
|
byte[][] serialized = metadata.serialize();
|
||||||
assertEquals(2, serialized.length);
|
assertEquals(2, serialized.length);
|
||||||
assertEquals(new String(serialized[0], StandardCharsets.US_ASCII), "test");
|
assertEquals(new String(serialized[0], StandardCharsets.US_ASCII), "test-bin");
|
||||||
assertArrayEquals(LANCE_BYTES, serialized[1]);
|
assertArrayEquals(LANCE_BYTES, serialized[1]);
|
||||||
assertSame(lance, metadata.get(KEY));
|
assertSame(lance, metadata.get(KEY));
|
||||||
// Serialized instance should be cached too
|
// Serialized instance should be cached too
|
||||||
|
|
@ -112,14 +101,8 @@ public class MetadataTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void integerMarshallerBytesIsBigEndian() {
|
public void integerMarshallerIsDecimal() {
|
||||||
assertEquals(Bytes.asList(new byte[] {0x12, 0x34, 0x56, 0x78}),
|
assertEquals("12345678", Metadata.INTEGER_MARSHALLER.toAsciiString(12345678));
|
||||||
Bytes.asList(Metadata.INTEGER_MARSHALLER.toBytes(0x12345678)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void integerMarshallerAsciiIsDecimal() {
|
|
||||||
assertEquals("12345678", Metadata.INTEGER_MARSHALLER.toAscii(12345678));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -132,8 +115,8 @@ public class MetadataTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void roundTripInteger(Integer i) {
|
private void roundTripInteger(Integer i) {
|
||||||
assertEquals(i, Metadata.INTEGER_MARSHALLER.parseBytes(Metadata.INTEGER_MARSHALLER.toBytes(i)));
|
assertEquals(i, Metadata.INTEGER_MARSHALLER.parseAsciiString(
|
||||||
assertEquals(i, Metadata.INTEGER_MARSHALLER.parseAscii(Metadata.INTEGER_MARSHALLER.toAscii(i)));
|
Metadata.INTEGER_MARSHALLER.toAsciiString(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Fish {
|
private static class Fish {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
package com.google.net.stubby.transport;
|
||||||
|
|
||||||
|
import static com.google.common.base.Charsets.US_ASCII;
|
||||||
|
import static com.google.common.base.Charsets.UTF_8;
|
||||||
|
import static com.google.net.stubby.Metadata.ASCII_STRING_MARSHALLER;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import com.google.net.stubby.Metadata.BinaryMarshaller;
|
||||||
|
import com.google.net.stubby.Metadata.Headers;
|
||||||
|
import com.google.net.stubby.Metadata.Key;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/** Unit tests for {@link TransportFrameUtil}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class TransportFrameUtilTest {
|
||||||
|
|
||||||
|
private static final String NONCOMPLIANT_ASCII_STRING = new String(new char[]{1, 2, 3});
|
||||||
|
|
||||||
|
private static final String COMPLIANT_ASCII_STRING = "Kyle";
|
||||||
|
|
||||||
|
private static final BinaryMarshaller<String> UTF8_STRING_MARSHALLER =
|
||||||
|
new BinaryMarshaller<String>() {
|
||||||
|
@Override
|
||||||
|
public byte[] toBytes(String value) {
|
||||||
|
return value.getBytes(UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseBytes(byte[] serialized) {
|
||||||
|
return new String(serialized, UTF_8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Key<String> PLAIN_STRING = Key.of("plainstring", ASCII_STRING_MARSHALLER);
|
||||||
|
private static final Key<String> BINARY_STRING = Key.of("string-bin", UTF8_STRING_MARSHALLER);
|
||||||
|
private static final Key<String> BINARY_STRING_WITHOUT_SUFFIX =
|
||||||
|
Key.of("string", ASCII_STRING_MARSHALLER);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToHttp2Headers() {
|
||||||
|
Headers headers = new Headers();
|
||||||
|
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);
|
||||||
|
// BINARY_STRING_WITHOUT_SUFFIX should not get in because it contains non-compliant ASCII
|
||||||
|
// characters but doesn't have "-bin" in the name.
|
||||||
|
byte[][] answer = new byte[][] {
|
||||||
|
"plainstring".getBytes(US_ASCII), COMPLIANT_ASCII_STRING.getBytes(US_ASCII),
|
||||||
|
"string-bin".getBytes(US_ASCII),
|
||||||
|
base64Encode(NONCOMPLIANT_ASCII_STRING.getBytes(US_ASCII))};
|
||||||
|
assertEquals(answer.length, http2Headers.length);
|
||||||
|
// http2Headers may re-sort the keys, so we cannot compare it with the answer side-by-side.
|
||||||
|
for (int i = 0; i < answer.length; i += 2) {
|
||||||
|
assertContains(http2Headers, answer[i], answer[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void binaryHeaderWithoutSuffix() {
|
||||||
|
Key.of("plainstring", UTF8_STRING_MARSHALLER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToAndFromHttp2Headers() {
|
||||||
|
Headers headers = new Headers();
|
||||||
|
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);
|
||||||
|
Headers recoveredHeaders = new Headers(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 static void assertContains(byte[][] headers, byte[] key, byte[] value) {
|
||||||
|
String keyString = new String(key, US_ASCII);
|
||||||
|
for (int i = 0; i < headers.length; i += 2) {
|
||||||
|
if (Arrays.equals(headers[i], key)) {
|
||||||
|
assertArrayEquals("value for key=" + keyString, value, headers[i + 1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("key=" + keyString + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] base64Encode(byte[] input) {
|
||||||
|
return BaseEncoding.base64().encode(input).getBytes(US_ASCII);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.google.net.stubby.Metadata;
|
import com.google.net.stubby.Metadata;
|
||||||
import com.google.net.stubby.SharedResourceHolder.Resource;
|
import com.google.net.stubby.SharedResourceHolder.Resource;
|
||||||
import com.google.net.stubby.transport.HttpUtil;
|
import com.google.net.stubby.transport.HttpUtil;
|
||||||
|
import com.google.net.stubby.transport.TransportFrameUtil;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
|
@ -78,7 +79,7 @@ class Utils {
|
||||||
headerValues[i++] = entry.getKey().array();
|
headerValues[i++] = entry.getKey().array();
|
||||||
headerValues[i++] = entry.getValue().array();
|
headerValues[i++] = entry.getValue().array();
|
||||||
}
|
}
|
||||||
return headerValues;
|
return TransportFrameUtil.toRawSerializedHeaders(headerValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Http2Headers convertClientHeaders(Metadata.Headers headers,
|
public static Http2Headers convertClientHeaders(Metadata.Headers headers,
|
||||||
|
|
@ -128,10 +129,10 @@ class Utils {
|
||||||
private static Http2Headers convertMetadata(Metadata headers) {
|
private static Http2Headers convertMetadata(Metadata headers) {
|
||||||
Preconditions.checkNotNull(headers, "headers");
|
Preconditions.checkNotNull(headers, "headers");
|
||||||
Http2Headers http2Headers = new DefaultHttp2Headers();
|
Http2Headers http2Headers = new DefaultHttp2Headers();
|
||||||
byte[][] serializedHeaders = headers.serialize();
|
byte[][] serializedHeaders = TransportFrameUtil.toHttp2Headers(headers);
|
||||||
for (int i = 0; i < serializedHeaders.length; i++) {
|
for (int i = 0; i < serializedHeaders.length; i += 2) {
|
||||||
http2Headers.add(new AsciiString(serializedHeaders[i], false),
|
http2Headers.add(new AsciiString(serializedHeaders[i], false),
|
||||||
new AsciiString(serializedHeaders[++i], false));
|
new AsciiString(serializedHeaders[i + 1], false));
|
||||||
}
|
}
|
||||||
return http2Headers;
|
return http2Headers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.net.stubby.Metadata;
|
import com.google.net.stubby.Metadata;
|
||||||
import com.google.net.stubby.transport.HttpUtil;
|
import com.google.net.stubby.transport.HttpUtil;
|
||||||
|
import com.google.net.stubby.transport.TransportFrameUtil;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.spdy.Header;
|
import com.squareup.okhttp.internal.spdy.Header;
|
||||||
|
|
||||||
|
|
@ -48,10 +49,10 @@ public class Headers {
|
||||||
okhttpHeaders.add(TE_HEADER);
|
okhttpHeaders.add(TE_HEADER);
|
||||||
|
|
||||||
// Now add any application-provided headers.
|
// Now add any application-provided headers.
|
||||||
byte[][] serializedHeaders = headers.serialize();
|
byte[][] serializedHeaders = TransportFrameUtil.toHttp2Headers(headers);
|
||||||
for (int i = 0; i < serializedHeaders.length; i++) {
|
for (int i = 0; i < serializedHeaders.length; i += 2) {
|
||||||
ByteString key = ByteString.of(serializedHeaders[i]);
|
ByteString key = ByteString.of(serializedHeaders[i]);
|
||||||
ByteString value = ByteString.of(serializedHeaders[++i]);
|
ByteString value = ByteString.of(serializedHeaders[i + 1]);
|
||||||
if (isApplicationHeader(key)) {
|
if (isApplicationHeader(key)) {
|
||||||
okhttpHeaders.add(new Header(key, value));
|
okhttpHeaders.add(new Header(key, value));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.google.net.stubby.transport.okhttp;
|
package com.google.net.stubby.transport.okhttp;
|
||||||
|
|
||||||
import com.google.net.stubby.Metadata;
|
import com.google.net.stubby.Metadata;
|
||||||
|
import com.google.net.stubby.transport.TransportFrameUtil;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.spdy.Header;
|
import com.squareup.okhttp.internal.spdy.Header;
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ class Utils {
|
||||||
headerValues[i++] = header.name.toByteArray();
|
headerValues[i++] = header.name.toByteArray();
|
||||||
headerValues[i++] = header.value.toByteArray();
|
headerValues[i++] = header.value.toByteArray();
|
||||||
}
|
}
|
||||||
return headerValues;
|
return TransportFrameUtil.toRawSerializedHeaders(headerValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Utils() {
|
private Utils() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue