|
40 | 40 | import io.netty.channel.ChannelInboundHandlerAdapter;
|
41 | 41 | import io.netty.channel.ChannelInitializer;
|
42 | 42 | import io.netty.channel.ChannelOption;
|
| 43 | +import io.netty.channel.ChannelPipeline; |
43 | 44 | import io.netty.channel.FixedRecvByteBufAllocator;
|
44 | 45 | import io.netty.channel.RecvByteBufAllocator;
|
| 46 | +import io.netty.channel.SimpleChannelInboundHandler; |
45 | 47 | import io.netty.channel.socket.nio.NioChannelOption;
|
46 | 48 | import io.netty.handler.codec.ByteToMessageDecoder;
|
47 | 49 | import io.netty.handler.codec.http.HttpContentCompressor;
|
48 | 50 | import io.netty.handler.codec.http.HttpContentDecompressor;
|
| 51 | +import io.netty.handler.codec.http.HttpMessage; |
49 | 52 | import io.netty.handler.codec.http.HttpObjectAggregator;
|
50 | 53 | import io.netty.handler.codec.http.HttpRequestDecoder;
|
51 | 54 | import io.netty.handler.codec.http.HttpResponseEncoder;
|
| 55 | +import io.netty.handler.codec.http.HttpServerCodec; |
| 56 | +import io.netty.handler.codec.http.HttpServerUpgradeHandler; |
| 57 | +import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec; |
| 58 | +import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; |
| 59 | +import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler; |
| 60 | +import io.netty.handler.codec.http2.Http2CodecUtil; |
| 61 | +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; |
| 62 | +import io.netty.handler.codec.http2.Http2MultiplexHandler; |
| 63 | +import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; |
| 64 | +import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec; |
| 65 | +import io.netty.handler.logging.LogLevel; |
| 66 | +import io.netty.handler.logging.LoggingHandler; |
52 | 67 | import io.netty.handler.timeout.ReadTimeoutException;
|
53 | 68 | import io.netty.handler.timeout.ReadTimeoutHandler;
|
| 69 | +import io.netty.util.AsciiString; |
54 | 70 | import io.netty.util.AttributeKey;
|
| 71 | +import io.netty.util.ReferenceCountUtil; |
| 72 | + |
55 | 73 | import org.apache.logging.log4j.LogManager;
|
56 | 74 | import org.apache.logging.log4j.Logger;
|
57 | 75 | import org.opensearch.ExceptionsHelper;
|
@@ -335,38 +353,152 @@ protected HttpChannelHandler(final Netty4HttpServerTransport transport, final Ht
|
335 | 353 | this.responseCreator = new Netty4HttpResponseCreator();
|
336 | 354 | }
|
337 | 355 |
|
| 356 | + public ChannelHandler getRequestHandler() { |
| 357 | + return requestHandler; |
| 358 | + } |
| 359 | + |
338 | 360 | @Override
|
339 | 361 | protected void initChannel(Channel ch) throws Exception {
|
340 | 362 | Netty4HttpChannel nettyHttpChannel = new Netty4HttpChannel(ch);
|
341 | 363 | ch.attr(HTTP_CHANNEL_KEY).set(nettyHttpChannel);
|
342 | 364 | ch.pipeline().addLast("byte_buf_sizer", byteBufSizer);
|
343 | 365 | ch.pipeline().addLast("read_timeout", new ReadTimeoutHandler(transport.readTimeoutMillis, TimeUnit.MILLISECONDS));
|
| 366 | + |
| 367 | + configurePipeline(ch); |
| 368 | + transport.serverAcceptedChannel(nettyHttpChannel); |
| 369 | + } |
| 370 | + |
| 371 | + @Override |
| 372 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| 373 | + ExceptionsHelper.maybeDieOnAnotherThread(cause); |
| 374 | + super.exceptionCaught(ctx, cause); |
| 375 | + } |
| 376 | + |
| 377 | + protected void configurePipeline(Channel ch) { |
| 378 | + final UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() { |
| 379 | + @Override |
| 380 | + public UpgradeCodec newUpgradeCodec(CharSequence protocol) { |
| 381 | + if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { |
| 382 | + return new Http2ServerUpgradeCodec( |
| 383 | + Http2FrameCodecBuilder.forServer().build(), |
| 384 | + new Http2MultiplexHandler(createHttp2ChannelInitializer(ch.pipeline())) |
| 385 | + ); |
| 386 | + } else { |
| 387 | + return null; |
| 388 | + } |
| 389 | + } |
| 390 | + }; |
| 391 | + |
| 392 | + final HttpServerCodec sourceCodec = new HttpServerCodec( |
| 393 | + handlingSettings.getMaxInitialLineLength(), |
| 394 | + handlingSettings.getMaxHeaderSize(), |
| 395 | + handlingSettings.getMaxChunkSize() |
| 396 | + ); |
| 397 | + |
| 398 | + final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory); |
| 399 | + final CleartextHttp2ServerUpgradeHandler cleartextUpgradeHandler = new CleartextHttp2ServerUpgradeHandler( |
| 400 | + sourceCodec, |
| 401 | + upgradeHandler, |
| 402 | + createHttp2ChannelInitializerPriorKnowledge() |
| 403 | + ); |
| 404 | + |
| 405 | + ch.pipeline().addLast(cleartextUpgradeHandler).addLast(new SimpleChannelInboundHandler<HttpMessage>() { |
| 406 | + @Override |
| 407 | + protected void channelRead0(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { |
| 408 | + final HttpObjectAggregator aggregator = new HttpObjectAggregator(handlingSettings.getMaxContentLength()); |
| 409 | + aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); |
| 410 | + |
| 411 | + // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP |
| 412 | + final ChannelPipeline pipeline = ctx.pipeline(); |
| 413 | + pipeline.addAfter(ctx.name(), "handler", getRequestHandler()); |
| 414 | + pipeline.replace(this, "aggregator", aggregator); |
| 415 | + |
| 416 | + ch.pipeline().addLast("decoder_compress", new HttpContentDecompressor()); |
| 417 | + ch.pipeline().addLast("encoder", new HttpResponseEncoder()); |
| 418 | + if (handlingSettings.isCompression()) { |
| 419 | + ch.pipeline() |
| 420 | + .addAfter("aggregator", "encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); |
| 421 | + } |
| 422 | + ch.pipeline().addBefore("handler", "request_creator", requestCreator); |
| 423 | + ch.pipeline().addBefore("handler", "response_creator", responseCreator); |
| 424 | + ch.pipeline() |
| 425 | + .addBefore("handler", "pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); |
| 426 | + |
| 427 | + ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); |
| 428 | + } |
| 429 | + }); |
| 430 | + } |
| 431 | + |
| 432 | + protected void configureDefaultHttpPipeline(ChannelPipeline pipeline) { |
344 | 433 | final HttpRequestDecoder decoder = new HttpRequestDecoder(
|
345 | 434 | handlingSettings.getMaxInitialLineLength(),
|
346 | 435 | handlingSettings.getMaxHeaderSize(),
|
347 | 436 | handlingSettings.getMaxChunkSize()
|
348 | 437 | );
|
349 | 438 | decoder.setCumulator(ByteToMessageDecoder.COMPOSITE_CUMULATOR);
|
350 |
| - ch.pipeline().addLast("decoder", decoder); |
351 |
| - ch.pipeline().addLast("decoder_compress", new HttpContentDecompressor()); |
352 |
| - ch.pipeline().addLast("encoder", new HttpResponseEncoder()); |
| 439 | + pipeline.addLast("decoder", decoder); |
| 440 | + pipeline.addLast("decoder_compress", new HttpContentDecompressor()); |
| 441 | + pipeline.addLast("encoder", new HttpResponseEncoder()); |
353 | 442 | final HttpObjectAggregator aggregator = new HttpObjectAggregator(handlingSettings.getMaxContentLength());
|
354 | 443 | aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents);
|
355 |
| - ch.pipeline().addLast("aggregator", aggregator); |
| 444 | + pipeline.addLast("aggregator", aggregator); |
356 | 445 | if (handlingSettings.isCompression()) {
|
357 |
| - ch.pipeline().addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); |
| 446 | + pipeline.addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); |
358 | 447 | }
|
359 |
| - ch.pipeline().addLast("request_creator", requestCreator); |
360 |
| - ch.pipeline().addLast("response_creator", responseCreator); |
361 |
| - ch.pipeline().addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); |
362 |
| - ch.pipeline().addLast("handler", requestHandler); |
363 |
| - transport.serverAcceptedChannel(nettyHttpChannel); |
| 448 | + pipeline.addLast("request_creator", requestCreator); |
| 449 | + pipeline.addLast("response_creator", responseCreator); |
| 450 | + pipeline.addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); |
| 451 | + pipeline.addLast("handler", requestHandler); |
364 | 452 | }
|
365 | 453 |
|
366 |
| - @Override |
367 |
| - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
368 |
| - ExceptionsHelper.maybeDieOnAnotherThread(cause); |
369 |
| - super.exceptionCaught(ctx, cause); |
| 454 | + protected void configureDefaultHttp2Pipeline(ChannelPipeline pipeline) { |
| 455 | + pipeline.addLast(Http2FrameCodecBuilder.forServer().build()) |
| 456 | + .addLast(new Http2MultiplexHandler(createHttp2ChannelInitializer(pipeline))); |
| 457 | + } |
| 458 | + |
| 459 | + private ChannelInitializer<Channel> createHttp2ChannelInitializerPriorKnowledge() { |
| 460 | + return new ChannelInitializer<Channel>() { |
| 461 | + @Override |
| 462 | + protected void initChannel(Channel childChannel) throws Exception { |
| 463 | + configureDefaultHttp2Pipeline(childChannel.pipeline()); |
| 464 | + } |
| 465 | + }; |
| 466 | + } |
| 467 | + |
| 468 | + /** |
| 469 | + * Http2MultiplexHandler creates new pipeline, we are preserving the old one in case some handlers need to be |
| 470 | + * access (like for example opensearch-security plugin which accesses SSL handlers). |
| 471 | + */ |
| 472 | + private ChannelInitializer<Channel> createHttp2ChannelInitializer(ChannelPipeline inboundPipeline) { |
| 473 | + return new ChannelInitializer<Channel>() { |
| 474 | + @Override |
| 475 | + protected void initChannel(Channel childChannel) throws Exception { |
| 476 | + final Netty4HttpChannel nettyHttpChannel = new Netty4HttpChannel(childChannel, inboundPipeline); |
| 477 | + childChannel.attr(HTTP_CHANNEL_KEY).set(nettyHttpChannel); |
| 478 | + |
| 479 | + final HttpObjectAggregator aggregator = new HttpObjectAggregator(handlingSettings.getMaxContentLength()); |
| 480 | + aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); |
| 481 | + |
| 482 | + childChannel.pipeline() |
| 483 | + .addLast(new LoggingHandler(LogLevel.DEBUG)) |
| 484 | + .addLast(new Http2StreamFrameToHttpObjectCodec(true)) |
| 485 | + .addLast("byte_buf_sizer", byteBufSizer) |
| 486 | + .addLast("read_timeout", new ReadTimeoutHandler(transport.readTimeoutMillis, TimeUnit.MILLISECONDS)) |
| 487 | + .addLast("decoder_decompress", new HttpContentDecompressor()); |
| 488 | + |
| 489 | + if (handlingSettings.isCompression()) { |
| 490 | + childChannel.pipeline() |
| 491 | + .addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); |
| 492 | + } |
| 493 | + |
| 494 | + childChannel.pipeline() |
| 495 | + .addLast("aggregator", aggregator) |
| 496 | + .addLast("request_creator", requestCreator) |
| 497 | + .addLast("response_creator", responseCreator) |
| 498 | + .addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)) |
| 499 | + .addLast("handler", getRequestHandler()); |
| 500 | + } |
| 501 | + }; |
370 | 502 | }
|
371 | 503 | }
|
372 | 504 |
|
|
0 commit comments