Savarese.Org
BareHTTP 1.0.2 Unit Test Coverage
[all classes][org.savarese.barehttp]

COVERAGE SUMMARY FOR SOURCE FILE [HTTPServer.java]

nameclass, %method, %block, %line, %
HTTPServer.java100% (3/3)95%  (21/22)85%  (238/279)85%  (57.5/68)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class HTTPServer$Server100% (1/1)100% (3/3)81%  (54/67)79%  (11/14)
HTTPServer$Server (HTTPServer, int, int, InetAddress): void 100% (1/1)60%  (15/25)80%  (4/5)
call (): Void 100% (1/1)92%  (35/38)71%  (5/7)
close (): void 100% (1/1)100% (4/4)100% (2/2)
     
class HTTPServer100% (1/1)94%  (16/17)85%  (162/190)84%  (39.5/47)
HTTPServer (String): void 0%   (0/1)0%   (0/6)0%   (0/2)
setMaxConnections (int): void 100% (1/1)67%  (10/15)75%  (3/4)
stop (long, TimeUnit): boolean 100% (1/1)71%  (22/31)82%  (6.5/8)
getBoundAddress (): InetAddress 100% (1/1)80%  (8/10)67%  (2/3)
getBoundPort (): int 100% (1/1)85%  (11/13)67%  (2/3)
start (): void 100% (1/1)90%  (37/41)83%  (5/6)
HTTPServer (String, int, int): void 100% (1/1)100% (24/24)100% (9/9)
decrementConnectionCount (): int 100% (1/1)100% (8/8)100% (1/1)
getBindAddress (): InetAddress 100% (1/1)100% (3/3)100% (1/1)
getConnectionCount (): int 100% (1/1)100% (3/3)100% (1/1)
getDocumentRoot (): String 100% (1/1)100% (3/3)100% (1/1)
getMaxConnections (): int 100% (1/1)100% (3/3)100% (1/1)
getPort (): int 100% (1/1)100% (3/3)100% (1/1)
incrementConnectionCount (): int 100% (1/1)100% (8/8)100% (1/1)
isRunning (): boolean 100% (1/1)100% (11/11)100% (1/1)
setBindAddress (InetAddress): void 100% (1/1)100% (4/4)100% (2/2)
setPort (int): void 100% (1/1)100% (4/4)100% (2/2)
     
class HTTPServer$Session100% (1/1)100% (2/2)100% (22/22)100% (7/7)
HTTPServer$Session (HTTPServer, HTTPSession): void 100% (1/1)100% (9/9)100% (3/3)
call (): Void 100% (1/1)100% (13/13)100% (4/4)

1/* Copyright 2001,2006,2012 Daniel F. Savarese
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     https://www.savarese.org/software/ApacheLicense-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 
16package org.savarese.barehttp;
17 
18import java.io.*;
19import java.net.*;
20import java.util.concurrent.*;
21 
22/**
23 * Implements a server that listens for incoming client connections
24 * and services each with {@link HTTPSession} instances.  A port
25 * number, bind address, and maximum number of client connections to
26 * service may be specified.
27 *
28 * @author <a href="https://www.savarese.org/">Daniel F. Savarese</a>
29 */
30public class HTTPServer {
31 
32  /**
33   * The default maximum number of concurrent client connections (10)
34   * that will be accepted if not specified.
35   */
36  public static final int DEFAULT_MAX_CONNECTIONS = 10;
37 
38  /**
39   * The default port number (8080) to bind to if not specified.
40   */
41  public static final int DEFAULT_PORT = 8080;
42 
43  InetAddress bindAddress;
44  int httpPort, maxConnections, backlog, connectionCount;
45  String documentRoot;
46  ExecutorService executor;
47  Server server;
48 
49  synchronized int incrementConnectionCount() {
50    return ++connectionCount;
51  }
52 
53  synchronized int decrementConnectionCount() {
54    return --connectionCount;
55  }
56 
57  /**
58   * Same as HTTPServer(documentRoot, DEFAULT_PORT, DEFAULT_MAX_CONNECTIONS);
59   */
60  public HTTPServer(String documentRoot) {
61    this(documentRoot, DEFAULT_PORT, DEFAULT_MAX_CONNECTIONS);
62  }
63 
64  /**
65   * Creates an HTTPServer instance.
66   *
67   * @param root The fully qualified document root directory pathname.
68   * @param port The port number the server should bind to.
69   * @param maxConnections The maximum number of client connections the
70   * server should accept.
71   */
72  public HTTPServer(String root, int port, int maxConnections) {
73    setPort(port);
74    setMaxConnections(maxConnections);
75    setBindAddress(null);
76    connectionCount = 0;
77    documentRoot    = root;
78    executor        = null;
79    server          = null;
80  }
81 
82  /**
83   * Returns the document root directory pathname.
84   *
85   * @return The document root directory pathname.
86   */
87  public String getDocumentRoot() {
88    return documentRoot;
89  }
90 
91  /**
92   * Sets the port number the server should bind to.  By default, the
93   * server binds to {@link #DEFAULT_PORT}.  The new port takes effect
94   * the next time {@link #start} is invoked (after a {@link #stop} if
95   * already running).
96   *
97   * @param port The port number the server should bind to.
98   */
99  public synchronized void setPort(int port) {
100    httpPort = port;
101  }
102 
103  /**
104   * The port number the server will bind to.
105   *
106   * @return The port number the server will bind to.
107   */
108  public int getPort() {
109    return httpPort;
110  }
111 
112  /**
113   * If the server is running, returns the port number currently bound
114   * to.  Otherwise, returns -1.
115   *
116   * @return The port number currently bound to or -1 if not bound.
117   */
118  public synchronized int getBoundPort() {
119    if(httpPort == 0 && server != null) {
120      return server.socket.getLocalPort();
121    }
122    return -1;
123  }
124 
125  /**
126   * Sets the maximum number of concurrent client connections the
127   * server should accept.
128   *
129   * @param maxConnections The maximum number of concurrent client
130   * connections the server should accept.
131   */
132  public synchronized void setMaxConnections(int maxConnections) {
133    this.maxConnections = backlog = maxConnections;
134    if(maxConnections > (DEFAULT_MAX_CONNECTIONS << 1)) {
135      backlog  = maxConnections >> 1;
136    }
137  }
138 
139  /**
140   * Returns the maximum number of concurrent client connections that
141   * will be accepted.
142   *
143   * @return The maximum number of concurrent client connections that
144   * will be accepted.
145   */
146  public int getMaxConnections() {
147    return maxConnections;
148  }
149 
150  /**
151   * Returns the number of client connections currently established.
152   *
153   * @return The number of client connections currently established.
154   */
155  public synchronized int getConnectionCount() {
156    return connectionCount;
157  }
158 
159  /**
160   * Sets the network interface address the server should bind to.  By
161   * default, the server binds to the wildcard address.  The new bind
162   * address takes effect the next time {@link #start} is invoked
163   * (after a {@link #stop} if already running).
164   *
165   * @param bindAddr The network interface the server should bind to.
166   * It may be null to reset to the wildcard.
167   */
168  public synchronized void setBindAddress(InetAddress bindAddr) {
169    bindAddress = bindAddr;
170  }
171 
172  /**
173   * Returns the network interface address the server will bind to.  A
174   * null return value signifies the wildcard address.
175   *
176   * @return The network interface address the server will bind to.
177   */
178  public InetAddress getBindAddress() {
179    return bindAddress;
180  }
181 
182  /**
183   * If the server is running, returns the address currently bound
184   * to.  Otherwise, returns null.
185   *
186   * @return The port number currently bound to or -1 if not bound.
187   */
188  public synchronized InetAddress getBoundAddress() {
189    if(server != null) {
190      return server.socket.getInetAddress();
191    }
192    return null;
193  }
194 
195  /**
196   * Returns true if the server is in a running state, false if not.
197   *
198   * @return True if the server is in a running state, false if not.
199   */
200  public synchronized boolean isRunning() {
201    return (executor != null && !executor.isTerminated());
202  }
203 
204  final class Session implements Callable<Void> {
205    HTTPSession session;
206 
207    Session(HTTPSession session) {
208      this.session = session;
209    }
210 
211    public Void call() throws Exception {
212      incrementConnectionCount();
213      session.execute();
214      decrementConnectionCount();
215      return null;
216    }
217  }
218 
219  class Server implements Callable<Void> {
220    ServerSocket socket;
221 
222    public Server(int port, int backlog, InetAddress address) 
223      throws IOException
224    {
225      if(address != null) {
226        socket = new ServerSocket(port, backlog, bindAddress);
227      } else {
228        socket = new ServerSocket(port, backlog);
229      }
230    }
231 
232    public Void call() throws Exception {
233      try {
234        while(true) {
235          Socket client = socket.accept();
236 
237          if(getConnectionCount() >= maxConnections) {
238            // Ungracefully close connection.
239            client.close();
240            continue;
241          }
242 
243          executor.submit(new Session(new HTTPSession(documentRoot,
244                                                   client.getInputStream(),
245                                                   client.getOutputStream())));
246        }
247      } finally {
248        return null;
249      }
250    }
251 
252    public void close() throws IOException {
253      socket.close();
254    }
255 
256  }
257 
258  /**
259   * Starts listening for incoming connectons in an asynchronously
260   * initiated thread.  The method returns immediately after the
261   * listening thread is established, making the HTTPServer instance
262   * an active object.
263   *
264   * @throws IOException If the server socket cannot be bound.
265   * @throws IllegalStateException If the server is already running.
266   */
267  public synchronized void start() throws IOException, IllegalStateException {
268    if(isRunning()) {
269      throw new IllegalStateException();
270    }
271 
272    server = new Server(httpPort, backlog, bindAddress);
273    executor =
274      new ThreadPoolExecutor(0, maxConnections + 1, 60L, TimeUnit.SECONDS,
275                             new SynchronousQueue<Runnable>());
276    executor.submit(server);
277  }
278 
279  /**
280   * Schedules termination of the server, closes the server socket,
281   * and waits for the specified amount of time or until the server is
282   * terminated before returning.
283   *
284   * @param timeout The maximum amount of time to wait for termination.
285   * @param unit The unit of time for the timeout.
286   * @return True if the server terminated before the method returned,
287   * false if not.  If false is returned, the server will not be
288   * completely terminated untl {@link #isRunning} returns false.
289   * Subsequent calls to stop will have no effect while terminating.
290   */
291  public synchronized boolean stop(long timeout, TimeUnit unit)
292    throws IOException
293  {
294    if(server != null) {
295      boolean result = false;
296 
297      try {
298        executor.shutdown();
299        server.close();
300        result = executor.awaitTermination(timeout, unit);
301      } finally { 
302        server = null;
303        return result;
304      }
305    }
306 
307    return isRunning();
308  }
309 
310}

[all classes][org.savarese.barehttp]
Savarese.Org