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

Jetty example source code file (Ajp13Parser.java)

This example Jetty source code file (Ajp13Parser.java) 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.

Java - Jetty tags/keywords

ajp13, ajp13, buffer, buffer, illegalstateexception, io, ioexception, ioexception, request, secret, servlet, state_end, state_end, state_start, view, word

The Jetty Ajp13Parser.java source code

//========================================================================
//Copyright 2006 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//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.mortbay.jetty.ajp;

import java.io.IOException;
import java.io.InterruptedIOException;

import javax.servlet.ServletInputStream;

import org.mortbay.io.Buffer;
import org.mortbay.io.BufferUtil;
import org.mortbay.io.Buffers;
import org.mortbay.io.EndPoint;
import org.mortbay.io.View;
import org.mortbay.jetty.EofException;
import org.mortbay.jetty.HttpTokens;
import org.mortbay.jetty.Parser;
import org.mortbay.log.Log;

/**
 * @author Markus Kobler
 */
public class Ajp13Parser implements Parser
{
    private final static int STATE_START = -1;
    private final static int STATE_END = 0;
    private final static int STATE_AJP13CHUNK_START = 1;
    private final static int STATE_AJP13CHUNK = 2;

    private int _state = STATE_START;
    private long _contentLength;
    private long _contentPosition;
    private int _chunkLength;
    private int _chunkPosition;
    private int _headers;
    private Buffers _buffers;
    private EndPoint _endp;
    private Buffer _buffer;
    private Buffer _header; // Buffer for header data (and small _content)
    private Buffer _body; // Buffer for large content
    private View _contentView = new View();
    private EventHandler _handler;
    private Ajp13Generator _generator;
    private View _tok0; // Saved token: header name, request method or response version
    private View _tok1; // Saved token: header value, request URI orresponse code
    protected int _length;
    protected int _packetLength;
    

    /* ------------------------------------------------------------------------------- */
    public Ajp13Parser(Buffers buffers, EndPoint endPoint, EventHandler handler, Ajp13Generator generator)
    {
        _buffers = buffers;
        _endp = endPoint;
        _handler = handler;
        _generator = generator;

    }

    /* ------------------------------------------------------------------------------- */
    public long getContentLength()
    {
        return _contentLength;
    }

    /* ------------------------------------------------------------------------------- */
    public int getState()
    {
        return _state;
    }

    /* ------------------------------------------------------------------------------- */
    public boolean inContentState()
    {
        return _state > 0;
    }

    /* ------------------------------------------------------------------------------- */
    public boolean inHeaderState()
    {
        return _state < 0;
    }

    /* ------------------------------------------------------------------------------- */
    public boolean isIdle()
    {
        return _state == STATE_START;
    }

    /* ------------------------------------------------------------------------------- */
    public boolean isComplete()
    {
        return _state == STATE_END;
    }

    /* ------------------------------------------------------------------------------- */
    public boolean isMoreInBuffer()
    {

        if (_header != null && _header.hasContent() || _body != null && _body.hasContent())
            return true;

        return false;
    }

    /* ------------------------------------------------------------------------------- */
    public boolean isState(int state)
    {
        return _state == state;
    }

    /* ------------------------------------------------------------------------------- */
    public void parse() throws IOException
    {
        if (_state == STATE_END)
            reset(false);
        if (_state != STATE_START)
            throw new IllegalStateException("!START");

        // continue parsing
        while (!isComplete())
        {
            parseNext();
        }
    }

    /* ------------------------------------------------------------------------------- */
    public long parseAvailable() throws IOException
    {
        long len = parseNext();
        long total = len > 0 ? len : 0;

        // continue parsing
        while (!isComplete() && _buffer != null && _buffer.length() > 0)
        {
            len = parseNext();
            if (len > 0)
                total += len;
            else
                break;
        }
        return total;
    }

    /* ------------------------------------------------------------------------------- */
    private int fill() throws IOException
    {
        int filled = -1;
        if (_body != null && _buffer != _body)
        {
            // mod_jk implementations may have some partial data from header
            // check if there are partial contents in the header
            // copy it to the body if there are any
            if(_header.length() > 0)
            {
                // copy the patial data from the header to the body
                _body.put(_header);
            }

            _buffer = _body;
            
            if (_buffer.length()>0)
            {            
                filled = _buffer.length();
                return filled;
            }
        }

        if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity())
            throw new IOException("FULL");
        if (_endp != null && filled <= 0)
        {
            // Compress buffer if handling _content buffer
            // TODO check this is not moving data too much
            if (_buffer == _body)
                _buffer.compact();

            if (_buffer.space() == 0)
                throw new IOException("FULL");

            try
            {
                filled = _endp.fill(_buffer);
            }
            catch (IOException e)
            {
                // This is normal in AJP since the socket closes on timeout only
                Log.debug(e);
                reset(true);
                throw (e instanceof EofException) ? e : new EofException(e);
            }
        }
        
        if (filled < 0)
        {
            if (_state > STATE_END)
            {
                _state = STATE_END;
                _handler.messageComplete(_contentPosition);
                return filled;
            }
            reset(true);
            throw new EofException();
        }
    
        return filled;
    }
    
    /* ------------------------------------------------------------------------------- */
    public long parseNext() throws IOException
    {
        long total_filled = -1;

        if (_buffer == null)
        {
            if (_header == null)
            {
                _header = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
                _header.clear();
            }
            _buffer = _header;
            _tok0 = new View(_header);
            _tok1 = new View(_header);
            _tok0.setPutIndex(_tok0.getIndex());
            _tok1.setPutIndex(_tok1.getIndex());
        }

        if (_state == STATE_END)
            throw new IllegalStateException("STATE_END");
        if (_state > STATE_END && _contentPosition == _contentLength)
        {
            _state = STATE_END;
            _handler.messageComplete(_contentPosition);
            return total_filled;
        }
        
        if (_state < 0)
        {
            // have we seen a packet?
            if (_packetLength<=0)
            {
                if (_buffer.length()<4)
                {
                    if (total_filled<0) 
                        total_filled=0;
                    total_filled+=fill();
                    if (_buffer.length()<4)
                        return total_filled;
                }
                
                _contentLength = HttpTokens.UNKNOWN_CONTENT;
                int _magic = Ajp13RequestPacket.getInt(_buffer);
                if (_magic != Ajp13RequestHeaders.MAGIC)
                    throw new IOException("Bad AJP13 rcv packet: " + "0x" + Integer.toHexString(_magic) + " expected " + "0x" + Integer.toHexString(Ajp13RequestHeaders.MAGIC) + " " + this);


                _packetLength = Ajp13RequestPacket.getInt(_buffer);
                if (_packetLength > Ajp13Packet.MAX_PACKET_SIZE)
                    throw new IOException("AJP13 packet (" + _packetLength + "bytes) too large for buffer");
                
            }
            
            if (_buffer.length() < _packetLength)
            {
                if (total_filled<0) 
                    total_filled=0;
                total_filled+=fill();
                if (_buffer.length() < _packetLength)
                    return total_filled;
            }

            // Parse Header
            Buffer bufHeaderName = null;
            Buffer bufHeaderValue = null;
            int attr_type = 0;

            byte packetType = Ajp13RequestPacket.getByte(_buffer);

            switch (packetType)
            {
                case Ajp13Packet.FORWARD_REQUEST_ORDINAL:
                    _handler.startForwardRequest();
                    break;
                case Ajp13Packet.CPING_REQUEST_ORDINAL:
                    ((Ajp13Generator) _generator).sendCPong();
                    
                    if(_header != null)
                    {
                        _buffers.returnBuffer(_header);
                        _header = null;
                    }

                    if(_body != null)
                    {
                        _buffers.returnBuffer(_body);
                        _body = null;
                    }

                    _buffer= null;

                    reset(true);

                    return -1;
                case Ajp13Packet.SHUTDOWN_ORDINAL:
                    shutdownRequest();

                    return -1;

                default:
                    // XXX Throw an Exception here?? Close
                    // connection!
                    Log.warn("AJP13 message type ({PING}: "+packetType+" ) not supported/recognized as an AJP request");
                throw new IllegalStateException("PING is not implemented");
            }


            _handler.parsedMethod(Ajp13RequestPacket.getMethod(_buffer));
            _handler.parsedProtocol(Ajp13RequestPacket.getString(_buffer, _tok0));
            _handler.parsedUri(Ajp13RequestPacket.getString(_buffer, _tok1));
            _handler.parsedRemoteAddr(Ajp13RequestPacket.getString(_buffer, _tok1));
            _handler.parsedRemoteHost(Ajp13RequestPacket.getString(_buffer, _tok1));
            _handler.parsedServerName(Ajp13RequestPacket.getString(_buffer, _tok1));
            _handler.parsedServerPort(Ajp13RequestPacket.getInt(_buffer));
            _handler.parsedSslSecure(Ajp13RequestPacket.getBool(_buffer));


            _headers = Ajp13RequestPacket.getInt(_buffer);

            for (int h=0;h<_headers;h++)
            {
                bufHeaderName = Ajp13RequestPacket.getHeaderName(_buffer, _tok0);
                bufHeaderValue = Ajp13RequestPacket.getString(_buffer, _tok1);

                if (bufHeaderName != null && bufHeaderName.toString().equals(Ajp13RequestHeaders.CONTENT_LENGTH))
                {
                    _contentLength = BufferUtil.toLong(bufHeaderValue);
                    if (_contentLength == 0)
                        _contentLength = HttpTokens.NO_CONTENT;
                }

                _handler.parsedHeader(bufHeaderName, bufHeaderValue);
            }



            attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
            while (attr_type != 0xFF)
            {

                switch (attr_type)
                {
                    // XXX How does this plug into the web
                    // containers
                    // authentication?

                    case Ajp13RequestHeaders.REMOTE_USER_ATTR:
                        _handler.parsedRemoteUser(Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;
                    case Ajp13RequestHeaders.AUTH_TYPE_ATTR:
                        _handler.parsedAuthorizationType(Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;

                    case Ajp13RequestHeaders.QUERY_STRING_ATTR:
                        _handler.parsedQueryString(Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;

                    case Ajp13RequestHeaders.JVM_ROUTE_ATTR:
                        // XXX Using old Jetty 5 key,
                        // should change!
                        // Note used in
                        // org.mortbay.jetty.servlet.HashSessionIdManager
                        _handler.parsedRequestAttribute("org.mortbay.http.ajp.JVMRoute", Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;

                    case Ajp13RequestHeaders.SSL_CERT_ATTR:
                        _handler.parsedSslCert(Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;

                    case Ajp13RequestHeaders.SSL_CIPHER_ATTR:
                        _handler.parsedSslCipher(Ajp13RequestPacket.getString(_buffer, _tok1));
                        // SslSocketConnector.customize()
                        break;

                    case Ajp13RequestHeaders.SSL_SESSION_ATTR:
                        _handler.parsedSslSession(Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;

                    case Ajp13RequestHeaders.REQUEST_ATTR:
                        _handler.parsedRequestAttribute(Ajp13RequestPacket.getString(_buffer, _tok0).toString(), Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;

                        // New Jk API?
                        // Check if experimental or can they
                        // assumed to be
                        // supported
                        
                    case Ajp13RequestHeaders.SSL_KEYSIZE_ATTR:
                        
                        // This has been implemented as either a string or a integer.
                        
                        // Does it look like a string containing digits?
                        int length = Ajp13RequestPacket.getInt(_buffer);
                        
                        if (length>0 && length<16)
                        {
                            // this must be a string length rather than a key length
                            _buffer.skip(-2);
                            _handler.parsedRequestAttribute("javax.servlet.request.key_size", Ajp13RequestPacket.getString(_buffer, _tok1));
                        }
                        else
                            _handler.parsedRequestAttribute("javax.servlet.request.key_size",length);
                        
                        break;

                        
                        // Used to lock down jk requests with a
                        // secreate
                        // key.
                        
                    case Ajp13RequestHeaders.SECRET_ATTR:
                        // XXX Investigate safest way to
                        // deal with
                        // this...
                        // should this tie into shutdown
                        // packet?
                        break;

                    case Ajp13RequestHeaders.STORED_METHOD_ATTR:
                        // XXX Confirm this should
                        // really overide
                        // previously parsed method?
                        // _handler.parsedMethod(Ajp13PacketMethods.CACHE.get(Ajp13RequestPacket.getString()));
                        break;


                    case Ajp13RequestHeaders.CONTEXT_ATTR:
                        _handler.parsedContextPath(Ajp13RequestPacket.getString(_buffer, _tok1));
                        break;
                    case Ajp13RequestHeaders.SERVLET_PATH_ATTR:
                        _handler.parsedServletPath(Ajp13RequestPacket.getString(_buffer, _tok1));

                        break;
                    default:
                        Log.warn("Unsupported Ajp13 Request Attribute {}", new Integer(attr_type));
                    break;
                }

                attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
            }






            _contentPosition = 0;
            switch ((int) _contentLength)
            {

                case HttpTokens.NO_CONTENT:
                    _state = STATE_END;
                    _handler.headerComplete();
                    _handler.messageComplete(_contentPosition);

                    break;

                case HttpTokens.UNKNOWN_CONTENT:

                    _generator.getBodyChunk();
                    if (_buffers != null && _body == null && _buffer == _header && _header.length() <= 0)
                    {
                        _body = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
                        _body.clear();
                    }
                    _state = STATE_AJP13CHUNK_START;
                    _handler.headerComplete(); // May recurse here!

                    return total_filled;

                default:

                    if (_buffers != null && _body == null && _buffer == _header && _contentLength > (_header.capacity() - _header.getIndex()))
                    {
                        _body = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
                        _body.clear();

                    }
                _state = STATE_AJP13CHUNK_START;
                _handler.headerComplete(); // May recurse here!
                return total_filled;
            }
        }


        Buffer chunk;

        while (_state>STATE_END)
        {
            switch (_state)
            {
                case STATE_AJP13CHUNK_START:
                    if (_buffer.length()<6)
                    {
                        if (total_filled<0) 
                            total_filled=0;
                        total_filled+=fill();
                        if (_buffer.length()<6)
                            return total_filled;
                    }
                    int _magic=Ajp13RequestPacket.getInt(_buffer);
                    if (_magic!=Ajp13RequestHeaders.MAGIC)
                    {
                        throw new IOException("Bad AJP13 rcv packet: "+"0x"+Integer.toHexString(_magic)+" expected "+"0x"
                                +Integer.toHexString(Ajp13RequestHeaders.MAGIC)+" "+this);
                    }
                    _chunkPosition=0;
                    _chunkLength=Ajp13RequestPacket.getInt(_buffer)-2;
                    Ajp13RequestPacket.getInt(_buffer);
                    if (_chunkLength==0)
                    {
                        _state=STATE_END;
                         _generator.gotBody();
                        _handler.messageComplete(_contentPosition);
                        return total_filled;
                    }
                    _state=STATE_AJP13CHUNK;

                case STATE_AJP13CHUNK:
                    if (_buffer.length()<_chunkLength)
                    {
                        if (total_filled<0) 
                            total_filled=0;
                        total_filled+=fill();
                        if (_buffer.length()<_chunkLength)
                            return total_filled;
                    }

                    int remaining=_chunkLength-_chunkPosition;

                    if (remaining==0)
                    {
                        _state=STATE_AJP13CHUNK_START;
                        if (_contentPosition<_contentLength)
                        {
                            _generator.getBodyChunk();
                        }
                        else
                        {
                            _generator.gotBody();
                        }

                        return total_filled;
                    }

                    if (_buffer.length()<remaining)
                    {
                        remaining=_buffer.length();
                    }

                    chunk=Ajp13RequestPacket.get(_buffer,(int)remaining);
                    _contentPosition+=chunk.length();
                    _chunkPosition+=chunk.length();
                    _contentView.update(chunk);

                    remaining=_chunkLength-_chunkPosition;

                    if (remaining==0)
                    {
                        _state=STATE_AJP13CHUNK_START;
                        if (_contentPosition<_contentLength || _contentLength == HttpTokens.UNKNOWN_CONTENT)
                        {
                            _generator.getBodyChunk();
                        }
                        else
                        {
                            _generator.gotBody();
                        }
                    }

                    _handler.content(chunk);

                return total_filled;

            default:
                throw new IllegalStateException("Invalid Content State");

            }

        }

        return total_filled;
    }

    /* ------------------------------------------------------------------------------- */
    public void reset(boolean returnBuffers)
    {
        _state = STATE_START;
        _contentLength = HttpTokens.UNKNOWN_CONTENT;
        _contentPosition = 0;
        _length = 0;
        _packetLength = 0;

        if (_body != null)
        {
            if (_body.hasContent())
            {
                _header.setMarkIndex(-1);
                _header.compact();
                // TODO if pipelined requests received after big
                // input - maybe this is not good?.
                _body.skip(_header.put(_body));

            }

            if (_body.length() == 0)
            {
                if (_buffers != null && returnBuffers)
                    _buffers.returnBuffer(_body);
                _body = null;
            }
            else
            {
                _body.setMarkIndex(-1);
                _body.compact();
            }
        }

        if (_header != null)
        {
            _header.setMarkIndex(-1);
            if (!_header.hasContent() && _buffers != null && returnBuffers)
            {
                _buffers.returnBuffer(_header);
                _header = null;
                _buffer = null;
            }
            else
            {
                _header.compact();
                _tok0.update(_header);
                _tok0.update(0, 0);
                _tok1.update(_header);
                _tok1.update(0, 0);
            }
        }

        _buffer = _header;
    }

    /* ------------------------------------------------------------------------------- */
    Buffer getHeaderBuffer()
    {
        return _buffer;
    }

    private void shutdownRequest()
    {
        _state = STATE_END;

        if(!Ajp13SocketConnector.__allowShutdown)
        {
            Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
            return;
        }

        if(Ajp13SocketConnector.__secretWord != null)
        {
            Log.warn("AJP13: Validating Secret Word");
            try
            {
                String secretWord = Ajp13RequestPacket.getString(_buffer, _tok1).toString();

                if(!Ajp13SocketConnector.__secretWord.equals(secretWord))
                {
                    Log.warn("AJP13: Shutdown Request Denied, Invalid Sercret word!!!");
                    throw new IllegalStateException("AJP13: Secret Word is Invalid: Peer has requested shutdown but, Secret Word did not match");
                }
            }
            catch (Exception e)
            {
                Log.warn("AJP13: Secret Word is Required!!!");
                Log.debug(e);
                throw new IllegalStateException("AJP13: Secret Word is Required: Peer has requested shutdown but, has not provided a Secret Word");
            }


            Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
            return;
        }

        Log.warn("AJP13: Peer Has Requested for Shutdown!!!");
        Log.warn("AJP13: Jetty 6 is shutting down !!!");
        System.exit(0);
    }

    /* ------------------------------------------------------------------------------- */
    public interface EventHandler
    {

        // public void shutdownRequest() throws IOException;
        // public void cpingRequest() throws IOException;

        public void content(Buffer ref) throws IOException;

        public void headerComplete() throws IOException;

        public void messageComplete(long contextLength) throws IOException;

        public void parsedHeader(Buffer name, Buffer value) throws IOException;

        public void parsedMethod(Buffer method) throws IOException;

        public void parsedProtocol(Buffer protocol) throws IOException;

        public void parsedQueryString(Buffer value) throws IOException;

        public void parsedRemoteAddr(Buffer addr) throws IOException;

        public void parsedRemoteHost(Buffer host) throws IOException;

        public void parsedRequestAttribute(String key, Buffer value) throws IOException;
        
        public void parsedRequestAttribute(String key, int value) throws IOException;

        public void parsedServerName(Buffer name) throws IOException;

        public void parsedServerPort(int port) throws IOException;

        public void parsedSslSecure(boolean secure) throws IOException;

        public void parsedUri(Buffer uri) throws IOException;

        public void startForwardRequest() throws IOException;

        public void parsedAuthorizationType(Buffer authType) throws IOException;
        
        public void parsedRemoteUser(Buffer remoteUser) throws IOException;

        public void parsedServletPath(Buffer servletPath) throws IOException;
        
        public void parsedContextPath(Buffer context) throws IOException;

        public void parsedSslCert(Buffer sslCert) throws IOException;

        public void parsedSslCipher(Buffer sslCipher) throws IOException;

        public void parsedSslSession(Buffer sslSession) throws IOException;







    }

    /* ------------------------------------------------------------ */
    /**
     * TODO Make this common with HttpParser
     * 
     */
    public static class Input extends ServletInputStream
    {
        private Ajp13Parser _parser;
        private EndPoint _endp;
        private long _maxIdleTime;
        private View _content;

        /* ------------------------------------------------------------ */
        public Input(Ajp13Parser parser, long maxIdleTime)
        {
            _parser = parser;
            _endp = parser._endp;
            _maxIdleTime = maxIdleTime;
            _content = _parser._contentView;
        }

        /* ------------------------------------------------------------ */
        public int read() throws IOException
        {
            int c = -1;
            if (blockForContent())
                c = 0xff & _content.get();
            return c;
        }

        /* ------------------------------------------------------------ */
        /*
         * @see java.io.InputStream#read(byte[], int, int)
         */
        public int read(byte[] b, int off, int len) throws IOException
        {
            int l = -1;
            if (blockForContent())
                l = _content.get(b, off, len);
            return l;
        }

        /* ------------------------------------------------------------ */
        private boolean blockForContent() throws IOException
        {
            if (_content.length() > 0)
                return true;
            if (_parser.isState(Ajp13Parser.STATE_END))
                return false;

            // Handle simple end points.
            if (_endp == null)
                _parser.parseNext();

            // Handle blocking end points
            else if (_endp.isBlocking())
            {
                _parser.parseNext();
                
                // parse until some progress is made (or IOException thrown for timeout)
                while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
                {
                    // Try to get more _parser._content
                    _parser.parseNext();
                }
            }
            else // Handle non-blocking end point
            {
                long filled = _parser.parseNext();
                boolean blocked = false;

                // parse until some progress is made (or
                // IOException thrown for timeout)
                while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
                {
                    // if fill called, but no bytes read,
                    // then block
                    if (filled > 0)
                        blocked = false;
                    else if (filled == 0)
                    {
                        if (blocked)
                            throw new InterruptedIOException("timeout");

                        blocked = true;
                        _endp.blockReadable(_maxIdleTime);
                    }

                    // Try to get more _parser._content
                    filled = _parser.parseNext();
                }
            }

            return _content.length() > 0;
        }

    }
}

Other Jetty examples (source code examples)

Here is a short list of links related to this Jetty Ajp13Parser.java source code file:

... 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.