alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/server/Ajp13Interceptor.java,v 1.24 2004/02/25 07:18:35 billbarker Exp $
 * $Revision: 1.24 $
 * $Date: 2004/02/25 07:18:35 $
 *
 *   
 *  Copyright 1999-2004 The Apache Sofware Foundation.
 *
 *  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.apache.tomcat.modules.server;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Properties;

import org.apache.tomcat.core.ContextManager;
import org.apache.tomcat.core.Request;
import org.apache.tomcat.core.Response;
import org.apache.tomcat.core.TomcatException;
import org.apache.tomcat.util.io.FileUtil;
import org.apache.tomcat.util.log.Log;
import org.apache.tomcat.util.net.TcpConnection;
import org.apache.tomcat.util.net.TcpConnectionHandler;

/* Frozen, bug fixes only: all active development goes in
     jakarta-tomcat-connectors/jk/org/apache/ajp/Ajp14*
*/


public class Ajp13Interceptor extends PoolTcpConnector
    implements  TcpConnectionHandler
{
    private boolean tomcatAuthentication=true;
    private boolean shutdownEnable=false;
    // true if the incloming uri is encoded.
    private boolean decoded=true;

    private int decodedNote;
    private String secret=null;
    private File ajpidFile=null;
    private boolean authenticateConnection=false;
    
    public Ajp13Interceptor()
    {
        super();
 	super.setSoLinger( 100 );
	super.setTcpNoDelay( true );
    }

    // -------------------- PoolTcpConnector --------------------

    /** Enable shutdown command. By default it is disabled, since
     *	ajp12 has an improved version with password checking.
     *
     *	In future we'll enable shutdown in ajp13/14 and deprecate ajp12,
     *	and merge various improvements from ajp12.
     *
     *  Note that this you can use ajp13 for communication with the server
     *	and ajp12 only for shutdown - that would allow some extra flexibility,
     *	especially if you use firewall rules.
    */
    public void setShutdownEnable(boolean b ) {
	shutdownEnable=b;
    }

    /** Legacy version
     */
    public void setShutDownEnable(boolean b ) {
	shutdownEnable=b;
    }

    /** Enable the use of a secret. The secret will be
     *  randomly generated. mod_jk must read the secret to
     *  communicate with tomcat. 
     *
     *  Note that we don't use the secret only for shutdown, but
     *  for normal request processing. A 'bad' request may forge
     *  auth, etc.
     */
    public void setUseSecret(boolean b ) {
	secret=Double.toString(Math.random());
        shutdownEnable=true;
    }

    /** Set the 'secret'. If this is set, all sensitive operations
     *   will be disabled unless the request includes a password.
     *
     *  This requires a recent version of mod_jk and the
     *    worker.NAME.secret property in workers.properties.
     */
    public void setSecret( String s ) {
        secret=s;
        shutdownEnable=true;
    }

    /** Specify ajpid file used when shutting down tomcat
     */
    public void setAjpidFile( String path ) {
        ajpidFile=( path==null?null:new File(path));
    }

    /** Specify if Ajp13 requests must be authenticated
     */
    public void setAuthenticateConnection( boolean b ) {
        authenticateConnection=b;
    }

    public void setDecodedUri( boolean b ) {
	decoded=b;
    }
    
    protected void localInit() throws Exception {
	ep.setConnectionHandler( this );
    }

    public void engineInit( ContextManager cm )
	throws TomcatException
    {
	super.engineInit( cm );
	decodedNote=cm.getNoteId(ContextManager.REQUEST_NOTE,
				  "req.decoded" );
        String ajpid13 = cm.getProperty("ajpid13");
        if( ajpid13 != null ) {
            if( ajpidFile != null ) {
                log( "Overriding ajpidFile with " + ajpid13 );
            }
            ajpidFile = new File(ajpid13);
        }
    }

    public void engineState(ContextManager cm, int state )
	throws TomcatException
    {

        if( state==ContextManager.STATE_START ) {
            // the engine is now started, create the ajp13.id
            // file that will allow us to stop the server and
            // know that the server is started ok.
            Ajp13Interceptor tcpCon=this;
            int portInt=tcpCon.getPort();
            InetAddress address=tcpCon.getAddress();
            File sf=FileUtil.getConfigFile(ajpidFile, new File(cm.getHome()),
                                           "conf/ajp13.id");
            Properties props=new Properties();
            
            if( ajpidFile != null || debug > 0)
                log( "Using stop file: "+sf);
            try {
                //  PrintWriter stopF=new PrintWriter
                //                     (new FileWriter(sf));
                FileOutputStream stopF=new FileOutputStream( sf );
                props.put( "port", Integer.toString( portInt ));
                // stopF.println( portInt );
                if( address==null ) {
                    // stopF.println( "" );
                } else {
                    //stopF.println( address.getHostAddress() );
                    props.put( "address", address.getHostAddress() );
                }
                if( secret !=null ) {
                    //stopF.println( secret );
                    props.put( "secret", secret );
                } else {
                    // stopF.println();
                }
                if( shutdownEnable )
                    props.put( "shutdown", "enabled" );
                //            stopF.close();
                props.save( stopF, "Automatically generated, don't edit" );
            } catch( IOException ex ) {
                log( "Can't create stop file: "+sf, ex );
            }
        }

    }

    
    // -------------------- Handler implementation --------------------
    
    public Object[] init()
    {
        Object thData[]=new Object[3];
        Ajp13Request req=new Ajp13Request();
        Ajp13Response res=new Ajp13Response();
        Ajp13 con=new Ajp13();
        con.setDebug(debug);
        req.setDebug(debug);
        con.setTomcatAuthentication(isTomcatAuthentication());
        cm.initRequest(req, res);
        thData[0]=req;
        thData[1]=res;
        thData[2]=con;

        return  thData;
    }

    // XXX
    //    Nothing overriden, right now AJPRequest implment AJP and read
    // everything.
    //    "Shortcuts" to be added here ( Vhost and context set by Apache, etc)
    // XXX handleEndpoint( Endpoint x )
    public void processConnection(TcpConnection connection, Object thData[])
    {
        try {
            if(connection == null) {
                return;
            }
            Socket socket = connection.getSocket();
            if(socket == null) {
                return;
            }

            Ajp13 con=null;
            Ajp13Request req=null;
            Ajp13Response res=null;

            if(thData != null) {
                req = (Ajp13Request)thData[0];
                res = (Ajp13Response)thData[1];
                con = (Ajp13)thData[2];
                if(req != null) req.recycle();
                if(res != null) res.recycle();
                if(con != null) con.recycle();
            }

            if(req == null || res == null || con == null) {
                req = new Ajp13Request();
                res = new Ajp13Response();
                con = new Ajp13();
                con.setTomcatAuthentication(isTomcatAuthentication());
                cm.initRequest( req, res );
            }
	    // XXX
	    req.ajp13=con;
	    res.ajp13=con;

            con.setSocket(socket);

            boolean moreRequests = true;
            boolean authenticated = false;
            // If we are not configured with a secret or we are
            // not authenticating the connection, assume
            // we trust the remote party ( as we did before )
            if( secret == null || !authenticateConnection )
                authenticated=true;
            
            while(moreRequests) {
		int status=req.receiveNextRequest();

                if( !authenticated ) {
                    // we need to authenticate - the user set a
                    // secret and expects the web server to send it
                    String conSecret=con.getSecret();
                    if(  conSecret == null ) {
                        log("Unauthenticated server");
                        break;
                    }
                    if( ! secret.equals( conSecret )) {
                        log("Bad server secret");
                        break;
                    }
                    // allow further requests without checking
                    authenticated=true;
                }
                
		if( status==-2) {
                    // check secret if set
                    if( secret != null && ! secret.equals(con.getSecret())) {
                        log("Shutdown command ignored. Secret didn't match.");
                        continue;
                    }
		    // special case - shutdown
		    // XXX need better communication, refactor it
		    if( !doShutdown(con,
                                    socket.getLocalAddress(),
				    socket.getInetAddress())) {
			moreRequests = false;
			continue;
		    }
		}

		// special case - low level AJP13 packet, like
		// PING/PONG ...
		if( status == 999 )
		{	
			req.recycle();
			continue; 
		} 

		// special case - invalid AJP13 packet, error 
		// decoding packet ...
		// we drop the connection rigth now
		if( status != 200 )
		    break;

		if( decoded )
		    req.setNote( decodedNote, this );
		
		cm.service(req, res);

		req.recycle();
		res.recycle();
            }
            log("Closing connection", Log.DEBUG);
            con.close();
	    socket.close();
        } catch (Exception e) {
	    log("Processing connection " + connection, e);
        }
    }

    public void setServer(Object contextM)
    {
        this.cm=(ContextManager)contextM;
    }

    protected boolean doShutdown(Ajp13 con,
                                 InetAddress serverAddr,
                                 InetAddress clientAddr)
    {
        try {
            // continue with the other checks. XXX We may allow shutdown
            // with the right secret from a different address.
	    // close the socket connection before handling any signal
	    // but get the addresses first so they are not corrupted
            if(shutdownEnable && Ajp12.isSameAddress(serverAddr, clientAddr)) {
		cm.shutdown();
                log( "Exiting" );
		// same behavior as in past, because it seems that
		// stopping everything doesn't work - need to figure
		// out what happens with the threads ( XXX )
		System.exit(0);
	    }
	} catch(Exception ignored) {
	    log("Ignored " + ignored);
	}
	log("Shutdown command ignored");
	return false;
    }

    public boolean isTomcatAuthentication() {
        return tomcatAuthentication;
    }

    public void setTomcatAuthentication(boolean newTomcatAuthentication) {
        tomcatAuthentication = newTomcatAuthentication;
    }

}

class Ajp13Request extends Request 
{
    Ajp13 ajp13 = new Ajp13();
    int   dL    = 0;
 
    public Ajp13Request() 
    {
        super();
    }
    
    public void setDebug(int level) {
        dL = level;
    }

    protected int receiveNextRequest() throws IOException 
    {
	return ajp13.receiveNextRequest( this );
    }
    
    public int doRead() throws IOException 
    {
	if( contentLength == -1 ) {
	    return ajp13.doRead();
	}
	if( available <= 0 )
	    return -1;
	available--;
	return ajp13.doRead();
    }
    
    public int doRead(byte[] b, int off, int len) throws IOException 
    {
	int rd=-1;
	if( contentLength == -1 ) {
	    rd=ajp13.doRead(b,off,len);
	    return rd;
	}
	if( available <= 0 )
	    return -1;
	rd=ajp13.doRead( b,off, len );
	available -= rd;
	if( dL > 0 ) d("Read: " + new String( b,off, len ));
	return rd;
    }
    
    public void recycle() 
    {
        super.recycle();
	if( ajp13!=null) ajp13.recycle();
    }

    private void d(String s ) {
	System.err.println( "Ajp13Request: " + s );
    }
}

class Ajp13Response extends Response 
{
    Ajp13 ajp13;
    boolean finished=false;
    
    public Ajp13Response() 
    {
	super();
    }

    public void recycle() {
	super.recycle();
	finished=false;
    }

    public void setSocket( Socket s ) {
	ajp13=((Ajp13Request)request).ajp13;
    }

    // XXX if more headers that MAX_SIZE, send 2 packets!   
    public void endHeaders() throws IOException 
    {
        super.endHeaders();
    
        if (request.protocol().isNull()) {
            return;
        }

	ajp13.sendHeaders(getStatus(), getMimeHeaders());
    } 
         
    public void finish() throws IOException 
    {
	if(!finished) {
	    super.finish();
		finished = true; // Avoid END_OF_RESPONSE sent 2 times
	    ajp13.finish();
	}
    }
    
    public void doWrite(  byte b[], int off, int len) throws IOException 
    {
	ajp13.doWrite(b, off, len );
    }
    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.