/* * Copyright 2008 the original author or authors. * Copyright 2005 Sun Microsystems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rioproject.tools.webster; import groovy.util.ConfigObject; import groovy.util.ConfigSlurper; import org.rioproject.config.Constants; import org.rioproject.net.HostUtil; import org.rioproject.net.PortRangeServerSocketFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ServerSocketFactory; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; /** * Webster is a HTTP server which can serve code from multiple codebases. * Environment variables used to control Webster are as follows: * * <table BORDER COLS=3 WIDTH="100%" > ** <tr> * <td>org.rioproject.tools.webster.port</td> * <td>Sets the port for webster to use</td> * <td>0</td> * </tr> * <tr> * <td>org.rioproject.tools.webster.port</td> * <td>Sets the port for webster to use</td> * <td>0</td> * </tr> * <td>org.rioproject.tools.webster.root</td> * <td>Root directory to serve code from. Webster supports multiple root * directories which are separated by a <code>;</code></td> * <td>System.getProperty(user.home)</td> * </tr> * * </table> * * @author Dennis Reedy */ @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") public class Webster implements Runnable { static final int DEFAULT_MAX_THREADS = 10; private ServerSocket ss; private int port; private boolean run = true; private static Properties MimeTypes = new Properties(); private String[] websterRoot; private ThreadPoolExecutor pool; private int maxThreads = DEFAULT_MAX_THREADS; private int soTimeout = 0; private static Logger logger = LoggerFactory.getLogger("org.rioproject.tools.webster"); private com.sun.jini.start.LifeCycle lifeCycle; private boolean debug = false; private ServerSocketFactory socketFactory; private String putDirectory; private static String SERVER_DESCRIPTION=Webster.class.getName(); /** * Create a new Webster using a Groovy config file * * @param websterConfig The Groovy configuration file. * * <p>The Webster will be serving code as determined by the either the * org.rioproject.tools.webster.root system property (if set) or defaulting to * the user.dir system property. * * @throws BindException if Webster cannot create a socket */ @SuppressWarnings("unchecked") public Webster(final File websterConfig) throws BindException, MalformedURLException { ConfigObject config = new ConfigSlurper().parse(websterConfig.toURI().toURL()); Map flattened = config.flatten(); port = (Integer)flattened.get("webster.port"); StringBuilder websterRoots = new StringBuilder(); for(String root : (List<String>)flattened.get("webster.roots")) { if(websterRoots.length()>0) websterRoots.append(";"); websterRoots.append(root); } String bindAddress = (String) flattened.get("webster.address"); if(flattened.get("webster.putDirectory")!=null) { putDirectory = (String) flattened.get("webster.putDirectory"); } initialize(websterRoots.toString(), bindAddress); } /** * Create a new Webster * * @param port The port to use * @param roots The root(s) to serve code from. This is a semi-colin * delimited list of directories * * @throws BindException if Webster cannot create a socket */ public Webster(int port, String roots) throws BindException { this(port, roots, null); } /** * Create a new Webster * * @param port The port to use * @param roots The root(s) to serve code from. This is a semi-colin * delimited list of directories * @param bindAddress TCP/IP address which Webster should bind to (null * implies no specific address) * * @throws BindException if Webster cannot create a socket */ public Webster(int port, String roots, String bindAddress) throws BindException { this.port = port; initialize(roots, bindAddress); } /** * Create a new Webster * * @param socketFactory The ServerSocketFactory to use when creating the socket the Webster will be bound to * @param roots The root(s) to serve code from. This is a semi-colin * delimited list of directories * @param bindAddress TCP/IP address which Webster should bind to (null * implies no specific address) * * @throws BindException if Webster cannot create a socket */ public Webster(ServerSocketFactory socketFactory, String roots, String bindAddress) throws BindException { this.socketFactory = socketFactory; initialize(roots, bindAddress); } /** * Create a new Webster, compatible with the ServiceStarter mechanism in * Apache River * * @param options String[] of options. Valid options are [-port port], * [-roots list-of-roots], [-bindAddress address], * [-maxThreads maxThreads] [-soTimeout soTimeout] [-portRange range]. * Note -port and -portRange are mutually exclusive * @param lifeCycle The LifeCycle object, may be null * * @throws BindException if Webster cannot create a socket * @throws IllegalArgumentException if both -port and -portRange are provided * @throws NumberFormatException if the ports cannot be parsed into an integer */ public Webster(String[] options, com.sun.jini.start.LifeCycle lifeCycle) throws BindException { if(options == null) throw new IllegalArgumentException("options are null"); this.lifeCycle = lifeCycle; String roots = null; String bindAddress = null; boolean parsedPort = false; for(int i = 0; i < options.length; i++) { String option = options[i]; if("-port".equals(option)) { i++; this.port = Integer.parseInt(options[i]); parsedPort = true; } else if("-portRange".equals(option)) { if(parsedPort) throw new IllegalArgumentException("both -port and -portRange " + "cannot be provided, choose one or the other"); i++; socketFactory = parsePortRange(options[i]); } else if("-roots".equals(option)) { i++; roots = options[i]; } else if("-bindAddress".equals(option)) { i++; bindAddress = options[i]; } else if("-maxThreads".equals(option)) { i++; maxThreads = Integer.parseInt(options[i]); } else if("-soTimeout".equals(option)) { i++; soTimeout = Integer.parseInt(options[i]); } else if("-putDirectory".equals(option)) { i++; putDirectory = options[i]; } else { throw new IllegalArgumentException(option); } } initialize(roots, bindAddress); } /* * Parse portRange string and return a ServerSocketFactory to deal with the port range */ private ServerSocketFactory parsePortRange(String portRange) { String[] range = portRange.split("-"); int startRange = Integer.parseInt(range[0]); int endRange = Integer.parseInt(range[1]); /* reset the port to '0', this way the range will be used as intended */ port = 0; return new PortRangeServerSocketFactory(startRange, endRange); } /* * Initialize Webster * * @param roots The root(s) to serve code from. This is a semi-colin * delimited list of directories */ private void initialize(String roots, String bindAddress) throws BindException { String d = System.getProperty("webster.debug"); if(d != null) debug = true; String str = System.getProperty("webster.put.dir"); if (str != null) { putDirectory = str; if (debug) System.out.println("putDirectory: " + putDirectory); } setupRoots(roots); try { InetAddress address; if(bindAddress==null) { address = HostUtil.getInetAddressFromProperty(Constants.RMI_HOST_ADDRESS); } else { address = InetAddress.getByName(bindAddress); } if(socketFactory==null) { ss = new ServerSocket(port, 0, address); } else { ss = socketFactory.createServerSocket(port, 0, address); } if(debug) System.out.println("Webster serving on : "+ss.getInetAddress().getHostAddress()+":"+port); if(logger.isDebugEnabled()) logger.debug("Webster serving on : {}:{}", ss.getInetAddress().getHostAddress(), port); port = ss.getLocalPort(); } catch(IOException ioe) { throw new BindException("Could not start Webster."); } if(debug) System.out.println("Webster listening on port : " + port); if(logger.isDebugEnabled()) logger.debug("Webster listening on port : " + port); pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(maxThreads); if(debug) System.out.println("Webster maxThreads ["+maxThreads+"]"); if(logger.isDebugEnabled()) logger.debug("Webster maxThreads [{}]", maxThreads); if(soTimeout>0) { if(debug) System.out.println("Webster Socket SO_TIMEOUT set to ["+soTimeout+"] millis"); if(logger.isDebugEnabled()) logger.debug("Webster Socket SO_TIMEOUT set to [{}]] millis", soTimeout); } if(putDirectory!=null && debug) { System.out.println("putDirectory: " + putDirectory); } /* Set system properties */ System.setProperty(Constants.WEBSTER, "http://"+ss.getInetAddress().getHostAddress()+":"+port); System.setProperty(Constants.WEBSTER_ROOTS, roots); Thread runner = new Thread(this, "Webster"); runner.setDaemon(true); runner.start(); } /** * Get the roots Webster is serving * * @return The roots Webster is serving as a semicolon delimited String */ public String getRoots() { StringBuilder buffer = new StringBuilder(); for(int i = 0; i < websterRoot.length; i++) { if(i > 0) buffer.append(";"); buffer.append(websterRoot[i]); } return (buffer.toString()); } /** * Get address that Webster is bound to * * @return The host address the server socket Webster is using is bound to. * If the socket is null, return null. */ public String getAddress() { if(ss==null) return(null); return(ss.getInetAddress().getHostAddress()); } /* * Setup the websterRoot property */ private void setupRoots(String roots) { if(roots == null) throw new IllegalArgumentException("roots is null"); StringTokenizer tok = new StringTokenizer(roots, ";"); websterRoot = new String[tok.countTokens()]; if(websterRoot.length > 1) { for(int j = 0; j < websterRoot.length; j++) { websterRoot[j] = tok.nextToken(); if(debug) System.out.println("Root " + j + " = " + websterRoot[j]); if(logger.isDebugEnabled()) logger.debug("Root " + j + " = " + websterRoot[j]); } } else { websterRoot[0] = roots; if(debug) System.out.println("Root = " + websterRoot[0]); if(logger.isDebugEnabled()) logger.debug("Root = " + websterRoot[0]); } } /** * Terminate a running Webster instance */ public void terminate() { run = false; if(ss!=null) { try { ss.close(); } catch(IOException e) { logger.warn("Exception closing Webster ServerSocket"); } } if(lifeCycle != null) lifeCycle.unregister(this); if(pool!=null) pool.shutdownNow(); } /** * Get the port Webster is bound to * * @return Te port Webster is bound to */ public int getPort() { return (port); } /* * Read up to CRLF, return false if EOF */ private String readRequest(InputStream inputStream) throws IOException { StringBuilder sb = new StringBuilder(); int read; int prev = -1; while ((read = inputStream.read()) != -1) { if (read != '\n' && read != '\r') sb.append((char) read); if (read == '\n' && prev == '\r') { break; } if (read == '\r' && prev == '0') { //sb.delete(0, sb.length()); break; } prev = read; } return sb.toString(); } public void run() { Socket s ; try { loadMimes(); String fileName; while (run) { s = ss.accept(); // accept incoming requests if(soTimeout>0) { s.setSoTimeout(soTimeout); } String line; Properties header = new Properties(); DataInputStream inputStream; try { inputStream = new DataInputStream(s.getInputStream()); StringBuilder lineBuilder = new StringBuilder(); StringTokenizer tokenizer; while ((line = readRequest(inputStream)).length() != 0) { if (lineBuilder.length() > 0) lineBuilder.append("\n"); lineBuilder.append(line); tokenizer = new StringTokenizer(line, ":"); String aToken = tokenizer.nextToken().trim(); if (tokenizer.hasMoreTokens()) { header.setProperty(aToken, tokenizer.nextToken().trim()); } } line = lineBuilder.toString(); int port = s.getPort(); String from = s.getInetAddress().getHostAddress() + ":" + port; if (debug) { StringBuilder buff = new StringBuilder(); buff.append("From: ").append(from).append(", "); if (soTimeout > 0) buff.append("SO_TIMEOUT: ").append(soTimeout).append(", "); buff.append("Request: ").append(line); System.out.println("\n"+buff.toString()); } if(logger.isDebugEnabled()) { StringBuilder buff = new StringBuilder(); buff.append("From: ").append(from).append(", "); if(soTimeout > 0) buff.append("SO_TIMEOUT: ").append(soTimeout).append(", "); buff.append("Request: ").append(line); logger.debug(buff.toString()); } if (line.length()>0) { tokenizer = new StringTokenizer(line, " "); if (!tokenizer.hasMoreTokens()) break; String token = tokenizer.nextToken(); fileName = tokenizer.nextToken(); if (fileName.startsWith("/")) fileName = fileName.substring(1); if ("GET".equals(token)) { header.setProperty("GET", fileName); } else if ("PUT".equals(token)) { header.setProperty("PUT", fileName); } else if ("DELETE".equals(token)) { header.setProperty("DELETE", fileName); } else if ("HEAD".equals(token)) { header.setProperty("HEAD", fileName); } while (tokenizer.hasMoreTokens()) { String aToken = tokenizer.nextToken().trim(); if (tokenizer.hasMoreTokens()) { header.setProperty(aToken, tokenizer.nextToken().trim()); } } if (header.getProperty("GET") != null) { pool.execute(new GetFile(s, fileName)); } else if (header.getProperty("PUT") != null) { if(putDirectory!=null) { pool.execute(new PutFile(s, fileName, header, inputStream)); } else { DataOutputStream clientStream = new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); clientStream.writeBytes("HTTP/1.1 405 Method Not Allowed\nWebster is in read-only mode\r\n\r\n"); clientStream.flush(); clientStream.close(); } } else if (header.getProperty("DELETE") != null) { pool.execute(new DelFile(s, fileName)); } else if (header.getProperty("HEAD") != null) { pool.execute(new Head(s, fileName)); } else { if (debug) System.out.println("bad request [" + line + "] from " + from); if (logger.isDebugEnabled()) logger.debug("bad request [{}] from {}", line, from); DataOutputStream clientStream = new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); clientStream.writeBytes("HTTP/1.1 400 Bad Request\r\n\r\n"); clientStream.flush(); clientStream.close(); } } /* if line != null */ } catch (Exception e) { DataOutputStream clientStream = new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); clientStream.writeBytes("HTTP/1.1 500 Internal Server Error\n"+ "MIME-Version: 1.0\n"+ "Server: "+SERVER_DESCRIPTION+"\n"+ "\n\n<H1>500 Internal Server Error</H1>\n" +e); clientStream.flush(); clientStream.close(); logger.warn("Getting Request", e); } } } catch(Exception e) { if(run) { logger.warn("Processing HTTP Request", e); } } } // load the properties file void loadMimes() throws IOException { if(debug) System.out.println("Loading mimetypes ... "); if(logger.isDebugEnabled()) logger.debug("Loading mimetypes ... "); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); URL fileURL = ccl.getResource("org/rioproject/tools/webster/mimetypes.properties"); if(fileURL != null) { try { InputStream is = fileURL.openStream(); MimeTypes.load(is); close(is); if(debug) System.out.println("Mimetypes loaded"); if(logger.isDebugEnabled()) logger.debug("Mimetypes loaded"); } catch(IOException ioe) { logger.error("Loading Mimetypes", ioe); } } else { if(debug) System.out.println("mimetypes.properties not found, loading defaults"); if(logger.isDebugEnabled()) logger.debug("mimetypes.properties not found, loading defaults"); MimeTypes.put("jpg", "image/jpg"); MimeTypes.put("jpeg", "image/jpg"); MimeTypes.put("jpe", "image/jpg"); MimeTypes.put("gif", "image/gif"); MimeTypes.put("htm", "text/html"); MimeTypes.put("html", "text/html"); MimeTypes.put("txt", "text/plain"); MimeTypes.put("qt", "video/quicktime"); MimeTypes.put("mov", "video/quicktime"); MimeTypes.put("class", "application/octet-stream"); MimeTypes.put("mpg", "video/mpeg"); MimeTypes.put("mpeg", "video/mpeg"); MimeTypes.put("mpe", "video/mpeg"); MimeTypes.put("au", "audio/basic"); MimeTypes.put("snd", "audio/basic"); MimeTypes.put("wav", "audio/x-wave"); MimeTypes.put("JNLP", "application/x-java-jnlp-file"); MimeTypes.put("jnlp", "application/x-java-jnlp-file"); MimeTypes.put("java", "application/java"); MimeTypes.put("jar", "application/java"); MimeTypes.put("JAR", "application/java"); } } // end of loadMimes protected File parseFileName(final String filename) { String fileNameWithSpacesHandled = filename.replace("%20", " "); StringBuilder fn = new StringBuilder(fileNameWithSpacesHandled); for (int i = 0; i < fn.length(); i++) { if (fn.charAt(i) == '/') fn.replace(i, i + 1, File.separator); } File f = null; String[] roots = expandRoots(); for (String root : roots) { f = new File(root, fn.toString()); if (f.exists()) { return (f); } } return (f); } protected String[] expandRoots() { List<String> expandedRoots = new LinkedList<String>(); if(hasWildcard()) { String[] rawRoots = websterRoot; for (String root : rawRoots) { int wildcard; if ((wildcard = root.indexOf('*')) != -1) { String prefix = root.substring(0, wildcard); File prefixFile = new File(prefix); if (prefixFile.exists()) { String suffix = (wildcard < (root.length() - 1)) ? root.substring(wildcard + 1) : ""; String[] children = prefixFile.list(); for (String aChildren : children) { expandedRoots.add(prefix + aChildren + suffix); } } // Eat the root entry if it's wildcarded and doesn't exist } else { expandedRoots.add(root); } } } String[] roots; if(!expandedRoots.isEmpty()) { roots = expandedRoots.toArray(new String[expandedRoots.size()]); } else { roots = websterRoot; } return(roots); } /* * See if the root is using a wildcard */ boolean hasWildcard() { boolean wildcarded = false; for (String root : websterRoot) { if ((root.indexOf('*')) != -1) { wildcarded = true; break; } } return(wildcarded); } void close(Closeable c) { if(c!=null) { try { c.close(); } catch (IOException e) { logger.warn("Closing Closeable", e); } } } class Head implements Runnable { private final Socket client; private final String fileName; Head(Socket s, String fileName) { client = s; this.fileName = fileName; } public void run() { StringBuilder dirData = new StringBuilder(); StringBuilder logData = new StringBuilder(); try { File getFile = parseFileName(fileName); logData.append("Do HEAD: input=") .append(fileName) .append(", parsed=") .append(getFile) .append(", "); int fileLength; String header; if(getFile.isDirectory()) { logData.append("directory located"); String files[] = getFile.list(); for (String file : files) { File f = new File(getFile, file); dirData.append(f.toString().substring(getFile.getParent().length())); dirData.append("\t"); if (f.isDirectory()) dirData.append("d"); else dirData.append("f"); dirData.append("\t"); dirData.append(f.length()); dirData.append("\t"); dirData.append(f.lastModified()); dirData.append("\n"); } fileLength = dirData.length(); String fileType = MimeTypes.getProperty("txt"); if(fileType==null) fileType = "application/java"; header = "HTTP/1.1 200 OK\n"+ "Allow: GET\nMIME-Version: 1.0\n"+ "Server: "+SERVER_DESCRIPTION+"\n"+ "Content-Type: "+ fileType+ "\n"+ "Content-Length: "+ fileLength + "\r\n\r\n"; } else if(getFile.exists()) { DataInputStream requestedFile = new DataInputStream( new BufferedInputStream(new FileInputStream(getFile))); fileLength = requestedFile.available(); String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()); fileType = MimeTypes.getProperty(fileType); logData.append("file size: [").append(fileLength).append("]"); header = "HTTP/1.1 200 OK\n" + "Allow: GET\nMIME-Version: 1.0\n" + "Server: "+SERVER_DESCRIPTION+"\n" + "Content-Type: " + fileType + "\n" + "Content-Length: " + fileLength + "\r\n\r\n"; close(requestedFile); } else { header = "HTTP/1.1 404 Not Found\r\n\r\n"; logData.append("not found"); } if(debug) System.out.println(logData.toString()); if(logger.isDebugEnabled()) logger.debug(logData.toString()); DataOutputStream clientStream = new DataOutputStream(new BufferedOutputStream(client.getOutputStream())); clientStream.writeBytes(header); clientStream.flush(); close(clientStream); } catch(IOException e) { logger.warn("Error closing Socket", e); } finally { try { client.close(); } catch(IOException e2) { logger.warn("Closing incoming socket", e2); } } } // end of Head } class GetFile implements Runnable { private final Socket client; private final String fileName; private int fileLength; GetFile(Socket s, String fileName) { client = s; this.fileName = fileName; } public void run() { StringBuilder dirData = new StringBuilder(); StringBuilder logData = new StringBuilder(); DataInputStream requestedFile = null; try { File getFile = parseFileName(fileName); logData.append("Do GET: input=") .append(fileName) .append(", " + "parsed=") .append(getFile) .append(", "); String header; if(getFile.isDirectory()) { logData.append("directory located"); String files[] = getFile.list(); for (String file : files) { File f = new File(getFile, file); dirData.append(f.toString().substring(getFile.getParent().length())); dirData.append("\t"); if (f.isDirectory()) dirData.append("d"); else dirData.append("f"); dirData.append("\t"); dirData.append(f.length()); dirData.append("\t"); dirData.append(f.lastModified()); dirData.append("\n"); } fileLength = dirData.length(); String fileType = MimeTypes.getProperty("txt"); if(fileType == null) fileType = "application/java"; header = "HTTP/1.1 200 OK\n" + "Allow: GET\nMIME-Version: 1.0\n" + "Server: "+SERVER_DESCRIPTION+"\n" + "Content-Type: " + fileType + "\n" + "Content-Length: " + fileLength + "\r\n\r\n"; } else if(getFile.exists()) { requestedFile = new DataInputStream(new BufferedInputStream(new FileInputStream(getFile))); fileLength = requestedFile.available(); String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()); fileType = MimeTypes.getProperty(fileType); header = "HTTP/1.1 200 OK\n" + "Allow: GET\nMIME-Version: 1.0\n" + "Server: "+SERVER_DESCRIPTION+"\n" + "Content-Type: " + fileType + "\n" + "Content-Length: " + fileLength + "\r\n\r\n"; } else { header = "HTTP/1.1 404 Not Found\r\n\r\n"; } DataOutputStream clientStream =new DataOutputStream(new BufferedOutputStream(client.getOutputStream())); clientStream.writeBytes(header); if(getFile.isDirectory()) { clientStream.writeBytes(dirData.toString()); } else if(getFile.exists() && requestedFile!=null) { byte[] buffer = new byte[fileLength]; requestedFile.readFully(buffer); logData.append("file size: [").append(fileLength).append("]"); try { clientStream.write(buffer); } catch(Exception e) { String s = "Sending ["+ getFile.getAbsolutePath()+"], "+ "size ["+fileLength+"], "+ "to client at "+ "["+ client.getInetAddress().getHostAddress()+ "]"; if(logger.isDebugEnabled()) logger.debug(s, e); if(debug) { System.out.println(s); e.printStackTrace(); } } } else { logData.append("not found"); } if(debug) System.out.println(logData.toString()); if(logger.isDebugEnabled()) logger.debug(logData.toString()); clientStream.flush(); clientStream.close(); } catch(IOException e) { logger.warn("Closing Socket", e); } finally { try { close(requestedFile); client.close(); } catch(IOException e2) { logger.warn("Closing incoming socket", e2); } } } // end of GetFile } class PutFile implements Runnable { private Socket client; private String fileName; private Properties rheader; private InputStream inputStream; final int BUFFER_SIZE = 4096; PutFile(Socket s, String fileName, Properties header, InputStream fromClient) { rheader = header; client = s; this.fileName = fileName; this.inputStream = fromClient; } public void run() { String s = ignoreCaseProperty(rheader, "Content-Length"); if (s == null) { try { sendResponse("HTTP/1.0 411 OK\n" + "Allow: PUT\n" + "MIME-Version: 1.0\n" + "Server: " + SERVER_DESCRIPTION + "\n" + "\n\n <H1>411 Webster refuses to accept the out request for " + fileName + " " + "without a defined Content-Length.</H1>\n"); } catch (IOException e) { e.printStackTrace(); } } else { String header; OutputStream requestedFileOutputStream = null; try { // check to see if the file exists if it does the return code // will be 200 if it doesn't it will be 201 File putFile = new File(putDirectory + File.separator + fileName); if (debug) System.out.println("tempDir: " + putDirectory + ", fileName: " + fileName + ", putFile: " + putFile.getPath()); if (putFile.exists()) { header = "HTTP/1.0 200 OK\n" + "Allow: PUT\n" + "MIME-Version: 1.0\n" + "Server: " + SERVER_DESCRIPTION + "\n" + "\n\n <H1>200 PUT File " + fileName + " updated</H1>\n"; if (debug) System.out.println("updated putFile: " + putFile); } else { header = "HTTP/1.0 201 Created\n" + "Allow: PUT\n" + "MIME-Version: 1.0\n" + "Server: " + SERVER_DESCRIPTION + "\n" + "\n\n <H1>201 PUT File " + fileName + " Created</H1>\n"; File parentDir = putFile.getParentFile(); System.out.println("Parent: " + parentDir.getPath() + ", exists? " + parentDir.exists()); if (!parentDir.exists()) { if (parentDir.mkdirs() && debug) { System.out.println("Created " + parentDir.getPath()); } } if (debug) System.out.println("Created putFile: " + putFile + ", exists? " + putFile.exists()); } int length = Integer.parseInt(ignoreCaseProperty(rheader, "Content-Length")); if (debug) System.out.println("Putting " + fileName + " size: " + length + ", header: " + rheader); try { requestedFileOutputStream = new DataOutputStream(new FileOutputStream(putFile)); int read; long amountRead = 0; byte[] buffer = new byte[length < BUFFER_SIZE ? length : BUFFER_SIZE]; while (amountRead < length) { read = inputStream.read(buffer); requestedFileOutputStream.write(buffer, 0, read); amountRead += read; } requestedFileOutputStream.flush(); System.out.println("Wrote: " + putFile.getPath() + " size: " + putFile.length()); } catch (IOException e) { e.printStackTrace(); header = "HTTP/1.0 500 Internal Server Error\n" + "Allow: PUT\n" + "MIME-Version: 1.0\n" + "Server: " + SERVER_DESCRIPTION + "\n" + "\n\n <H1>500 Internal Server Error</H1>\n" + e; } finally { if (requestedFileOutputStream != null) requestedFileOutputStream.close(); sendResponse(header); } } catch (Exception e) { logger.warn( "Closing Socket", e); } finally { try { if (requestedFileOutputStream != null) requestedFileOutputStream.close(); client.close(); } catch (IOException e2) { logger.warn("Closing incoming socket", e2); } } } } void sendResponse(String header) throws IOException { DataOutputStream clientStream = new DataOutputStream(new BufferedOutputStream(client.getOutputStream())); clientStream.writeBytes(header); clientStream.flush(); clientStream.close(); } public String ignoreCaseProperty(Properties props, String field) { Enumeration<?> names = props.propertyNames(); while (names.hasMoreElements()) { String propName = (String) names.nextElement(); if (field.equalsIgnoreCase(propName)) { return (props.getProperty(propName)); } } return (null); } } class DelFile implements Runnable { private final Socket client; private final String fileName; DelFile(Socket s, String fileName) { client = s; this.fileName = fileName; } public void run() { try { File putFile = parseFileName(fileName); String header; if(!putFile.exists()) { header = "HTTP/1.1 404 File not found\n" + "Allow: GET\n" + "MIME-Version: 1.0\n" +"Server: "+SERVER_DESCRIPTION+"\n" + "\n\n <H1>404 File not Found</H1>\n" + "<BR>"; } else if(putFile.delete()) { header = "HTTP/1.1 200 OK\n" + "Allow: PUT\n" + "MIME-Version: 1.0\n" +"Server: "+SERVER_DESCRIPTION+"\n" + "\n\n <H1>200 File succesfully deleted</H1>\n"; } else { header = "HTTP/1.1 500 Internal Server Error\n" + "Allow: PUT\n" + "MIME-Version: 1.0\n" +"Server: "+SERVER_DESCRIPTION+"\n" + "\n\n <H1>500 File could not be deleted</H1>\n"; } DataOutputStream clientStream = new DataOutputStream(new BufferedOutputStream(client.getOutputStream())); clientStream.writeBytes(header); clientStream.flush(); close(clientStream); } catch(IOException e) { logger.warn("Closing Socket", e); } finally { try { client.close(); } catch(IOException e2) { logger.warn("Closing incoming socket", e2); } } } } }