2017-09-06 9 views
2

「サーバー送信イベント」(SSE)を実行するNettyベースのHTTP/2サービスを作成していますが、HTTP/2テストクライアントクラスを作成する必要があります。統合テストで使用しますが、サーバーから個々のイベントを取得できるようにクライアントのパイプラインを設定する方法を検討するのは苦労しています。個々のフレームを取得するためにNetty 4 HTTP/2クライアントを設定する方法

私が最初に1.1↔HTTP/2アダプタクラス(InboundHttp2ToHttpAdapter + HttpToHttp2ConnectionHandler)/ HTTPを使用してみましたが、ストリームが閉じられた後に、このコンボで私だけFullHttpResponseを持って、ない個々のオブジェクトHttpContent。

次に、個々の発信HTTP/2フレームを送信し、着信HTTP/2フレームを受信するパイプラインを設定しようとしましたが、次のように、発信HTTP/2フレームエンコーダが正しくインストールされていないようです例外:

Exception in thread "main" java.lang.RuntimeException: io.netty.handler.codec.UnsupportedMessageTypeException: io.netty.handler.codec.http2.DefaultHttp2HeadersFrame (expected: io.netty.buffer.ByteBuf) 
    at example.AsyncHttpClient.run(AsyncHttpClient.java:116) 
    at example.AsyncHttpClient.main(AsyncHttpClient.java:49) 
Caused by: io.netty.handler.codec.UnsupportedMessageTypeException: io.netty.handler.codec.http2.DefaultHttp2HeadersFrame (expected: io.netty.buffer.ByteBuf) 
    at io.netty.handler.ssl.SslHandler.write(SslHandler.java:694) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730) 
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:816) 
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723) 
    at io.netty.handler.codec.http2.Http2ConnectionHandler.write(Http2ConnectionHandler.java:498) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730) 
    at io.netty.channel.AbstractChannelHandlerContext.access$1900(AbstractChannelHandlerContext.java:38) 
    at io.netty.channel.AbstractChannelHandlerContext$AbstractWriteTask.write(AbstractChannelHandlerContext.java:1081) 
    at io.netty.channel.AbstractChannelHandlerContext$WriteAndFlushTask.write(AbstractChannelHandlerContext.java:1128) 
    at io.netty.channel.AbstractChannelHandlerContext$AbstractWriteTask.run(AbstractChannelHandlerContext.java:1070) 
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) 
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:403) 
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463) 
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) 
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138) 
    at java.lang.Thread.run(Thread.java:745) 

私が間違っていることについてのアイデアはありますか?私はパッケージio.netty.handler.codec.http2がクラスの構成方法に関して非常に混乱していることがわかりました。私はGithubのNettyの例を参考にしようとしましたが、私が見つけたものから、ほとんどの場合HTTP/1.1 < - > HTTP/2アダプタクラスを使用しています。これらのクラスの使い方に関するより良い例がある場合は、私に教えてください!

(もgithub上)完全なソースコード:

package example; 

import io.netty.bootstrap.Bootstrap; 
import io.netty.buffer.ByteBuf; 
import io.netty.channel.Channel; 
import io.netty.channel.ChannelFuture; 
import io.netty.channel.ChannelFutureListener; 
import io.netty.channel.ChannelHandlerContext; 
import io.netty.channel.ChannelInitializer; 
import io.netty.channel.ChannelPipeline; 
import io.netty.channel.nio.NioEventLoopGroup; 
import io.netty.channel.socket.SocketChannel; 
import io.netty.channel.socket.nio.NioSocketChannel; 
import io.netty.handler.codec.http2.DefaultHttp2Connection; 
import io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder; 
import io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder; 
import io.netty.handler.codec.http2.DefaultHttp2FrameReader; 
import io.netty.handler.codec.http2.DefaultHttp2FrameWriter; 
import io.netty.handler.codec.http2.DefaultHttp2Headers; 
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; 
import io.netty.handler.codec.http2.Http2ConnectionHandler; 
import io.netty.handler.codec.http2.Http2ConnectionHandlerBuilder; 
import io.netty.handler.codec.http2.Http2Exception; 
import io.netty.handler.codec.http2.Http2FrameAdapter; 
import io.netty.handler.codec.http2.Http2Headers; 
import io.netty.handler.codec.http2.Http2SecurityUtil; 
import io.netty.handler.ssl.ApplicationProtocolConfig; 
import io.netty.handler.ssl.ApplicationProtocolNames; 
import io.netty.handler.ssl.OpenSsl; 
import io.netty.handler.ssl.SslContext; 
import io.netty.handler.ssl.SslContextBuilder; 
import io.netty.handler.ssl.SslHandler; 
import io.netty.handler.ssl.SslProvider; 
import io.netty.handler.ssl.SupportedCipherSuiteFilter; 
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; 

import javax.net.ssl.SSLException; 
import java.util.Collections; 
import java.util.Map; 

import static io.netty.channel.ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE; 

public class AsyncHttpClient { 

    private final Channel channel; 

    public static void main(String[] args) throws InterruptedException { 
     final AsyncHttpClient client = new AsyncHttpClient("google.com", 443); 
     client.run("GET", "/", Collections.emptyMap()); 
     Thread.sleep(10000); 
    } 

    AsyncHttpClient(String host, int port) throws InterruptedException { 
     final Bootstrap bootstrap = new Bootstrap() 
       .group(new NioEventLoopGroup()) 
       .channel(NioSocketChannel.class) 
       .handler(new ChannelInitializer<SocketChannel>() { 
        @Override 
        protected void initChannel(SocketChannel ch) throws Exception { 
         final Http2FrameAdapter adapter = new Http2FrameAdapter() { 

          @Override 
          public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endStream) throws Http2Exception { 
           System.out.println("onHeadersRead(ctx, streamId, headers, padding, endStream)"); 
           super.onHeadersRead(ctx, streamId, headers, padding, endStream); 
          } 

          @Override 
          public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception { 
           System.out.println("onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream)"); 
           super.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); 
          } 

          @Override 
          public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) throws Http2Exception { 
           System.out.println("onDataRead(ctx, streamId, data, padding, endOfStream)"); 
           return super.onDataRead(ctx, streamId, data, padding, endOfStream); 
          } 
         }; 

         final DefaultHttp2Connection connection = new DefaultHttp2Connection(false); 
         final DefaultHttp2FrameReader frameReader = new DefaultHttp2FrameReader(); 
         final DefaultHttp2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); 
         final DefaultHttp2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, frameWriter); 
         final DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, frameReader); 
         final Http2ConnectionHandler connectionHandler = new Http2ConnectionHandlerBuilder() 
           .codec(decoder, encoder) 
           .frameListener(adapter) 
           .build(); 

         final ChannelPipeline pipeline = ch.pipeline(); 
         pipeline.addLast(createSslHandler(ch)); 
         pipeline.addLast(connectionHandler); 
        } 
       }); 

     final ChannelFuture channelFuture = bootstrap.connect(host, port); 
     this.channel = channelFuture.sync().channel(); 
    } 

    void run(String method, String path, Map<String, String> headerMap) { 
     final DefaultHttp2Headers headers = new DefaultHttp2Headers(true); 
     headers.scheme("https"); 
     headers.method(method); 
     headers.path(path); 
     for (Map.Entry<String, String> header : headerMap.entrySet()) { 
      headers.add(header.getKey(), header.getValue()); 
     } 

     final DefaultHttp2HeadersFrame frame = new DefaultHttp2HeadersFrame(headers, false); 
     try { 
      channel.writeAndFlush(frame) 
        .addListener(FIRE_EXCEPTION_ON_FAILURE) 
        .sync(); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    private SslHandler createSslHandler(SocketChannel channel) { 
     try { 
      final SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK; 
      final SslContext sslCtx = SslContextBuilder.forClient().sslProvider(provider) 
      /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification. 
      * Please refer to the HTTP/2 specification for cipher requirements. */ 
        .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) 
        .trustManager(InsecureTrustManagerFactory.INSTANCE) 
        .applicationProtocolConfig(new ApplicationProtocolConfig(
          ApplicationProtocolConfig.Protocol.ALPN, 
          // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. 
          ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, 
          // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. 
          ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, 
          ApplicationProtocolNames.HTTP_2)) 
        .build(); 
      return sslCtx.newHandler(channel.alloc()); 
     } catch (SSLException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

答えて

2

あなたがHttp2FrameCodecHttp2MultiplexCodecと全くHttp2ConnectionHandlerのいずれかを使用する必要がHTTP2 *フレームオブジェクトを使用したい場合。詳しくはnettyの例と単体テストを参照してください。

+0

ありがとうございます!私は 'Http2FrameCodec'を見ましたが、nettyの例や一般的なGithubでは使えないクライアントの例は見つかりませんでした。私がそのクラスを使って働くクライアントを実装することができれば、私はnettyリポジトリにPRのサンプルを投稿しています。 'Http2FrameCodec'は、そのAPIが不安定で不完全だと言っていますが、本当に悪いですか? – oddbjorn

+2

この動作を得るために、HTTP/1.1↔HTTP/2アダプタクラス 'InboundHttp2ToHttpAdapter'と' HttpToHttp2ConnectionHandler'を使いました。 HttpResponse'、 'HttpHeaders'、' HttpContent'、 'LastHttpContent'をパイプラインに送出するために、HTTP/1.1用の' DetailedHttpObjectAggregator'とHTTP/2用の 'InboundHttp2ToHttpAdapter'を拡張しました。完全なソースコード[こちら](https://github.com/oddbjornkvalsund/async-http-client/tree/master/src/main/java/example) – oddbjorn

+0

Http2MultiplexCodecとHttp2FameCodecを使用する次の日にクライアントの例を追加します。あなたはサーバーの例がatm –

関連する問題