1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/test/org/apache/commons/httpclient/server/SimpleHttpServer.java $
3    * $Revision: 1425331 $
4    * $Date: 2012-12-22 19:29:41 +0100 (Sat, 22 Dec 2012) $
5    *
6    * ====================================================================
7    *
8    *  Licensed to the Apache Software Foundation (ASF) under one or more
9    *  contributor license agreements.  See the NOTICE file distributed with
10   *  this work for additional information regarding copyright ownership.
11   *  The ASF licenses this file to You under the Apache License, Version 2.0
12   *  (the "License"); you may not use this file except in compliance with
13   *  the License.  You may obtain a copy of the License at
14   *
15   *      http://www.apache.org/licenses/LICENSE-2.0
16   *
17   *  Unless required by applicable law or agreed to in writing, software
18   *  distributed under the License is distributed on an "AS IS" BASIS,
19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   *  See the License for the specific language governing permissions and
21   *  limitations under the License.
22   * ====================================================================
23   *
24   * This software consists of voluntary contributions made by many
25   * individuals on behalf of the Apache Software Foundation.  For more
26   * information on the Apache Software Foundation, please see
27   * <http://www.apache.org/>.
28   *
29   */
30  
31  package org.apache.commons.httpclient.server;
32  
33  import java.io.IOException;
34  import java.net.InetAddress;
35  import java.net.ServerSocket;
36  import java.net.Socket;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /***
42   * A simple, but extensible HTTP server, mostly for testing purposes.
43   * 
44   * @author Christian Kohlschuetter
45   * @author Oleg Kalnichevski
46   */
47  public class SimpleHttpServer implements Runnable {
48      private static final Log LOG = LogFactory.getLog(SimpleHttpServer.class);
49      
50      private String testname = "Simple test";
51      private long count = 0;
52      private ServerSocket listener = null;
53      private Thread t;
54      private ThreadGroup tg;
55      private boolean stopped = false;
56  
57      private SimpleConnSet connections = new SimpleConnSet();
58  
59      private HttpRequestHandler requestHandler = null;
60  
61      /***
62       * Creates a new HTTP server instance, using an arbitrary free TCP port
63       * 
64       * @throws IOException  if anything goes wrong during initialization
65       */
66      public SimpleHttpServer() throws IOException {
67          this(null, 0);
68      }
69  
70      /***
71       * Creates a new HTTP server instance, using the specified socket
72       * factory and the TCP port
73       * 
74       * @param   port    Desired TCP port
75       * @throws IOException  if anything goes wrong during initialization
76       */
77      public SimpleHttpServer(SimpleSocketFactory socketfactory, int port) 
78          throws IOException {
79          if (socketfactory == null) {
80          	socketfactory = new SimplePlainSocketFactory();
81          }
82          listener = socketfactory.createServerSocket(port);
83          if(LOG.isDebugEnabled()) {
84              LOG.debug("Starting test HTTP server on port " + getLocalPort());
85          }
86          tg = new ThreadGroup("SimpleHttpServer thread group");
87          t = new Thread(tg, this, "SimpleHttpServer listener");
88          t.setDaemon(true);
89          t.start();
90      }
91  
92      /***
93       * Creates a new HTTP server instance, using the specified TCP port
94       * 
95       * @param   port    Desired TCP port
96       * @throws IOException  if anything goes wrong during initialization
97       */
98      public SimpleHttpServer(int port) throws IOException {
99          this(null, port);
100     }
101 
102     public String getTestname() {
103         return this.testname;
104     }
105 
106     public void setTestname(final String testname) {
107         this.testname = testname;
108     }
109     
110     /***
111      * Returns the TCP port that this HTTP server instance is bound to.
112      *
113      * @return  TCP port, or -1 if not running
114      */
115     public int getLocalPort() {
116         return listener.getLocalPort();
117     }
118     
119     /***
120      * Returns the IP address that this HTTP server instance is bound to.
121      * @return String representation of the IP address or <code>null</code> if not running
122      */
123     public String getLocalAddress() {
124         InetAddress address = listener.getInetAddress();
125         // Ugly work-around for older JDKs
126         byte[] octets = address.getAddress();
127         if ((octets[0] == 0) 
128          && (octets[1] == 0) 
129          && (octets[2] == 0) 
130          && (octets[3] == 0)) {
131             return "localhost"; 
132         } else {
133             return address.getHostAddress();
134         }
135     }
136 
137     /***
138      * Checks if this HTTP server instance is running.
139      * 
140      * @return  true/false
141      */
142     public boolean isRunning() {
143         if(t == null) {
144             return false;
145         }
146         return t.isAlive();
147     }
148 
149     /***
150      * Stops this HTTP server instance.
151      */
152     public synchronized void destroy() {
153         if (stopped) {
154             return;
155         }
156 
157         this.stopped = true;
158         if(LOG.isDebugEnabled()) {
159             LOG.debug("Stopping test HTTP server on port " + getLocalPort());
160         }
161         tg.interrupt();
162         
163         if (listener != null) {
164             try {
165                 listener.close();
166             } catch(IOException e) {
167                 
168             }
169         }
170         this.connections.shutdown();
171     }
172 
173     /***
174      * Returns the currently used HttpRequestHandler by this SimpleHttpServer
175      * 
176      * @return The used HttpRequestHandler, or null.
177      */
178     public HttpRequestHandler getRequestHandler() {
179         return requestHandler;
180     }
181 
182     /***
183      * Sets the HttpRequestHandler to be used for this SimpleHttpServer.
184      * 
185      * @param rh    Request handler to be used, or null to disable.
186      */
187     public void setRequestHandler(HttpRequestHandler rh) {
188         this.requestHandler = rh;
189     }
190 
191     public void setHttpService(HttpService service) {
192         setRequestHandler(new HttpServiceHandler(service));
193     }
194 
195     public void run() {
196         try {
197             while (!this.stopped && !Thread.interrupted()) {
198                 Socket socket = listener.accept();
199                 try {
200                     if (this.requestHandler == null) {
201                         socket.close();
202                         break;
203                     }
204                     SimpleHttpServerConnection conn = new SimpleHttpServerConnection(socket); 
205                     this.connections.addConnection(conn);
206 
207                     Thread t = new SimpleConnectionThread(
208                             tg,
209                             this.testname + " thread " + this.count,
210                             conn, 
211                             this.connections,
212                             this.requestHandler);
213                     t.setDaemon(true);
214                     t.start();
215                 } catch (IOException e) {
216                     LOG.error("I/O error: " + e.getMessage());
217                 }
218                 this.count++;
219                 Thread.sleep(100);
220             }
221         } catch (InterruptedException accept) {
222         } catch (IOException e) {
223             if (!stopped) {
224                 LOG.error("I/O error: " + e.getMessage());
225             }
226         } finally {
227             destroy();
228         }
229     }
230 }