View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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   * HTTP/1.1 server side message exchange handler.
65   *
66   * @since 4.4
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      * Closes this process or endpoint and releases any system resources associated
199      * with it. If the endpoint or the process is already closed then invoking this
200      * method has no effect.
201      *
202      * @param closeMode How to close the receiver.
203      * @param timeout  How long to wait for the HttpServer to close gracefully.
204      * @since 5.2
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 }