下面列出了 io.netty.handler.codec.http2.Http2ServerUpgradeCodec #io.netty.util.AsciiString 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
protected TTransport newTransport(String uri, HttpHeaders headers) throws TTransportException {
final SSLContext sslContext;
try {
sslContext = SSLContextBuilder.create()
.loadTrustMaterial((TrustStrategy) (chain, authType) -> true)
.build();
} catch (GeneralSecurityException e) {
throw new TTransportException("failed to initialize an SSL context", e);
}
final THttpClient client = new THttpClient(
uri, HttpClientBuilder.create()
.setSSLHostnameVerifier((hostname, session) -> true)
.setSSLContext(sslContext)
.build());
client.setCustomHeaders(
headers.names().stream()
.collect(toImmutableMap(AsciiString::toString,
name -> String.join(", ", headers.getAll(name)))));
return client;
}
/**
* Filter the {@link HttpHeaderNames#TE} header according to the
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.2">special rules in the HTTP/2 RFC</a>.
*
* @param entry the entry whose name is {@link HttpHeaderNames#TE}.
* @param out the resulting HTTP/2 headers.
*/
private static void toHttp2HeadersFilterTE(Entry<CharSequence, CharSequence> entry,
HttpHeadersBuilder out) {
if (AsciiString.indexOf(entry.getValue(), ',', 0) == -1) {
if (AsciiString.contentEqualsIgnoreCase(AsciiString.trim(entry.getValue()),
HttpHeaderValues.TRAILERS)) {
out.add(HttpHeaderNames.TE, HttpHeaderValues.TRAILERS.toString());
}
} else {
final List<CharSequence> teValues = StringUtil.unescapeCsvFields(entry.getValue());
for (CharSequence teValue : teValues) {
if (AsciiString.contentEqualsIgnoreCase(AsciiString.trim(teValue),
HttpHeaderValues.TRAILERS)) {
out.add(HttpHeaderNames.TE, HttpHeaderValues.TRAILERS.toString());
break;
}
}
}
}
@SuppressWarnings("BetaApi") // BaseEncoding is stable in Guava 20.0
protected Http2Headers add(AsciiString name, AsciiString value) {
byte[] nameBytes = bytes(name);
byte[] valueBytes;
if (!name.endsWith(binaryHeaderSuffix)) {
valueBytes = bytes(value);
addHeader(value, nameBytes, valueBytes);
return this;
}
int startPos = 0;
int endPos = -1;
while (endPos < value.length()) {
int indexOfComma = value.indexOf(',', startPos);
endPos = indexOfComma == AsciiString.INDEX_NOT_FOUND ? value.length() : indexOfComma;
AsciiString curVal = value.subSequence(startPos, endPos, false);
valueBytes = BaseEncoding.base64().decode(curVal);
startPos = indexOfComma + 1;
addHeader(curVal, nameBytes, valueBytes);
}
return this;
}
public static HttpHeaders mergeTrailers(HttpHeaders headers, HttpHeaders additionalTrailers) {
if (additionalTrailers.isEmpty()) {
return headers;
}
if (headers.isEmpty()) {
return additionalTrailers;
}
final HttpHeadersBuilder builder = headers.toBuilder();
for (AsciiString name : additionalTrailers.names()) {
if (!ADDITIONAL_RESPONSE_HEADER_BLACKLIST.contains(name) &&
!isTrailerBlacklisted(name)) {
builder.remove(name);
additionalTrailers.forEachValue(name, value -> builder.add(name, value));
}
}
return builder.build();
}
/** Returns a {@link ProtocolNegotiator} for plaintext client channel. */
public static InternalProtocolNegotiator.ProtocolNegotiator plaintext() {
final io.grpc.netty.ProtocolNegotiator negotiator = ProtocolNegotiators.plaintext();
final class PlaintextNegotiator implements InternalProtocolNegotiator.ProtocolNegotiator {
@Override
public AsciiString scheme() {
return negotiator.scheme();
}
@Override
public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
return negotiator.newHandler(grpcHandler);
}
@Override
public void close() {
negotiator.close();
}
}
return new PlaintextNegotiator();
}
/**
* Converts the specified Armeria HTTP/2 response headers into Netty HTTP/2 headers.
*
* @param inputHeaders the HTTP/2 response headers to convert.
*/
public static Http2Headers toNettyHttp2ServerTrailer(HttpHeaders inputHeaders) {
final Http2Headers outputHeaders = new DefaultHttp2Headers(false, inputHeaders.size());
for (Entry<AsciiString, String> entry : inputHeaders) {
final AsciiString name = entry.getKey();
final String value = entry.getValue();
if (HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(name)) {
continue;
}
if (ADDITIONAL_RESPONSE_HEADER_BLACKLIST.contains(name)) {
continue;
}
if (isTrailerBlacklisted(name)) {
continue;
}
outputHeaders.add(name, value);
}
return outputHeaders;
}
@Test
void aggregatedJson() throws Exception {
expectAggregatedJson(Stream.of(TEST_STRINGS))
.expectComplete()
.verify();
expectAggregatedJson(Flux.fromArray(TEST_STRINGS))
.expectComplete()
.verify();
// Iterable with trailers.
final HttpHeaders trailer = HttpHeaders.of(AsciiString.of("x-trailer"), "value");
expectAggregatedJson(Arrays.asList(TEST_STRINGS), JSON_HEADERS, trailer)
.expectNext(trailer)
.expectComplete()
.verify();
}
@Test
public void closeBeforeClientHalfCloseShouldSucceed() throws Exception {
ListMultimap<CharSequence, CharSequence> expectedHeaders =
ImmutableListMultimap.copyOf(new DefaultHttp2Headers()
.status(new AsciiString("200"))
.set(new AsciiString("content-type"), new AsciiString("application/grpc"))
.set(new AsciiString("grpc-status"), new AsciiString("0")));
stream().close(Status.OK, new Metadata());
ArgumentCaptor<SendResponseHeadersCommand> sendHeadersCap =
ArgumentCaptor.forClass(SendResponseHeadersCommand.class);
verify(writeQueue).enqueue(sendHeadersCap.capture(), eq(true));
SendResponseHeadersCommand sendHeaders = sendHeadersCap.getValue();
assertThat(sendHeaders.stream()).isSameInstanceAs(stream.transportState());
assertThat(ImmutableListMultimap.copyOf(sendHeaders.headers()))
.containsExactlyEntriesIn(expectedHeaders);
assertThat(sendHeaders.endOfStream()).isTrue();
verifyZeroInteractions(serverListener);
// Sending complete. Listener gets closed()
stream().transportState().complete();
verify(serverListener).closed(Status.OK);
assertNull("no message expected", listenerMessageQueue.poll());
}
private static void setHttp2Scheme(HttpHeaders in, URI uri, Http2Headers out) {
String value = uri.getScheme();
if (value != null) {
out.scheme(new AsciiString(value));
return;
}
// Consume the Scheme extension header if present
CharSequence cValue = in.get(ExtensionHeaderNames.SCHEME.text());
if (cValue != null) {
out.scheme(AsciiString.of(cValue));
return;
}
if (uri.getPort() == HTTPS.port()) {
out.scheme(HTTPS.name());
} else if (uri.getPort() == HTTP.port()) {
out.scheme(HTTP.name());
} else {
throw new IllegalArgumentException(":scheme must be specified. " +
"see https://tools.ietf.org/html/rfc7540#section-8.1.2.3");
}
}
@Test
public void headersWithInvalidMethodShouldFail() throws Exception {
manualSetUp();
Http2Headers headers = new DefaultHttp2Headers()
.method(HTTP_FAKE_METHOD)
.set(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC)
.path(new AsciiString("/foo/bar"));
ByteBuf headersFrame = headersFrame(STREAM_ID, headers);
channelRead(headersFrame);
Http2Headers responseHeaders = new DefaultHttp2Headers()
.set(InternalStatus.CODE_KEY.name(), String.valueOf(Code.INTERNAL.value()))
.set(InternalStatus.MESSAGE_KEY.name(), "Method 'FAKE' is not supported")
.status("" + 405)
.set(CONTENT_TYPE_HEADER, "text/plain; encoding=utf-8");
verifyWrite()
.writeHeaders(
eq(ctx()),
eq(STREAM_ID),
eq(responseHeaders),
eq(0),
eq(false),
any(ChannelPromise.class));
}
@Test
public void headersWithInvalidPathShouldFail() throws Exception {
manualSetUp();
Http2Headers headers = new DefaultHttp2Headers()
.method(HTTP_METHOD)
.set(CONTENT_TYPE_HEADER, CONTENT_TYPE_GRPC)
.path(new AsciiString("foo/bar"));
ByteBuf headersFrame = headersFrame(STREAM_ID, headers);
channelRead(headersFrame);
Http2Headers responseHeaders = new DefaultHttp2Headers()
.set(InternalStatus.CODE_KEY.name(), String.valueOf(Code.UNIMPLEMENTED.value()))
.set(InternalStatus.MESSAGE_KEY.name(), "Expected path to start with /: foo/bar")
.status("" + 404)
.set(CONTENT_TYPE_HEADER, "text/plain; encoding=utf-8");
verifyWrite().writeHeaders(eq(ctx()), eq(STREAM_ID), eq(responseHeaders), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), any(ChannelPromise.class));
}
@Test
public void testAbsoluteFormRequestTargetHandledFromHeaders() throws Exception {
bootstrapEnv(2, 1, 0);
final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/pub/WWW/TheProject.html");
final HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders.set(HttpHeaderNames.HOST, "[email protected]:5555");
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.PATH.text(), "ignored_path");
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "https");
final Http2Headers http2Headers =
new DefaultHttp2Headers().method(new AsciiString("GET"))
.path(new AsciiString("/pub/WWW/TheProject.html"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https"));
ChannelPromise writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
}
private void createStream() throws Exception {
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);
channelRead(headersFrame);
ArgumentCaptor<NettyServerStream> streamCaptor =
ArgumentCaptor.forClass(NettyServerStream.class);
ArgumentCaptor<String> methodCaptor = ArgumentCaptor.forClass(String.class);
verify(transportListener).streamCreated(streamCaptor.capture(), methodCaptor.capture(),
any(Metadata.class));
stream = streamCaptor.getValue();
}
@Test
public void testHeadersOnlyRequest() throws Exception {
bootstrapEnv(2, 1, 0);
final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET,
"http://[email protected]:5555/example");
final HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders.set(HttpHeaderNames.HOST, "[email protected]:5555");
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http");
httpHeaders.add(of("foo"), of("goo"));
httpHeaders.add(of("foo"), of("goo2"));
httpHeaders.add(of("foo2"), of("goo2"));
final Http2Headers http2Headers =
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/example"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
}
/**
* Returns the number of bytes required to Huffman encode the input string literal.
*
* @param data the string literal to be Huffman encoded
* @return the number of bytes required to Huffman encode {@code data}
*/
int getEncodedLength(CharSequence data) {
if (data instanceof AsciiString) {
AsciiString string = (AsciiString) data;
try {
encodedLengthProcessor.reset();
string.forEachByte(encodedLengthProcessor);
return encodedLengthProcessor.length();
} catch (Exception e) {
PlatformDependent.throwException(e);
return -1;
}
} else {
return getEncodedLengthSlowPath(data);
}
}
public static AsciiString readLEAsciiString(ByteBuf buffer) {
Preconditions.checkNotNull(buffer, "buffer");
int length = buffer.readIntLE();
byte[] bytes = new byte[length];
buffer.readBytes(bytes);
return new AsciiString(bytes);
}
@Override
public void serialize(ByteBuf buffer, LoginPacket packet) {
buffer.writeInt(packet.getProtocolVersion());
AsciiString chainData = packet.getChainData();
AsciiString skinData = packet.getSkinData();
VarInts.writeUnsignedInt(buffer, chainData.length() + skinData.length() + 8);
BedrockUtils.writeLEAsciiString(buffer, chainData);
BedrockUtils.writeLEAsciiString(buffer, skinData);
}
/**
* Fetch MIME type part from Content-Type header value as a char sequence.从Content-Type头值中提取MIME类型部分作为char序列。
*
* @param contentTypeValue Content-Type header value to parse
* @return the MIME type as a {@code CharSequence} from message's Content-Type header
* or {@code null} if content-type header or MIME type part of this header are not presented
* <p/>
* "content-type: text/html; charset=utf-8" - "text/html" will be returned <br/>
* "content-type: text/html" - "text/html" will be returned <br/>
* "content-type: empty header - {@code null} we be returned
* @throws NullPointerException in case if {@code contentTypeValue == null}
*/
public static CharSequence getMimeType(CharSequence contentTypeValue) {
if (contentTypeValue == null) {
throw new NullPointerException("contentTypeValue");
}
int indexOfSemicolon = AsciiString.indexOfIgnoreCaseAscii(contentTypeValue, SEMICOLON, 0);
if (indexOfSemicolon != AsciiString.INDEX_NOT_FOUND) {
return contentTypeValue.subSequence(0, indexOfSemicolon);
} else {
return contentTypeValue.length() > 0 ? contentTypeValue : null;
}
}
static void setHttp2Authority(String authority, Http2Headers out) {
// The authority MUST NOT include the deprecated "userinfo" subcomponent
if (authority != null) {
if (authority.isEmpty()) {
out.authority(EMPTY_STRING);
} else {
int start = authority.indexOf('@') + 1;
int length = authority.length() - start;
if (length == 0) {
throw new IllegalArgumentException("authority: " + authority);
}
out.authority(new AsciiString(authority, start, length));
}
}
}
private GrpcHttp2OutboundHeaders(AsciiString[] preHeaders, byte[][] serializedMetadata) {
normalHeaders = new AsciiString[serializedMetadata.length];
for (int i = 0; i < normalHeaders.length; i++) {
normalHeaders[i] = new AsciiString(serializedMetadata[i], false);
}
this.preHeaders = preHeaders;
}
@Test
public void decodeShouldSucceed() throws Exception {
ByteBuf buf = encode(b(":method"), b("GET"), b("akey"), b("avalue"), randomBytes(), randomBytes());
try {
Http2Headers headers = decoder.decodeHeaders(0, buf);
assertEquals(3, headers.size());
assertEquals("GET", headers.method().toString());
assertEquals("avalue", headers.get(new AsciiString("akey")).toString());
} finally {
buf.release();
}
}
private boolean isUpgradeRequest(FullHttpRequest request) {
if (!request.decoderResult().isSuccess()) {
return false;
}
CharSequence connectionHeaderValue =
request.headers().get(HttpHeaderNames.CONNECTION);
if (connectionHeaderValue == null) {
return false;
}
AsciiString connectionHeaderString =
new AsciiString(connectionHeaderValue);
AsciiString[] connectionHeaderFields =
connectionHeaderString.toLowerCase().split(',');
boolean hasUpgradeField = false;
AsciiString upgradeValue = HttpHeaderValues.UPGRADE.toLowerCase();
for (AsciiString s : connectionHeaderFields) {
if (upgradeValue.equals(s.trim())) {
hasUpgradeField = true;
break;
}
}
if (!hasUpgradeField) {
return false;
}
if (!request.headers().contains(
HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true)) {
return false;
}
return request.uri().equals(websocketPath);
}
private static void writeAscii(ByteBuf buf, int offset, CharSequence value) {
if (value instanceof AsciiString) {
ByteBufUtil.copy((AsciiString) value, 0, buf, offset, value.length());
} else {
buf.setCharSequence(offset, value, CharsetUtil.US_ASCII);
}
}
@Test
void testHeaderNameNormalization() {
final HttpHeadersBase headers = newHttp2Headers();
headers.add("Foo", "bar");
assertThat(headers.getAll("foo")).containsExactly("bar");
assertThat(headers.getAll("fOO")).containsExactly("bar");
assertThat(headers.names()).contains(HttpHeaderNames.of("foo"))
.doesNotContain(AsciiString.of("Foo"));
}
private void addHeader(AsciiString value, byte[] nameBytes, byte[] valueBytes) {
if (namesAndValuesIdx == namesAndValues.length) {
expandHeadersAndValues();
}
values[namesAndValuesIdx / 2] = value;
namesAndValues[namesAndValuesIdx] = nameBytes;
namesAndValuesIdx++;
namesAndValues[namesAndValuesIdx] = valueBytes;
namesAndValuesIdx++;
}
/** Foo bar. */
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public AsciiString old() {
return new AsciiString("/" + method.getFullMethodName());
}
private Http2ConnectionHandler newHttp2ConnectionHandler(ChannelPipeline pipeline, AsciiString scheme) {
final Timer keepAliveTimer = newKeepAliveTimer(scheme == SCHEME_HTTP ? H2C : H2);
return new Http2ServerConnectionHandlerBuilder(pipeline.channel(), config, keepAliveTimer,
gracefulShutdownSupport, scheme.toString())
.server(true)
.initialSettings(http2Settings())
.build();
}
static GrpcHttp2OutboundHeaders clientRequestHeaders(byte[][] serializedMetadata,
AsciiString authority, AsciiString path, AsciiString method, AsciiString scheme,
AsciiString userAgent) {
AsciiString[] preHeaders = new AsciiString[] {
Http2Headers.PseudoHeaderName.AUTHORITY.value(), authority,
Http2Headers.PseudoHeaderName.PATH.value(), path,
Http2Headers.PseudoHeaderName.METHOD.value(), method,
Http2Headers.PseudoHeaderName.SCHEME.value(), scheme,
Utils.CONTENT_TYPE_HEADER, Utils.CONTENT_TYPE_GRPC,
Utils.TE_HEADER, Utils.TE_TRAILERS,
Utils.USER_AGENT, userAgent,
};
return new GrpcHttp2OutboundHeaders(preHeaders, serializedMetadata);
}
/**
* Indicates whether the specified header follows the pseudo-header format (begins with ':' character)
*
* @return {@code true} if the header follow the pseudo-header format
*/
public static boolean hasPseudoHeaderFormat(CharSequence headerName) {
if (headerName instanceof AsciiString) {
final AsciiString asciiHeaderName = (AsciiString) headerName;
return asciiHeaderName.length() > 0 && asciiHeaderName.byteAt(0) == PSEUDO_HEADER_PREFIX_BYTE;
} else {
return headerName.length() > 0 && headerName.charAt(0) == PSEUDO_HEADER_PREFIX;
}
}
private static Http2Headers headersOfSize(final int minSize) {
final AsciiString singleByte = new AsciiString(new byte[]{0}, false);
DefaultHttp2Headers headers = new DefaultHttp2Headers(false);
for (int size = 0; size < minSize; size += 2) {
headers.add(singleByte, singleByte);
}
return headers;
}