You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/12/18 23:16:37 UTC
cvs commit: jakarta-tomcat-connectors/jk/java/org/apache/jk Ajp13.java Ajp13Packet.java AjpHandler.java NegociationHandler.java RequestHandler.java
costin 01/12/18 14:16:37
Added: jk/java/org/apache/jk Ajp13.java Ajp13Packet.java
AjpHandler.java NegociationHandler.java
RequestHandler.java
Log:
Adding the files from org.apache.ajp.
This is the same code, as startup point for jk2 and refactoring.
Revision Changes Path
1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/Ajp13.java
Index: Ajp13.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.jk;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.net.Socket;
import java.util.Enumeration;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.HttpMessages;
import org.apache.tomcat.util.http.BaseRequest;
/**
* Represents a single, persistent connection between the web server and
* the servlet container. Uses the Apache JServ Protocol version 1.3 for
* communication. Because this protocal does not multiplex requests, this
* connection can only be associated with a single request-handling cycle
* at a time.<P>
*
* This class contains knowledge about how an individual packet is laid out
* (via the <CODE>Ajp13Packet</CODE> class), and also about the
* stages of communicaton between the server and the servlet container. It
* translates from Tomcat's internal servlet support methods
* (e.g. doWrite) to the correct packets to send to the web server.
*
* @see Ajp13Interceptor
*
* @author Dan Milstein [danmil@shore.net]
* @author Keith Wannamaker [Keith@Wannamaker.org]
* @author Kevin Seguin [seguin@apache.org]
* @author Henri Gomez [hgomez@slib.fr]
* @author Costin Manolache
*/
public class Ajp13 {
public static final int MAX_PACKET_SIZE=8192;
public static final int H_SIZE=4; // Size of basic packet header
public static final int MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
public static final int MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE - 4;
// Prefix codes for message types from server to container
public static final byte JK_AJP13_SHUTDOWN = 7;
// Error code for Ajp13
public static final int JK_AJP13_BAD_HEADER = -100;
public static final int JK_AJP13_NO_HEADER = -101;
public static final int JK_AJP13_COMM_CLOSED = -102;
public static final int JK_AJP13_COMM_BROKEN = -103;
public static final int JK_AJP13_BAD_BODY = -104;
public static final int JK_AJP13_INCOMPLETE_BODY = -105;
// ============ Instance Properties ====================
OutputStream out;
InputStream in;
// Buffer used of output body and headers
public Ajp13Packet outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
// Buffer used for input body
Ajp13Packet inBuf = new Ajp13Packet( MAX_PACKET_SIZE );
// Buffer used for request head ( and headers )
Ajp13Packet hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
// Holds incoming reads of request body data (*not* header data)
byte []bodyBuff = new byte[MAX_READ_SIZE];
int blen; // Length of current chunk of body data in buffer
int pos; // Current read position within that buffer
boolean end_of_stream; // true if we've received an empty packet
// Required handler - essential request processing
public RequestHandler reqHandler;
// AJP14 - detect protocol,set communication parameters, login
// If no password is set, use only Ajp13 messaging
boolean backwardCompat=true;
boolean logged=false;
public Ajp13() {
super();
initBuf();
reqHandler=new RequestHandler();
reqHandler.init( this );
}
public Ajp13(RequestHandler reqHandler ) {
super();
initBuf();
this.reqHandler=reqHandler;
reqHandler.init( this );
}
/** Will be overriden
*/
public void initBuf() {
outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
inBuf = new Ajp13Packet( MAX_PACKET_SIZE );
hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
}
public void recycle() {
if (debug > 0) {
logger.log("recycle()");
}
// This is a touch cargo-cultish, but I think wise.
blen = 0;
pos = 0;
end_of_stream = false;
logged=false;
}
/**
* Associate an open socket with this instance.
*/
public void setSocket( Socket socket ) throws IOException {
if (debug > 0) {
logger.log("setSocket()");
}
socket.setSoLinger( true, 100);
out = socket.getOutputStream();
in = socket.getInputStream();
pos = 0;
}
/**
* Backward compat mode, no login needed
*/
public void setBackward(boolean b)
{
backwardCompat=b;
}
public boolean isLogged() {
return logged;
}
void setLogged( boolean b ) {
logged=b;
}
// -------------------- Handlers registry --------------------
static final int MAX_HANDLERS=32;
static final int RESERVED=16; // reserved names, backward compat
// Note that we don't make distinction between in and out
// messages ( i.e. one id is used only in one direction )
AjpHandler handlers[]=new AjpHandler[MAX_HANDLERS];
String handlerName[]=new String[MAX_HANDLERS];
int currentId=RESERVED;
public int registerMessageType( int id, String name, AjpHandler h,
String sig[] )
{
if( id < 0 ) {
// try to find it by name
for( int i=0; i< handlerName.length; i++ )
if( name.equals( handlerName[i] ) ) return i;
handlerName[currentId]=name;
handlers[currentId]=h;
currentId++;
return currentId;
}
// fixed id
handlerName[id]=name;
handlers[id]=h;
return id;
}
// -------------------- Main dispatch --------------------
/**
* Read a new packet from the web server and decode it. If it's a
* forwarded request, store its properties in the passed-in AjpRequest
* object.
*
* @param req An empty (newly-recycled) request object.
*
* @return 200 in case of a successful read of a forwarded request, 500
* if there were errors in the reading of the request, and -2 if the
* server is asking the container to shut itself down.
*/
public int receiveNextRequest(BaseRequest req) throws IOException {
if (debug > 0) {
logger.log("receiveNextRequest()");
}
// XXX The return values are awful.
int err = 0;
// if we receive an IOException here, it must be because
// the remote just closed the ajp13 connection, and it's not
// an error, we just need to close the AJP13 connection
try {
err = receive(hBuf);
} catch (IOException ioe) {
if(debug >0 ) logger.log( "IOException receiving message ");
return -1; // Indicate it's a disconnection from the remote end
}
if(err < 0) {
if(debug >0 ) logger.log( "Error receiving message ");
return 500;
}
int type = (int)hBuf.getByte();
// System.out.println("XXX " + this );
return handleMessage( type, hBuf, req );
}
/** Override for ajp14, temporary
*/
public int handleMessage( int type, Ajp13Packet hBuf, BaseRequest req )
throws IOException
{
if( type > handlers.length ) {
logger.log( "Invalid handler " + type );
return 500;
}
if( debug > 0 )
logger.log( "Received " + type + " " + handlerName[type]);
// Ajp14, unlogged
if( ! backwardCompat && ! isLogged() ) {
if( type != NegociationHandler.JK_AJP14_LOGINIT_CMD &&
type != NegociationHandler.JK_AJP14_LOGCOMP_CMD ) {
logger.log( "Ajp14 error: not logged " +
type + " " + handlerName[type]);
return 300;
}
// else continue
}
// Ajp13 messages
switch(type) {
case RequestHandler.JK_AJP13_FORWARD_REQUEST:
return reqHandler.decodeRequest(this, hBuf, req);
case JK_AJP13_SHUTDOWN:
return -2;
}
// logged || loging message
AjpHandler handler=handlers[type];
if( handler==null ) {
logger.log( "Unknown message " + type + handlerName[type] );
return 200;
}
if( debug > 0 )
logger.log( "Ajp14 handler " + handler );
return handler.handleAjpMessage( type, this, hBuf, req );
}
// ==================== Servlet Input Support =================
/** @deprecated -- Will use reqHandler, make sure nobody else
calls this */
public int available() throws IOException {
return reqHandler.available(this);
}
public int doRead() throws IOException
{
return reqHandler.doRead( this );
}
public int doRead(byte[] b, int off, int len) throws IOException
{
return reqHandler.doRead( this, b, off, len );
}
private boolean refillReadBuffer() throws IOException
{
return reqHandler.refillReadBuffer(this);
}
public void beginSendHeaders(int status,
String statusMessage,
int numHeaders) throws IOException {
reqHandler.beginSendHeaders( this, outBuf,
status, statusMessage,
numHeaders);
}
public void sendHeader(String name, String value) throws IOException {
reqHandler.sendHeader( outBuf, name, value );
}
public void endSendHeaders() throws IOException {
reqHandler.endSendHeaders(this, outBuf);
}
public void sendHeaders(int status, MimeHeaders headers)
throws IOException
{
reqHandler.sendHeaders(this, outBuf, status,
HttpMessages.getMessage(status),
headers);
}
public void sendHeaders(int status, String statusMessage,
MimeHeaders headers)
throws IOException
{
reqHandler.sendHeaders( this, outBuf, status,
statusMessage, headers );
}
public void finish() throws IOException {
reqHandler.finish(this, outBuf );
}
public void doWrite(byte b[], int off, int len) throws IOException {
reqHandler.doWrite( this, outBuf, b, off, len );
}
// ========= Internal Packet-Handling Methods =================
/**
* Read N bytes from the InputStream, and ensure we got them all
* Under heavy load we could experience many fragmented packets
* just read Unix Network Programming to recall that a call to
* read didn't ensure you got all the data you want
*
* from read() Linux manual
*
* On success, the number of bytes read is returned (zero indicates end of file),
* and the file position is advanced by this number.
* It is not an error if this number is smaller than the number of bytes requested;
* this may happen for example because fewer bytes
* are actually available right now (maybe because we were close to end-of-file,
* or because we are reading from a pipe, or from a
* terminal), or because read() was interrupted by a signal.
* On error, -1 is returned, and errno is set appropriately. In this
* case it is left unspecified whether the file position (if any) changes.
*
**/
private int readN(InputStream in, byte[] b, int offset, int len) throws IOException {
int pos = 0;
int got;
while(pos < len) {
got = in.read(b, pos + offset, len - pos);
if (debug > 10) {
logger.log("read got # " + got);
}
// connection just closed by remote.
if (got <= 0) {
// This happens periodically, as apache restarts
// periodically.
// It should be more gracefull ! - another feature for Ajp14
return JK_AJP13_COMM_BROKEN;
}
pos += got;
}
return pos;
}
/**
* Read in a packet from the web server and store it in the passed-in
* <CODE>Ajp13Packet</CODE> object.
*
* @param msg The object into which to store the incoming packet -- any
* current contents will be overwritten.
*
* @return The number of bytes read on a successful read or -1 if there
* was an error.
**/
public int receive(Ajp13Packet msg) throws IOException {
if (debug > 0) {
logger.log("receive()");
}
// XXX If the length in the packet header doesn't agree with the
// actual number of bytes read, it should probably return an error
// value. Also, callers of this method never use the length
// returned -- should probably return true/false instead.
byte b[] = msg.getBuff();
int rd = readN(in, b, 0, H_SIZE );
// XXX - connection closed (JK_AJP13_COMM_CLOSED)
// - connection broken (JK_AJP13_COMM_BROKEN)
//
if(rd < 0) {
// Most likely normal apache restart.
return rd;
}
int len = msg.checkIn();
if( debug > 5 )
logger.log( "Received " + rd + " " + len + " " + b[0] );
// XXX check if enough space - it's assert()-ed !!!
int total_read = 0;
total_read = readN(in, b, H_SIZE, len);
if (total_read <= 0) {
logger.log("can't read body, waited #" + len);
return JK_AJP13_BAD_BODY;
}
if (total_read != len) {
logger.log( "incomplete read, waited #" + len +
" got only " + total_read);
return JK_AJP13_INCOMPLETE_BODY;
}
if (debug > 0)
logger.log("receive: total read = " + total_read);
return total_read;
}
/**
* Send a packet to the web server. Works for any type of message.
*
* @param msg A packet with accumulated data to send to the server --
* this method will write out the length in the header.
*/
public void send( Ajp13Packet msg ) throws IOException {
if (debug > 0) {
logger.log("send()");
}
msg.end(); // Write the packet header
byte b[] = msg.getBuff();
int len = msg.getLen();
if (debug > 5 )
logger.log("send() " + len + " " + b[0] );
out.write( b, 0, len );
}
/**
* Close the socket connection to the web server. In general, sockets
* are maintained across many requests, so this will not be called
* after finish().
*
* @see Ajp13Interceptor#processConnection
*/
public void close() throws IOException {
if (debug > 0) {
logger.log("close()");
}
if(null != out) {
out.close();
}
if(null !=in) {
in.close();
}
setLogged( false ); // no more logged now
}
// -------------------- Debug --------------------
protected int debug = 0;
public void setDebug(int debug) {
this.debug = debug;
}
public void setLogger(Logger l) {
this.logger = l;
}
/**
* XXX place holder...
*/
Logger logger = new Logger();
public static class Logger {
void log(String msg) {
System.out.println("[Ajp13] " + msg);
}
void log(String msg, Throwable t) {
System.out.println("[Ajp13] " + msg);
t.printStackTrace(System.out);
}
}
}
1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/Ajp13Packet.java
Index: Ajp13Packet.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.jk;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Enumeration;
import java.security.*;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.HttpMessages;
import org.apache.tomcat.util.buf.HexUtils;
/**
* A single packet for communication between the web server and the
* container. Designed to be reused many times with no creation of
* garbage. Understands the format of data types for these packets.
* Can be used (somewhat confusingly) for both incoming and outgoing
* packets.
*
* @see Ajp14/Ajp13Packet
*
* @author Henri Gomez [hgomez@slib.fr]
* @author Dan Milstein [danmil@shore.net]
* @author Keith Wannamaker [Keith@Wannamaker.org]
* @author Kevin Seguin
* @author Costin Manolache
*/
public class Ajp13Packet {
public static final String DEFAULT_CHAR_ENCODING = "8859_1";
public static final int AJP13_WS_HEADER = 0x1234;
public static final int AJP13_SW_HEADER = 0x4142; // 'AB'
/**
* encoding to use when converting byte[] <-> string
*/
String encoding = DEFAULT_CHAR_ENCODING;
/**
* Holds the bytes of the packet
*/
byte buff[];
/**
* The current read or write position in the buffer
*/
int pos;
/**
* This actually means different things depending on whether the
* packet is read or write. For read, it's the length of the
* payload (excluding the header). For write, it's the length of
* the packet as a whole (counting the header). Oh, well.
*/
int len;
/**
* Create a new packet with an internal buffer of given size.
* @param size packet size
*/
public Ajp13Packet( int size ) {
buff = new byte[size];
}
/**
* Create a new packet with given bytes
* @param b this packet's bytes.
*/
public Ajp13Packet( byte b[] ) {
buff = b;
}
/**
* Set the encoding to use for byte[] <-> string
* conversions.
* @param encoding the encoding to use.
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Get the encoding used for byte[] <-> string
* conversions.
* @return the encoding used.
*/
public String getEncoding() {
return encoding;
}
/**
* Get the internal buffer
* @return internal buffer
*/
public byte[] getBuff() {
return buff;
}
/**
* Get length.
* @return length -- This actually means different things depending on whether the
* packet is read or write. For read, it's the length of the
* payload (excluding the header). For write, it's the length of
* the packet as a whole (counting the header). Oh, well.
*/
public int getLen() {
return len;
}
/**
* Get offset into internal buffer.
* @return offset
*/
public int getByteOff() {
return pos;
}
/**
* Set offset into internal buffer.
* @param c new offset
*/
public void setByteOff(int c) {
pos=c;
}
/**
* Parse the packet header for a packet sent from the web server to
* the container. Set the read position to immediately after
* the header.
*
* @return The length of the packet payload, as encoded in the
* header, or -1 if the packet doesn't have a valid header.
*/
public int checkIn() {
pos = 0;
int mark = getInt();
len = getInt();
if( mark != AJP13_WS_HEADER ) {
// XXX Logging
System.out.println("BAD packet " + mark);
dump( "In: " );
return -1;
}
return len;
}
/**
* Prepare this packet for accumulating a message from the container to
* the web server. Set the write position to just after the header
* (but leave the length unwritten, because it is as yet unknown).
*/
public void reset() {
len = 4;
pos = 4;
buff[0] = (byte)(AJP13_SW_HEADER >> 8);
buff[1] = (byte)(AJP13_SW_HEADER & 0xFF);
}
/**
* For a packet to be sent to the web server, finish the process of
* accumulating data and write the length of the data payload into
* the header.
*/
public void end() {
len = pos;
setInt( 2, len-4 );
}
// ============ Data Writing Methods ===================
/**
* Write an 32 bit integer at an arbitrary position in the packet,but don't
* change the write position.
*
* @param bpos The 0-indexed position within the buffer at which to
* write the integer (where 0 is the beginning of the header).
* @param val The integer to write.
*/
private void setInt( int bPos, int val ) {
buff[bPos] = (byte) ((val >>> 8) & 0xFF);
buff[bPos+1] = (byte) (val & 0xFF);
}
public void appendInt( int val ) {
setInt( pos, val );
pos += 2;
}
public void appendByte( byte val ) {
buff[pos++] = val;
}
public void appendBool( boolean val) {
buff[pos++] = (byte) (val ? 1 : 0);
}
/**
* Write a String out at the current write position. Strings are
* encoded with the length in two bytes first, then the string, and
* then a terminating \0 (which is <B>not</B> included in the
* encoded length). The terminator is for the convenience of the C
* code, where it saves a round of copying. A null string is
* encoded as a string with length 0.
*/
public void appendString(String str) throws UnsupportedEncodingException {
// Dual use of the buffer - as Ajp13Packet and as OutputBuffer
// The idea is simple - fewer buffers, smaller footprint and less
// memcpy. The code is a bit tricky, but only local to this
// function.
if(str == null) {
setInt( pos, 0);
buff[pos + 2] = 0;
pos += 3;
return;
}
//
// XXX i don't have OutputBuffer in tc4... ks.
// fix this later...
//
byte[] bytes = str.getBytes(encoding);
appendBytes(bytes, 0, bytes.length);
/* XXX XXX XXX XXX Try to add it back.
int strStart=pos;
// This replaces the old ( buggy and slow ) str.length()
// and str.getBytes(). str.length() is chars, may be != bytes
// and getBytes is _very_ slow.
// XXX setEncoding !!!
ob.setByteOff( pos+2 );
try {
ob.write( str );
ob.flushChars();
} catch( IOException ex ) {
ex.printStackTrace();
}
int strEnd=ob.getByteOff();
buff[strEnd]=0; // The \0 terminator
int strLen=strEnd-strStart;
setInt( pos, strEnd - strStart );
pos += strLen + 3;
*/
}
/**
* Copy a chunk of bytes into the packet, starting at the current
* write position. The chunk of bytes is encoded with the length
* in two bytes first, then the data itself, and finally a
* terminating \0 (which is <B>not</B> included in the encoded
* length).
*
* @param b The array from which to copy bytes.
* @param off The offset into the array at which to start copying
* @param len The number of bytes to copy.
*/
public void appendBytes( byte b[], int off, int numBytes ) {
appendInt( numBytes );
if( pos + numBytes >= buff.length ) {
System.out.println("Buffer overflow " + buff.length + " " + pos + " " + numBytes );
// XXX Log
}
System.arraycopy( b, off, buff, pos, numBytes);
buff[pos + numBytes] = 0; // Terminating \0
pos += numBytes + 1;
}
/**
* Write a 32 bits integer at an arbitrary position in the packet, but don't
* change the write position.
*
* @param bpos The 0-indexed position within the buffer at which to
* write the integer (where 0 is the beginning of the header).
* @param val The integer to write.
*/
private void setLongInt( int bPos, int val ) {
buff[bPos] = (byte) ((val >>> 24) & 0xFF);
buff[bPos+1] = (byte) ((val >>> 16) & 0xFF);
buff[bPos+2] = (byte) ((val >>> 8) & 0xFF);
buff[bPos+3] = (byte) (val & 0xFF);
}
public void appendLongInt( int val ) {
setLongInt( pos, val );
pos += 4;
}
/**
* Copy a chunk of bytes into the packet, starting at the current
* write position. The chunk of bytes IS NOT ENCODED with ANY length
* header.
*
* @param b The array from which to copy bytes.
* @param off The offset into the array at which to start copying
* @param len The number of bytes to copy.
*/
public void appendXBytes(byte[] b, int off, int numBytes) {
if( pos + numBytes > buff.length ) {
System.out.println("appendXBytes - Buffer overflow " + buff.length + " " + pos + " " + numBytes );
// XXX Log
}
System.arraycopy( b, off, buff, pos, numBytes);
pos += numBytes;
}
// ============ Data Reading Methods ===================
/**
* Read an integer from packet, and advance the read position past
* it. Integers are encoded as two unsigned bytes with the
* high-order byte first, and, as far as I can tell, in
* little-endian order within each byte.
*/
public int getInt() {
int result = peekInt();
pos += 2;
return result;
}
/**
* Read an integer from the packet, but don't advance the read
* position past it.
*/
public int peekInt() {
int b1 = buff[pos] & 0xFF; // No swap, Java order
int b2 = buff[pos + 1] & 0xFF;
return (b1<<8) + b2;
}
public byte getByte() {
byte res = buff[pos];
pos++;
return res;
}
public byte peekByte() {
return buff[pos];
}
public boolean getBool() {
return (getByte() == (byte) 1);
}
public void getMessageBytes(MessageBytes mb) {
int length = getInt();
if( (length == 0xFFFF) || (length == -1) ) {
mb.setString( null );
return;
}
mb.setBytes( buff, pos, length );
pos += length;
pos++; // Skip the terminating \0
}
public MessageBytes addHeader(MimeHeaders headers) {
int length = getInt();
if( (length == 0xFFFF) || (length == -1) ) {
return null;
}
MessageBytes vMB=headers.addValue( buff, pos, length );
pos += length;
pos++; // Skip the terminating \0
return vMB;
}
/**
* Read a String from the packet, and advance the read position
* past it. See appendString for details on string encoding.
**/
public String getString() throws java.io.UnsupportedEncodingException {
int length = getInt();
if( (length == 0xFFFF) || (length == -1) ) {
// System.out.println("null string " + length);
return null;
}
String s = new String(buff, pos, length, encoding);
pos += length;
pos++; // Skip the terminating \0
return s;
}
/**
* Copy a chunk of bytes from the packet into an array and advance
* the read position past the chunk. See appendBytes() for details
* on the encoding.
*
* @return The number of bytes copied.
*/
public int getBytes(byte dest[]) {
int length = getInt();
if( length > buff.length ) {
// XXX Should be if(pos + length > buff.legth)?
System.out.println("XXX Assert failed, buff too small ");
}
if( (length == 0xFFFF) || (length == -1) ) {
System.out.println("null string " + length);
return 0;
}
System.arraycopy( buff, pos, dest, 0, length );
pos += length;
pos++; // Skip terminating \0 XXX I believe this is wrong but harmless
return length;
}
/**
* Read a 32 bits integer from packet, and advance the read position past
* it. Integers are encoded as four unsigned bytes with the
* high-order byte first, and, as far as I can tell, in
* little-endian order within each byte.
*/
public int getLongInt() {
int result = peekLongInt();
pos += 4;
return result;
}
/**
* Copy a chunk of bytes from the packet into an array and advance
* the read position past the chunk. See appendXBytes() for details
* on the encoding.
*
* @return The number of bytes copied.
*/
public int getXBytes(byte[] dest, int length) {
if( length > buff.length ) {
// XXX Should be if(pos + length > buff.legth)?
System.out.println("XXX Assert failed, buff too small ");
}
System.arraycopy( buff, pos, dest, 0, length );
pos += length;
return length;
}
/**
* Read a 32 bits integer from the packet, but don't advance the read
* position past it.
*/
public int peekLongInt() {
int b1 = buff[pos] & 0xFF; // No swap, Java order
b1 <<= 8;
b1 |= (buff[pos + 1] & 0xFF);
b1 <<= 8;
b1 |= (buff[pos + 2] & 0xFF);
b1 <<=8;
b1 |= (buff[pos + 3] & 0xFF);
return b1;
}
// ============== Debugging code =========================
private String hex( int x ) {
// if( x < 0) x=256 + x;
String h=Integer.toHexString( x );
if( h.length() == 1 ) h = "0" + h;
return h.substring( h.length() - 2 );
}
private void hexLine( int start ) {
for( int i=start; i< start+16 ; i++ ) {
if( i < len + 4)
System.out.print( hex( buff[i] ) + " ");
else
System.out.print( " " );
}
System.out.print(" | ");
for( int i=start; i < start+16 && i < len + 4; i++ ) {
if( Character.isLetterOrDigit( (char)buff[i] ))
System.out.print( new Character((char)buff[i]) );
else
System.out.print( "." );
}
System.out.println();
}
public void dump(String msg) {
System.out.println( msg + ": " + buff + " " + pos +"/" + (len + 4));
for( int j=0; j < len + 4; j+=16 )
hexLine( j );
System.out.println();
}
}
1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/AjpHandler.java
Index: AjpHandler.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.jk;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Enumeration;
import java.security.*;
import org.apache.tomcat.util.http.*;
import org.apache.tomcat.util.buf.*;
/**
* Base class for handlers of Ajp messages. Jk provide a simple bidirectional
* communication mechanism between the web server and a servlet container. It is
* based on messages that are passed between the 2 server, using a single
* thread on each side.
*
* The container side must be able to deal with at least the "REQUEST FORWARD",
* the server side must be able to deal with at least "HEADERS", "BODY",
* "END" messages.
*
* @author Henri Gomez
* @author Costin Manolache
*/
public class AjpHandler
{
public static final int UNKNOWN=-1;
Ajp13 channel;
public void init( Ajp13 channel ) {
this.channel=channel;
}
/** Execute the callback
*/
public int handleAjpMessage( int type, Ajp13 channel,
Ajp13Packet ajp, BaseRequest req )
throws IOException
{
return UNKNOWN;
}
}
1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/NegociationHandler.java
Index: NegociationHandler.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.jk;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Enumeration;
import java.security.*;
import org.apache.tomcat.util.http.*;
import org.apache.tomcat.util.buf.*;
/**
* Handler for the protocol negotiation. It will authenticate and
* exchange information about supported messages on each end.
*
*
* @author Henri Gomez [hgomez@slib.fr]
* @author Dan Milstein [danmil@shore.net]
* @author Keith Wannamaker [Keith@Wannamaker.org]
* @author Costin Manolache
*/
public class NegociationHandler extends AjpHandler
{
public static final byte JK_AJP13_SHUTDOWN=7;
// Initial Login Phase (web server -> servlet engine)
public static final byte JK_AJP14_LOGINIT_CMD=0x10;
// Second Login Phase (servlet engine -> web server), md5 seed is received
public static final byte JK_AJP14_LOGSEED_CMD=0x11;
// Third Login Phase (web server -> servlet engine),
// md5 of seed + secret is sent
public static final byte JK_AJP14_LOGCOMP_CMD=0x12;
// Login Accepted (servlet engine -> web server)
public static final byte JK_AJP14_LOGOK_CMD=0x13;
// Login Rejected (servlet engine -> web server), will be logged
public static final byte JK_AJP14_LOGNOK_CMD=0x14;
// Context Query (web server -> servlet engine),
// which URI are handled by servlet engine ?
public static final byte JK_AJP14_CONTEXT_QRY_CMD=0x15;
// Context Info (servlet engine -> web server), URI handled response
public static final byte JK_AJP14_CONTEXT_INFO_CMD= 0x16;
// Context Update (servlet engine -> web server), status of context changed
public static final byte JK_AJP14_CONTEXT_UPDATE_CMD= 0x17;
// Servlet Engine Status (web server -> servlet engine),
// what's the status of the servlet engine ?
public static final byte JK_AJP14_STATUS_CMD= 0x18;
// Secure Shutdown command (web server -> servlet engine),
//please servlet stop yourself.
public static final byte JK_AJP14_SHUTDOWN_CMD= 0x19;
// Secure Shutdown command Accepted (servlet engine -> web server)
public static final byte JK_AJP14_SHUTOK_CMD= 0x1A;
// Secure Shutdown Rejected (servlet engine -> web server)
public static final byte JK_AJP14_SHUTNOK_CMD= 0x1B;
// Context Status (web server -> servlet engine),
//what's the status of the context ?
public static final byte JK_AJP14_CONTEXT_STATE_CMD= 0x1C;
// Context Status Reply (servlet engine -> web server), status of context
public static final byte JK_AJP14_CONTEXT_STATE_REP_CMD = 0x1D;
// Unknown Packet Reply (web server <-> servlet engine),
//when a packet couldn't be decoded
public static final byte JK_AJP14_UNKNOW_PACKET_CMD = 0x1E;
// -------------------- Other constants --------------------
// Entropy Packet Size
public static final int AJP14_ENTROPY_SEED_LEN= 32;
public static final int AJP14_COMPUTED_KEY_LEN= 32;
// web-server want context info after login
public static final int AJP14_CONTEXT_INFO_NEG= 0x80000000;
// web-server want context updates
public static final int AJP14_CONTEXT_UPDATE_NEG= 0x40000000;
// web-server want compressed stream
public static final int AJP14_GZIP_STREAM_NEG= 0x20000000;
// web-server want crypted DES56 stream with secret key
public static final int AJP14_DES56_STREAM_NEG= 0x10000000;
// Extended info on server SSL vars
public static final int AJP14_SSL_VSERVER_NEG= 0x08000000;
// Extended info on client SSL vars
public static final int AJP14_SSL_VCLIENT_NEG= 0x04000000;
// Extended info on crypto SSL vars
public static final int AJP14_SSL_VCRYPTO_NEG= 0x02000000;
// Extended info on misc SSL vars
public static final int AJP14_SSL_VMISC_NEG= 0x01000000;
// mask of protocol supported
public static final int AJP14_PROTO_SUPPORT_AJPXX_NEG=0x00FF0000;
// communication could use AJP14
public static final int AJP14_PROTO_SUPPORT_AJP14_NEG=0x00010000;
// communication could use AJP15
public static final int AJP14_PROTO_SUPPORT_AJP15_NEG=0x00020000;
// communication could use AJP16
public static final int AJP14_PROTO_SUPPORT_AJP16_NEG=0x00040000;
// Some failure codes
public static final int AJP14_BAD_KEY_ERR= 0xFFFFFFFF;
public static final int AJP14_ENGINE_DOWN_ERR = 0xFFFFFFFE;
public static final int AJP14_RETRY_LATER_ERR = 0xFFFFFFFD;
public static final int AJP14_SHUT_AUTHOR_FAILED_ERR = 0xFFFFFFFC;
// Some status codes
public static final byte AJP14_CONTEXT_DOWN= 0x01;
public static final byte AJP14_CONTEXT_UP= 0x02;
public static final byte AJP14_CONTEXT_OK= 0x03;
// -------------------- Parameters --------------------
String containerSignature="Ajp14-based container";
String seed="seed";// will use random
String password;
int webserverNegociation=0;
// String webserverName;
public NegociationHandler() {
setSeed("myveryrandomentropy");
setPassword("myverysecretkey");
}
public void setContainerSignature( String s ) {
containerSignature=s;
}
// -------------------- State --------------------
// public String getWebserverName() {
// return webserverName;
// }
// -------------------- Parameters --------------------
/**
* Set the original entropy seed
*/
public void setSeed(String pseed)
{
String[] credentials = new String[1];
credentials[0] = pseed;
seed = digest(credentials, "md5");
}
/**
* Get the original entropy seed
*/
public String getSeed()
{
return seed;
}
/**
* Set the secret password
*/
public void setPassword(String ppwd)
{
password = ppwd;
}
/**
* Get the secret password
*/
public String getPassword()
{
return password;
}
// -------------------- Initialization --------------------
public void init( Ajp13 ajp14 ) {
super.init(ajp14);
// register incoming message handlers
ajp14.registerMessageType( JK_AJP14_LOGINIT_CMD,"JK_AJP14_LOGINIT_CMD",
this, null); //
ajp14.registerMessageType( JK_AJP14_LOGCOMP_CMD,"JK_AJP14_LOGCOMP_CMD",
this, null); //
ajp14.registerMessageType( JK_AJP13_SHUTDOWN,"JK_AJP13_SHUTDOWN",
this, null); //
ajp14.registerMessageType( JK_AJP14_CONTEXT_QRY_CMD,
"JK_AJP14_CONTEXT_QRY_CMD",
this, null); //
ajp14.registerMessageType( JK_AJP14_STATUS_CMD,"JK_AJP14_STATUS_CMD",
this, null); //
ajp14.registerMessageType( JK_AJP14_SHUTDOWN_CMD,
"JK_AJP14_SHUTDOWN_CMD",
this, null); //
ajp14.registerMessageType( JK_AJP14_CONTEXT_STATE_CMD,
"JK_AJP14_CONTEXT_STATE_CMD",
this, null); //
ajp14.registerMessageType( JK_AJP14_UNKNOW_PACKET_CMD,
"JK_AJP14_UNKNOW_PACKET_CMD",
this, null); //
// register outgoing messages handler
ajp14.registerMessageType( JK_AJP14_LOGNOK_CMD,"JK_AJP14_LOGNOK_CMD",
this,null );
}
// -------------------- Dispatch --------------------
public int handleAjpMessage( int type, Ajp13 ch, Ajp13Packet hBuf,
BaseRequest req )
throws IOException
{
System.out.println("handleAjpMessage: " + type );
Ajp13Packet outBuf=ch.outBuf;
// Valid requests when not logged:
switch( type ) {
case JK_AJP14_LOGINIT_CMD :
return handleLogInit(ch, hBuf, outBuf);
case JK_AJP14_LOGCOMP_CMD :
return handleLogComp(ch, hBuf, outBuf);
case JK_AJP13_SHUTDOWN:
return -2;
case JK_AJP14_CONTEXT_QRY_CMD :
return handleContextQuery(ch, hBuf, outBuf);
case JK_AJP14_STATUS_CMD :
return handleStatus(hBuf, outBuf);
case JK_AJP14_SHUTDOWN_CMD :
return handleShutdown(hBuf, outBuf);
case JK_AJP14_CONTEXT_STATE_CMD :
return handleContextState(hBuf, outBuf);
case JK_AJP14_UNKNOW_PACKET_CMD :
return handleUnknowPacket(hBuf, outBuf);
default:
log("unknown command " + type + " received");
return 200; // XXX This is actually an error condition
}
//return UNKNOWN;
}
//----------- Implementation for various protocol commands -----------
/**
* Handle the Initial Login Message from Web-Server
*
* Get the requested Negociation Flags
* Get also the Web-Server Name
*
* Send Login Seed (MD5 of seed)
*/
private int handleLogInit( Ajp13 ch, Ajp13Packet msg,
Ajp13Packet outBuf )
throws IOException
{
webserverNegociation = msg.getLongInt();
String webserverName = msg.getString();
log("in handleLogInit with nego " +
decodeNegociation(webserverNegociation) +
" from webserver " + webserverName);
outBuf.reset();
outBuf.appendByte(JK_AJP14_LOGSEED_CMD);
String[] credentials = new String[1];
credentials[0] = getSeed();
outBuf.appendXBytes(getSeed().getBytes(), 0, AJP14_ENTROPY_SEED_LEN);
log("in handleLogInit: sent entropy " + getSeed());
outBuf.end();
ch.send(outBuf);
return 304;
}
/**
* Handle the Second Phase of Login (accreditation)
*
* Get the MD5 digest of entropy + secret password
* If the authentification is valid send back LogOk
* If the authentification failed send back LogNok
*/
private int handleLogComp( Ajp13 ch, Ajp13Packet msg,
Ajp13Packet outBuf )
throws IOException
{
// log("in handleLogComp :");
byte [] rdigest = new byte[AJP14_ENTROPY_SEED_LEN];
if (msg.getXBytes(rdigest, AJP14_ENTROPY_SEED_LEN) < 0)
return 200;
String[] credentials = new String[2];
credentials[0] = getSeed();
credentials[1] = getPassword();
String computed = digest(credentials, "md5");
String received = new String(rdigest);
// XXX temp workaround, to test the rest of the connector.
if ( ! computed.equalsIgnoreCase(received)) {
log("in handleLogComp : authentification failure received=" +
received + " awaited=" + computed);
}
if (false ) { // ! computed.equalsIgnoreCase(received)) {
log("in handleLogComp : authentification failure received=" +
received + " awaited=" + computed);
// we should have here a security mecanism which could maintain
// a list of remote IP which failed too many times
// so we could reject them quickly at next connect
outBuf.reset();
outBuf.appendByte(JK_AJP14_LOGNOK_CMD);
outBuf.appendLongInt(AJP14_BAD_KEY_ERR);
outBuf.end();
ch.send(outBuf);
return 200;
} else {
// logged we can go process requests
channel.setLogged(true);
outBuf.reset();
outBuf.appendByte(JK_AJP14_LOGOK_CMD);
outBuf.appendLongInt(getProtocolFlags(webserverNegociation));
outBuf.appendString( containerSignature );
outBuf.end();
ch.send(outBuf);
}
return (304);
}
private int handleContextQuery( Ajp13 ch, Ajp13Packet msg,
Ajp13Packet outBuf )
throws IOException
{
log("in handleContextQuery :");
String virtualHost = msg.getString();
log("in handleContextQuery for virtual" + virtualHost);
outBuf.reset();
outBuf.appendByte(JK_AJP14_CONTEXT_INFO_CMD);
outBuf.appendString( virtualHost );
log("in handleContextQuery for virtual " + virtualHost +
"examples URI/MIMES");
outBuf.appendString("examples"); // first context - examples
outBuf.appendString("servlet/*"); // examples/servlet/*
outBuf.appendString("*.jsp"); // examples/*.jsp
outBuf.appendString(""); // no more URI/MIMES
log("in handleContextQuery for virtual " + virtualHost +
"send admin URI/MIMES");
outBuf.appendString("admin"); // second context - admin
outBuf.appendString("servlet/*"); // /admin//servlet/*
outBuf.appendString("*.jsp"); // /admin/*.jsp
outBuf.appendString(""); // no more URI/MIMES
outBuf.appendString(""); // no more contexts
outBuf.end();
ch.send(outBuf);
return (304);
}
private int handleStatus( Ajp13Packet msg, Ajp13Packet outBuf )
throws IOException
{
log("in handleStatus :");
return (304);
}
private int handleShutdown( Ajp13Packet msg, Ajp13Packet outBuf )
throws IOException
{
log("in handleShutdown :");
return (304);
}
private int handleContextState( Ajp13Packet msg , Ajp13Packet outBuf)
throws IOException
{
log("in handleContextState :");
return (304);
}
private int handleUnknowPacket( Ajp13Packet msg, Ajp13Packet outBuf )
throws IOException
{
log("in handleUnknowPacket :");
return (304);
}
// -------------------- Utils --------------------
/**
* Compute the Protocol Negociation Flags
*
* Depending the protocol fatures implemented on servet-engine,
* we'll drop requested features which could be asked by web-server
*
* Hopefully this functions could be overrided by decendants
*/
private int getProtocolFlags(int wanted)
{
// no real-time context update
wanted &= ~(AJP14_CONTEXT_UPDATE_NEG |
// no gzip compression yet
AJP14_GZIP_STREAM_NEG |
// no DES56 cyphering yet
AJP14_DES56_STREAM_NEG |
// no Extended info on server SSL vars yet
AJP14_SSL_VSERVER_NEG |
// no Extended info on client SSL vars yet
AJP14_SSL_VCLIENT_NEG |
// no Extended info on crypto SSL vars yet
AJP14_SSL_VCRYPTO_NEG |
// no Extended info on misc SSL vars yet
AJP14_SSL_VMISC_NEG |
// Reset AJP protocol mask
AJP14_PROTO_SUPPORT_AJPXX_NEG);
// Only strict AJP14 supported
return (wanted | AJP14_PROTO_SUPPORT_AJP14_NEG);
}
/**
* Compute a digest (MD5 in AJP14) for an array of String
*/
public final String digest(String[] credentials, String algorithm) {
try {
// Obtain a new message digest with MD5 encryption
MessageDigest md =
(MessageDigest)MessageDigest.getInstance(algorithm).clone();
// encode the credentials items
for (int i = 0; i < credentials.length; i++) {
if( debug > 0 )
log("Credentials : " + i + " " + credentials[i]);
if( credentials[i] != null )
md.update(credentials[i].getBytes());
}
// obtain the byte array from the digest
byte[] dig = md.digest();
return HexUtils.convert(dig);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
// -------------------- Debugging --------------------
// Very usefull for develoment
/**
* Display Negociation field in human form
*/
private String decodeNegociation(int nego)
{
StringBuffer buf = new StringBuffer(128);
if ((nego & AJP14_CONTEXT_INFO_NEG) != 0)
buf.append(" CONTEXT-INFO");
if ((nego & AJP14_CONTEXT_UPDATE_NEG) != 0)
buf.append(" CONTEXT-UPDATE");
if ((nego & AJP14_GZIP_STREAM_NEG) != 0)
buf.append(" GZIP-STREAM");
if ((nego & AJP14_DES56_STREAM_NEG) != 0)
buf.append(" DES56-STREAM");
if ((nego & AJP14_SSL_VSERVER_NEG) != 0)
buf.append(" SSL-VSERVER");
if ((nego & AJP14_SSL_VCLIENT_NEG) != 0)
buf.append(" SSL-VCLIENT");
if ((nego & AJP14_SSL_VCRYPTO_NEG) != 0)
buf.append(" SSL-VCRYPTO");
if ((nego & AJP14_SSL_VMISC_NEG) != 0)
buf.append(" SSL-VMISC");
if ((nego & AJP14_PROTO_SUPPORT_AJP14_NEG) != 0)
buf.append(" AJP14");
if ((nego & AJP14_PROTO_SUPPORT_AJP15_NEG) != 0)
buf.append(" AJP15");
if ((nego & AJP14_PROTO_SUPPORT_AJP16_NEG) != 0)
buf.append(" AJP16");
return (buf.toString());
}
private static int debug=10;
void log(String s) {
System.out.println("Ajp14Negotiation: " + s );
}
}
1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/RequestHandler.java
Index: RequestHandler.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.jk;
import java.io.*;
import java.net.Socket;
import java.util.Enumeration;
import java.security.*;
import java.security.cert.*;
import org.apache.tomcat.util.http.*;
import org.apache.tomcat.util.buf.*;
/**
* Handle messages related with basic request information.
*
* This object can handle the following incoming messages:
* - "FORWARD_REQUEST" input message ( sent when a request is passed from the web server )
* - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in response to GET_BODY_CHUNK )
*
* It can handle the following outgoing messages:
* - SEND_HEADERS. Pass the status code and headers.
* - SEND_BODY_CHUNK. Send a chunk of body
* - GET_BODY_CHUNK. Request a chunk of body data
* - END_RESPONSE. Notify the end of a request processing.
*
* @author Henri Gomez [hgomez@slib.fr]
* @author Dan Milstein [danmil@shore.net]
* @author Keith Wannamaker [Keith@Wannamaker.org]
* @author Costin Manolache
*/
public class RequestHandler extends AjpHandler
{
// XXX Will move to a registry system.
// Prefix codes for message types from server to container
public static final byte JK_AJP13_FORWARD_REQUEST = 2;
// Prefix codes for message types from container to server
public static final byte JK_AJP13_SEND_BODY_CHUNK = 3;
public static final byte JK_AJP13_SEND_HEADERS = 4;
public static final byte JK_AJP13_END_RESPONSE = 5;
public static final byte JK_AJP13_GET_BODY_CHUNK = 6;
// Integer codes for common response header strings
public static final int SC_RESP_CONTENT_TYPE = 0xA001;
public static final int SC_RESP_CONTENT_LANGUAGE = 0xA002;
public static final int SC_RESP_CONTENT_LENGTH = 0xA003;
public static final int SC_RESP_DATE = 0xA004;
public static final int SC_RESP_LAST_MODIFIED = 0xA005;
public static final int SC_RESP_LOCATION = 0xA006;
public static final int SC_RESP_SET_COOKIE = 0xA007;
public static final int SC_RESP_SET_COOKIE2 = 0xA008;
public static final int SC_RESP_SERVLET_ENGINE = 0xA009;
public static final int SC_RESP_STATUS = 0xA00A;
public static final int SC_RESP_WWW_AUTHENTICATE = 0xA00B;
// Integer codes for common (optional) request attribute names
public static final byte SC_A_CONTEXT = 1; // XXX Unused
public static final byte SC_A_SERVLET_PATH = 2; // XXX Unused
public static final byte SC_A_REMOTE_USER = 3;
public static final byte SC_A_AUTH_TYPE = 4;
public static final byte SC_A_QUERY_STRING = 5;
public static final byte SC_A_JVM_ROUTE = 6;
public static final byte SC_A_SSL_CERT = 7;
public static final byte SC_A_SSL_CIPHER = 8;
public static final byte SC_A_SSL_SESSION = 9;
public static final byte SC_A_SSL_KEYSIZE = 11;
// Used for attributes which are not in the list above
public static final byte SC_A_REQ_ATTRIBUTE = 10;
// Terminates list of attributes
public static final byte SC_A_ARE_DONE = (byte)0xFF;
// Translates integer codes to names of HTTP methods
public static final String []methodTransArray = {
"OPTIONS",
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
"TRACE",
"PROPFIND",
"PROPPATCH",
"MKCOL",
"COPY",
"MOVE",
"LOCK",
"UNLOCK",
"ACL",
"REPORT",
"VERSION-CONTROL",
"CHECKIN",
"CHECKOUT",
"UNCHECKOUT",
"SEARCH"
};
// id's for common request headers
public static final int SC_REQ_ACCEPT = 1;
public static final int SC_REQ_ACCEPT_CHARSET = 2;
public static final int SC_REQ_ACCEPT_ENCODING = 3;
public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
public static final int SC_REQ_AUTHORIZATION = 5;
public static final int SC_REQ_CONNECTION = 6;
public static final int SC_REQ_CONTENT_TYPE = 7;
public static final int SC_REQ_CONTENT_LENGTH = 8;
public static final int SC_REQ_COOKIE = 9;
public static final int SC_REQ_COOKIE2 = 10;
public static final int SC_REQ_HOST = 11;
public static final int SC_REQ_PRAGMA = 12;
public static final int SC_REQ_REFERER = 13;
public static final int SC_REQ_USER_AGENT = 14;
// AJP14 new header
public static final byte SC_A_SSL_KEY_SIZE = 11; // XXX ???
// Translates integer codes to request header names
public static final String []headerTransArray = {
"accept",
"accept-charset",
"accept-encoding",
"accept-language",
"authorization",
"connection",
"content-type",
"content-length",
"cookie",
"cookie2",
"host",
"pragma",
"referer",
"user-agent"
};
public RequestHandler()
{
}
public void init( Ajp13 ajp14 ) {
// register incoming message handlers
ajp14.registerMessageType( JK_AJP13_FORWARD_REQUEST,
"JK_AJP13_FORWARD_REQUEST",
this, null); // 2
// register outgoing messages handler
ajp14.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3
"JK_AJP13_SEND_BODY_CHUNK",
this,null );
ajp14.registerMessageType( JK_AJP13_SEND_HEADERS, // 4
"JK_AJP13_SEND_HEADERS",
this,null );
ajp14.registerMessageType( JK_AJP13_END_RESPONSE, // 5
"JK_AJP13_END_RESPONSE",
this,null );
ajp14.registerMessageType( JK_AJP13_GET_BODY_CHUNK, // 6
"JK_AJP13_GET_BODY_CHUNK",
this, null );
}
// -------------------- Incoming message --------------------
public int handleAjpMessage( int type, Ajp13 channel,
Ajp13Packet ajp, BaseRequest req )
throws IOException
{
switch( type ) {
case RequestHandler.JK_AJP13_FORWARD_REQUEST:
return decodeRequest(channel, channel.hBuf, req );
default:
return UNKNOWN;
}
}
/**
* Parse a FORWARD_REQUEST packet from the web server and store its
* properties in the passed-in request object.
*
* @param req An empty (newly-recycled) request object.
* @param msg Holds the packet which has just been sent by the web
* server, with its read position just past the packet header (which in
* this case includes the prefix code for FORWARD_REQUEST).
*
* @return 200 in case of a successful decoduing, 500 in case of error.
*/
protected int decodeRequest(Ajp13 ch, Ajp13Packet msg, BaseRequest req)
throws IOException
{
if (debug > 0) {
log("decodeRequest()");
}
// XXX Awful return values
boolean isSSL = false;
// Translate the HTTP method code to a String.
byte methodCode = msg.getByte();
req.method().setString(methodTransArray[(int)methodCode - 1]);
msg.getMessageBytes(req.protocol());
msg.getMessageBytes(req.requestURI());
msg.getMessageBytes(req.remoteAddr());
msg.getMessageBytes(req.remoteHost());
msg.getMessageBytes(req.serverName());
req.setServerPort(msg.getInt());
isSSL = msg.getBool();
// Decode headers
MimeHeaders headers = req.headers();
int hCount = msg.getInt();
for(int i = 0 ; i < hCount ; i++) {
String hName = null;
// Header names are encoded as either an integer code starting
// with 0xA0, or as a normal string (in which case the first
// two bytes are the length).
int isc = msg.peekInt();
int hId = isc & 0xFF;
MessageBytes vMB=null;
isc &= 0xFF00;
if(0xA000 == isc) {
msg.getInt(); // To advance the read position
hName = headerTransArray[hId - 1];
vMB= headers.addValue(hName);
} else {
// XXX Not very elegant
vMB = msg.addHeader(headers);
if (vMB == null) {
return 500; // wrong packet
}
}
msg.getMessageBytes(vMB);
// set content length, if this is it...
if (hId == SC_REQ_CONTENT_LENGTH) {
int contentLength = (vMB == null) ? -1 : vMB.getInt();
req.setContentLength(contentLength);
} else if (hId == SC_REQ_CONTENT_TYPE) {
ByteChunk bchunk = vMB.getByteChunk();
req.contentType().setBytes(bchunk.getBytes(),
bchunk.getOffset(),
bchunk.getLength());
}
}
byte attributeCode;
for(attributeCode = msg.getByte() ;
attributeCode != SC_A_ARE_DONE ;
attributeCode = msg.getByte()) {
switch(attributeCode) {
case SC_A_CONTEXT :
break;
case SC_A_SERVLET_PATH :
break;
case SC_A_REMOTE_USER :
msg.getMessageBytes(req.remoteUser());
break;
case SC_A_AUTH_TYPE :
msg.getMessageBytes(req.authType());
break;
case SC_A_QUERY_STRING :
msg.getMessageBytes(req.queryString());
break;
case SC_A_JVM_ROUTE :
msg.getMessageBytes(req.jvmRoute());
break;
case SC_A_SSL_CERT :
isSSL = true;
// Transform the string into certificate.
String certString = msg.getString();
byte[] certData = certString.getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(certData);
// Fill the first element.
X509Certificate jsseCerts[] = null;
try {
CertificateFactory cf =
CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)
cf.generateCertificate(bais);
jsseCerts = new X509Certificate[1];
jsseCerts[0] = cert;
} catch(java.security.cert.CertificateException e) {
log("Certificate convertion failed" + e );
}
req.setAttribute("javax.servlet.request.X509Certificate",
jsseCerts);
break;
case SC_A_SSL_CIPHER :
isSSL = true;
req.setAttribute("javax.servlet.request.cipher_suite",
msg.getString());
break;
case SC_A_SSL_SESSION :
isSSL = true;
req.setAttribute("javax.servlet.request.ssl_session",
msg.getString());
break;
case SC_A_REQ_ATTRIBUTE :
req.setAttribute(msg.getString(),
msg.getString());
break;
case SC_A_SSL_KEY_SIZE: // Ajp13 !
req.setAttribute("javax.servlet.request.key_size",
Integer.toString(msg.getInt()));
return 200;
default:
return 500; // Error
}
}
if(isSSL) {
req.setScheme(req.SCHEME_HTTPS);
req.setSecure(true);
}
// set cookies on request now that we have all headers
req.cookies().setHeaders(req.headers());
// Check to see if there should be a body packet coming along
// immediately after
if(req.getContentLength() > 0) {
/* Read present data */
int err = ch.receive(ch.inBuf);
if(err < 0) {
return 500;
}
ch.blen = ch.inBuf.peekInt();
ch.pos = 0;
ch.inBuf.getBytes(ch.bodyBuff);
}
if (debug > 5) {
log(req.toString());
}
return 200; // Success
}
// -------------------- Messages from container to server ------------------
/**
* Send the HTTP headers back to the web server and on to the browser.
*
* @param status The HTTP status code to send.
* @param statusMessage the HTTP status message to send.
* @param headers The set of all headers.
*/
public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
int status, String statusMessage,
MimeHeaders headers)
throws IOException
{
// XXX if more headers that MAX_SIZE, send 2 packets!
if( statusMessage==null ) statusMessage=HttpMessages.getMessage(status);
outBuf.reset();
outBuf.appendByte(JK_AJP13_SEND_HEADERS);
outBuf.appendInt(status);
outBuf.appendString(statusMessage);
int numHeaders = headers.size();
outBuf.appendInt(numHeaders);
for( int i=0 ; i < numHeaders ; i++ ) {
String headerName = headers.getName(i).toString();
int sc = headerNameToSc(headerName);
if(-1 != sc) {
outBuf.appendInt(sc);
} else {
outBuf.appendString(headerName);
}
outBuf.appendString(headers.getValue(i).toString() );
}
outBuf.end();
ch.send(outBuf);
}
/**
* Signal the web server that the servlet has finished handling this
* request, and that the connection can be reused.
*/
public void finish(Ajp13 ch, Ajp13Packet outBuf) throws IOException {
if (debug > 0) log("finish()");
outBuf.reset();
outBuf.appendByte(JK_AJP13_END_RESPONSE);
outBuf.appendBool(true); // Reuse this connection
outBuf.end();
ch.send(outBuf);
}
/**
* Send a chunk of response body data to the web server and on to the
* browser.
*
* @param b A huffer of bytes to send.
* @param off The offset into the buffer from which to start sending.
* @param len The number of bytes to send.
*/
public void doWrite(Ajp13 ch, Ajp13Packet outBuf,
byte b[], int off, int len)
throws IOException
{
if (debug > 0) log("doWrite(byte[], " + off + ", " + len + ")");
int sent = 0;
while(sent < len) {
int to_send = len - sent;
to_send = to_send > Ajp13.MAX_SEND_SIZE ? Ajp13.MAX_SEND_SIZE : to_send;
outBuf.reset();
outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);
outBuf.appendBytes(b, off + sent, to_send);
ch.send(outBuf);
sent += to_send;
}
}
// -------------------- Utils --------------------
/**
* Translate an HTTP response header name to an integer code if
* possible. Case is ignored.
*
* @param name The name of the response header to translate.
*
* @return The code for that header name, or -1 if no code exists.
*/
protected int headerNameToSc(String name)
{
switch(name.charAt(0)) {
case 'c':
case 'C':
if(name.equalsIgnoreCase("Content-Type")) {
return SC_RESP_CONTENT_TYPE;
} else if(name.equalsIgnoreCase("Content-Language")) {
return SC_RESP_CONTENT_LANGUAGE;
} else if(name.equalsIgnoreCase("Content-Length")) {
return SC_RESP_CONTENT_LENGTH;
}
break;
case 'd':
case 'D':
if(name.equalsIgnoreCase("Date")) {
return SC_RESP_DATE;
}
break;
case 'l':
case 'L':
if(name.equalsIgnoreCase("Last-Modified")) {
return SC_RESP_LAST_MODIFIED;
} else if(name.equalsIgnoreCase("Location")) {
return SC_RESP_LOCATION;
}
break;
case 's':
case 'S':
if(name.equalsIgnoreCase("Set-Cookie")) {
return SC_RESP_SET_COOKIE;
} else if(name.equalsIgnoreCase("Set-Cookie2")) {
return SC_RESP_SET_COOKIE2;
}
break;
case 'w':
case 'W':
if(name.equalsIgnoreCase("WWW-Authenticate")) {
return SC_RESP_WWW_AUTHENTICATE;
}
break;
}
return -1;
}
private static int debug=0;
void log(String s) {
System.out.println("RequestHandler: " + s );
}
// ==================== Servlet Input Support =================
// XXX DEPRECATED
public int available(Ajp13 ch) throws IOException {
if (debug > 0) {
log("available()");
}
if (ch.pos >= ch.blen) {
if( ! refillReadBuffer(ch)) {
return 0;
}
}
return ch.blen - ch.pos;
}
/**
* Return the next byte of request body data (to a servlet).
*
* @see Request#doRead
*/
public int doRead(Ajp13 ch) throws IOException
{
if (debug > 0) {
log("doRead()");
}
if(ch.pos >= ch.blen) {
if( ! refillReadBuffer(ch)) {
return -1;
}
}
return (char) ch.bodyBuff[ch.pos++];
}
/**
* Store a chunk of request data into the passed-in byte buffer.
*
* @param b A buffer to fill with data from the request.
* @param off The offset in the buffer at which to start filling.
* @param len The number of bytes to copy into the buffer.
*
* @return The number of bytes actually copied into the buffer, or -1
* if the end of the stream has been reached.
*
* @see Request#doRead
*/
public int doRead(Ajp13 ch, byte[] b, int off, int len) throws IOException
{
if (debug > 0) {
log("doRead(byte[], int, int)");
}
if(ch.pos >= ch.blen) {
if( ! refillReadBuffer(ch)) {
return -1;
}
}
if(ch.pos + len <= ch.blen) { // Fear the off by one error
// Sanity check b.length > off + len?
System.arraycopy(ch.bodyBuff, ch.pos, b, off, len);
ch.pos += len;
return len;
}
// Not enough data (blen < pos + len)
int toCopy = len;
while(toCopy > 0) {
int bytesRemaining = ch.blen - ch.pos;
if(bytesRemaining < 0)
bytesRemaining = 0;
int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
System.arraycopy(ch.bodyBuff, ch.pos, b, off, c);
toCopy -= c;
off += c;
ch.pos += c; // In case we exactly consume the buffer
if(toCopy > 0)
if( ! refillReadBuffer(ch)) { // Resets blen and pos
break;
}
}
return len - toCopy;
}
/**
* Get more request body data from the web server and store it in the
* internal buffer.
*
* @return true if there is more data, false if not.
*/
public boolean refillReadBuffer(Ajp13 ch) throws IOException
{
if (debug > 0) {
log("refillReadBuffer()");
}
// If the server returns an empty packet, assume that that end of
// the stream has been reached (yuck -- fix protocol??).
// Why not use outBuf??
ch.inBuf.reset();
ch.inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
ch.inBuf.appendInt(Ajp13.MAX_READ_SIZE);
ch.send(ch.inBuf);
int err = ch.receive(ch.inBuf);
if(err < 0) {
throw new IOException();
}
ch.blen = ch.inBuf.peekInt();
ch.pos = 0;
ch.inBuf.getBytes(ch.bodyBuff);
return (ch.blen > 0);
}
// ==================== Servlet Output Support =================
/**
*/
public void beginSendHeaders(Ajp13 ch, Ajp13Packet outBuf,
int status,
String statusMessage,
int numHeaders) throws IOException {
if (debug > 0) {
log("sendHeaders()");
}
// XXX if more headers that MAX_SIZE, send 2 packets!
outBuf.reset();
outBuf.appendByte(JK_AJP13_SEND_HEADERS);
if (debug > 0) {
log("status is: " + status +
"(" + statusMessage + ")");
}
// set status code and message
outBuf.appendInt(status);
outBuf.appendString(statusMessage);
// write the number of headers...
outBuf.appendInt(numHeaders);
}
public void sendHeader(Ajp13Packet outBuf,
String name, String value)
throws IOException
{
int sc = headerNameToSc(name);
if(-1 != sc) {
outBuf.appendInt(sc);
} else {
outBuf.appendString(name);
}
outBuf.appendString(value);
}
public void endSendHeaders(Ajp13 ch, Ajp13Packet outBuf)
throws IOException
{
outBuf.end();
ch.send(outBuf);
}
/**
* Send the HTTP headers back to the web server and on to the browser.
*
* @param status The HTTP status code to send.
* @param headers The set of all headers.
*/
public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
int status, MimeHeaders headers)
throws IOException
{
sendHeaders(ch, outBuf, status, HttpMessages.getMessage(status),
headers);
}
}
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>