mirror of https://github.com/grpc/grpc-java.git
core: hide access to pseudo headers
Previously anyone could create metadata keys with a leading ":", due to not having a way to prevent it. This change effectively only allows internal code to make use of this.
This commit is contained in:
parent
5713ebccba
commit
e60a179772
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc;
|
||||
|
||||
import io.grpc.Metadata.AsciiMarshaller;
|
||||
import io.grpc.Metadata.Key;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
|
|
@ -44,7 +45,14 @@ public final class InternalMetadata {
|
|||
|
||||
@Internal
|
||||
public static <T> Key<T> keyOf(String name, TrustedAsciiMarshaller<T> marshaller) {
|
||||
return Metadata.Key.of(name, marshaller);
|
||||
boolean isPseudo = name != null && !name.isEmpty() && name.charAt(0) == ':';
|
||||
return Metadata.Key.of(name, isPseudo, marshaller);
|
||||
}
|
||||
|
||||
@Internal
|
||||
public static <T> Key<T> keyOf(String name, AsciiMarshaller<T> marshaller) {
|
||||
boolean isPseudo = name != null && !name.isEmpty() && name.charAt(0) == ':';
|
||||
return Metadata.Key.of(name, isPseudo, marshaller);
|
||||
}
|
||||
|
||||
@Internal
|
||||
|
|
|
|||
|
|
@ -599,11 +599,15 @@ public final class Metadata {
|
|||
* <b>not</b> end with {@link #BINARY_HEADER_SUFFIX}
|
||||
*/
|
||||
public static <T> Key<T> of(String name, AsciiMarshaller<T> marshaller) {
|
||||
return new AsciiKey<T>(name, marshaller);
|
||||
return of(name, false, marshaller);
|
||||
}
|
||||
|
||||
static <T> Key<T> of(String name, TrustedAsciiMarshaller<T> marshaller) {
|
||||
return new TrustedAsciiKey<T>(name, marshaller);
|
||||
static <T> Key<T> of(String name, boolean pseudo, AsciiMarshaller<T> marshaller) {
|
||||
return new AsciiKey<T>(name, pseudo, marshaller);
|
||||
}
|
||||
|
||||
static <T> Key<T> of(String name, boolean pseudo, TrustedAsciiMarshaller<T> marshaller) {
|
||||
return new TrustedAsciiKey<T>(name, pseudo, marshaller);
|
||||
}
|
||||
|
||||
private final String originalName;
|
||||
|
|
@ -626,13 +630,12 @@ public final class Metadata {
|
|||
return valid;
|
||||
}
|
||||
|
||||
private static String validateName(String n) {
|
||||
private static String validateName(String n, boolean pseudo) {
|
||||
checkNotNull(n, "name");
|
||||
checkArgument(n.length() != 0, "token must have at least 1 tchar");
|
||||
checkArgument(!n.isEmpty(), "token must have at least 1 tchar");
|
||||
for (int i = 0; i < n.length(); i++) {
|
||||
char tChar = n.charAt(i);
|
||||
// TODO(notcarl): remove this hack once pseudo headers are properly handled
|
||||
if (tChar == ':' && i == 0) {
|
||||
if (pseudo && tChar == ':' && i == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -642,9 +645,9 @@ public final class Metadata {
|
|||
return n;
|
||||
}
|
||||
|
||||
private Key(String name) {
|
||||
private Key(String name, boolean pseudo) {
|
||||
this.originalName = checkNotNull(name, "name");
|
||||
this.name = validateName(this.originalName.toLowerCase(Locale.ROOT));
|
||||
this.name = validateName(this.originalName.toLowerCase(Locale.ROOT), pseudo);
|
||||
this.nameBytes = this.name.getBytes(US_ASCII);
|
||||
}
|
||||
|
||||
|
|
@ -722,7 +725,7 @@ public final class Metadata {
|
|||
|
||||
/** Keys have a name and a binary marshaller used for serialization. */
|
||||
private BinaryKey(String name, BinaryMarshaller<T> marshaller) {
|
||||
super(name);
|
||||
super(name, false /* not pseudo */);
|
||||
checkArgument(
|
||||
name.endsWith(BINARY_HEADER_SUFFIX),
|
||||
"Binary header is named %s. It must end with %s",
|
||||
|
|
@ -747,8 +750,8 @@ public final class Metadata {
|
|||
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);
|
||||
private AsciiKey(String name, boolean pseudo, AsciiMarshaller<T> marshaller) {
|
||||
super(name, pseudo);
|
||||
Preconditions.checkArgument(
|
||||
!name.endsWith(BINARY_HEADER_SUFFIX),
|
||||
"ASCII header is named %s. Only binary headers may end with %s",
|
||||
|
|
@ -772,8 +775,8 @@ public final class Metadata {
|
|||
private final TrustedAsciiMarshaller<T> marshaller;
|
||||
|
||||
/** Keys have a name and an ASCII marshaller used for serialization. */
|
||||
private TrustedAsciiKey(String name, TrustedAsciiMarshaller<T> marshaller) {
|
||||
super(name);
|
||||
private TrustedAsciiKey(String name, boolean pseudo, TrustedAsciiMarshaller<T> marshaller) {
|
||||
super(name, pseudo);
|
||||
Preconditions.checkArgument(
|
||||
!name.endsWith(BINARY_HEADER_SUFFIX),
|
||||
"ASCII header is named %s. Only binary headers may end with %s",
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ public final class Status {
|
|||
* Key to bind status code to trailing metadata.
|
||||
*/
|
||||
static final Metadata.Key<Status> CODE_KEY
|
||||
= Metadata.Key.of("grpc-status", new StatusCodeMarshaller());
|
||||
= Metadata.Key.of("grpc-status", false /* not pseudo */, new StatusCodeMarshaller());
|
||||
|
||||
/**
|
||||
* Marshals status messages for ({@link #MESSAGE_KEY}. gRPC does not use binary coding of
|
||||
|
|
@ -377,7 +377,7 @@ public final class Status {
|
|||
* Key to bind status message to trailing metadata.
|
||||
*/
|
||||
static final Metadata.Key<String> MESSAGE_KEY =
|
||||
Metadata.Key.of("grpc-message", STATUS_MESSAGE_MARSHALLER);
|
||||
Metadata.Key.of("grpc-message", false /* not pseudo */, STATUS_MESSAGE_MARSHALLER);
|
||||
|
||||
/**
|
||||
* Extract an error {@link Status} from the causal chain of a {@link Throwable}.
|
||||
|
|
|
|||
|
|
@ -63,6 +63,14 @@ public class MetadataTest {
|
|||
private static final byte[] LANCE_BYTES = LANCE.getBytes(US_ASCII);
|
||||
private static final Metadata.Key<Fish> KEY = Metadata.Key.of("test-bin", FISH_MARSHALLER);
|
||||
|
||||
@Test
|
||||
public void noPseudoHeaders() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Invalid character");
|
||||
|
||||
Metadata.Key.of(":test-bin", FISH_MARSHALLER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutations() {
|
||||
Fish lance = new Fish(LANCE);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import static org.mockito.Matchers.same;
|
|||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import io.grpc.InternalMetadata;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.Status.Code;
|
||||
|
|
@ -40,6 +41,10 @@ import org.mockito.MockitoAnnotations;
|
|||
/** Unit tests for {@link Http2ClientStreamTransportState}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class Http2ClientStreamTransportStateTest {
|
||||
|
||||
private final Metadata.Key<String> testStatusMashaller =
|
||||
InternalMetadata.keyOf(":status", Metadata.ASCII_STRING_MARSHALLER);
|
||||
|
||||
@Mock private ClientStreamListener mockListener;
|
||||
@Captor private ArgumentCaptor<Status> statusCaptor;
|
||||
|
||||
|
|
@ -53,7 +58,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportHeadersReceived(headers);
|
||||
|
|
@ -67,7 +72,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "500");
|
||||
headers.put(testStatusMashaller, "500");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportHeadersReceived(headers);
|
||||
|
|
@ -96,7 +101,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), "text/html");
|
||||
state.transportHeadersReceived(headers);
|
||||
state.transportDataReceived(ReadableBuffers.empty(), true);
|
||||
|
|
@ -112,7 +117,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "401");
|
||||
headers.put(testStatusMashaller, "401");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), "text/html");
|
||||
state.transportHeadersReceived(headers);
|
||||
state.transportDataReceived(ReadableBuffers.empty(), true);
|
||||
|
|
@ -130,14 +135,14 @@ public class Http2ClientStreamTransportStateTest {
|
|||
state.setListener(mockListener);
|
||||
|
||||
Metadata infoHeaders = new Metadata();
|
||||
infoHeaders.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "100");
|
||||
infoHeaders.put(testStatusMashaller, "100");
|
||||
state.transportHeadersReceived(infoHeaders);
|
||||
Metadata infoHeaders2 = new Metadata();
|
||||
infoHeaders2.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "101");
|
||||
infoHeaders2.put(testStatusMashaller, "101");
|
||||
state.transportHeadersReceived(infoHeaders2);
|
||||
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportHeadersReceived(headers);
|
||||
|
|
@ -151,7 +156,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportHeadersReceived(headers);
|
||||
|
|
@ -170,7 +175,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), "text/html");
|
||||
state.transportHeadersReceived(headers);
|
||||
Metadata headersAgain = new Metadata();
|
||||
|
|
@ -201,7 +206,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), "text/html");
|
||||
state.transportHeadersReceived(headers);
|
||||
String testString = "This is a test";
|
||||
|
|
@ -216,7 +221,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata trailers = new Metadata();
|
||||
trailers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
trailers.put(testStatusMashaller, "200");
|
||||
trailers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
trailers.put(Metadata.Key.of("grpc-status", Metadata.ASCII_STRING_MARSHALLER), "0");
|
||||
|
|
@ -231,7 +236,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportHeadersReceived(headers);
|
||||
|
|
@ -248,7 +253,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata trailers = new Metadata();
|
||||
trailers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
trailers.put(testStatusMashaller, "200");
|
||||
trailers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
trailers.put(Metadata.Key.of("grpc-status", Metadata.ASCII_STRING_MARSHALLER), "1");
|
||||
|
|
@ -263,7 +268,7 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata trailers = new Metadata();
|
||||
trailers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "401");
|
||||
trailers.put(testStatusMashaller, "401");
|
||||
trailers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportTrailersReceived(trailers);
|
||||
|
|
@ -308,12 +313,12 @@ public class Http2ClientStreamTransportStateTest {
|
|||
BaseTransportState state = new BaseTransportState();
|
||||
state.setListener(mockListener);
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "200");
|
||||
headers.put(testStatusMashaller, "200");
|
||||
headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"application/grpc");
|
||||
state.transportHeadersReceived(headers);
|
||||
Metadata trailers = new Metadata();
|
||||
trailers.put(Metadata.Key.of(":status", Metadata.ASCII_STRING_MARSHALLER), "401");
|
||||
trailers.put(testStatusMashaller, "401");
|
||||
state.transportTrailersReceived(trailers);
|
||||
|
||||
verify(mockListener).headersRead(headers);
|
||||
|
|
|
|||
Loading…
Reference in New Issue