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.nio.ByteBuffer;
32 import java.util.concurrent.atomic.AtomicBoolean;
33
34 import org.apache.hc.core5.annotation.Internal;
35 import org.apache.hc.core5.concurrent.FutureCallback;
36 import org.apache.hc.core5.http.ConnectionClosedException;
37 import org.apache.hc.core5.http.HttpVersion;
38 import org.apache.hc.core5.http.URIScheme;
39 import org.apache.hc.core5.http.impl.nio.BufferedData;
40 import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler;
41 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
42 import org.apache.hc.core5.http2.HttpVersionPolicy;
43 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
44 import org.apache.hc.core5.reactor.IOSession;
45 import org.apache.hc.core5.reactor.ProtocolIOSession;
46 import org.apache.hc.core5.reactor.ssl.TlsDetails;
47 import org.apache.hc.core5.util.Args;
48
49
50
51
52
53
54
55
56 @Internal
57 public class ServerHttpProtocolNegotiator extends ProtocolNegotiatorBase {
58
59 final static byte[] PREFACE = ClientHttpProtocolNegotiator.PREFACE;
60
61 private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
62 private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
63 private final HttpVersionPolicy versionPolicy;
64 private final BufferedData inBuf;
65 private final AtomicBoolean initialized;
66
67 private volatile boolean expectValidH2Preface;
68
69 public ServerHttpProtocolNegotiator(
70 final ProtocolIOSession ioSession,
71 final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
72 final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
73 final HttpVersionPolicy versionPolicy) {
74 this(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, versionPolicy, null);
75 }
76
77
78
79
80 public ServerHttpProtocolNegotiator(
81 final ProtocolIOSession ioSession,
82 final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
83 final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
84 final HttpVersionPolicy versionPolicy,
85 final FutureCallback<ProtocolIOSession> resultCallback) {
86 super(ioSession, resultCallback);
87 this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
88 this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
89 this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
90 this.inBuf = BufferedData.allocate(1024);
91 this.initialized = new AtomicBoolean();
92 }
93
94 private void startHttp1(final TlsDetails tlsDetails, final ByteBuffer data) throws IOException {
95 startProtocol(HttpVersion.HTTP_1_1, new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(
96 tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
97 ioSession)), data);
98 }
99
100 private void startHttp2(final ByteBuffer data) throws IOException {
101 startProtocol(HttpVersion.HTTP_2, new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data);
102 }
103
104 private void initialize() throws IOException {
105 final TlsDetails tlsDetails = ioSession.getTlsDetails();
106 switch (versionPolicy) {
107 case NEGOTIATE:
108 if (tlsDetails != null &&
109 ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
110 expectValidH2Preface = true;
111 }
112 break;
113 case FORCE_HTTP_2:
114 if (tlsDetails == null ||
115 !ApplicationProtocol.HTTP_1_1.id.equals(tlsDetails.getApplicationProtocol())) {
116 expectValidH2Preface = true;
117 }
118 break;
119 case FORCE_HTTP_1:
120 startHttp1(tlsDetails, null);
121 break;
122 }
123 }
124
125 @Override
126 public void connected(final IOSession session) throws IOException {
127 if (initialized.compareAndSet(false, true)) {
128 initialize();
129 }
130 }
131
132 @Override
133 public void inputReady(final IOSession session, final ByteBuffer src) throws IOException {
134 if (src != null) {
135 inBuf.put(src);
136 }
137 boolean endOfStream = false;
138 if (inBuf.length() < PREFACE.length) {
139 final int bytesRead = inBuf.readFrom(session);
140 if (bytesRead == -1) {
141 endOfStream = true;
142 }
143 }
144 final ByteBuffer data = inBuf.data();
145 if (data.remaining() >= PREFACE.length) {
146 boolean validH2Preface = true;
147 for (int i = 0; i < PREFACE.length; i++) {
148 if (data.get() != PREFACE[i]) {
149 if (expectValidH2Preface) {
150 throw new ProtocolNegotiationException("Unexpected HTTP/2 preface");
151 }
152 validH2Preface = false;
153 }
154 }
155 if (validH2Preface) {
156 startHttp2(data.hasRemaining() ? data : null);
157 } else {
158 data.rewind();
159 startHttp1(ioSession.getTlsDetails(), data);
160 }
161 } else {
162 if (endOfStream) {
163 throw new ConnectionClosedException();
164 }
165 }
166 }
167
168 @Override
169 public void outputReady(final IOSession session) throws IOException {
170 if (initialized.compareAndSet(false, true)) {
171 initialize();
172 }
173 }
174
175 @Override
176 public String toString() {
177 return getClass().getName() + "/" + versionPolicy;
178 }
179
180 }