低级 Http2(H2) 客户端和服务器实现

如何解决低级 Http2(H2) 客户端和服务器实现

我正在寻找使用 JETTY EMBEDDED(Jetty 10/Jetty 11) 实现 HTTP/2(客户端-服务器)所有功能的示例示例,例如 STREAMS、FRAMES PUSH_PROMISE、HPACK 以及详细配置? 它应该能够在本地主机上运行,​​并且如果低级 https2 详细方式需要 ssl 证书来实现它。 文档中的示例并不是很清楚,而是分部分进行了解释。我和他们一起尝试了很多。希望对这个问题的成功回答将帮助许多想要使用嵌入式码头实现低级别 h2 的业余爱好者。谁能帮我参考一下?

解决方法

供他人参考的代码。

H2Server.java

import java.net.InetSocketAddress; 
import java.net.Socket; 
import java.nio.ByteBuffer; 
import java.nio.charset.StandardCharsets; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.TimeUnit; 
import java.util.function.UnaryOperator;
import org.eclipse.jetty.http.HttpFields; 
import org.eclipse.jetty.http.HttpMethod; 
import org.eclipse.jetty.http.HttpStatus; 
import org.eclipse.jetty.http.HttpURI; 
import org.eclipse.jetty.http.HttpVersion; 
import org.eclipse.jetty.http.MetaData; 
import org.eclipse.jetty.http2.ErrorCode; 
import org.eclipse.jetty.http2.api.Session; 
import org.eclipse.jetty.http2.api.Stream; 
import org.eclipse.jetty.http2.api.server.ServerSessionListener; 
import org.eclipse.jetty.http2.frames.DataFrame; 
import org.eclipse.jetty.http2.frames.GoAwayFrame; 
import org.eclipse.jetty.http2.frames.HeadersFrame; 
import org.eclipse.jetty.http2.frames.PushPromiseFrame; 
import org.eclipse.jetty.http2.frames.ResetFrame; 
import org.eclipse.jetty.http2.frames.SettingsFrame; 
import org.eclipse.jetty.http2.parser.Parser; 
import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
import org.eclipse.jetty.io.ByteBufferPool; 
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory; 
import org.eclipse.jetty.server.SecureRequestCustomizer; 
import org.eclipse.jetty.server.Server; 
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.BufferUtil; 
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.resource.Resource; 
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import static java.lang.System.Logger.Level.INFO;
import java.io.OutputStream;

@SuppressWarnings("unused") public class H2Server {

public static void main(String[] args) throws Exception {
    // TODO Auto-generated method stub
    // Create a Server instance.
    QueuedThreadPool serverExecutor = new QueuedThreadPool();
    serverExecutor.setName("server");
   // server = new Server(serverExecutor);
    Server server = new Server(serverExecutor);

    // ServerSessionListener sessionListener = new ServerSessionListener.Adapter();

    ServerSessionListener sessionListener = new ServerSessionListener.Adapter() {
        @Override
        public Map<Integer,Integer> onPreface(Session session) {
            System.out.println("onPreface Called");
            // Customize the settings,for example:
            Map<Integer,Integer> settings = new HashMap<>();

            // Tell the client that HTTP/2 push is disabled.
            settings.put(SettingsFrame.ENABLE_PUSH,0);
            settings.put(SettingsFrame.ENABLE_CONNECT_PROTOCOL,8);

            return settings;
        }

        @Override
        public void onAccept(Session session) {
            System.out.println("onAccept Called");
            InetSocketAddress remoteAddress = session.getRemoteAddress();
            System.getLogger("http2").log(INFO,"Connection from {0}",remoteAddress);
            
        }

        @Override
        public Stream.Listener onNewStream(Stream stream,HeadersFrame frame) {
            System.out.println("onNewStream Called");
            // This is the "new stream" event,so it's guaranteed to be a request.
            MetaData.Request request = (MetaData.Request) frame.getMetaData();

            if (frame.isEndStream()) {
                respond(stream,request);
                return null;
            } else {
                // Return a Stream.Listener to handle the request events,// for example request content events or a request reset.
                return new Stream.Listener.Adapter() {
                    @Override
                    public void onData(Stream stream,DataFrame frame,Callback callback) {
                        // Get the content buffer.
                        ByteBuffer buffer = frame.getData();

                        // Consume the buffer,here - as an example - just log it.
                        // System.getLogger("http2").log(INFO,"Consuming buffer {0}",buffer);
                        System.getLogger("http2").log(INFO,buffer);
                        System.out.println("Consuming buffer {0}  " +StandardCharsets.UTF_8.decode(buffer).toString());
                        // Tell the implementation that the buffer has been consumed.
                        callback.succeeded();
                        // By returning from the method,implicitly tell the implementation
                        // to deliver to this method more DATA frames when they are available.
                        
                        if (frame.isEndStream()) {
                            System.out.println("EndStream");
                            respond(stream,request);
                        }
                        
                    }
                };
            }
        }

        private void respond(Stream stream,MetaData.Request request) {
            // Prepare the response HEADERS frame.
            System.out.println("respond Called");
            // The response HTTP status and HTTP headers.
            MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2,HttpStatus.OK_200,HttpFields.EMPTY);

            if (HttpMethod.GET.is(request.getMethod())) {
                // The response content.
                ByteBuffer resourceBytes = getResourceBytes(request);
                System.out.println("Request==GET resourceBytes== "+ StandardCharsets.UTF_8.decode(resourceBytes).toString());
                // Send the HEADERS frame with the response status and headers,// and a DATA frame with the response content bytes.
                stream.headers(new HeadersFrame(stream.getId(),response,null,false))
                        .thenCompose(s -> s.data(new DataFrame(s.getId(),resourceBytes,true)));
            } else {
                // Send just the HEADERS frame with the response status and headers.
                System.out.println("Request==POST response== "+ response);
                String content1 = "{\"greet\": \"Welcome!!!\"}";
                ByteBuffer buffer1 = StandardCharsets.UTF_8.encode(content1);
                //stream.headers(new HeadersFrame(stream.getId(),true));
                stream.headers(new HeadersFrame(stream.getId(),false))
                .thenCompose(s -> s.data(new DataFrame(s.getId(),buffer1,true)));
            }
        }
        
         private ByteBuffer getResourceBytes(MetaData.Request request)
            {
                return ByteBuffer.allocate(1024);
            }
    };

    // HTTP Configuration
    HttpConfiguration httpConfig = new HttpConfiguration();
    // httpConfig.setSecureScheme("https");
    httpConfig.setSecureScheme("https");
    // httpConfig.setSecurePort(8443);
    httpConfig.setSecurePort(8443);
    httpConfig.setSendXPoweredBy(true);
    httpConfig.setSendServerVersion(true);
    // httpConfig.setRequestHeaderSize(16 * 1024);

    // HTTPS Configuration
    HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
    httpsConfig.addCustomizer(new SecureRequestCustomizer());

    // Create a ServerConnector with RawHTTP2ServerConnectionFactory.
    RawHTTP2ServerConnectionFactory http2 = new RawHTTP2ServerConnectionFactory(httpConfig,sessionListener);

    // Configure RawHTTP2ServerConnectionFactory,for example:

    // Configure the max number of concurrent requests.
    http2.setMaxConcurrentStreams(128);
    // Enable support for CONNECT.
    http2.setConnectProtocolEnabled(true);

    // Create the ServerConnector.
    ServerConnector connector = new ServerConnector(server,http2);
//  connector.setPort(8080);
    connector.setPort(8443);
    connector.setHost("localhost");
    connector.setAcceptQueueSize(128);
    // Add the Connector to the Server
    server.addConnector(connector);

    // Start the Server so it starts accepting connections from clients.
    server.start();
    // new H2Server().testNoPrefaceBytes();
}

}

H2Client.java

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;

public class H2Client {

public static void main(String[] args) throws Exception {
    
    HTTP2Client http2Client = new HTTP2Client();
    http2Client.start();

    ClientConnector connector = http2Client.getClientConnector();

    // Address of the server's encrypted port.
    SocketAddress serverAddress = new InetSocketAddress("localhost",8443);
//  SocketAddress serverAddress = new InetSocketAddress("http://www.google.com/",8080);
    // Address of the server's encrypted port.
 //   SocketAddress serverAddress = new InetSocketAddress("localhost",8443);
//   CompletableFuture<Session> sessionCF = http2Client.connect(connector.getSslContextFactory(),serverAddress,new Session.Listener.Adapter());

    CompletableFuture<Session> sessionCF = http2Client.connect(serverAddress,new Session.Listener.Adapter()
            {
        @Override
        public Map<Integer,Integer> onPreface(Session session)
        {
            System.out.println("onPreface Called");
            Map<Integer,Integer> configuration = new HashMap<>();

            // Disable push from the server.
            configuration.put(SettingsFrame.ENABLE_PUSH,0);

            // Override HTTP2Client.initialStreamRecvWindow for this session.
            configuration.put(SettingsFrame.INITIAL_WINDOW_SIZE,1024 * 1024);

            return configuration;
        }
        
            });
    Session session = sessionCF.get();
    
    /*
    // Configure the request headers.
    HttpFields requestHeaders = HttpFields.build()
        .put(HttpHeader.USER_AGENT,"Jetty HTTP2Client {version}");

    // The request metadata with method,URI and headers.
    MetaData.Request request = new MetaData.Request("GET",HttpURI.from("http://localhost:61432"),HttpVersion.HTTP_2,requestHeaders);
//  MetaData.Request request = new MetaData.Request("GET",HttpURI.from("https://www.google.com/"),requestHeaders);

    // The HTTP/2 HEADERS frame,with endStream=true
    // to signal that this request has no content.
    HeadersFrame headersFrame = new HeadersFrame(request,true);
    
    // Open a Stream by sending the HEADERS frame.
    session.newStream(headersFrame,new Promise.Adapter<>(),new Stream.Listener.Adapter()
    { 
        
        @Override
        public void onHeaders(Stream stream,HeadersFrame frame)
        {
            System.out.println("onHeaders Called");
            MetaData metaData = frame.getMetaData();

            // Is this HEADERS frame the response or the trailers?
            if (metaData.isResponse())
            {
                MetaData.Response response = (MetaData.Response)metaData;
                System.out.println( "Received response {0}== "+ response);
            }
            else
            {
                System.out.println("Received trailers {0}== " + metaData.getFields());
            }
        }

        @Override
        public void onData(Stream stream,Callback callback)
        {
            System.out.println("onData Called");
            // Get the content buffer.
            ByteBuffer buffer = frame.getData();

            // Consume the buffer,here - as an example - just log it.
            System.out.println("Consuming buffer {0}" +buffer);

            // Tell the implementation that the buffer has been consumed.
            callback.succeeded();

            // By returning from the method,implicitly tell the implementation
            // to deliver to this method more DATA frames when they are available.
        }
    });
    */
    
    // Configure the request headers.
    HttpFields requestHeaders = HttpFields.build()
        .put(HttpHeader.CONTENT_TYPE,"application/json");
    
    // The request metadata with method,URI and headers.
    MetaData.Request request = new MetaData.Request("POST",HttpURI.from("http://localhost:8443/"),requestHeaders);
  //  MetaData.Request request = new MetaData.Request("POST",HttpURI.from("0.0.0.0:63780"),requestHeaders);
    
    // The HTTP/2 HEADERS frame,with endStream=false to
    // signal that there will be more frames in this stream.
    HeadersFrame headersFrame = new HeadersFrame(request,false);
    
 // Open a Stream by sending the HEADERS frame.
//    CompletableFuture<Stream> streamCF = session.newStream(headersFrame,new Stream.Listener.Adapter());
    

   // Open a Stream by sending the HEADERS frame.
      CompletableFuture<Stream> streamCF = session.newStream(headersFrame,new Stream.Listener.Adapter()
      {
          public void onHeaders(Stream stream,HeadersFrame frame)
        {
            System.out.println("onHeaders Called");
            MetaData metaData = frame.getMetaData();

            // Is this HEADERS frame the response or the trailers?
            if (metaData.isResponse())
            {
                MetaData.Response response = (MetaData.Response)metaData;
                System.out.println( "Received response {0}== "+ response);
            }
            else
            {
                System.out.println("Received trailers {0}== " + metaData.getFields());
            }
        }
          
          @Override
        public void onData(Stream stream,here - as an example - just log it.
            System.out.println("Consuming buffer {0}" +StandardCharsets.UTF_8.decode(buffer).toString());

            // Tell the implementation that the buffer has been consumed.
            callback.succeeded();

            // By returning from the method,implicitly tell the implementation
            // to deliver to this method more DATA frames when they are available.
        }
      });

    // Block to obtain the Stream.
    // Alternatively you can use the CompletableFuture APIs to avoid blocking.
    Stream stream = streamCF.get();

    // The request content,in two chunks.
    String content1 = "{\"greet\": \"hello world\"}";
    ByteBuffer buffer1 = StandardCharsets.UTF_8.encode(content1);
    String content2 = "{\"user\": \"jetty\"}";
    ByteBuffer buffer2 = StandardCharsets.UTF_8.encode(content2);

    // Send the first DATA frame on the stream,with endStream=false
    // to signal that there are more frames in this stream.
    CompletableFuture<Stream> dataCF1 = stream.data(new DataFrame(stream.getId(),false));

    // Only when the first chunk has been sent we can send the second,// with endStream=true to signal that there are no more frames.
    dataCF1.thenCompose(s -> s.data(new DataFrame(s.getId(),buffer2,true)));
    // end::newStreamWithData[]
     System.out.println("EOF");
    
    /*
    
    */
} 
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res