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.util.concurrent.atomic.AtomicReference;
34
35 import javax.net.ssl.SSLSession;
36
37 import org.apache.hc.core5.annotation.Internal;
38 import org.apache.hc.core5.http.ConnectionClosedException;
39 import org.apache.hc.core5.http.EndpointDetails;
40 import org.apache.hc.core5.http.HttpException;
41 import org.apache.hc.core5.http.ProtocolVersion;
42 import org.apache.hc.core5.http.URIScheme;
43 import org.apache.hc.core5.http.impl.nio.BufferedData;
44 import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
45 import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler;
46 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexer;
47 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
48 import org.apache.hc.core5.http.nio.command.CommandSupport;
49 import org.apache.hc.core5.http2.HttpVersionPolicy;
50 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
51 import org.apache.hc.core5.io.CloseMode;
52 import org.apache.hc.core5.io.SocketTimeoutExceptionFactory;
53 import org.apache.hc.core5.reactor.IOSession;
54 import org.apache.hc.core5.reactor.ProtocolIOSession;
55 import org.apache.hc.core5.reactor.ssl.TlsDetails;
56 import org.apache.hc.core5.util.Args;
57 import org.apache.hc.core5.util.Timeout;
58
59
60
61
62
63
64
65
66 @Internal
67 public class ServerHttpProtocolNegotiator implements HttpConnectionEventHandler {
68
69 final static byte[] PREFACE = ClientHttpProtocolNegotiator.PREFACE;
70
71 private final ProtocolIOSession ioSession;
72 private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
73 private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
74 private final HttpVersionPolicy versionPolicy;
75 private final BufferedData inBuf;
76 private final AtomicReference<HttpConnectionEventHandler> protocolHandlerRef;
77
78 private volatile boolean expectValidH2Preface;
79
80 public ServerHttpProtocolNegotiator(
81 final ProtocolIOSession ioSession,
82 final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
83 final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
84 final HttpVersionPolicy versionPolicy) {
85 this.ioSession = Args.notNull(ioSession, "I/O session");
86 this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
87 this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
88 this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
89 this.inBuf = BufferedData.allocate(1024);
90 this.protocolHandlerRef = new AtomicReference<>(null);
91 }
92
93 @Override
94 public void connected(final IOSession session) {
95 try {
96 final TlsDetails tlsDetails = ioSession.getTlsDetails();
97 switch (versionPolicy) {
98 case NEGOTIATE:
99 if (tlsDetails != null &&
100 ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
101 expectValidH2Preface = true;
102 }
103 break;
104 case FORCE_HTTP_2:
105 if (tlsDetails == null ||
106 !ApplicationProtocol.HTTP_1_1.id.equals(tlsDetails.getApplicationProtocol())) {
107 expectValidH2Preface = true;
108 }
109 break;
110 case FORCE_HTTP_1:
111 final ServerHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(
112 tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
113 ioSession);
114 final HttpConnectionEventHandler protocolHandler = new ServerHttp1IOEventHandler(http1StreamHandler);
115 ioSession.upgrade(protocolHandler);
116 protocolHandlerRef.set(protocolHandler);
117 http1StreamHandler.onConnect();
118 break;
119 }
120 } catch (final Exception ex) {
121 exception(session, ex);
122 }
123 }
124
125 @Override
126 public void inputReady(final IOSession session, final ByteBuffer src) {
127 try {
128 if (src != null) {
129 inBuf.put(src);
130 }
131 boolean endOfStream = false;
132 if (inBuf.length() < PREFACE.length) {
133 final int bytesRead = inBuf.readFrom(session);
134 if (bytesRead == -1) {
135 endOfStream = true;
136 }
137 }
138 final ByteBuffer data = inBuf.data();
139 if (data.remaining() >= PREFACE.length) {
140 boolean validH2Preface = true;
141 for (int i = 0; i < PREFACE.length; i++) {
142 if (data.get() != PREFACE[i]) {
143 if (expectValidH2Preface) {
144 throw new HttpException("Unexpected HTTP/2 preface");
145 }
146 validH2Preface = false;
147 }
148 }
149 if (validH2Preface) {
150 final ServerH2StreamMultiplexer http2StreamHandler = http2StreamHandlerFactory.create(ioSession);
151 final HttpConnectionEventHandler protocolHandler = new ServerH2IOEventHandler(http2StreamHandler);
152 ioSession.upgrade(protocolHandler);
153 protocolHandlerRef.set(protocolHandler);
154 http2StreamHandler.onConnect();
155 http2StreamHandler.onInput(data.hasRemaining() ? data : null);
156 } else {
157 final TlsDetails tlsDetails = ioSession.getTlsDetails();
158 final ServerHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(
159 tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
160 ioSession);
161 final HttpConnectionEventHandler protocolHandler = new ServerHttp1IOEventHandler(http1StreamHandler);
162 ioSession.upgrade(protocolHandler);
163 protocolHandlerRef.set(protocolHandler);
164 data.rewind();
165 http1StreamHandler.onConnect();
166 http1StreamHandler.onInput(data);
167 }
168 } else {
169 if (endOfStream) {
170 throw new ConnectionClosedException();
171 }
172 }
173 data.clear();
174 } catch (final Exception ex) {
175 exception(session, ex);
176 }
177 }
178
179 @Override
180 public void outputReady(final IOSession session) {
181 }
182
183 @Override
184 public void timeout(final IOSession session, final Timeout timeout) {
185 exception(session, SocketTimeoutExceptionFactory.create(timeout));
186 }
187
188 @Override
189 public void exception(final IOSession session, final Exception cause) {
190 session.close(CloseMode.IMMEDIATE);
191 final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.get();
192 if (protocolHandler != null) {
193 protocolHandler.exception(session, cause);
194 } else {
195 CommandSupport.failCommands(session, cause);
196 }
197 }
198
199 @Override
200 public void disconnected(final IOSession session) {
201 final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.getAndSet(null);
202 if (protocolHandler != null) {
203 protocolHandler.disconnected(ioSession);
204 } else {
205 CommandSupport.cancelCommands(session);
206 }
207 }
208
209 @Override
210 public SSLSession getSSLSession() {
211 final TlsDetails tlsDetails = ioSession.getTlsDetails();
212 return tlsDetails != null ? tlsDetails.getSSLSession() : null;
213 }
214
215 @Override
216 public EndpointDetails getEndpointDetails() {
217 final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.get();
218 return protocolHandler != null ? protocolHandler.getEndpointDetails() : null;
219 }
220
221 @Override
222 public void setSocketTimeout(final Timeout timeout) {
223 ioSession.setSocketTimeout(timeout);
224 }
225
226 @Override
227 public Timeout getSocketTimeout() {
228 return ioSession.getSocketTimeout();
229 }
230
231 @Override
232 public ProtocolVersion getProtocolVersion() {
233 final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.get();
234 return protocolHandler != null ? protocolHandler.getProtocolVersion() : null;
235 }
236
237 @Override
238 public SocketAddress getRemoteAddress() {
239 return ioSession.getRemoteAddress();
240 }
241
242 @Override
243 public SocketAddress getLocalAddress() {
244 return ioSession.getLocalAddress();
245 }
246
247 @Override
248 public boolean isOpen() {
249 return ioSession.isOpen();
250 }
251
252 @Override
253 public void close() throws IOException {
254 ioSession.close();
255 }
256
257 @Override
258 public void close(final CloseMode closeMode) {
259 ioSession.close(closeMode);
260 }
261
262 @Override
263 public String toString() {
264 return "[" +
265 "versionPolicy=" + versionPolicy +
266 ", expectValidH2Preface=" + expectValidH2Preface +
267 ']';
268 }
269
270 }