org.eclipse.jetty.ajp.Ajp13Parser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testatoo-container-jetty-full Show documentation
Show all versions of testatoo-container-jetty-full Show documentation
Testatoo Jetty Container with JSP support
// ========================================================================
// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.ajp;
import java.io.IOException;
import java.io.InterruptedIOException;
import javax.servlet.ServletInputStream;
import org.eclipse.jetty.http.HttpTokens;
import org.eclipse.jetty.http.Parser;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.BufferUtil;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.io.View;
import org.eclipse.jetty.util.log.Log;
/**
*
*/
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_START_AFTER_LENGTH = 2;
private final static int STATE_AJP13CHUNK = 3;
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)
{
_buffers = buffers;
_endp = endPoint;
}
/* ------------------------------------------------------------------------------- */
public void setEventHandler(EventHandler handler)
{
_handler=handler;
}
/* ------------------------------------------------------------------------------- */
public void setGenerator(Ajp13Generator generator)
{
_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.getHeader();
_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:
(_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:
//XXX JASPI how does this make sense?
_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.eclipse.jetty.servlet.HashSessionIdManager
_handler.parsedRequestAttribute("org.eclipse.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 in AJP13 as either a string or a integer.
// Servlet specs say javax.servlet.request.key_size must be an 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.parsedSslKeySize(Integer.parseInt(Ajp13RequestPacket.getString(_buffer, _tok1).toString()));
}
else
_handler.parsedSslKeySize(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();
_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();
_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()<4)
{
if (total_filled<0)
total_filled=0;
total_filled+=fill();
if (_buffer.length()<4)
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;
int rawChunkLength=Ajp13RequestPacket.getInt(_buffer);
_chunkLength = rawChunkLength - 2;
if (rawChunkLength==0 || _chunkLength==0)
{
_state=STATE_END;
_generator.gotBody();
_handler.messageComplete(_contentPosition);
return total_filled;
}
_state=STATE_AJP13CHUNK_START_AFTER_LENGTH;
case STATE_AJP13CHUNK_START_AFTER_LENGTH:
if (_buffer.length() < 2)
{
if (total_filled < 0)
total_filled = 0;
total_filled += fill();
if (_buffer.length() < 2)
return total_filled;
}
Ajp13RequestPacket.getInt(_buffer);
_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() 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;
}
}
}