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