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><!--#commandName param1=val1 * ... paramn=valn --></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 + -
显示快捷键?