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 package org.apache.hc.core5.http2.impl.nio;
29
30 import java.io.IOException;
31 import java.net.SocketAddress;
32 import java.nio.ByteBuffer;
33 import java.nio.channels.ByteChannel;
34
35 import javax.net.ssl.SSLSession;
36
37 import org.apache.hc.core5.annotation.Internal;
38 import org.apache.hc.core5.http.EndpointDetails;
39 import org.apache.hc.core5.http.HttpException;
40 import org.apache.hc.core5.http.ProtocolVersion;
41 import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
42 import org.apache.hc.core5.http.nio.command.CommandSupport;
43 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
44 import org.apache.hc.core5.io.CloseMode;
45 import org.apache.hc.core5.io.SocketTimeoutExceptionFactory;
46 import org.apache.hc.core5.reactor.IOEventHandler;
47 import org.apache.hc.core5.reactor.IOSession;
48 import org.apache.hc.core5.reactor.ProtocolIOSession;
49 import org.apache.hc.core5.reactor.ssl.TlsDetails;
50 import org.apache.hc.core5.util.Args;
51 import org.apache.hc.core5.util.TextUtils;
52 import org.apache.hc.core5.util.Timeout;
53
54
55
56
57
58
59
60
61 @Internal
62 public class H2OnlyClientProtocolNegotiator implements HttpConnectionEventHandler {
63
64 private final ProtocolIOSession ioSession;
65 private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
66 private final boolean strictALPNHandshake;
67
68 private final ByteBuffer preface;
69
70 public H2OnlyClientProtocolNegotiator(
71 final ProtocolIOSession ioSession,
72 final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
73 final boolean strictALPNHandshake) {
74 this.ioSession = Args.notNull(ioSession, "I/O session");
75 this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
76 this.strictALPNHandshake = strictALPNHandshake;
77 this.preface = ByteBuffer.wrap(ClientHttpProtocolNegotiator.PREFACE);
78 }
79
80 @Override
81 public void connected(final IOSession session) {
82 try {
83 final TlsDetails tlsDetails = ioSession.getTlsDetails();
84 if (tlsDetails != null) {
85 final String applicationProtocol = tlsDetails.getApplicationProtocol();
86 if (TextUtils.isEmpty(applicationProtocol)) {
87 if (strictALPNHandshake) {
88 throw new HttpException("ALPN: missing application protocol");
89 }
90 } else {
91 if (!ApplicationProtocol.HTTP_2.id.equals(applicationProtocol)) {
92 throw new HttpException("ALPN: unexpected application protocol '" + applicationProtocol + "'");
93 }
94 }
95 }
96 writePreface(session);
97 } catch (final Exception ex) {
98 session.close(CloseMode.IMMEDIATE);
99 exception(session, ex);
100 }
101 }
102
103 private void writePreface(final IOSession session) throws IOException {
104 if (preface.hasRemaining()) {
105 final ByteChannel channel = session;
106 channel.write(preface);
107 }
108 if (!preface.hasRemaining()) {
109 final ClientH2StreamMultiplexer streamMultiplexer = http2StreamHandlerFactory.create(ioSession);
110 final IOEventHandler newHandler = new ClientH2IOEventHandler(streamMultiplexer);
111 newHandler.connected(session);
112 ioSession.upgrade(newHandler);
113 }
114 }
115
116 @Override
117 public void inputReady(final IOSession session, final ByteBuffer src) {
118 outputReady(session);
119 }
120
121 @Override
122 public void outputReady(final IOSession session) {
123 try {
124 if (preface != null) {
125 writePreface(session);
126 } else {
127 session.close(CloseMode.GRACEFUL);
128 }
129 } catch (final IOException ex) {
130 session.close(CloseMode.IMMEDIATE);
131 exception(session, ex);
132 }
133 }
134
135 @Override
136 public void timeout(final IOSession session, final Timeout timeout) {
137 exception(session, SocketTimeoutExceptionFactory.create(timeout));
138 }
139
140 @Override
141 public void exception(final IOSession session, final Exception cause) {
142 try {
143 CommandSupport.failCommands(session, cause);
144 } finally {
145 session.close(CloseMode.IMMEDIATE);
146 }
147 }
148
149 @Override
150 public void disconnected(final IOSession session) {
151 CommandSupport.cancelCommands(session);
152 }
153
154 @Override
155 public SSLSession getSSLSession() {
156 final TlsDetails tlsDetails = ioSession.getTlsDetails();
157 return tlsDetails != null ? tlsDetails.getSSLSession() : null;
158 }
159
160 @Override
161 public EndpointDetails getEndpointDetails() {
162 return null;
163 }
164
165 @Override
166 public void setSocketTimeout(final Timeout timeout) {
167 ioSession.setSocketTimeout(timeout);
168 }
169
170 @Override
171 public Timeout getSocketTimeout() {
172 return ioSession.getSocketTimeout();
173 }
174
175 @Override
176 public ProtocolVersion getProtocolVersion() {
177 return null;
178 }
179
180 @Override
181 public SocketAddress getRemoteAddress() {
182 return ioSession.getRemoteAddress();
183 }
184
185 @Override
186 public SocketAddress getLocalAddress() {
187 return ioSession.getLocalAddress();
188 }
189
190 @Override
191 public boolean isOpen() {
192 return ioSession.isOpen();
193 }
194
195 @Override
196 public void close() throws IOException {
197 ioSession.close();
198 }
199
200 @Override
201 public void close(final CloseMode closeMode) {
202 ioSession.close(closeMode);
203 }
204
205 }