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.AtomicBoolean;
34 import java.util.concurrent.atomic.AtomicReference;
35
36 import javax.net.ssl.SSLSession;
37
38 import org.apache.hc.core5.annotation.Internal;
39 import org.apache.hc.core5.concurrent.FutureCallback;
40 import org.apache.hc.core5.http.ConnectionClosedException;
41 import org.apache.hc.core5.http.EndpointDetails;
42 import org.apache.hc.core5.http.HttpVersion;
43 import org.apache.hc.core5.http.ProtocolVersion;
44 import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
45 import org.apache.hc.core5.http.nio.command.CommandSupport;
46 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
47 import org.apache.hc.core5.io.CloseMode;
48 import org.apache.hc.core5.io.SocketTimeoutExceptionFactory;
49 import org.apache.hc.core5.reactor.IOSession;
50 import org.apache.hc.core5.reactor.ProtocolIOSession;
51 import org.apache.hc.core5.reactor.ssl.TlsDetails;
52 import org.apache.hc.core5.util.Args;
53 import org.apache.hc.core5.util.TextUtils;
54 import org.apache.hc.core5.util.Timeout;
55
56
57
58
59 @Internal
60 public class HttpProtocolNegotiator implements HttpConnectionEventHandler {
61
62 private final ProtocolIOSession ioSession;
63 private final FutureCallback<ProtocolIOSession> resultCallback;
64 private final AtomicBoolean completed;
65 private final AtomicReference<ProtocolVersion> negotiatedProtocolRef;
66
67 public HttpProtocolNegotiator(
68 final ProtocolIOSession ioSession,
69 final FutureCallback<ProtocolIOSession> resultCallback) {
70 this.ioSession = Args.notNull(ioSession, "I/O session");
71 this.resultCallback = resultCallback;
72 this.completed = new AtomicBoolean();
73 this.negotiatedProtocolRef = new AtomicReference<>();
74 }
75
76 void startProtocol(final HttpVersion httpVersion) {
77 ioSession.switchProtocol(
78 httpVersion == HttpVersion.HTTP_2 ? ApplicationProtocol.HTTP_2.id : ApplicationProtocol.HTTP_1_1.id,
79 resultCallback);
80 negotiatedProtocolRef.set(httpVersion);
81 }
82
83 @Override
84 public void connected(final IOSession session) throws IOException {
85 final HttpVersion httpVersion;
86 final TlsDetails tlsDetails = ioSession.getTlsDetails();
87 if (tlsDetails != null) {
88 final String appProtocol = tlsDetails.getApplicationProtocol();
89 if (TextUtils.isEmpty(appProtocol)) {
90 httpVersion = HttpVersion.HTTP_1_1;
91 } else if (appProtocol.equals(ApplicationProtocol.HTTP_1_1.id)) {
92 httpVersion = HttpVersion.HTTP_1_1;
93 } else if (appProtocol.equals(ApplicationProtocol.HTTP_2.id)) {
94 httpVersion = HttpVersion.HTTP_2;
95 } else {
96 throw new ProtocolNegotiationException("Unsupported application protocol: " + appProtocol);
97 }
98 } else {
99 httpVersion = HttpVersion.HTTP_1_1;
100 }
101 startProtocol(httpVersion);
102 }
103 @Override
104 public void inputReady(final IOSession session, final ByteBuffer src) throws IOException {
105 throw new ProtocolNegotiationException("Unexpected input");
106 }
107
108 @Override
109 public void outputReady(final IOSession session) throws IOException {
110 throw new ProtocolNegotiationException("Unexpected output");
111 }
112
113 @Override
114 public void timeout(final IOSession session, final Timeout timeout) {
115 exception(session, SocketTimeoutExceptionFactory.create(timeout));
116 }
117
118 @Override
119 public void exception(final IOSession session, final Exception cause) {
120 try {
121 session.close(CloseMode.IMMEDIATE);
122 CommandSupport.failCommands(session, cause);
123 } catch (final Exception ex) {
124 if (completed.compareAndSet(false, true) && resultCallback != null) {
125 resultCallback.failed(ex);
126 }
127 }
128 }
129
130 @Override
131 public void disconnected(final IOSession session) {
132 try {
133 CommandSupport.cancelCommands(session);
134 } finally {
135 if (completed.compareAndSet(false, true) && resultCallback != null) {
136 resultCallback.failed(new ConnectionClosedException());
137 }
138 }
139 }
140
141 @Override
142 public SSLSession getSSLSession() {
143 final TlsDetails tlsDetails = ioSession.getTlsDetails();
144 return tlsDetails != null ? tlsDetails.getSSLSession() : null;
145 }
146
147 @Override
148 public EndpointDetails getEndpointDetails() {
149 return null;
150 }
151
152 @Override
153 public void setSocketTimeout(final Timeout timeout) {
154 ioSession.setSocketTimeout(timeout);
155 }
156
157 @Override
158 public Timeout getSocketTimeout() {
159 return ioSession.getSocketTimeout();
160 }
161
162 @Override
163 public ProtocolVersion getProtocolVersion() {
164 return negotiatedProtocolRef.get();
165 }
166
167 @Override
168 public SocketAddress getRemoteAddress() {
169 return ioSession.getRemoteAddress();
170 }
171
172 @Override
173 public SocketAddress getLocalAddress() {
174 return ioSession.getLocalAddress();
175 }
176
177 @Override
178 public boolean isOpen() {
179 return ioSession.isOpen();
180 }
181
182 @Override
183 public void close() throws IOException {
184 ioSession.close();
185 }
186
187 @Override
188 public void close(final CloseMode closeMode) {
189 ioSession.close(closeMode);
190 }
191
192 @Override
193 public String toString() {
194 return getClass().getName();
195 }
196
197 }