普通http/https客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public class HttpClient {

public static void main(String[] args) throws Exception {

// 创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();

try {
// 构建SSL上下文
SslContext sslContext = SslContextBuilder.forClient()
.sslProvider(SslProvider.OPENSSL)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();

// 创建Bootstrap,并配置相关参数
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.remoteAddress(new InetSocketAddress("127.0.0.1", 8080))
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline()
.addLast(sslContext.newHandler(ch.alloc()))
// 添加HTTP帧编解码器
.addLast(new HttpClientCodec())
.addLast(new HttpObjectAggregator(1000000))
.addLast(new HttpClientHandler());
}
});

// 连接服务器
ChannelFuture channelFuture = bootstrap.connect().sync();
Channel channel = channelFuture.channel();

String url = "/query";
DefaultFullHttpRequest defaultHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, url);
defaultHttpRequest.headers().set(HttpHeaderNames.HOST, "127.0.0.1:8080");

/**
* POST /query HTTP/1.1
* Host: 127.0.0.1:8080
* Content-Type: application/x-www-form-urlencoded
*
* param1=value1&param2=value2
*/
// 方式一:直接设置content的值
// defaultHttpRequest.content().writeBytes("query=query".getBytes());
// defaultHttpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, ContentType.FORM_URLENCODED);
// defaultHttpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, "param1=value1&param2=value2".length());
// channel.writeAndFlush(defaultHttpRequest);

// 方式二
// HttpPostRequestEncoder httpPostRequestEncoder = new HttpPostRequestEncoder(defaultHttpRequest, false);
// httpPostRequestEncoder.addBodyAttribute("param1","value1");
// httpPostRequestEncoder.addBodyAttribute("param2","value2");
// HttpRequest httpRequest = httpPostRequestEncoder.finalizeRequest();
// channel.writeAndFlush(httpRequest);

/**
* POST /your-path HTTP/1.1
* Host: example.com
* Content-Type: multipart/form-data; boundary=---------------------------1234567890
*
* -----------------------------1234567890
* Content-Disposition: form-data; name="param1"
*
* value1
* -----------------------------1234567890
* Content-Disposition: form-data; name="param2"
*
* value2
* -----------------------------1234567890
* Content-Disposition: form-data; name="file"; filename="example.txt"
* Content-Type: text/plain
*
* This is the content of the file.
* -----------------------------1234567890--
*/

// 直接设置content的值的方式参考上面,下面是通过httpPostRequestEncoder的方式
HttpPostRequestEncoder httpPostRequestEncoder = new HttpPostRequestEncoder(defaultHttpRequest, true);
httpPostRequestEncoder.addBodyAttribute("param1","value1");
httpPostRequestEncoder.addBodyAttribute("param2","value2");
httpPostRequestEncoder.addBodyFileUpload("file","example.txt", new File("C:\\Users\\pengtong\\Desktop\\example.txt"), ContentType.TEXT_PLAIN.getValue(), true);
HttpRequest httpRequest = httpPostRequestEncoder.finalizeRequest();
channel.write(httpRequest);
HttpContent httpContent = httpPostRequestEncoder.readChunk(channel.alloc());
channel.writeAndFlush(httpContent);

// 关闭连接
channel.closeFuture().sync();
} finally {
// 关闭EventLoopGroup
group.shutdownGracefully();
}
}

@ChannelHandler.Sharable
static class HttpClientHandler extends ChannelInboundHandlerAdapter {

int i = 0;

long start = System.currentTimeMillis();

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpMessage) {
System.out.println(((FullHttpMessage)msg).content().toString(CharsetUtil.UTF_8));
System.out.println(System.currentTimeMillis() - start + "----------" + (i++));
}
}

}

}

http与https的区别就在于https需要构建一个SslContext,并且通过它生成一个SslHandler添加到channel pipeline中

http升级http2客户端

这里面的注释详细介绍到了netty channel pipeline的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
public class Http2UpgradeClient {

private static final String HOST = "127.0.0.1";
private static final int PORT = 8080;

private SendHandler sendHandler = new SendHandler();

public static void main(String[] args) throws Exception {
// 创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();

try {

// 创建Bootstrap,并配置相关参数
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.remoteAddress(new InetSocketAddress(HOST, PORT))
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpClientHandler());
}
});

// 连接服务器
ChannelFuture channelFuture = bootstrap.connect().sync();
Channel channel = channelFuture.channel();

DefaultFullHttpRequest upgradeRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/query");
upgradeRequest.headers()
.add("HTTP2-Settings", "")
.add(HttpHeaderNames.UPGRADE, "h2c")
.add(HttpHeaderNames.CONNECTION, "HTTP2-Settings,upgrade");
channel.writeAndFlush(upgradeRequest);

// 关闭连接
channel.closeFuture().sync();
} finally {
// 关闭EventLoopGroup
group.shutdownGracefully();
}
}

@ChannelHandler.Sharable
static class HttpClientHandler extends HttpObjectAggregator {

private HttpRequestEncoder httpRequestEncoder;

private HttpResponseDecoder httpResponseDecoder;

public HttpClientHandler() {
super(1024);
}

@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
httpRequestEncoder = new HttpRequestEncoder();
httpResponseDecoder = new HttpResponseDecoder();
ctx.pipeline().addBefore(ctx.name(), null, httpRequestEncoder).addBefore(ctx.name(), null, httpResponseDecoder);

SendHandler.sendHandler((line) -> {
DefaultFullHttpRequest defaultFullHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/query", Unpooled.wrappedBuffer(line.getBytes()));
defaultFullHttpRequest.headers()
.add(HttpHeaderNames.CONNECTION, "keep-alive")
.add(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(line.length()));
ctx.writeAndFlush(defaultFullHttpRequest);
});

}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpObject) {
List<Object> out = new ArrayList<>();
super.decode(ctx, (HttpObject) msg, out);
if (out.isEmpty()) {
return;
}
assert out.size() == 1;
FullHttpResponse fullHttpResponse = (FullHttpResponse) out.get(0);
if (fullHttpResponse.status() == HttpResponseStatus.SWITCHING_PROTOCOLS) {
Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient().build();
//httpRequestEncoder放在最前面删除是因为,在http2FrameCodec被添加时,http2FrameCodec会发送一个Http2序言和http2Setting的ByteBuf
//如果这里不删除的话,http2FrameCodec发送Http2序言和http2Setting的ByteBuf时,因为httpRequestEncoder在http2FrameCodec前面
//所以Http2序言和http2Setting的ByteBuf会被交给httpRequestEncoder处理,而httpRequestEncoder无法处理ByteBuf,就会出现问题
//而如果在添加http2FrameCodec之前删除的话,http2FrameCodec在发送Http2序言和http2Setting时,这两种消息就会被直接交给HeadContext处理
//当然,如果http2FrameCodec可以被添加到httpRequestEncoder之前,也可以不用删除,因为按照顺序,
//http2FrameCodec发送Http2序言和http2Setting的ByteBuf就不会交到httpRequestEncoder处理
ctx.pipeline().remove(httpRequestEncoder);
ctx.pipeline().addLast(http2FrameCodec).addLast(new Http2ClientHandler()).remove(this).remove(httpResponseDecoder);
//httpResponseDecoder放到最后删是因为删除入站通道处理程序,会直接处理没处理的消息,因为除了升级响应以外,
//还有一个Http2Setting,因为httpResponseDecoder处理不了,所以会保持ByteBuf的状态,所以httpResponseDecoder持有三条消息,
//分别是httpResponse、lastEmptyContent、以及ByteBuf状态的Http2Setting,但是到现在还只处理到lastEmptyContent
//所以这时候删除httpResponseDecoder,ByteBuf状态的Http2Setting还没有处理,所以删除的时候,会将ByteBuf状态的Http2Setting交到下一个handler处理,
//调用删除方法时,httpResponseDecoder后面就是http2FrameCodec,而http2FrameCodec可以解析ByteBuf状态的Http2Setting
//假设调用的是addFirst(http2FrameCodec),那么在删除httpResponseDecoder时,http2FrameCodec就在httpResponseDecoder前面
//http2FrameCodec就收不到httpResponseDecoder的ByteBuf状态的Http2Setting,就会出现问题

}
}
}
}

@ChannelHandler.Sharable
static class Http2ClientHandler extends Http2ChannelDuplexHandler {

@Override
protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
SendHandler.sendHandler((line) -> {
Http2Headers headers = new DefaultHttp2Headers()
.scheme("http")
.method("GET")
.path("/query")
.set(HttpHeaderNames.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.UTF_8))
.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(line.length()))
.authority(HOST);

Http2FrameStream stream = newStream();
DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers).stream(stream);
Http2DataFrame http2DataFrame = new DefaultHttp2DataFrame(Unpooled.copiedBuffer(line, CharsetUtil.UTF_8), true).stream(stream);

ctx.writeAndFlush(headersFrame);
ctx.writeAndFlush(http2DataFrame);
});
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof Http2DataFrame) {
// 处理HTTP/2响应数据
Http2DataFrame dataFrame = (Http2DataFrame) msg;
String content = dataFrame.content().toString(CharsetUtil.UTF_8);
System.out.println("Received response: " + content + " : " + dataFrame.stream().id());
}

}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

static class SendHandler {

private Consumer<String> consumer;

private final Thread sendThread;

private static SendHandler sendHandler;

public static SendHandler sendHandler() {
if (sendHandler == null) {
sendHandler = new SendHandler();
}
return sendHandler;
}

public static SendHandler sendHandler(Consumer<String> consumer) {
if (sendHandler == null) {
sendHandler = new SendHandler(consumer);
} else {
sendHandler.setConsumer(consumer);
}
return sendHandler;
}

private SendHandler() {
this(System.out::println);
}

private SendHandler(Consumer<String> consumer) {
this.consumer = consumer;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
sendThread = new Thread(() -> {
try {
while (true) {
String line = bufferedReader.readLine();
if (Thread.currentThread().isInterrupted()) {
break;
}
this.consumer.accept(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
sendThread.start();
}

public void shutdown() {
sendThread.interrupt();
}

public void setConsumer(Consumer<String> consumer) {
this.consumer = consumer;
}
}

}

这部分代码整体逻辑就是客户端提交一个http2的升级请求,如果返回的响应的状态码是101,则后续通过http2的逻辑发送请求以及接收响应。netty本身已经实现了一个HttpClientUpgradeHandler类,实现逻辑大致和这部分代码类似

http2客户端

这里的http2客户端没有使用加密,按道理一般的http2默认是加密的,这里的代码使用到了Http2StreamChannelBootstrap和Http2StreamChannel,可参考学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class Http2Client {

private static final String HOST = "127.0.0.1";
private static final int PORT = 8080;

public static void main(String[] args) throws Exception {

// 创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();

try {
// 创建Bootstrap,并配置相关参数
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.remoteAddress(new InetSocketAddress(HOST, PORT))
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline()
// 添加HTTP/2帧编解码器
.addLast(Http2FrameCodecBuilder.forClient().build())
//添加Http/2多路复用处理器,new ChannelInboundHandlerAdapter()只有在被动接收信息的时候才生效,例如服务端接收客户端的信息
.addLast(new Http2MultiplexHandler(new ChannelInboundHandlerAdapter()));
}
});

// 连接服务器
ChannelFuture channelFuture = bootstrap.connect().sync();
Channel channel = channelFuture.channel();

new Thread(() -> {
Http2StreamChannelBootstrap http2StreamChannelBootstrap = new Http2StreamChannelBootstrap(channel);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String line;
try {
line = bufferedReader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}

Http2StreamChannel streamChannel = http2StreamChannelBootstrap.open().syncUninterruptibly().getNow();
streamChannel.pipeline().addLast(new Http2ClientHandler());

Http2Headers headers = new DefaultHttp2Headers()
.method("GET")
.path("/query")
.scheme("http")
.set(HttpHeaderNames.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.UTF_8))
.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(line.length()))
.authority(HOST);
DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers).stream(streamChannel.stream());
Http2DataFrame http2DataFrame = new DefaultHttp2DataFrame(Unpooled.copiedBuffer(line, CharsetUtil.UTF_8), true).stream(streamChannel.stream());
streamChannel.writeAndFlush(headersFrame);
streamChannel.writeAndFlush(http2DataFrame);
}
}).start();

// 关闭连接
channel.closeFuture().sync();
} finally {
// 关闭EventLoopGroup
group.shutdownGracefully();
}
}

@ChannelHandler.Sharable
static class Http2ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof Http2DataFrame) {
// 处理HTTP/2响应数据
Http2DataFrame dataFrame = (Http2DataFrame) msg;
String content = dataFrame.content().toString(CharsetUtil.UTF_8);
System.out.println("Received response: " + content + " : " + dataFrame.stream().id());
ctx.channel().close();//关闭创建的Http2StreamChannel
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

}

https/http2(ssl/tls)客户端

访问https的http2,需要加上加密,而且还需要配置应用层协议协商(ALPN),并且设置支持的协议包括h2、http/1.1,服务端会根据自身配置返回一个支持的协议并且后续按照该协议进行通讯,如果不配置应用层协议协商,那么会按照服务端配置的默认协议进行通讯,这个时候就很有可能出现客户端和服务端协议不一致的问题,比如服务端默认协议是http/1.1,而如果这个时候客户端按照http2协议进行请求,当客户端发送setting帧时,收到服务端的消息还是http1.1格式的报文,就会出现问题。除了ALPN,NPN也是协议协商,只是NPN基本被ALPN取代了。下面的代码同时支持http/1.1和http2,通过userEventTriggered获取ALPN协商的结果,然后修改客户端支持的协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package com.example.netty.client;

import cn.hutool.http.ContentType;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http2.*;
import io.netty.handler.ssl.*;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.CharsetUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

public class MyHttpClient {

private static final String HOST = "127.0.0.1";
private static final int PORT = 8080;

public static void main(String[] args) throws Exception {

// 创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();

// 构建SSL上下文
SslContext sslContext = SslContextBuilder.forClient()
.sslProvider(SslProvider.OPENSSL)
.trustManager(InsecureTrustManagerFactory.INSTANCE)//如果服务端使用非认证的证书,必须使用该代码
.applicationProtocolConfig(new ApplicationProtocolConfig(
ApplicationProtocolConfig.Protocol.ALPN,
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_1_1,
ApplicationProtocolNames.HTTP_2))
.build();

try {
// 创建Bootstrap,并配置相关参数
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.remoteAddress(new InetSocketAddress(HOST, PORT))
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline()
.addLast(sslContext.newHandler(ch.alloc()))
// 添加HTTP/2帧编解码器
.addLast(new FrontHandler());
}
});

// 连接服务器
ChannelFuture channelFuture = bootstrap.connect().sync();
Channel channel = channelFuture.channel();

new Thread(() -> {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String line;
try {
line = bufferedReader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}

channel.writeAndFlush(line);
}
}).start();

// 关闭连接,阻塞直到通道关闭
channel.closeFuture().sync();
} finally {
// 关闭EventLoopGroup
group.shutdownGracefully();
}
}

static class FrontHandler extends ChannelInboundHandlerAdapter {

private final HttpClientCodec httpClientCodec;

private final HttpObjectAggregator httpObjectAggregator;

private final HttpClientHandler httpClientHandler;

private final Http2FrameCodec http2FrameCodec;

private final Http2ClientHandler http2ClientHandler;

public FrontHandler() {
this.httpClientCodec = new HttpClientCodec();
this.httpObjectAggregator = new HttpObjectAggregator(10240);
this.httpClientHandler = new HttpClientHandler();
this.http2FrameCodec = Http2FrameCodecBuilder.forClient().build();
this.http2ClientHandler = new Http2ClientHandler();
}

@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
ctx.pipeline().addLast(httpClientCodec, httpObjectAggregator, httpClientHandler);
}

//sslHandler处理完协议协商后,会触发SslHandshakeCompletionEvent,可以通过SslHandshakeCompletionEvent修改客户端使用协议
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof SslHandshakeCompletionEvent){
SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
String protocol = sslHandler.applicationProtocol();
if (protocol.equals(ApplicationProtocolNames.HTTP_2)) {
ctx.pipeline().remove(httpClientHandler).remove(httpObjectAggregator).remove(httpClientCodec)
.addLast(http2FrameCodec, http2ClientHandler)
.remove(this);
} else {
ctx.pipeline().remove(this);
}
}
}
}

@ChannelHandler.Sharable
static class Http2ClientHandler extends Http2ChannelDuplexHandler {

private Map<Integer, ByteBuf> byteBufMap = new HashMap<>();

Http2ClientHandler() {
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof Http2DataFrame) {
// 处理HTTP/2响应数据
Http2DataFrame dataFrame = (Http2DataFrame) msg;
int id = dataFrame.stream().id();
ByteBuf byteBuf = byteBufMap.get(id);
if (byteBuf == null){
byteBuf = dataFrame.content();
byteBufMap.put(id, byteBuf);
}else{
byteBuf.writeBytes(dataFrame.content());
}
if (dataFrame.isEndStream()){
String content = byteBuf.toString(CharsetUtil.UTF_8);
System.out.println("Received response: " + content + " : " + id);
byteBufMap.remove(id);
}
}
}

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
String message = (String) msg;
Http2FrameStream http2FrameStream = newStream();
Http2Headers headers = new DefaultHttp2Headers()
.method("GET")
.path("/activity/servicetime")
.scheme("https")
.authority(HOST + ":" + PORT)
.set(HttpHeaderNames.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.UTF_8))
.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(message.length()));
DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers).stream(http2FrameStream);
Http2DataFrame http2DataFrame = new DefaultHttp2DataFrame(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8), true).stream(http2FrameStream);
ctx.write(headersFrame);
ctx.writeAndFlush(http2DataFrame);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

@ChannelHandler.Sharable
static class HttpClientHandler extends ChannelDuplexHandler {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpMessage) {
System.out.println(((FullHttpMessage) msg).content().toString(CharsetUtil.UTF_8));
}
}

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
String message = (String) msg;
DefaultFullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/activity/servicetime", Unpooled.wrappedBuffer(message.getBytes()));
httpRequest.headers().set(HttpHeaderNames.HOST, HOST + ":" + PORT);
httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, message.length());
httpRequest.headers().set(HttpHeaderNames.CONNECTION, "keep-alive");
ctx.writeAndFlush(httpRequest);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}

}

应用层协议协商是在建立TLS连接时就确定好的,不是在http请求时才确定的,所以在浏览器请求的时候看不到相关的信息,只能通过抓包查看TLS建立连接时的信息查看,下面是浏览器和服务器建立连接时的应用层协议协商,其中client hello是浏览器发送给服务器的,可以看到,浏览器支持h2和http/1.1,server hello是服务器发送给浏览器的,可以看到,服务器决定按照h2进行通讯。Application-Layer Protocol Negotiation是ALPN全拼


本站总访问量:次本站总访问数:人
本站由 Peng Tong 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。