ssiframe.java

来自「很棒的web服务器源代码」· Java 代码 · 共 938 行 · 第 1/2 页

JAVA
938
字号
// Ssiframe.java// $Id: SSIFrame.java,v 1.13 2000/08/16 21:37:46 ylafon Exp $// (c) COPYRIGHT MIT and INRIA, 1996.// Please first read the full copyright statement in file COPYRIGHT.htmlpackage org.w3c.jigsaw.ssi ;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.PrintStream;import java.io.RandomAccessFile;import java.util.Dictionary;import java.util.Vector;import org.w3c.www.http.HTTP;import org.w3c.www.http.HeaderValue;import org.w3c.www.http.HttpDate;import org.w3c.www.http.HttpEntityMessage;import org.w3c.www.http.HttpInteger;import org.w3c.www.http.HttpMessage;import org.w3c.www.http.HttpReplyMessage;import org.w3c.www.http.HttpRequestMessage;import org.w3c.tools.resources.Attribute;import org.w3c.tools.resources.AttributeHolder;import org.w3c.tools.resources.AttributeRegistry;import org.w3c.tools.resources.BooleanAttribute;import org.w3c.tools.resources.ClassAttribute;import org.w3c.tools.resources.FileResource;import org.w3c.tools.resources.IntegerAttribute;import org.w3c.tools.resources.ProtocolException;import org.w3c.tools.resources.ReplyInterface;import org.w3c.tools.resources.RequestInterface;import org.w3c.tools.resources.Resource;import org.w3c.tools.resources.ResourceException;import org.w3c.tools.resources.event.AttributeChangedEvent;import org.w3c.jigsaw.http.HTTPException;import org.w3c.jigsaw.http.Reply;import org.w3c.jigsaw.http.Request;import org.w3c.jigsaw.frames.HTTPFrame;import org.w3c.util.ArrayDictionary;import org.w3c.jigsaw.ssi.commands.CommandRegistry;import org.w3c.tools.resources.ProtocolException;import org.w3c.tools.resources.ResourceException;/** * This resource implements server-side parsing of HTML documents. * Any comment of the form <code>&lt;!--#commandName param1=val1 * ... paramn=valn --&gt;</code> will be interpreted as an include * directive. * <p> Commands are looked up in an instance of the class * supplied in the registryClass attribute, which must be a subclass * of <code>org.w3c.jigsaw.ssi.CommandRegistry</code>. * * @author Antonio Ramirez <anto@mit.edu> * @author Benoit Mahe <bmahe@sophia.inria.fr> * @see org.w3c.jigsaw.ssi.commands.CommandRegistry * @see org.w3c.jigsaw.ssi.commands.Command */public class SSIFrame extends HTTPFrame {    public static final boolean debug = false ;    /** Attributes index - The segments */    private static int ATTR_SEGMENTS = -1 ;    /** Attributes index - The total unparsed size */    private static int ATTR_UNPARSED_SIZE = -1 ;    /** Attribute index - The class to use for making the CommandRegistry */    private static int ATTR_REGISTRY_CLASS = -1 ;    /** Attributes index - The maximum recursive parsing depth */    private static int ATTR_MAX_DEPTH = -1 ;    /** Attribute index - Whether or not to deny insecure commands */    private static int ATTR_SECURE = -1 ;    /**     * The command registry used by the resource.     */    private CommandRegistry commReg = null ;    /**     * The most specific class of the current command registry.     */    private Class regClass = null ;    /**     * Here we keep track of created registries to avoid     * making more than one per registry class.     */    private static Dictionary regs = new ArrayDictionary(5) ;    /** Will hold the increments for finding "<!--#" */    private static byte startIncrements[] = new byte[128] ;    /** Same thing for "-->" */    private static byte endIncrements[] = new byte[128] ;    /** The start pattern */    private static byte startPat[] =    {	(byte)'<',(byte)'!',(byte)'-',(byte)'-',(byte)'#'    };    /** The end pattern */    private static byte endPat[] =    {	(byte)'-',(byte)'-',(byte)'>'    };    // For value-less parameters     private static final String emptyString = "" ;    /**     * Our "very global" variables     */    protected Dictionary vars = null;    /**     * Message state - the current recursion depth     */    public static final String STATE_DEPTH =	"org.w3c.jigsaw.ssi.SSIResource.depth" ;    /**     * Message state - the current variables     */    public static final String STATE_VARIABLES =	"org.w3c.jigsaw.ssi.SSIResource.variables" ;    private boolean cacheReplies = true;    protected void doNotCacheReply() {	cacheReplies = false;    }    protected boolean cacheReplies() {	return cacheReplies;    }    /**     * Listen its resource.     */    public void attributeChanged(AttributeChangedEvent evt) {	super.attributeChanged(evt);	String name = evt.getAttribute().getName();	if ((name.equals("file-stamp")) || (name.equals("file-length"))) {	    setValue(ATTR_SEGMENTS, (Segment[]) null);	}    }    private final int getUnparsedSize()    {	return getInt(ATTR_UNPARSED_SIZE,-1) ;    }    private final void setUnparsedSize(int unparsedSize)    {	setValue(ATTR_UNPARSED_SIZE,new Integer(unparsedSize)) ;    }    /**     * Makes sure that checkContent() is called on _any_ HTTP method,     * so that the internal representation of commands is always consistent.     * @param request The HTTPRequest     * @param filters The filters to apply     * @return a ReplyInterface instance     * @exception ProtocolException If processing the request failed.     * @exception ResourceException If this resource got a fatal error.     */    public ReplyInterface perform(RequestInterface request)	throws ProtocolException, ResourceException    {	if (! checkRequest(request)) {	    return performFrames(request);	}	if (fresource != null)	    fresource.checkContent();	return super.perform(request) ;    }    /**     * Perform a get (associated with a FileResource)     * @param request the HTTP request     * @return a Reply instance.     * @exception ProtocolException If processing the request failed.     * @exception ResourceException If this resource got a fatal error.     */    protected Reply getFileResource(Request request)	throws ProtocolException, ResourceException    {	Reply reply = handle(request) ;	return reply != null	    ? reply	    : super.getFileResource(request) ;    }    /**     * Perform a post.     * @param request the HTTP request     * @return a Reply instance.     * @exception ProtocolException If processing the request failed.     * @exception ResourceException If this resource got a fatal error.     */    public Reply post(Request request)	throws ProtocolException, ResourceException    {	Reply reply = handle(request) ;	return reply != null	    ? reply	    : super.post(request) ;    }    /**     * Handles all relevant HTTP methods.     * Merges the partial replies from each of the segments into     * one global reply.     * <strong>Remark</strong>: no direct relation to PostableResource.handle()     * @param request The HTTP request     * @return a Reply instance.     * @exception ProtocolException If processing the request failed.     */    public Reply handle(Request request)	throws ProtocolException    {	if (fresource == null)	    return null;	if(SSIFrame.debug)	    System.out.println("@@@@ handle: "+			       (request.isInternal() 				? "internal" : "external") ) ;	fresource.checkContent() ;		Integer depth =	    (Integer) request.getState(STATE_DEPTH) ;	if(depth == null) depth = new Integer(0) ;	int unparsedSize = 0 ;	Segment[] segments = getSegments() ;	if(segments == null) {	    parseFirstTime() ;	    if( (segments = getSegments()) == null )		return null ; // Last resort: fall back to superclass	}	Reply reply = null ;	try {	    // Obtain a command registry	    updateRegistry() ;	    vars = (Dictionary)		request.getState(STATE_VARIABLES) ;	    // Initialize the registry-dependent variables:	    vars = commReg.initVariables(this,request,vars) ;	    // Add our "very global" variables	    vars.put("secure",getValue(ATTR_SECURE,Boolean.TRUE)) ;	    vars.put("maxDepth",getValue(ATTR_MAX_DEPTH,new Integer(10))) ;	    vars.put("depth",depth) ;	    vars.put("registry",commReg) ;	    // Prepare the initial reply	    // (which represents the unparsed parts of the document)	    // and a prototype reply for segments that return null.	    reply = createDefaultReply(request,HTTP.OK) ; 	    Reply defSegReply = createDefaultReply(request,HTTP.OK) ;	    int unpSize = getUnparsedSize() ;	    if(unpSize == -1) 		reply.setHeaderValue(Reply.H_CONTENT_LENGTH,null) ;	    else 		reply.setContentLength(unpSize) ;	    defSegReply.setHeaderValue(Reply.H_CONTENT_LENGTH,null) ;	    long ims = request.getIfModifiedSince() ;	    long cmt = fresource.getFileStamp() ;		    // used to be getLastModified()	    // should be something better	    // than either	    if(SSIFrame.debug)		System.out.println("@@@@ IMS: "+cmt+" vs "+ims) ;	    if(ims != -1 && cmt != -1 && cmt <= ims) {		reply.setStatus(HTTP.NOT_MODIFIED) ;		defSegReply.setStatus(HTTP.NOT_MODIFIED) ;	    } else if(ims != -1) {		if(SSIFrame.debug)		    System.out.println("@@@@ Removed NOT MODIFIED") ;	    }	    	    if(cmt != -1)		defSegReply.setLastModified(cmt) ;	    	    // For each segment:	    // 	. obtain a reply,	    // 	. merge its headers with the global reply's headers,	    Reply[] partReps = new Reply[segments.length] ;	    for(int i=0;i<segments.length;i++) {		if(!segments[i].isUnparsed()) {		    if(SSIFrame.debug)			System.out.println("@@@@ Analyzing segment " +					   segments[i]) ;		    partReps[i] = segments[i].init(this, request, 						   vars, commReg, i);		    if(SSIFrame.debug) {			if (partReps[i] == null)			    System.out.println("@@@@ (null segment)") ;			System.out.println("@@@@ cacheReplies : "+					   cacheReplies());		    }		    		    merge(reply,			  partReps[i] != null ? partReps[i] : defSegReply) ;		}	    }	    // Set a stream, unless we're not supposed to.	    // Also handle the case of no command segments.	    switch(reply.getStatus()) {	    default:		reply.setStream		    (new SSIStream(cacheReplies(),				   segments,				   partReps,				   new RandomAccessFile(fresource.getFile(),							"r"))) ;	    case HTTP.NO_CONTENT:	    case HTTP.NOT_MODIFIED:	    }	    if(SSIFrame.debug)		System.out.println("@@@@ Last-modified: " +				   reply.getLastModified()) ;	    reply.setDate(System.currentTimeMillis()) ;	    return reply ;	} catch(SSIException ex) {	    reply = createDefaultReply(request,HTTP.INTERNAL_SERVER_ERROR) ;	    reply.setContent("SSIFrame is misconfigured: "+			     ex.getMessage());	    throw new HTTPException(reply) ;	} catch(Exception ex) {	    ex.printStackTrace() ;	    if(SSIFrame.debug) {		if(SSIFrame.debug)		    System.out.println("@@@@ Fallback to FileResource") ;	    }	    return null ;  // Last resort: fall back to superclass	}    }    // The headers to merge and their corresponding callbacks    // (more to come)    private static final int mergeHeaders[] =    {	Reply.H_AGE,	Reply.H_CONTENT_LENGTH,	Reply.H_EXPIRES,	Reply.H_LAST_MODIFIED,    } ;    private static final Merger mergers[] =    {	new IntMaximizer(),	// 	Reply.H_AGE	new IntAdder(),		// 	Reply.H_CONTENT_LENGTH	new DateMinimizer(),	// 	Reply.H_EXPIRES	new DateMaximizer()	// 	Reply.H_LAST_MODIFIED    } ;    /**     * Merges the headers (and status code) of a segment's reply with     * those of the global reply.     *     * @param glob the global reply     * @param part the segment's partial reply     */    private void merge(Reply glob, Reply part)    {	// Deal with status code first	int pstat = part.getStatus() ;	int gstat = glob.getStatus() ;		if(pstat == HTTP.NOT_MODIFIED) {	    switch(gstat) {	    default:		glob.setStatus(HTTP.OK) ;	    case HTTP.NOT_MODIFIED:	    }	} else if(gstat == HTTP.NOT_MODIFIED) {	    if(SSIFrame.debug)		System.out.println("**** removed NOT MODIFIED") ;	    glob.setStatus(HTTP.OK) ;	}		// Now handle headers	// "pointers to methods" would make this simpler	for(int i=0;i<mergeHeaders.length;i++) 	    glob.setHeaderValue(mergeHeaders[i],				mergers[i]				.merge(glob.getHeaderValue(mergeHeaders[i]),				       part.getHeaderValue(mergeHeaders[i]))) ;		// Now handle annoying quasi-headers:	int pint,gint ;	// Cache-Control: max-age=n	// (don't merge if set as attribute)	if( getMaxAge() != -1 &&	    (pint = part.getMaxAge()) != -1 ) {	    if( (gint = glob.getMaxAge()) != -1)		pint = Math.min(gint,pint) ;	    glob.setMaxAge(pint) ;	}    }    /**     * Retrieves the segments from the attribute     * @return An array of segments     */    private final Segment[] getSegments()    {	return (Segment[]) getValue(ATTR_SEGMENTS,null) ;    }	    /**     * Updates the working command registry if either the registryClass     * attribute has changed or it has never been created before.     * <p>To avoid unnecessarily creating command registry     * instances, this method will keep track of which kinds of command     * registries have been created, and avoid making duplicates.     * @exception SSIException If the operation can't be performed.     */    private void updateRegistry() 	throws SSIException     {	try {	    Class attrRegClass = (Class) getValue(ATTR_REGISTRY_CLASS, null) ;	    if(attrRegClass == null)		attrRegClass = Class.forName		    ("org.w3c.jigsaw.ssi.commands.DefaultCommandRegistry") ;	    	    if(regClass == null ||	       !attrRegClass.equals(regClass)) {		regClass = attrRegClass ;		commReg = fetchRegistry(regClass) ;	    }	} catch(ClassNotFoundException ex) {	    throw new SSIException("Cannot make registry: "+ex.getMessage()) ;	}    }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?