1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package com.psibt.framework.net;
18
19 import java.net;
20 import java.io;
21 import java.util;
22 import org.apache.log4j;
23
24 /**
25 * This class implements a HTTP-server frame. All HTTP-requests are handled by HTTPRequestHandler
26 * classes which implement the <code>HTTPRequestHandler</code> interface. Every RequestHandler has
27 * to be registered in the PluggableHTTPServer with the <code>addRequestHandler</code> method.
28 * A new thread is created for each connection to handle the request. If all reply data are sent
29 * to the client the connection is closed and the thread ends.
30 * An example how to use the PluggableHTTPServer class can be found in the <code>main</code> method
31 * at the end of the source file.
32 *
33 * @author <a HREF="mailto:V.Mentzner@psi-bt.de">Volker Mentzner</a>
34 */
35 public class PluggableHTTPServer implements Runnable {
36
37 public static final int DEFAULT_PORT = 80;
38 static Category cat = Category.getInstance("PluggableHTTPServer");
39 private int port;
40 private Vector handler;
41 private ServerSocket server;
42
43 /**
44 * Creates a new server object on the given TCP port.
45 * If the port is occupied by another process a IOException (java.net.BindException) is thrown.
46 *
47 * @param port - TCP port number to listen on for requests
48 */
49 public PluggableHTTPServer(int port) throws IOException {
50 this.port = port;
51 this.handler = new Vector();
52 cat.setPriority(Priority.ERROR);
53 server = new ServerSocket(this.port);
54 }
55
56 /**
57 * Creates a new server object on the default TCP port 80
58 * If the port is occupied by another process a IOException (java.net.BindException) is thrown.
59 */
60 public PluggableHTTPServer() throws IOException {
61 this(DEFAULT_PORT);
62 }
63
64 /**
65 * Registers the given HTTPRequestHandler
66 *
67 * @param h - the HTTPRequestHandler to register
68 */
69 public void addRequestHandler(HTTPRequestHandler h) {
70 handler.add(h);
71 }
72
73 /**
74 * Unregisters the given HTTPRequestHandler
75 *
76 * @param h - the HTTPRequestHandler to unregister
77 */
78 public void removeRequestHandler(HTTPRequestHandler h) {
79 handler.remove(h);
80 }
81
82 /**
83 * Sends the HTTP message 404 - File Not Found
84 * see RFC2616 for details
85 *
86 * @param out - Out stream for sending data to client browser
87 */
88 public static void replyNotFound(Writer out) {
89 try {
90 out.write("HTTP/1.0 404 Not Found\r\n");
91 out.write("<HTML><HEAD><TITLE>Not Found</TITLE></HEAD>\r\n");
92 out.write("<BODY><H1>Not Found</H1>\r\n");
93 out.write("</BODY></HTML>\r\n");
94 out.flush();
95 } // end try
96 catch (IOException e) {
97 }
98 }
99
100 /**
101 * Sends the HTTP message 405 - Method Not Allowed
102 * see RFC2616 for details
103 *
104 * @param out - Out stream for sending data to client browser
105 */
106 public static void replyMethodNotAllowed(Writer out) {
107 try {
108 out.write("HTTP/1.1 405 Method Not Allowed\r\n");
109 out.write("Allow: GET, PUT\r\n");
110 out.write("<HTML><HEAD><TITLE>Method Not Allowed</TITLE></HEAD>\r\n");
111 out.write("<BODY><H1>Method Not Allowed</H1>\r\n");
112 out.write("</BODY></HTML>\r\n");
113 out.flush();
114 } // end try
115 catch (IOException e) {
116 }
117 }
118
119 /**
120 * Creates the ReplyHTML data for the root page
121 *
122 * @param index - index of the RootRequestHandler
123 */
124 public void autoCreateRootPage(int index) {
125 if (handler.get(index) instanceof RootRequestHandler) {
126 RootRequestHandler r = (RootRequestHandler)handler.get(index);
127 String html = "<HTML><HEAD><TITLE>"+r.getTitle()+"</TITLE></HEAD>\r\n";
128 html = html + "<BODY><H1>"+r.getDescription()+"</H1>\r\n";
129 for (int i = 0; i < handler.size(); i++) {
130 html = html + "<a href=\"" + ((HTTPRequestHandler)handler.get(i)).getHandledPath();
131 html = html + "\">" + ((HTTPRequestHandler)handler.get(i)).getDescription() + "</a><br>";
132 }
133 html = html + "</BODY></HTML>\r\n";
134 r.setReplyHTML(html);
135 }
136 }
137
138 /**
139 * Main loop of the PluggableHTTPServer
140 */
141 public void run() {
142 while (true) {
143 try {
144 Socket s = server.accept();
145 Thread t = new ServerThread(s);
146 t.start();
147 }
148 catch (IOException e) {
149 }
150 }
151 }
152
153 /**
154 * This class handles the incomming connection for one request.
155 */
156 class ServerThread extends Thread {
157
158 private Socket connection;
159
160 ServerThread(Socket s) {
161 this.connection = s;
162 }
163
164 /**
165 * Serves the HTTP request.
166 */
167 public void run() {
168 try {
169 Writer out = new BufferedWriter(
170 new OutputStreamWriter(
171 connection.getOutputStream(), "ASCII"
172 )
173 );
174 Reader in = new InputStreamReader(
175 new BufferedInputStream(
176 connection.getInputStream()
177 )
178 );
179
180 // read the first line only; that's all we need
181 StringBuffer req = new StringBuffer(80);
182 while (true) {
183 int c = in.read();
184 if (c == '\r' || c == '\n' || c == -1) break;
185 req.append((char) c);
186 }
187 String get = req.toString();
188 cat.debug(get);
189 StringTokenizer st = new StringTokenizer(get);
190 String method = st.nextToken();
191 String request = st.nextToken();
192 String version = st.nextToken();
193
194 if (method.equalsIgnoreCase("GET")) {
195 boolean served = false;
196 for (int i = 0; i < handler.size(); i++) {
197 if (handler.get(i) instanceof HTTPRequestHandler) {
198 if (((HTTPRequestHandler)handler.get(i)).handleRequest(request, out)) {
199 served = true;
200 break;
201 }
202 }
203 }
204 if (!served)
205 PluggableHTTPServer.replyNotFound(out);
206 }
207 else {
208 PluggableHTTPServer.replyMethodNotAllowed(out);
209 }
210 } // end try
211 catch (IOException e) {
212 }
213 finally {
214 try {
215 if (connection != null) connection.close();
216 }
217 catch (IOException e) {}
218 }
219 } // end run
220 } // end class ServerThread
221
222 /**
223 * Demo how to use the PluggableHTTPServer.
224 */
225 public static void main(String[] args) {
226
227 int thePort;
228
229 // create some logging stuff
230 BasicConfigurator.configure();
231 Category cat1 = Category.getInstance("cat1");
232 cat1.addAppender(new org.apache.log4j.ConsoleAppender(new PatternLayout("%m%n")));
233 Category cat2 = Category.getInstance("cat2");
234 cat2.setPriority(Priority.INFO);
235 cat2.addAppender(new org.apache.log4j.ConsoleAppender(new PatternLayout("%c - %m%n")));
236
237 // set TCP port number
238 try {
239 thePort = Integer.parseInt(args[1]);
240 }
241 catch (Exception e) {
242 thePort = PluggableHTTPServer.DEFAULT_PORT;
243 }
244
245 PluggableHTTPServer server = null;
246 while (server == null) {
247 try {
248 server = new PluggableHTTPServer(thePort);
249 server.addRequestHandler(new RootRequestHandler());
250 server.addRequestHandler(new Log4jRequestHandler());
251 server.addRequestHandler(new UserDialogRequestHandler());
252 server.autoCreateRootPage(0);
253 Thread t = new Thread(server);
254 t.start();
255 } catch (IOException e) {
256 server = null;
257 thePort++;
258 }
259 }
260
261 } // end main
262 }