Check in modified Acme classes
This commit is contained in:
commit
c25c44dbb3
9 changed files with 3191 additions and 0 deletions
303
src/Acme/LruHashtable.java
Normal file
303
src/Acme/LruHashtable.java
Normal file
|
@ -0,0 +1,303 @@
|
|||
// LruHashtable - a Hashtable that expires least-recently-used objects
|
||||
//
|
||||
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/// A Hashtable that expires least-recently-used objects.
|
||||
// <P>
|
||||
// Use just like java.util.Hashtable, except that the initial-capacity
|
||||
// parameter is required. Instead of growing bigger than that size,
|
||||
// it will throw out objects that haven't been looked at in a while.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/LruHashtable.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
// <P>
|
||||
// @see java.util.Hashtable
|
||||
|
||||
public class LruHashtable extends Hashtable
|
||||
{
|
||||
|
||||
// Number of buckets.
|
||||
private static final int nBuckets = 2;
|
||||
|
||||
// Load factor.
|
||||
private float loadFactor;
|
||||
|
||||
// When count exceeds this threshold, expires the old table.
|
||||
private int threshold;
|
||||
|
||||
// Capacity of each bucket.
|
||||
private int eachCapacity;
|
||||
|
||||
// The tables.
|
||||
private Hashtable oldTable;
|
||||
private Hashtable newTable;
|
||||
|
||||
/// Constructs a new, empty hashtable with the specified initial
|
||||
// capacity and the specified load factor.
|
||||
// Unlike a plain Hashtable, an LruHashtable will never grow or
|
||||
// shrink from this initial capacity.
|
||||
// @param initialCapacity the initial number of buckets
|
||||
// @param loadFactor a number between 0.0 and 1.0, it defines
|
||||
// the threshold for expiring old entries
|
||||
// @exception IllegalArgumentException If the initial capacity
|
||||
// is less than or equal to zero.
|
||||
// @exception IllegalArgumentException If the load factor is
|
||||
// less than or equal to zero.
|
||||
public LruHashtable( int initialCapacity, float loadFactor )
|
||||
{
|
||||
// We have to call a superclass constructor, but we're not actually
|
||||
// going to use it at all. The only reason we want to extend Hashtable
|
||||
// is for type conformance. So, make a parent hash table of minimum
|
||||
// size and then ignore it.
|
||||
super( 1 );
|
||||
|
||||
if ( initialCapacity <= 0 || loadFactor <= 0.0 )
|
||||
throw new IllegalArgumentException();
|
||||
this.loadFactor = loadFactor;
|
||||
threshold = (int) ( initialCapacity * loadFactor ) - 1;
|
||||
eachCapacity = initialCapacity / nBuckets + 1;
|
||||
oldTable = new Hashtable( eachCapacity, loadFactor );
|
||||
newTable = new Hashtable( eachCapacity, loadFactor );
|
||||
}
|
||||
|
||||
/// Constructs a new, empty hashtable with the specified initial
|
||||
// capacity.
|
||||
// Unlike a plain Hashtable, an LruHashtable will never grow or
|
||||
// shrink from this initial capacity.
|
||||
// @param initialCapacity the initial number of buckets
|
||||
public LruHashtable( int initialCapacity )
|
||||
{
|
||||
this( initialCapacity, 0.75F );
|
||||
}
|
||||
|
||||
/// Returns the number of elements contained in the hashtable.
|
||||
public int size()
|
||||
{
|
||||
return newTable.size() + oldTable.size();
|
||||
}
|
||||
|
||||
/// Returns true if the hashtable contains no elements.
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/// Returns an enumeration of the hashtable's keys.
|
||||
// @see LruHashtable#elements
|
||||
// @see Enumeration
|
||||
public synchronized Enumeration keys()
|
||||
{
|
||||
return new LruHashtableEnumerator( oldTable, newTable, true );
|
||||
}
|
||||
|
||||
/// Returns an enumeration of the elements. Use the Enumeration methods
|
||||
// on the returned object to fetch the elements sequentially.
|
||||
// @see LruHashtable#keys
|
||||
// @see Enumeration
|
||||
public synchronized Enumeration elements()
|
||||
{
|
||||
return new LruHashtableEnumerator( oldTable, newTable, false );
|
||||
}
|
||||
|
||||
/// Returns true if the specified object is an element of the hashtable.
|
||||
// This operation is more expensive than the containsKey() method.
|
||||
// @param value the value that we are looking for
|
||||
// @exception NullPointerException If the value being searched
|
||||
// for is equal to null.
|
||||
// @see LruHashtable#containsKey
|
||||
public synchronized boolean contains( Object value )
|
||||
{
|
||||
if ( newTable.contains( value ) )
|
||||
return true;
|
||||
if ( oldTable.contains( value ) )
|
||||
{
|
||||
// We would like to move the object from the old table to the
|
||||
// new table. However, we need keys to re-add the objects, and
|
||||
// there's no good way to find all the keys for the given object.
|
||||
// We'd have to enumerate through all the keys and check each
|
||||
// one. Yuck. For now we just punt. Anyway, contains() is
|
||||
// probably not a commonly-used operation.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if the collection contains an element for the key.
|
||||
// @param key the key that we are looking for
|
||||
// @see LruHashtable#contains
|
||||
public synchronized boolean containsKey( Object key )
|
||||
{
|
||||
if ( newTable.containsKey( key ) )
|
||||
return true;
|
||||
if ( oldTable.containsKey( key ) )
|
||||
{
|
||||
// Move object from old table to new table.
|
||||
Object value = oldTable.get( key );
|
||||
newTable.put( key, value );
|
||||
oldTable.remove( key );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Gets the object associated with the specified key in the
|
||||
// hashtable.
|
||||
// @param key the specified key
|
||||
// @returns the element for the key or null if the key
|
||||
// is not defined in the hash table.
|
||||
// @see LruHashtable#put
|
||||
public synchronized Object get( Object key )
|
||||
{
|
||||
Object value;
|
||||
value = newTable.get( key );
|
||||
if ( value != null )
|
||||
return value;
|
||||
value = oldTable.get( key );
|
||||
if ( value != null )
|
||||
{
|
||||
// Move object from old table to new table.
|
||||
newTable.put( key, value );
|
||||
oldTable.remove( key );
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Puts the specified element into the hashtable, using the specified
|
||||
// key. The element may be retrieved by doing a get() with the same key.
|
||||
// The key and the element cannot be null.
|
||||
// @param key the specified key in the hashtable
|
||||
// @param value the specified element
|
||||
// @exception NullPointerException If the value of the element
|
||||
// is equal to null.
|
||||
// @see LruHashtable#get
|
||||
// @return the old value of the key, or null if it did not have one.
|
||||
public synchronized Object put( Object key, Object value )
|
||||
{
|
||||
Object oldValue = newTable.put( key, value );
|
||||
if ( oldValue != null )
|
||||
return oldValue;
|
||||
oldValue = oldTable.get( key );
|
||||
if ( oldValue != null )
|
||||
oldTable.remove( key );
|
||||
else
|
||||
{
|
||||
if ( size() >= threshold )
|
||||
{
|
||||
// Rotate the tables.
|
||||
oldTable = newTable;
|
||||
newTable = new Hashtable( eachCapacity, loadFactor );
|
||||
}
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/// Removes the element corresponding to the key. Does nothing if the
|
||||
// key is not present.
|
||||
// @param key the key that needs to be removed
|
||||
// @return the value of key, or null if the key was not found.
|
||||
public synchronized Object remove( Object key )
|
||||
{
|
||||
Object oldValue = newTable.remove( key );
|
||||
if ( oldValue == null )
|
||||
oldValue = oldTable.remove( key );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/// Clears the hash table so that it has no more elements in it.
|
||||
public synchronized void clear()
|
||||
{
|
||||
newTable.clear();
|
||||
oldTable.clear();
|
||||
}
|
||||
|
||||
/// Creates a clone of the hashtable. A shallow copy is made,
|
||||
// the keys and elements themselves are NOT cloned. This is a
|
||||
// relatively expensive operation.
|
||||
public synchronized Object clone()
|
||||
{
|
||||
LruHashtable n = (LruHashtable) super.clone();
|
||||
n.newTable = (Hashtable) n.newTable.clone();
|
||||
n.oldTable = (Hashtable) n.oldTable.clone();
|
||||
return n;
|
||||
}
|
||||
|
||||
// toString() can be inherited.
|
||||
|
||||
}
|
||||
|
||||
|
||||
class LruHashtableEnumerator implements Enumeration
|
||||
{
|
||||
Enumeration oldEnum;
|
||||
Enumeration newEnum;
|
||||
boolean old;
|
||||
|
||||
LruHashtableEnumerator( Hashtable oldTable, Hashtable newTable, boolean keys )
|
||||
{
|
||||
if ( keys )
|
||||
{
|
||||
oldEnum = oldTable.keys();
|
||||
newEnum = newTable.keys();
|
||||
}
|
||||
else
|
||||
{
|
||||
oldEnum = oldTable.elements();
|
||||
newEnum = newTable.elements();
|
||||
}
|
||||
old = true;
|
||||
}
|
||||
|
||||
public boolean hasMoreElements()
|
||||
{
|
||||
boolean r;
|
||||
if ( old )
|
||||
{
|
||||
r = oldEnum.hasMoreElements();
|
||||
if ( ! r )
|
||||
{
|
||||
old = false;
|
||||
r = newEnum.hasMoreElements();
|
||||
}
|
||||
}
|
||||
else
|
||||
r = newEnum.hasMoreElements();
|
||||
return r;
|
||||
}
|
||||
|
||||
public Object nextElement()
|
||||
{
|
||||
if ( old )
|
||||
return oldEnum.nextElement();
|
||||
return newEnum.nextElement();
|
||||
}
|
||||
|
||||
}
|
313
src/Acme/Serve/CgiServlet.java
Normal file
313
src/Acme/Serve/CgiServlet.java
Normal file
|
@ -0,0 +1,313 @@
|
|||
// CgiServlet - runs CGI programs
|
||||
//
|
||||
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/// Runs CGI programs.
|
||||
// <P>
|
||||
// Note: although many implementations of CGI set the working directory of
|
||||
// the subprocess to be the directory containing the executable file, the
|
||||
// CGI spec actually says nothing about the working directory. Since
|
||||
// Java has no method for setting the working directory, this implementation
|
||||
// does not set it.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/CgiServlet.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
// <P>
|
||||
// @see Acme.Serve.Serve
|
||||
|
||||
public class CgiServlet extends HttpServlet
|
||||
{
|
||||
|
||||
/// Returns a string containing information about the author, version, and
|
||||
// copyright of the servlet.
|
||||
public String getServletInfo()
|
||||
{
|
||||
return "runs CGI programs";
|
||||
}
|
||||
|
||||
/// Services a single request from the client.
|
||||
// @param req the servlet request
|
||||
// @param req the servlet response
|
||||
// @exception ServletException when an exception has occurred
|
||||
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException
|
||||
{
|
||||
if ( ! ( req.getMethod().equalsIgnoreCase( "get" ) ||
|
||||
req.getMethod().equalsIgnoreCase( "post" ) ) )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_NOT_IMPLEMENTED );
|
||||
return;
|
||||
}
|
||||
|
||||
String path = req.getServletPath();
|
||||
if ( path == null || path.charAt( 0 ) != '/' )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_BAD_REQUEST );
|
||||
return;
|
||||
}
|
||||
if ( path.indexOf( "/../" ) != -1 || path.endsWith( "/.." ) )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_FORBIDDEN );
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a version without the leading /.
|
||||
String pathname = path.substring( 1 );
|
||||
if ( pathname.length() == 0 )
|
||||
pathname = "./";
|
||||
|
||||
dispatchPathname( req, res, path, pathname );
|
||||
}
|
||||
|
||||
|
||||
private void dispatchPathname( HttpServletRequest req, HttpServletResponse res, String path, String pathname ) throws IOException
|
||||
{
|
||||
String filename = pathname.replace( '/', File.separatorChar );
|
||||
if ( filename.charAt( filename.length() - 1 ) == File.separatorChar )
|
||||
filename = filename.substring( 0, filename.length() - 1 );
|
||||
filename = getServletContext().getRealPath( filename );
|
||||
File file = new File( filename );
|
||||
if ( file.exists() )
|
||||
serveFile( req, res, path, filename, file );
|
||||
else
|
||||
res.sendError( HttpServletResponse.SC_NOT_FOUND );
|
||||
}
|
||||
|
||||
|
||||
private void serveFile( HttpServletRequest req, HttpServletResponse res, String path, String filename, File file ) throws IOException
|
||||
{
|
||||
String queryString = req.getQueryString();
|
||||
int contentLength = req.getContentLength();
|
||||
int c;
|
||||
|
||||
log( "running " + path );
|
||||
|
||||
// Make argument list.
|
||||
Vector argVec = new Vector();
|
||||
argVec.addElement( filename );
|
||||
if ( queryString != null && queryString.indexOf( "=" ) == -1 )
|
||||
{
|
||||
Enumeration enum = new StringTokenizer( queryString, "+" );
|
||||
while ( enum.hasMoreElements() )
|
||||
argVec.addElement( (String) enum.nextElement() );
|
||||
}
|
||||
String argList[] = makeList( argVec );
|
||||
|
||||
// Make environment list.
|
||||
Vector envVec = new Vector();
|
||||
envVec.addElement( makeEnv(
|
||||
"PATH", "/usr/local/bin:/usr/ucb:/bin:/usr/bin" ) );
|
||||
envVec.addElement( makeEnv( "GATEWAY_INTERFACE", "CGI/1.1" ) );
|
||||
envVec.addElement( makeEnv(
|
||||
"SERVER_SOFTWARE", getServletContext().getServerInfo() ) );
|
||||
envVec.addElement( makeEnv( "SERVER_NAME", req.getServerName() ) );
|
||||
envVec.addElement( makeEnv(
|
||||
"SERVER_PORT", Integer.toString( req.getServerPort() ) ) );
|
||||
envVec.addElement( makeEnv( "REMOTE_ADDR", req.getRemoteAddr() ) );
|
||||
envVec.addElement( makeEnv( "REMOTE_HOST", req.getRemoteHost() ) );
|
||||
envVec.addElement( makeEnv( "REQUEST_METHOD", req.getMethod() ) );
|
||||
if ( contentLength != -1 )
|
||||
envVec.addElement( makeEnv(
|
||||
"CONTENT_LENGTH", Integer.toString( contentLength ) ) );
|
||||
if ( req.getContentType() != null )
|
||||
envVec.addElement( makeEnv(
|
||||
"CONTENT_TYPE", req.getContentType() ) );
|
||||
envVec.addElement( makeEnv( "SCRIPT_NAME", req.getServletPath() ) );
|
||||
if ( req.getPathInfo() != null )
|
||||
envVec.addElement( makeEnv( "PATH_INFO", req.getPathInfo() ) );
|
||||
if ( req.getPathTranslated() != null )
|
||||
envVec.addElement( makeEnv(
|
||||
"PATH_TRANSLATED", req.getPathTranslated() ) );
|
||||
if ( queryString != null )
|
||||
envVec.addElement( makeEnv( "QUERY_STRING", queryString ) );
|
||||
envVec.addElement( makeEnv( "SERVER_PROTOCOL", req.getProtocol() ) );
|
||||
if ( req.getRemoteUser() != null )
|
||||
envVec.addElement( makeEnv( "REMOTE_USER", req.getRemoteUser() ) );
|
||||
if ( req.getAuthType() != null )
|
||||
envVec.addElement( makeEnv( "AUTH_TYPE", req.getAuthType() ) );
|
||||
Enumeration enum = req.getHeaderNames();
|
||||
while ( enum.hasMoreElements() )
|
||||
{
|
||||
String name = (String) enum.nextElement();
|
||||
String value = req.getHeader( name );
|
||||
if ( value == null )
|
||||
value = "";
|
||||
envVec.addElement( makeEnv(
|
||||
"HTTP_" + name.toUpperCase().replace( '-', '_' ), value ) );
|
||||
}
|
||||
String envList[] = makeList( envVec );
|
||||
|
||||
// Start the command.
|
||||
Process proc = Runtime.getRuntime().exec( argList, envList );
|
||||
|
||||
try
|
||||
{
|
||||
// If it's a POST, copy the request data to the process.
|
||||
if ( req.getMethod().equalsIgnoreCase( "post" ) )
|
||||
{
|
||||
InputStream reqIn = req.getInputStream();
|
||||
OutputStream procOut = proc.getOutputStream();
|
||||
for ( int i = 0; i < contentLength; ++i )
|
||||
{
|
||||
c = reqIn.read();
|
||||
if ( c == -1 )
|
||||
break;
|
||||
procOut.write( c );
|
||||
}
|
||||
procOut.close();
|
||||
}
|
||||
|
||||
// Now read the response from the process.
|
||||
BufferedReader procIn = new BufferedReader( new InputStreamReader(
|
||||
proc.getInputStream() ) );
|
||||
OutputStream resOut = res.getOutputStream();
|
||||
// Some of the headers have to be intercepted and handled.
|
||||
boolean firstLine = true;
|
||||
while ( true )
|
||||
{
|
||||
String line = procIn.readLine();
|
||||
if ( line == null )
|
||||
break;
|
||||
line = line.trim();
|
||||
if ( line.equals( "" ) )
|
||||
break;
|
||||
int colon = line.indexOf( ":" );
|
||||
if ( colon == -1 )
|
||||
{
|
||||
// No colon. If it's the first line, parse it for status.
|
||||
if ( firstLine )
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer( line, " " );
|
||||
try
|
||||
{
|
||||
switch( tok.countTokens() )
|
||||
{
|
||||
case 2:
|
||||
tok.nextToken();
|
||||
res.setStatus(
|
||||
Integer.parseInt( tok.nextToken() ) );
|
||||
break;
|
||||
case 3:
|
||||
tok.nextToken();
|
||||
res.setStatus(
|
||||
Integer.parseInt( tok.nextToken() ),
|
||||
tok.nextToken() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch ( NumberFormatException ignore ) {}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No colon and it's not the first line? Ignore.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There's a colon. Check for certain special headers.
|
||||
String name = line.substring( 0, colon );
|
||||
String value = line.substring( colon + 1 ).trim();
|
||||
if ( name.equalsIgnoreCase( "Status" ) )
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer( value, " " );
|
||||
try
|
||||
{
|
||||
switch( tok.countTokens() )
|
||||
{
|
||||
case 1:
|
||||
res.setStatus(
|
||||
Integer.parseInt( tok.nextToken() ) );
|
||||
break;
|
||||
case 2:
|
||||
res.setStatus(
|
||||
Integer.parseInt( tok.nextToken() ),
|
||||
tok.nextToken() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch ( NumberFormatException ignore ) {}
|
||||
}
|
||||
else if ( name.equalsIgnoreCase( "Content-type" ) )
|
||||
{
|
||||
res.setContentType( value );
|
||||
}
|
||||
else if ( name.equalsIgnoreCase( "Content-length" ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
res.setContentLength( Integer.parseInt( value ) );
|
||||
}
|
||||
catch ( NumberFormatException ignore ) {}
|
||||
}
|
||||
else if ( name.equalsIgnoreCase( "Location" ) )
|
||||
{
|
||||
res.setStatus(
|
||||
HttpServletResponse.SC_MOVED_TEMPORARILY );
|
||||
res.setHeader( name, value );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a special header. Just set it.
|
||||
res.setHeader( name, value );
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy the rest of the data uninterpreted.
|
||||
Acme.Utils.copyStream( procIn, resOut );
|
||||
procIn.close();
|
||||
resOut.close();
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
//res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
|
||||
// There's some weird bug in Java, when reading from a Process
|
||||
// you get a spurious IOException. We have to ignore it.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String makeEnv( String name, String value )
|
||||
{
|
||||
return name + "=" + value;
|
||||
}
|
||||
|
||||
|
||||
private static String[] makeList( Vector vec )
|
||||
{
|
||||
String list[] = new String[vec.size()];
|
||||
for ( int i = 0; i < vec.size(); ++i )
|
||||
list[i] = (String) vec.elementAt( i );
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
316
src/Acme/Serve/FileServlet.java
Normal file
316
src/Acme/Serve/FileServlet.java
Normal file
|
@ -0,0 +1,316 @@
|
|||
// FileServlet - servlet similar to a standard httpd
|
||||
//
|
||||
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/// Servlet similar to a standard httpd.
|
||||
// <P>
|
||||
// Implements the "GET" and "HEAD" methods for files and directories.
|
||||
// Handles index.html.
|
||||
// Redirects directory URLs that lack a trailing /.
|
||||
// Handles If-Modified-Since and Range.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/FileServlet.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
// <P>
|
||||
// @see Acme.Serve.Serve
|
||||
|
||||
public class FileServlet extends HttpServlet
|
||||
{
|
||||
|
||||
// We keep a single throttle table for all instances of the servlet.
|
||||
// Normally there is only one instance; the exception is subclasses.
|
||||
static Acme.WildcardDictionary throttleTab = null;
|
||||
|
||||
/// Constructor.
|
||||
public FileServlet()
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructor with throttling.
|
||||
// @param throttles filename containing throttle settings
|
||||
// @see ThrottledOutputStream
|
||||
public FileServlet( String throttles ) throws IOException
|
||||
{
|
||||
this();
|
||||
readThrottles( throttles );
|
||||
}
|
||||
|
||||
private void readThrottles( String throttles ) throws IOException
|
||||
{
|
||||
Acme.WildcardDictionary newThrottleTab =
|
||||
ThrottledOutputStream.parseThrottleFile( throttles );
|
||||
if ( throttleTab == null )
|
||||
throttleTab = newThrottleTab;
|
||||
else
|
||||
{
|
||||
// Merge the new one into the old one.
|
||||
Enumeration keys = newThrottleTab.keys();
|
||||
Enumeration elements = newThrottleTab.elements();
|
||||
while ( keys.hasMoreElements() )
|
||||
{
|
||||
Object key = keys.nextElement();
|
||||
Object element = elements.nextElement();
|
||||
throttleTab.put( key, element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a string containing information about the author, version, and
|
||||
// copyright of the servlet.
|
||||
public String getServletInfo()
|
||||
{
|
||||
return "servlet similar to a standard httpd";
|
||||
}
|
||||
|
||||
|
||||
/// Services a single request from the client.
|
||||
// @param req the servlet request
|
||||
// @param req the servlet response
|
||||
// @exception ServletException when an exception has occurred
|
||||
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException
|
||||
{
|
||||
boolean headOnly;
|
||||
if ( req.getMethod().equalsIgnoreCase( "get" ) )
|
||||
headOnly = false;
|
||||
else if ( ! req.getMethod().equalsIgnoreCase( "head" ) )
|
||||
headOnly = true;
|
||||
else
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_NOT_IMPLEMENTED );
|
||||
return;
|
||||
}
|
||||
|
||||
String path = req.getServletPath();
|
||||
if ( path == null || path.charAt( 0 ) != '/' )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_BAD_REQUEST );
|
||||
return;
|
||||
}
|
||||
if ( path.indexOf( "/../" ) != -1 || path.endsWith( "/.." ) )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_FORBIDDEN );
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a version without the leading /.
|
||||
String pathname = path.substring( 1 );
|
||||
if ( pathname.length() == 0 )
|
||||
pathname = "./";
|
||||
|
||||
dispatchPathname( req, res, headOnly, path, pathname );
|
||||
}
|
||||
|
||||
|
||||
private void dispatchPathname( HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, String pathname ) throws IOException
|
||||
{
|
||||
String filename = pathname.replace( '/', File.separatorChar );
|
||||
if ( filename.charAt( filename.length() - 1 ) == File.separatorChar )
|
||||
filename = filename.substring( 0, filename.length() - 1 );
|
||||
filename = getServletContext().getRealPath( filename );
|
||||
File file = new File( filename );
|
||||
if ( file.exists() )
|
||||
{
|
||||
if ( ! file.isDirectory() )
|
||||
serveFile( req, res, headOnly, path, filename, file );
|
||||
else
|
||||
{
|
||||
if ( pathname.charAt( pathname.length() - 1 ) != '/' )
|
||||
redirectDirectory( req, res, path, file );
|
||||
else
|
||||
{
|
||||
String indexFilename =
|
||||
filename + File.separatorChar + "index.html";
|
||||
File indexFile = new File( indexFilename );
|
||||
if ( indexFile.exists() )
|
||||
serveFile(
|
||||
req, res, headOnly, path, indexFilename,
|
||||
indexFile );
|
||||
else
|
||||
serveDirectory(
|
||||
req, res, headOnly, path, filename, file );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pathname.endsWith( "/index.html" ) )
|
||||
dispatchPathname(
|
||||
req, res, headOnly, path,
|
||||
pathname.substring( 0, pathname.length() - 10 ) );
|
||||
else if ( pathname.equals( "index.html" ) )
|
||||
dispatchPathname( req, res, headOnly, path, "./" );
|
||||
else
|
||||
res.sendError( HttpServletResponse.SC_NOT_FOUND );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void serveFile( HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, String filename, File file ) throws IOException
|
||||
{
|
||||
log( "getting " + path );
|
||||
if ( ! file.canRead() )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_FORBIDDEN );
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle If-Modified-Since.
|
||||
res.setStatus( HttpServletResponse.SC_OK );
|
||||
long lastMod = file.lastModified();
|
||||
String ifModSinceStr = req.getHeader( "If-Modified-Since" );
|
||||
long ifModSince = -1;
|
||||
if ( ifModSinceStr != null )
|
||||
{
|
||||
int semi = ifModSinceStr.indexOf( ';' );
|
||||
if ( semi != -1 )
|
||||
ifModSinceStr = ifModSinceStr.substring( 0, semi );
|
||||
try
|
||||
{
|
||||
ifModSince =
|
||||
DateFormat.getDateInstance().parse( ifModSinceStr ).getTime();
|
||||
}
|
||||
catch ( Exception ignore ) {}
|
||||
}
|
||||
if ( ifModSince != -1 && ifModSince >= lastMod )
|
||||
{
|
||||
res.setStatus( HttpServletResponse.SC_NOT_MODIFIED );
|
||||
headOnly = true;
|
||||
}
|
||||
|
||||
String rangeStr = req.getHeader( "Range" );
|
||||
if ( rangeStr != null )
|
||||
{
|
||||
//!!!
|
||||
}
|
||||
|
||||
res.setContentType( getServletContext().getMimeType( filename ) );
|
||||
res.setContentLength( (int) file.length() );
|
||||
res.setDateHeader( "Last-modified", lastMod );
|
||||
OutputStream out = res.getOutputStream();
|
||||
if ( ! headOnly )
|
||||
{
|
||||
// Check throttle.
|
||||
if ( throttleTab != null )
|
||||
{
|
||||
ThrottleItem throttleItem =
|
||||
(ThrottleItem) throttleTab.get( path );
|
||||
if ( throttleItem != null )
|
||||
{
|
||||
// !!! Need to account for multiple simultaneous fetches.
|
||||
out = new ThrottledOutputStream(
|
||||
out, throttleItem.getMaxBps() );
|
||||
}
|
||||
}
|
||||
|
||||
InputStream in = new FileInputStream( file );
|
||||
copyStream( in, out );
|
||||
in.close();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
/// Copy a file from in to out.
|
||||
// Sub-classes can override this in order to do filtering of some sort.
|
||||
public void copyStream( InputStream in, OutputStream out ) throws IOException
|
||||
{
|
||||
Acme.Utils.copyStream( in, out );
|
||||
}
|
||||
|
||||
|
||||
private void serveDirectory( HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, String filename, File file ) throws IOException
|
||||
{
|
||||
log( "indexing " + path );
|
||||
if ( ! file.canRead() )
|
||||
{
|
||||
res.sendError( HttpServletResponse.SC_FORBIDDEN );
|
||||
return;
|
||||
}
|
||||
res.setStatus( HttpServletResponse.SC_OK );
|
||||
res.setContentType( "text/html" );
|
||||
OutputStream out = res.getOutputStream();
|
||||
if ( ! headOnly )
|
||||
{
|
||||
PrintStream p = new PrintStream( new BufferedOutputStream( out ) );
|
||||
p.println( "<HTML><HEAD>" );
|
||||
p.println( "<TITLE>Index of " + path + "</TITLE>" );
|
||||
p.println( "</HEAD><BODY BGCOLOR=\"#ffffff\">" );
|
||||
p.println( "<H2>Index of " + path + "</H2>" );
|
||||
p.println( "<PRE>" );
|
||||
p.println( "mode bytes last-changed name" );
|
||||
p.println( "<HR>" );
|
||||
String[] names = file.list();
|
||||
Acme.Utils.sortStrings( names );
|
||||
for ( int i = 0; i < names.length; ++i )
|
||||
{
|
||||
String aFilename = filename + File.separatorChar + names[i];
|
||||
File aFile = new File( aFilename );
|
||||
String aFileType;
|
||||
if ( aFile.isDirectory() )
|
||||
aFileType = "d";
|
||||
else if ( aFile.isFile() )
|
||||
aFileType = "-";
|
||||
else
|
||||
aFileType = "?";
|
||||
String aFileRead = ( aFile.canRead() ? "r" : "-" );
|
||||
String aFileWrite = ( aFile.canWrite() ? "w" : "-" );
|
||||
String aFileExe = "-";
|
||||
String aFileSize = Acme.Fmt.fmt( aFile.length(), 8 );
|
||||
String aFileDate =
|
||||
Acme.Utils.lsDateStr( new Date( aFile.lastModified() ) );
|
||||
String aFileDirsuf = ( aFile.isDirectory() ? "/" : "" );
|
||||
String aFileSuf = ( aFile.isDirectory() ? "/" : "" );
|
||||
p.println(
|
||||
aFileType + aFileRead + aFileWrite + aFileExe +
|
||||
" " + aFileSize + " " + aFileDate + " " +
|
||||
"<A HREF=\"" + names[i] + aFileDirsuf + "\">" +
|
||||
names[i] + aFileSuf + "</A>" );
|
||||
}
|
||||
p.println( "</PRE>" );
|
||||
p.println( "<HR>" );
|
||||
ServeUtils.writeAddress( p );
|
||||
p.println( "</BODY></HTML>" );
|
||||
p.flush();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
|
||||
protected void redirectDirectory( HttpServletRequest req, HttpServletResponse res, String path, File file ) throws IOException
|
||||
{
|
||||
log( "redirecting " + path );
|
||||
res.sendRedirect( path + "/" );
|
||||
}
|
||||
|
||||
}
|
70
src/Acme/Serve/SampleServlet.java
Normal file
70
src/Acme/Serve/SampleServlet.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
// SampleServlet - trivial servlet
|
||||
//
|
||||
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/// Trivial servlet.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/SampleServlet.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
|
||||
public class SampleServlet extends HttpServlet
|
||||
{
|
||||
|
||||
/// Returns a string containing information about the author, version, and
|
||||
// copyright of the servlet.
|
||||
public String getServletInfo()
|
||||
{
|
||||
return "trivial servlet";
|
||||
}
|
||||
|
||||
/// Services a single request from the client.
|
||||
// @param req the servlet request
|
||||
// @param req the servlet response
|
||||
// @exception ServletException when an exception has occurred
|
||||
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException
|
||||
{
|
||||
log( "called" );
|
||||
res.setStatus( HttpServletResponse.SC_OK );
|
||||
res.setContentType( "text/html" );
|
||||
ServletOutputStream p = res.getOutputStream();
|
||||
p.println( "<HTML><HEAD>" );
|
||||
p.println( "<TITLE>Sample Servlet Output</TITLE>" );
|
||||
p.println( "</HEAD><BODY>" );
|
||||
p.println( "<H2>Sample Servlet Output</H2>" );
|
||||
p.println( "<P>Output from a sample servlet." );
|
||||
p.println( "</BODY></HTML>" );
|
||||
p.flush();
|
||||
p.close();
|
||||
}
|
||||
|
||||
}
|
1753
src/Acme/Serve/Serve.java
Normal file
1753
src/Acme/Serve/Serve.java
Normal file
File diff suppressed because it is too large
Load diff
93
src/Acme/Serve/ServeUtils.java
Normal file
93
src/Acme/Serve/ServeUtils.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
// ServeUtils - static utilities for minimal Java HTTP server
|
||||
//
|
||||
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/// Static utilities for minimal Java HTTP server.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/ServeUtils.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
|
||||
public class ServeUtils
|
||||
{
|
||||
|
||||
// Server identification.
|
||||
public static final String serverName = "Hop";
|
||||
public static final String serverVersion = "1.1 p1";
|
||||
public static final String serverUrl = "http://helma.org/";
|
||||
|
||||
/// Write a standard-format HTML address for this server.
|
||||
public static void writeAddress( OutputStream o ) throws IOException
|
||||
{
|
||||
PrintStream p = new PrintStream( o );
|
||||
p.println(
|
||||
"<ADDRESS><A HREF=\"" + serverUrl + "\">" +
|
||||
serverName + " " + serverVersion + "</A></ADDRESS>" );
|
||||
}
|
||||
|
||||
|
||||
/// Get a cookie of a given name.
|
||||
public static String getCookie( HttpServletRequest req, String name )
|
||||
{
|
||||
String h = req.getHeader( "Cookie" );
|
||||
if ( h == null )
|
||||
return null;
|
||||
StringTokenizer st = new StringTokenizer( h, "; " );
|
||||
while ( st.hasMoreTokens() )
|
||||
{
|
||||
String tk = st.nextToken();
|
||||
int eq = tk.indexOf( '=' );
|
||||
String n, v;
|
||||
if ( eq == -1 )
|
||||
{
|
||||
n = tk;
|
||||
v = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
n = tk.substring( 0, eq );
|
||||
v = tk.substring( eq + 1 );
|
||||
}
|
||||
if ( name.equals( n ) )
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Set a cookie.
|
||||
public static void setCookie( HttpServletResponse res, String name, String value )
|
||||
{
|
||||
res.setHeader( "Set-Cookie", name + "=" + value );
|
||||
}
|
||||
|
||||
}
|
132
src/Acme/Serve/TestServlet.java
Normal file
132
src/Acme/Serve/TestServlet.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
// TestServlet - simple servlet that tests the Servlet API
|
||||
//
|
||||
// Copyright (C) 1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
/// Simple servlet that tests the Servlet API.
|
||||
// Sample output:
|
||||
// <PRE>
|
||||
// getContentLength(): -1
|
||||
// getContentType(): null
|
||||
// getProtocol(): HTTP/1.0
|
||||
// getScheme(): http
|
||||
// getServerName(): www.acme.com
|
||||
// getServerPort(): 1234
|
||||
// getRemoteAddr(): 192.100.66.1
|
||||
// getRemoteHost(): acme.com
|
||||
// getMethod(): GET
|
||||
// getRequestURI(): http://www.acme.com:1234/TestServlet?foo=bar
|
||||
// getServletPath(): /TestServlet
|
||||
// getPathInfo(): null
|
||||
// getPathTranslated(): null
|
||||
// getQueryString(): foo=bar
|
||||
// getRemoteUser(): null
|
||||
// getAuthType(): null
|
||||
//
|
||||
// Parameters:
|
||||
// foo = bar
|
||||
//
|
||||
// Header:
|
||||
// accept: text/html, image/gif, image/jpeg, *; q=.2
|
||||
// user-agent: Java1.0.2
|
||||
// </PRE>
|
||||
// <A HREF="/resources/classes/Acme/Serve/TestServlet.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
|
||||
public class TestServlet extends HttpServlet
|
||||
{
|
||||
|
||||
/// Returns a string containing information about the author, version, and
|
||||
// copyright of the servlet.
|
||||
public String getServletInfo()
|
||||
{
|
||||
return "simple servlet that tests the Servlet API";
|
||||
}
|
||||
|
||||
/// Services a single request from the client.
|
||||
// @param req the servlet request
|
||||
// @param req the servlet response
|
||||
// @exception ServletException when an exception has occurred
|
||||
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException
|
||||
{
|
||||
Enumeration en;
|
||||
log( "called" );
|
||||
res.setStatus( HttpServletResponse.SC_OK );
|
||||
res.setContentType( "text/html" );
|
||||
ServletOutputStream p = res.getOutputStream();
|
||||
p.println( "<HTML><HEAD>" );
|
||||
p.println( "<TITLE>Test Servlet Output</TITLE>" );
|
||||
p.println( "</HEAD><BODY>" );
|
||||
p.println( "<H2>Test Servlet Output</H2>" );
|
||||
p.println( "<HR>" );
|
||||
p.println( "<PRE>" );
|
||||
p.println( "getContentLength(): " + req.getContentLength() );
|
||||
p.println( "getContentType(): " + req.getContentType() );
|
||||
p.println( "getProtocol(): " + req.getProtocol() );
|
||||
p.println( "getScheme(): " + req.getScheme() );
|
||||
p.println( "getServerName(): " + req.getServerName() );
|
||||
p.println( "getServerPort(): " + req.getServerPort() );
|
||||
p.println( "getRemoteAddr(): " + req.getRemoteAddr() );
|
||||
p.println( "getRemoteHost(): " + req.getRemoteHost() );
|
||||
p.println( "getMethod(): " + req.getMethod() );
|
||||
p.println( "getRequestURI(): " + req.getRequestURI() );
|
||||
p.println( "getServletPath(): " + req.getServletPath() );
|
||||
p.println( "getPathInfo(): " + req.getPathInfo() );
|
||||
p.println( "getPathTranslated(): " + req.getPathTranslated() );
|
||||
p.println( "getQueryString(): " + req.getQueryString() );
|
||||
p.println( "getRemoteUser(): " + req.getRemoteUser() );
|
||||
p.println( "getAuthType(): " + req.getAuthType() );
|
||||
p.println( "" );
|
||||
p.println( "Parameters:" );
|
||||
en = req.getParameterNames();
|
||||
while ( en.hasMoreElements() )
|
||||
{
|
||||
String name = (String) en.nextElement();
|
||||
p.println( " " + name + " = " + req.getParameter( name ) );
|
||||
}
|
||||
p.println( "" );
|
||||
p.println( "Headers:" );
|
||||
en = req.getHeaderNames();
|
||||
while ( en.hasMoreElements() )
|
||||
{
|
||||
String name = (String) en.nextElement();
|
||||
p.println( " " + name + ": " + req.getHeader( name ) );
|
||||
}
|
||||
p.println( "</PRE>" );
|
||||
p.println( "<HR>" );
|
||||
p.println( "</BODY></HTML>" );
|
||||
p.flush();
|
||||
p.close();
|
||||
}
|
||||
|
||||
}
|
55
src/Acme/Serve/ThrottleItem.java
Normal file
55
src/Acme/Serve/ThrottleItem.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
// ThrottleItem - data item for ThrottledOutputStream
|
||||
//
|
||||
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/// Data item for ThrottledOutputStream.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/ThrottleItem.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
|
||||
public class ThrottleItem
|
||||
{
|
||||
|
||||
private long maxBps;
|
||||
|
||||
/// Constructor.
|
||||
public ThrottleItem( long maxBps )
|
||||
{
|
||||
this.maxBps = maxBps;
|
||||
}
|
||||
|
||||
public long getMaxBps()
|
||||
{
|
||||
return maxBps;
|
||||
}
|
||||
|
||||
}
|
156
src/Acme/Serve/ThrottledOutputStream.java
Normal file
156
src/Acme/Serve/ThrottledOutputStream.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
// ThrottledOutputStream - output stream with throttling
|
||||
//
|
||||
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
//
|
||||
// Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||
// fine Java utilities: http://www.acme.com/java/
|
||||
|
||||
package Acme.Serve;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/// Output stream with throttling.
|
||||
// <P>
|
||||
// Restricts output to a specified rate. Also includes a static utility
|
||||
// routine for parsing a file of throttle settings.
|
||||
// <P>
|
||||
// <A HREF="/resources/classes/Acme/Serve/ThrottledOutputStream.java">Fetch the software.</A><BR>
|
||||
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
|
||||
|
||||
public class ThrottledOutputStream extends FilterOutputStream
|
||||
{
|
||||
|
||||
/// Parses a standard throttle file.
|
||||
// <P>
|
||||
// A throttle file lets you set maximum byte rates on filename patterns.
|
||||
// The format of the throttle file is very simple. A # starts a
|
||||
// comment, and the rest of the line is ignored. Blank lines are ignored.
|
||||
// The rest of the lines should consist of a pattern, whitespace, and a
|
||||
// number. The pattern is a simple shell-style filename pattern, using
|
||||
// ? and *, or multiple such patterns separated by |.
|
||||
// <P>
|
||||
// The numbers in the file are byte rates, specified in units of bytes
|
||||
// per second. For comparison, a v.32b/v.42b modem gives about
|
||||
// 1500/2000 B/s depending on compression, a double-B-channel ISDN line
|
||||
// about 12800 B/s, and a T1 line is about 150000 B/s.
|
||||
// <P>
|
||||
// Example:
|
||||
// <BLOCKQUOTE><PRE>
|
||||
// # throttle file for www.acme.com
|
||||
// * 100000 # limit total web usage to 2/3 of our T1
|
||||
// *.jpg|*.gif 50000 # limit images to 1/3 of our T1
|
||||
// *.mpg 20000 # and movies to even less
|
||||
// jef/* 20000 # jef's pages are too popular
|
||||
// </PRE></BLOCKQUOTE>
|
||||
// <P>
|
||||
// The routine returns a WildcardDictionary. Do a lookup in this
|
||||
// dictionary using a filename, and you'll get back a ThrottleItem
|
||||
// containing the corresponding byte rate.
|
||||
public static Acme.WildcardDictionary parseThrottleFile( String filename ) throws IOException
|
||||
{
|
||||
Acme.WildcardDictionary wcd = new Acme.WildcardDictionary();
|
||||
BufferedReader br = new BufferedReader( new FileReader( filename ) );
|
||||
while ( true )
|
||||
{
|
||||
String line = br.readLine();
|
||||
if ( line == null )
|
||||
break;
|
||||
int i = line.indexOf( '#' );
|
||||
if ( i != -1 )
|
||||
line = line.substring( 0, i );
|
||||
line = line.trim();
|
||||
if ( line.length() == 0 )
|
||||
continue;
|
||||
String[] words = Acme.Utils.splitStr( line );
|
||||
if ( words.length != 2 )
|
||||
throw new IOException( "malformed throttle line: " + line );
|
||||
try
|
||||
{
|
||||
wcd.put(
|
||||
words[0], new ThrottleItem( Long.parseLong( words[1] ) ) );
|
||||
}
|
||||
catch ( NumberFormatException e )
|
||||
{
|
||||
throw new IOException(
|
||||
"malformed number in throttle line: " + line );
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
return wcd;
|
||||
}
|
||||
|
||||
|
||||
private long maxBps;
|
||||
private long bytes;
|
||||
private long start;
|
||||
|
||||
/// Constructor.
|
||||
public ThrottledOutputStream( OutputStream out, long maxBps )
|
||||
{
|
||||
super( out );
|
||||
this.maxBps = maxBps;
|
||||
bytes = 0;
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private byte[] oneByte = new byte[1];
|
||||
|
||||
/// Writes a byte. This method will block until the byte is actually
|
||||
// written.
|
||||
// @param b the byte to be written
|
||||
// @exception IOException if an I/O error has occurred
|
||||
public void write( int b ) throws IOException
|
||||
{
|
||||
oneByte[0] = (byte) b;
|
||||
write( oneByte, 0, 1 );
|
||||
}
|
||||
|
||||
/// Writes a subarray of bytes.
|
||||
// @param b the data to be written
|
||||
// @param off the start offset in the data
|
||||
// @param len the number of bytes that are written
|
||||
// @exception IOException if an I/O error has occurred
|
||||
public void write( byte b[], int off, int len ) throws IOException
|
||||
{
|
||||
// Check the throttle.
|
||||
bytes += len;
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
long bps = bytes * 1000L / elapsed;
|
||||
if ( bps > maxBps )
|
||||
{
|
||||
// Oops, sending too fast.
|
||||
long wakeElapsed = bytes * 1000L / maxBps;
|
||||
try
|
||||
{
|
||||
Thread.sleep( wakeElapsed - elapsed );
|
||||
}
|
||||
catch ( InterruptedException ignore ) {}
|
||||
}
|
||||
|
||||
// Write the bytes.
|
||||
out.write( b, off, len );
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue