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 package org.apache.hc.core5.http.impl.bootstrap;
28
29 import java.io.IOException;
30 import java.net.InetAddress;
31 import java.net.ServerSocket;
32 import java.util.Set;
33 import java.util.concurrent.SynchronousQueue;
34 import java.util.concurrent.ThreadPoolExecutor;
35 import java.util.concurrent.TimeUnit;
36 import java.util.concurrent.atomic.AtomicReference;
37
38 import javax.net.ServerSocketFactory;
39 import javax.net.ssl.SSLContext;
40 import javax.net.ssl.SSLParameters;
41 import javax.net.ssl.SSLServerSocket;
42 import javax.net.ssl.SSLServerSocketFactory;
43
44 import org.apache.hc.core5.annotation.Internal;
45 import org.apache.hc.core5.concurrent.DefaultThreadFactory;
46 import org.apache.hc.core5.function.Callback;
47 import org.apache.hc.core5.http.ExceptionListener;
48 import org.apache.hc.core5.http.URIScheme;
49 import org.apache.hc.core5.http.config.CharCodingConfig;
50 import org.apache.hc.core5.http.config.Http1Config;
51 import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection;
52 import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory;
53 import org.apache.hc.core5.http.impl.io.HttpService;
54 import org.apache.hc.core5.http.io.HttpConnectionFactory;
55 import org.apache.hc.core5.http.io.SocketConfig;
56 import org.apache.hc.core5.io.CloseMode;
57 import org.apache.hc.core5.io.Closer;
58 import org.apache.hc.core5.io.ModalCloseable;
59 import org.apache.hc.core5.util.Args;
60 import org.apache.hc.core5.util.TimeValue;
61 import org.apache.hc.core5.util.Timeout;
62
63
64
65
66
67
68 public class HttpServer implements ModalCloseable {
69
70 enum Status { READY, ACTIVE, STOPPING }
71
72 private final int port;
73 private final InetAddress ifAddress;
74 private final SocketConfig socketConfig;
75 private final ServerSocketFactory serverSocketFactory;
76 private final HttpService httpService;
77 private final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory;
78 private final SSLContext sslContext;
79 private final Callback<SSLParameters> sslSetupHandler;
80 private final ExceptionListener exceptionListener;
81 private final ThreadPoolExecutor listenerExecutorService;
82 private final ThreadGroup workerThreads;
83 private final WorkerPoolExecutor workerExecutorService;
84 private final AtomicReference<Status> status;
85
86 private volatile ServerSocket serverSocket;
87 private volatile RequestListener requestListener;
88
89 @Internal
90 public HttpServer(
91 final int port,
92 final HttpService httpService,
93 final InetAddress ifAddress,
94 final SocketConfig socketConfig,
95 final ServerSocketFactory serverSocketFactory,
96 final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory,
97 final SSLContext sslContext,
98 final Callback<SSLParameters> sslSetupHandler,
99 final ExceptionListener exceptionListener) {
100 this.port = Args.notNegative(port, "Port value is negative");
101 this.httpService = Args.notNull(httpService, "HTTP service");
102 this.ifAddress = ifAddress;
103 this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
104 this.serverSocketFactory = serverSocketFactory != null ? serverSocketFactory : ServerSocketFactory.getDefault();
105 this.connectionFactory = connectionFactory != null ? connectionFactory : new DefaultBHttpServerConnectionFactory(
106 this.serverSocketFactory instanceof SSLServerSocketFactory ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
107 Http1Config.DEFAULT,
108 CharCodingConfig.DEFAULT);
109 this.sslContext = sslContext;
110 this.sslSetupHandler = sslSetupHandler;
111 this.exceptionListener = exceptionListener != null ? exceptionListener : ExceptionListener.NO_OP;
112 this.listenerExecutorService = new ThreadPoolExecutor(
113 1, 1, 0L, TimeUnit.MILLISECONDS,
114 new SynchronousQueue<>(),
115 new DefaultThreadFactory("HTTP-listener-" + this.port));
116 this.workerThreads = new ThreadGroup("HTTP-workers");
117 this.workerExecutorService = new WorkerPoolExecutor(
118 0, Integer.MAX_VALUE, 1L, TimeUnit.SECONDS,
119 new SynchronousQueue<>(),
120 new DefaultThreadFactory("HTTP-worker", this.workerThreads, true));
121 this.status = new AtomicReference<>(Status.READY);
122 }
123
124 public InetAddress getInetAddress() {
125 final ServerSocket localSocket = this.serverSocket;
126 if (localSocket != null) {
127 return localSocket.getInetAddress();
128 }
129 return null;
130 }
131
132 public int getLocalPort() {
133 final ServerSocket localSocket = this.serverSocket;
134 if (localSocket != null) {
135 return localSocket.getLocalPort();
136 }
137 return -1;
138 }
139
140 public void start() throws IOException {
141 if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
142 this.serverSocket = this.serverSocketFactory.createServerSocket(
143 this.port, this.socketConfig.getBacklogSize(), this.ifAddress);
144 this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress());
145 if (this.socketConfig.getRcvBufSize() > 0) {
146 this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
147 }
148 if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
149 final SSLServerSocket sslServerSocket = (SSLServerSocket) this.serverSocket;
150 final SSLParameters sslParameters = sslServerSocket.getSSLParameters();
151 this.sslSetupHandler.execute(sslParameters);
152 sslServerSocket.setSSLParameters(sslParameters);
153 }
154 this.requestListener = new RequestListener(
155 this.socketConfig,
156 this.serverSocket,
157 this.httpService,
158 this.connectionFactory,
159 this.sslContext != null ? this.sslContext.getSocketFactory() : null,
160 this.sslSetupHandler,
161 this.exceptionListener,
162 this.workerExecutorService);
163 this.listenerExecutorService.execute(this.requestListener);
164 }
165 }
166
167 public void stop() {
168 if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) {
169 this.listenerExecutorService.shutdownNow();
170 this.workerExecutorService.shutdown();
171 final RequestListener local = this.requestListener;
172 if (local != null) {
173 try {
174 local.terminate();
175 } catch (final IOException ex) {
176 this.exceptionListener.onError(ex);
177 }
178 }
179 this.workerThreads.interrupt();
180 }
181 }
182
183 public void initiateShutdown() {
184 stop();
185 }
186
187 public void awaitTermination(final TimeValue waitTime) throws InterruptedException {
188 Args.notNull(waitTime, "Wait time");
189 this.workerExecutorService.awaitTermination(waitTime.getDuration(), waitTime.getTimeUnit());
190 }
191
192 @Override
193 public void close(final CloseMode closeMode) {
194 close(closeMode, Timeout.ofSeconds(5));
195 }
196
197
198
199
200
201
202
203
204
205
206 public void close(final CloseMode closeMode, final Timeout timeout) {
207 initiateShutdown();
208 if (closeMode == CloseMode.GRACEFUL) {
209 try {
210 awaitTermination(timeout);
211 } catch (final InterruptedException ex) {
212 Thread.currentThread().interrupt();
213 }
214 }
215 final Set<Worker> workers = this.workerExecutorService.getWorkers();
216 for (final Worker worker: workers) {
217 Closer.close(worker.getConnection(), CloseMode.GRACEFUL);
218 }
219 }
220
221 @Override
222 public void close() {
223 close(CloseMode.GRACEFUL);
224 }
225
226 }