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
28
29
30
31
32 package org.apache.commons.httpclient.server;
33
34 import java.io.IOException;
35 import java.net.InetAddress;
36 import java.net.ServerSocket;
37 import java.net.Socket;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.Set;
41
42 import org.apache.commons.httpclient.Header;
43 import org.apache.commons.httpclient.HttpStatus;
44 import org.apache.commons.logging.Log;
45 import org.apache.commons.logging.LogFactory;
46
47 /***
48 * A simple, but extensible HTTP server, mostly for testing purposes.
49 *
50 * @author Christian Kohlschuetter
51 * @author Oleg Kalnichevski
52 */
53 public class SimpleHttpServer implements Runnable {
54 private static final Log LOG = LogFactory.getLog(SimpleHttpServer.class);
55
56 private ServerSocket server = null;
57 private Thread t;
58 private ThreadGroup tg;
59 private boolean stopped = false;
60
61 private Set connections = new HashSet();
62
63 private HttpRequestHandler requestHandler = null;
64 private HttpService serivce = null;
65
66 /***
67 * Creates a new HTTP server instance, using an arbitrary free TCP port
68 *
69 * @throws IOException if anything goes wrong during initialization
70 */
71 public SimpleHttpServer() throws IOException {
72 this(0);
73 }
74
75 /***
76 * Creates a new HTTP server instance, using the specified TCP port
77 *
78 * @param port Desired TCP port
79 * @throws IOException if anything goes wrong during initialization
80 */
81 public SimpleHttpServer(int port) throws IOException {
82 server = new ServerSocket(port);
83 if(LOG.isInfoEnabled()) {
84 LOG.info("New SimpleHttpServer on port " + getLocalPort());
85 }
86 tg = new ThreadGroup("SimpleHttpServer group");
87 t = new Thread(tg, this, "SimpleHttpServer connection handler");
88 t.setDaemon(true);
89 t.start();
90 }
91
92 /***
93 * Returns the TCP port that this HTTP server instance is bound to.
94 *
95 * @return TCP port, or -1 if not running
96 */
97 public int getLocalPort() {
98 return server.getLocalPort();
99 }
100
101 /***
102 * Returns the IP address that this HTTP server instance is bound to.
103 * @return String representation of the IP address or <code>null</code> if not running
104 */
105 public String getLocalAddress() {
106 InetAddress address = server.getInetAddress();
107
108 byte[] octets = address.getAddress();
109 if ((octets[0] == 0)
110 && (octets[1] == 0)
111 && (octets[2] == 0)
112 && (octets[3] == 0)) {
113 return "localhost";
114 } else {
115 return address.getHostAddress();
116 }
117 }
118
119 /***
120 * Checks if this HTTP server instance is running.
121 *
122 * @return true/false
123 */
124 public boolean isRunning() {
125 if(t == null) {
126 return false;
127 }
128 return t.isAlive();
129 }
130
131 /***
132 * Stops this HTTP server instance.
133 */
134 public void destroy() {
135 if (stopped) {
136 return;
137 }
138
139 stopped = true;
140 if(LOG.isInfoEnabled()) {
141 LOG.info("Stopping SimpleHttpServer on port " + getLocalPort());
142 }
143
144 tg.interrupt();
145
146 if (server != null) {
147 try {
148 server.close();
149 } catch(IOException e) {
150
151 }
152 }
153
154 for (Iterator it = connections.iterator(); it.hasNext();) {
155 SimpleHttpServerConnection conn =
156 (SimpleHttpServerConnection) it.next();
157 conn.destroy();
158 }
159 }
160
161 /***
162 * Returns the currently used HttpRequestHandler by this SimpleHttpServer
163 *
164 * @return The used HttpRequestHandler, or null.
165 */
166 public HttpRequestHandler getRequestHandler() {
167 return requestHandler;
168 }
169
170 /***
171 * Sets the HttpRequestHandler to be used for this SimpleHttpServer.
172 *
173 * @param rh Request handler to be used, or null to disable.
174 */
175 public void setRequestHandler(HttpRequestHandler rh) {
176 this.requestHandler = rh;
177 }
178
179 public void setHttpService(HttpService service) {
180 this.serivce = service;
181 }
182
183 public void removeConnection(SimpleHttpServerConnection conn) {
184 connections.remove(conn);
185 }
186
187 public void processRequest(
188 final SimpleHttpServerConnection conn,
189 final SimpleRequest request) throws IOException
190 {
191 if (conn == null) {
192 throw new IllegalArgumentException("Connection may not be null");
193 }
194 if (request == null) {
195 throw new IllegalArgumentException("Request may not be null");
196 }
197 boolean complete = false;
198 if (this.requestHandler != null) {
199 complete = requestHandler.processRequest(conn, request);
200 if (complete) {
201 return;
202 }
203 }
204 SimpleResponse response = null;
205 if (this.serivce != null) {
206 response = new SimpleResponse();
207 complete = this.serivce.process(request, response);
208 }
209 if (!complete) {
210 response = ErrorResponse.getInstance().
211 getResponse(HttpStatus.SC_SERVICE_UNAVAILABLE);
212 conn.connectionClose();
213 }
214 writeResponse(conn, response);
215 }
216
217 public void writeResponse(
218 final SimpleHttpServerConnection conn,
219 final SimpleResponse response) throws IOException
220 {
221 if (response == null) {
222 return;
223 }
224 ResponseWriter out = conn.getWriter();
225 if (!response.containsHeader("Content-Length")) {
226 int len = 0;
227 if (response.getBodyString() != null) {
228 len = response.getBodyString().length();
229 }
230 response.addHeader(
231 new Header("Content-Length", Integer.toString(len), true));
232 }
233 if (!response.containsHeader("Content-Type")) {
234 StringBuffer buffer = new StringBuffer();
235 if (response.getContentType() != null) {
236 buffer.append(response.getContentType());
237 if (out.getEncoding() != null) {
238 buffer.append("; charset=");
239 buffer.append(out.getEncoding());
240 }
241 }
242 response.addHeader(
243 new Header("Content-Type", buffer.toString(), true));
244 }
245
246 if (!conn.isKeepAlive()) {
247 response.setHeader(
248 new Header("Connection", "close", true));
249 }
250 out.println(response.getStatusLine());
251 Iterator item = response.getHeaderIterator();
252 while (item.hasNext()) {
253 Header header = (Header) item.next();
254 out.print(header.toExternalForm());
255 }
256 out.println();
257 if (response.getBodyString() != null) {
258 out.print(response.getBodyString());
259 }
260 out.flush();
261 }
262
263 public void run() {
264 try {
265 while (!Thread.interrupted()) {
266 Socket socket = server.accept();
267 try {
268
269 SimpleHttpServerConnection conn =
270 new SimpleHttpServerConnection(this, socket);
271
272 connections.add(conn);
273
274 Thread t =
275 new Thread(tg, conn, "SimpleHttpServer connection");
276 t.setDaemon(true);
277 t.start();
278 } catch (IOException e) {
279 LOG.error("I/O error: " + e.getMessage());
280 }
281 Thread.sleep(100);
282 }
283 } catch (InterruptedException accept) {
284 } catch (IOException e) {
285 if (!stopped) {
286 LOG.error("I/O error: " + e.getMessage());
287 }
288 } finally {
289 destroy();
290 }
291 }
292 }