Webserver.java revision 69e17611504376e4d4603925f8528dfc890fd2c6
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Javassist, a Java-bytecode translator toolkit. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The contents of this file are subject to the Mozilla Public License Version 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.1 (the "License"); you may not use this file except in compliance with 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the License. Alternatively, the contents of this file may be used under 81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * the terms of the GNU Lesser General Public License Version 2.1 or later. 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Software distributed under the License is distributed on an "AS IS" basis, 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for the specific language governing rights and limitations under the 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * License. 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)package javassist.tools.web; 171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import java.net.*; 191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import java.io.*; 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import java.util.Date; 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import javassist.*; 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A web server for running sample programs. 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * <p>This enables a Java program to instrument class files loaded by 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * web browsers for applets. Since the (standard) security manager 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * does not allow an applet to create and use a class loader, 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * instrumenting class files must be done by this web server. 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * <p><b>Note:</b> although this class is included in the Javassist API, 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * it is provided as a sample implementation of the web server using 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Javassist. Especially, there might be security flaws in this server. 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Please use this with YOUR OWN RISK. 351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public class Webserver { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private ServerSocket socket; 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private ClassPool classPool; 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected Translator translator; 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private final static byte[] endofline = { 0x0d, 0x0a }; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private final static int typeHtml = 1; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private final static int typeClass = 2; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private final static int typeGif = 3; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private final static int typeJpeg = 4; 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private final static int typeText = 5; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * If this field is not null, the class files taken from 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * <code>ClassPool</code> are written out under the directory 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * specified by this field. The directory name must not end 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * with a directory separator. 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public String debugDir = null; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The top directory of html (and .gif, .class, ...) files. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * It must end with the directory separator such as "/". 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * (For portability, "/" should be used as the directory separator. 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Javassist automatically translates "/" into a platform-dependent 62e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * character.) 63e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * If this field is null, the top directory is the current one where 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the JVM is running. 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * <p>If the given URL indicates a class file and the class file 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * is not found under the directory specified by this variable, 68e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * then <code>Class.getResourceAsStream()</code> is called 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for searching the Java class paths. 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public String htmlfileBase = null; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Starts a web server. 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The port number is specified by the first argument. 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public static void main(String[] args) throws IOException { 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (args.length == 1) { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Webserver web = new Webserver(args[0]); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web.run(); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.err.println( 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Usage: java javassist.tools.web.Webserver <port number>"); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Constructs a web server. 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param port port number 911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public Webserver(String port) throws IOException { 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this(Integer.parseInt(port)); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Constructs a web server. 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param port port number 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public Webserver(int port) throws IOException { 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) socket = new ServerSocket(port); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classPool = null; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) translator = null; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Requests the web server to use the specified 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * <code>ClassPool</code> object for obtaining a class file. 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void setClassPool(ClassPool loader) { 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classPool = loader; 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch /** 1167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * Adds a translator, which is called whenever a client requests 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a class file. 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 11968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * @param cp the <code>ClassPool</code> object for obtaining 12068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) * a class file. 1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * @param t a translator. 12268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) */ 12368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) public void addTranslator(ClassPool cp, Translator t) 12468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) throws NotFoundException, CannotCompileException 12568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) { 12668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) classPool = cp; 12768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) translator = t; 12868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) t.start(classPool); 12968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) } 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Closes the socket. 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void end() throws IOException { 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) socket.close(); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Prints a log message. 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void logging(String msg) { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.println(msg); 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) /** 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Prints a log message. 1477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) */ 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void logging(String msg1, String msg2) { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(msg1); 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(" "); 1517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch System.out.println(msg2); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Prints a log message. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void logging(String msg1, String msg2, String msg3) { 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(msg1); 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(" "); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(msg2); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(" "); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.println(msg3); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Prints a log message with indentation. 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public void logging2(String msg) { 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.print(" "); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) System.out.println(msg); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 173 /** 174 * Begins the HTTP service. 175 */ 176 public void run() { 177 System.err.println("ready to service..."); 178 for (;;) 179 try { 180 ServiceThread th = new ServiceThread(this, socket.accept()); 181 th.start(); 182 } 183 catch (IOException e) { 184 logging(e.toString()); 185 } 186 } 187 188 final void process(Socket clnt) throws IOException { 189 InputStream in = new BufferedInputStream(clnt.getInputStream()); 190 String cmd = readLine(in); 191 logging(clnt.getInetAddress().getHostName(), 192 new Date().toString(), cmd); 193 while (skipLine(in) > 0){ 194 } 195 196 OutputStream out = new BufferedOutputStream(clnt.getOutputStream()); 197 try { 198 doReply(in, out, cmd); 199 } 200 catch (BadHttpRequest e) { 201 replyError(out, e); 202 } 203 204 out.flush(); 205 in.close(); 206 out.close(); 207 clnt.close(); 208 } 209 210 private String readLine(InputStream in) throws IOException { 211 StringBuffer buf = new StringBuffer(); 212 int c; 213 while ((c = in.read()) >= 0 && c != 0x0d) 214 buf.append((char)c); 215 216 in.read(); /* skip 0x0a (LF) */ 217 return buf.toString(); 218 } 219 220 private int skipLine(InputStream in) throws IOException { 221 int c; 222 int len = 0; 223 while ((c = in.read()) >= 0 && c != 0x0d) 224 ++len; 225 226 in.read(); /* skip 0x0a (LF) */ 227 return len; 228 } 229 230 /** 231 * Proceses a HTTP request from a client. 232 * 233 * @param out the output stream to a client 234 * @param cmd the command received from a client 235 */ 236 public void doReply(InputStream in, OutputStream out, String cmd) 237 throws IOException, BadHttpRequest 238 { 239 int len; 240 int fileType; 241 String filename, urlName; 242 243 if (cmd.startsWith("GET /")) 244 filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5)); 245 else 246 throw new BadHttpRequest(); 247 248 if (filename.endsWith(".class")) 249 fileType = typeClass; 250 else if (filename.endsWith(".html") || filename.endsWith(".htm")) 251 fileType = typeHtml; 252 else if (filename.endsWith(".gif")) 253 fileType = typeGif; 254 else if (filename.endsWith(".jpg")) 255 fileType = typeJpeg; 256 else 257 fileType = typeText; // or textUnknown 258 259 len = filename.length(); 260 if (fileType == typeClass 261 && letUsersSendClassfile(out, filename, len)) 262 return; 263 264 checkFilename(filename, len); 265 if (htmlfileBase != null) 266 filename = htmlfileBase + filename; 267 268 if (File.separatorChar != '/') 269 filename = filename.replace('/', File.separatorChar); 270 271 File file = new File(filename); 272 if (file.canRead()) { 273 sendHeader(out, file.length(), fileType); 274 FileInputStream fin = new FileInputStream(file); 275 byte[] filebuffer = new byte[4096]; 276 for (;;) { 277 len = fin.read(filebuffer); 278 if (len <= 0) 279 break; 280 else 281 out.write(filebuffer, 0, len); 282 } 283 284 fin.close(); 285 return; 286 } 287 288 // If the file is not found under the html-file directory, 289 // then Class.getResourceAsStream() is tried. 290 291 if (fileType == typeClass) { 292 InputStream fin 293 = getClass().getResourceAsStream("/" + urlName); 294 if (fin != null) { 295 ByteArrayOutputStream barray = new ByteArrayOutputStream(); 296 byte[] filebuffer = new byte[4096]; 297 for (;;) { 298 len = fin.read(filebuffer); 299 if (len <= 0) 300 break; 301 else 302 barray.write(filebuffer, 0, len); 303 } 304 305 byte[] classfile = barray.toByteArray(); 306 sendHeader(out, classfile.length, typeClass); 307 out.write(classfile); 308 fin.close(); 309 return; 310 } 311 } 312 313 throw new BadHttpRequest(); 314 } 315 316 private void checkFilename(String filename, int len) 317 throws BadHttpRequest 318 { 319 for (int i = 0; i < len; ++i) { 320 char c = filename.charAt(i); 321 if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/') 322 throw new BadHttpRequest(); 323 } 324 325 if (filename.indexOf("..") >= 0) 326 throw new BadHttpRequest(); 327 } 328 329 private boolean letUsersSendClassfile(OutputStream out, 330 String filename, int length) 331 throws IOException, BadHttpRequest 332 { 333 if (classPool == null) 334 return false; 335 336 byte[] classfile; 337 String classname 338 = filename.substring(0, length - 6).replace('/', '.'); 339 try { 340 if (translator != null) 341 translator.onLoad(classPool, classname); 342 343 CtClass c = classPool.get(classname); 344 classfile = c.toBytecode(); 345 if (debugDir != null) 346 c.writeFile(debugDir); 347 } 348 catch (Exception e) { 349 throw new BadHttpRequest(e); 350 } 351 352 sendHeader(out, classfile.length, typeClass); 353 out.write(classfile); 354 return true; 355 } 356 357 private void sendHeader(OutputStream out, long dataLength, int filetype) 358 throws IOException 359 { 360 out.write("HTTP/1.0 200 OK".getBytes()); 361 out.write(endofline); 362 out.write("Content-Length: ".getBytes()); 363 out.write(Long.toString(dataLength).getBytes()); 364 out.write(endofline); 365 if (filetype == typeClass) 366 out.write("Content-Type: application/octet-stream".getBytes()); 367 else if (filetype == typeHtml) 368 out.write("Content-Type: text/html".getBytes()); 369 else if (filetype == typeGif) 370 out.write("Content-Type: image/gif".getBytes()); 371 else if (filetype == typeJpeg) 372 out.write("Content-Type: image/jpg".getBytes()); 373 else if (filetype == typeText) 374 out.write("Content-Type: text/plain".getBytes()); 375 376 out.write(endofline); 377 out.write(endofline); 378 } 379 380 private void replyError(OutputStream out, BadHttpRequest e) 381 throws IOException 382 { 383 logging2("bad request: " + e.toString()); 384 out.write("HTTP/1.0 400 Bad Request".getBytes()); 385 out.write(endofline); 386 out.write(endofline); 387 out.write("<H1>Bad Request</H1>".getBytes()); 388 } 389} 390 391class ServiceThread extends Thread { 392 Webserver web; 393 Socket sock; 394 395 public ServiceThread(Webserver w, Socket s) { 396 web = w; 397 sock = s; 398 } 399 400 public void run() { 401 try { 402 web.process(sock); 403 } 404 catch (IOException e) { 405 } 406 } 407} 408