costin 01/12/31 11:02:01 Added: jk/java/org/apache/jk/common ChannelSocket.java HandlerEcho.java HandlerRequest.java MsgAjp.java WorkerDummy.java Log: "New" jk. The code is cut&pasted from the old jk, with a bit of cleanup and reorganization to match the object model on the C side. The most significant change is the removal of String from MsgAjp ( which is the old AjpPacket ). Jk doesn't have enough knowledge to deal with strings - the higher layer ( the connector ) should do the byte-char conversion, we just marshal byte[], int. The main reason is that charset encoding is hard to detect at the packet level ( it can be in the Session, or extracted from headers, or set by user in 2.3 ). The old code was not working well with different charsets. The second reason is performance - doing String.getBytes() proved to be very inefficient. The connector should use C2B or B2C or JDK1.4 to do the conversion. You may notice ChannelSocket, which now does send/receive the messages ( part of the old Ajp13 object ). The intention is to add other channels, using different transports ( unix sockets, etc ). Msg abstracts the encoding, and again we may have a different impl. in future ( but less likely, the current ajp13 encoding seems fine, maybe we can experiment with alignment or CDR ). Endpoint represents the point to point connection between java and c sides, it's very 'light' on the java side ( only generic notes ). In future we should simplify the C endpoint too. Worker is the link to the higher layer ( tomcatXX ). It is no longer the factory for endpoint, etc - the endpoint/channel/msg are now a bit more independent ( i.e. in java ), to avoid duplicating code. Of course, nothing is final :-) It kind of works now, but needs more testing, and the objects are certain to change when we'll have the first 'alternative' transport. Revision Changes Path 1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/common/ChannelSocket.java Index: ChannelSocket.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 [EMAIL PROTECTED] * * 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.common; import java.io.*; import java.net.*; import java.util.*; import org.apache.tomcat.util.buf.*; import org.apache.tomcat.util.http.*; import org.apache.tomcat.util.threads.*; import org.apache.jk.core.*; /* XXX Make the 'message type' pluggable */ /* A lot of the 'original' behavior is hardcoded - this uses Ajp13 wire protocol, TCP, Ajp14 API etc. As we add other protocols/transports/APIs this will change, the current goal is to get the same level of functionality as in the original jk connector. */ /** * Jk2 can use multiple protocols/transports. * Various container adapters should load this object ( as a bean ), * set configurations and use it. Note that the connector will handle * all incoming protocols - it's not specific to ajp1x. The protocol * is abstracted by Endpoint/Message/Channel. */ /** Accept ( and send ) TCP messages. * * @author Costin Manolache */ public class ChannelSocket extends Channel { int port; InetAddress inet; int serverTimeout; boolean tcpNoDelay; int linger=100; int socketTimeout; Worker worker; ThreadPool tp=new ThreadPool(); /* ==================== Tcp socket options ==================== */ public ThreadPool getThreadPool() { return tp; } public void setPort( int port ) { this.port=port; } public void setWorker( Worker w ) { worker=w; } public Worker getWorker() { return worker; } public void setAddress(InetAddress inet) { this.inet=inet; } /** * Sets the timeout in ms of the server sockets created by this * server. This method allows the developer to make servers * more or less responsive to having their server sockets * shut down. * * <p>By default this value is 1000ms. */ public void setServerTimeout(int timeout) { this.serverTimeout = timeout; } public void setTcpNoDelay( boolean b ) { tcpNoDelay=b; } public void setSoLinger( int i ) { linger=i; } public void setSoTimeout( int i ) { socketTimeout=i; } /* ==================== ==================== */ ServerSocket sSocket; int socketNote=1; int isNote=2; int osNote=3; public void accept( Endpoint ep ) throws IOException { Socket s=sSocket.accept(); ep.setNote( socketNote, s ); if(dL>0 ) d("Accepted socket " + s ); if( linger > 0 ) s.setSoLinger( true, linger); InputStream is=new BufferedInputStream(s.getInputStream()); OutputStream os= s.getOutputStream(); ep.setNote( isNote, is ); ep.setNote( osNote, os ); } public void init() throws IOException { sSocket=new ServerSocket( port ); if( serverTimeout > 0 ) sSocket.setSoTimeout( serverTimeout ); // Run a thread that will accept connections. tp.start(); SocketAcceptor acceptAjp=new SocketAcceptor( this ); tp.runIt( acceptAjp); } public void open(Endpoint ep) throws IOException { } public void close(Endpoint ep) throws IOException { Socket s=(Socket)ep.getNote( socketNote ); s.close(); } public void destroy() throws IOException { try { tp.shutdown(); // Need to create a connection to unlock the accept(); Socket s; if (inet == null) { s=new Socket("127.0.0.1", port ); }else{ s=new Socket(inet, port ); // setting soLinger to a small value will help shutdown the // connection quicker s.setSoLinger(true, 0); } s.close(); sSocket.close(); // XXX? } catch(Exception e) { e.printStackTrace(); } } public void write( Endpoint ep, byte[] b, int offset, int len) throws IOException { OutputStream os=(OutputStream)ep.getNote( osNote ); os.write( b, offset, len ); } /** * 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. * **/ public int read( Endpoint ep, byte[] b, int offset, int len) throws IOException { InputStream is=(InputStream)ep.getNote( isNote ); int pos = 0; int got; if (dL > 5) { d("reading # " + b + " " + (b==null ? 0: b.length) + " " + offset + " " + len); } while(pos < len) { got = is.read(b, pos + offset, len - pos); if (dL > 5) { d("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 -3; } pos += got; } return pos; } public Endpoint createEndpoint() { return new Endpoint(); } boolean running=true; /** Accept incoming connections, dispatch to the thread pool */ void acceptConnections() { if( dL>0 ) d("Accepting ajp connections"); while( running ) { try { Endpoint ep=this.createEndpoint(); this.accept(ep); SocketConnection ajpConn= new SocketConnection(this, ep); tp.runIt( ajpConn ); } catch( Exception ex ) { ex.printStackTrace(); } } } /** Process a single ajp connection. */ void processConnection(Endpoint ep) { if( dL > 0 ) d( "New ajp connection "); try { MsgAjp recv=new MsgAjp(); while( running ) { recv.receive( this, ep ); int status=we.processCallbacks( this, ep, recv ); } this.close( ep ); } catch( Exception ex ) { ex.printStackTrace(); } } private static final int dL=10; private static void d(String s ) { System.err.println( "ChannelSocket: " + s ); } } class SocketAcceptor implements ThreadPoolRunnable { ChannelSocket wajp; SocketAcceptor(ChannelSocket wajp ) { this.wajp=wajp; } public Object[] getInitData() { return null; } public void runIt(Object thD[]) { wajp.acceptConnections(); } } class SocketConnection implements ThreadPoolRunnable { ChannelSocket wajp; Endpoint ep; SocketConnection(ChannelSocket wajp, Endpoint ep) { this.wajp=wajp; this.ep=ep; } public Object[] getInitData() { return null; } public void runIt(Object perTh[]) { wajp.processConnection(ep); } } 1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/common/HandlerEcho.java Index: HandlerEcho.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 [EMAIL PROTECTED] * * 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.common; import java.io.*; import java.net.Socket; import java.util.Enumeration; import java.security.*; import java.security.cert.*; import org.apache.jk.core.*; import org.apache.tomcat.util.http.*; import org.apache.tomcat.util.buf.*; /** * Accept requests and send a dummy response. Usefull to tune the protocol * (it takes the higher layer off the picture ). * * @author Costin Manolache */ public class HandlerEcho extends Handler { public HandlerEcho() { } public void init( WorkerEnv we ) { // register incoming message handlers // we.registerMessageType( JK_AJP13_FORWARD_REQUEST, // "JK_AJP13_FORWARD_REQUEST", // this, null); // 2 } // -------------------- Incoming message -------------------- public int callback( MsgAjp msg ) throws IOException { int type=msg.getByte(); return 200; } } 1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/common/HandlerRequest.java Index: HandlerRequest.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 [EMAIL PROTECTED] * * 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.common; import java.io.*; import java.net.Socket; import java.util.Enumeration; import java.security.*; import java.security.cert.*; import org.apache.jk.core.*; 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 [[EMAIL PROTECTED]] * @author Dan Milstein [[EMAIL PROTECTED]] * @author Keith Wannamaker [[EMAIL PROTECTED]] * @author Costin Manolache */ public class HandlerRequest extends Handler { // 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 HandlerRequest() { } public void init( WorkerEnv we ) { // register incoming message handlers we.registerMessageType( JK_AJP13_FORWARD_REQUEST, "JK_AJP13_FORWARD_REQUEST", this, null); // 2 // register outgoing messages handler we.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3 "JK_AJP13_SEND_BODY_CHUNK", this,null ); we.registerMessageType( JK_AJP13_SEND_HEADERS, // 4 "JK_AJP13_SEND_HEADERS", this,null ); we.registerMessageType( JK_AJP13_END_RESPONSE, // 5 "JK_AJP13_END_RESPONSE", this,null ); we.registerMessageType( JK_AJP13_GET_BODY_CHUNK, // 6 "JK_AJP13_GET_BODY_CHUNK", this, null ); } // -------------------- Incoming message -------------------- int reqNote=4; int postMsgNote=5; int tmpBufNote=6; Worker w; public void setWorker( Worker w ) { this.w=w; } public int callback(int type, Channel ch, Endpoint ep, Msg msg) throws IOException { // FORWARD_REQUEST handler BaseRequest req=(BaseRequest)ep.getNote( reqNote ); if( req==null ) { req=new BaseRequest(); ep.setNote( reqNote, req ); } decodeRequest( msg, req, ch, ep ); /* XXX it should be computed from request, by workerEnv */ w.service( req, ch, ep ); return OK; } private int decodeRequest( Msg msg, BaseRequest req, Channel ch, Endpoint ep ) throws IOException { // Translate the HTTP method code to a String. byte methodCode = msg.getByte(); String mName=methodTransArray[(int)methodCode - 1]; req.method().setString(mName); msg.getBytes(req.protocol()); msg.getBytes(req.requestURI()); msg.getBytes(req.remoteAddr()); msg.getBytes(req.remoteHost()); msg.getBytes(req.serverName()); req.setServerPort(msg.getInt()); boolean isSSL = msg.getByte() != 0; if( isSSL ) req.setSecure( true ); decodeHeaders( ep, msg, req ); decodeAttributes( ep, msg, req ); if(req.getSecure() ) { req.setScheme(req.SCHEME_HTTPS); } // 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) { Msg postMsg=(Msg)ep.getNote( postMsgNote ); if( postMsg==null ) { postMsg=new MsgAjp(); ep.setNote( postMsgNote, postMsg ); } /* Read present data */ int err = postMsg.receive(ch, ep); } if (dL > 5) { d(req.toString()); } return OK; } private int decodeAttributes( Endpoint ep, Msg msg, BaseRequest req ) { boolean moreAttr=true; MessageBytes tmpMB=(MessageBytes)ep.getNote( tmpBufNote ); if( tmpMB==null ) { tmpMB=new MessageBytes(); ep.setNote( tmpBufNote, tmpMB); } while( moreAttr ) { byte attributeCode=msg.getByte(); if( attributeCode == SC_A_ARE_DONE ) return 200; /* Special case ( XXX in future API make it separate type !) */ if( attributeCode == SC_A_SSL_KEY_SIZE ) { // int ???... req.setAttribute("javax.servlet.request.key_size", Integer.toString(msg.getInt())); } if( attributeCode == SC_A_REQ_ATTRIBUTE ) { // 2 strings ???... msg.getBytes( tmpMB ); String n=tmpMB.toString(); msg.getBytes( tmpMB ); String v=tmpMB.toString(); req.setAttribute(n, v ); } // 1 string attributes switch(attributeCode) { case SC_A_CONTEXT : msg.getBytes( tmpMB ); // nothing break; case SC_A_SERVLET_PATH : msg.getBytes( tmpMB ); // nothing break; case SC_A_REMOTE_USER : msg.getBytes(req.remoteUser()); break; case SC_A_AUTH_TYPE : msg.getBytes(req.authType()); break; case SC_A_QUERY_STRING : msg.getBytes(req.queryString()); break; case SC_A_JVM_ROUTE : msg.getBytes(req.jvmRoute()); break; case SC_A_SSL_CERT : req.setSecure( true ); // Transform the string into certificate. msg.getBytes(tmpMB); String certString = tmpMB.toString(); 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) { d("Certificate convertion failed" + e ); e.printStackTrace(); } req.setAttribute("javax.servlet.request.X509Certificate", jsseCerts); break; case SC_A_SSL_CIPHER : req.setSecure( true ); msg.getBytes(tmpMB); req.setAttribute("javax.servlet.request.cipher_suite", tmpMB.toString()); break; case SC_A_SSL_SESSION : req.setSecure( true ); msg.getBytes(tmpMB); req.setAttribute("javax.servlet.request.ssl_session", tmpMB.toString()); break; default: return 500; // Error } } return 200; } private void decodeHeaders( Endpoint ep, Msg msg, BaseRequest req ) { // Decode headers MimeHeaders headers = req.headers(); MessageBytes tmpMB=(MessageBytes)ep.getNote( tmpBufNote ); if( tmpMB==null ) { tmpMB=new MessageBytes(); ep.setNote( tmpBufNote, tmpMB); } 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]; } else { msg.getBytes( tmpMB ); hName=tmpMB.toString(); } vMB=headers.addValue( hName ); msg.getBytes(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()); } } } private static final int dL=10; private static void d(String s ) { System.err.println( "HandlerRequest: " + s ); } } 1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/common/MsgAjp.java Index: MsgAjp.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 [EMAIL PROTECTED] * * 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.common; 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; import org.apache.jk.core.*; /** * 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 [[EMAIL PROTECTED]] * @author Dan Milstein [[EMAIL PROTECTED]] * @author Keith Wannamaker [[EMAIL PROTECTED]] * @author Kevin Seguin * @author Costin Manolache */ public class MsgAjp extends Msg { byte buf[]=new byte[8300]; // 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; /** * 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; } /** * 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; int dLen=len-4; buf[0] = (byte)0x41; buf[1] = (byte)0x42; buf[2]= (byte)((dLen>>>8 ) & 0xFF ); buf[3] = (byte)(dLen & 0xFF); } // ============ Data Writing Methods =================== /** * Add an int. * * @param val The integer to write. */ public void appendInt( int val ) { buf[pos++] = (byte) ((val >>> 8) & 0xFF); buf[pos++] = (byte) (val & 0xFF); } public void appendByte( int val ) { buf[pos++] = (byte)val; } public void appendLongInt( int val ) { buf[pos++] = (byte) ((val >>> 24) & 0xFF); buf[pos++] = (byte) ((val >>> 16) & 0xFF); buf[pos++] = (byte) ((val >>> 8) & 0xFF); buf[pos++] = (byte) (val & 0xFF); } /** * 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 appendBytes(MessageBytes mb) throws IOException { if(mb==null || mb.isNull() ) { appendInt( 0); appendByte(0); return; } byte[] bytes = mb.getByteChunk().getBytes(); int start=mb.getByteChunk().getStart(); appendInt( mb.getLength() ); appendBytes(bytes, start, mb.getLength()); appendByte(0); } /** * 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 ) { if( pos + numBytes >= buf.length ) { System.out.println("Buffer overflow " + buf.length + " " + pos + " " + numBytes ); return; } System.arraycopy( b, off, buf, pos, numBytes); pos += numBytes; // buf[pos + numBytes] = 0; // Terminating \0 } // ============ 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 b1 = buf[pos++] & 0xFF; // No swap, Java order int b2 = buf[pos++] & 0xFF; return (b1<<8) + b2; } public int peekInt() { int b1 = buf[pos] & 0xFF; // No swap, Java order int b2 = buf[pos+1] & 0xFF; return (b1<<8) + b2; } public byte getByte() { byte res = buf[pos++]; return res; } public void getBytes(MessageBytes mb) { int length = getInt(); if( (length == 0xFFFF) || (length == -1) ) { mb.setString( null ); return; } mb.setBytes( buf, pos, length ); pos += length; pos++; // Skip the terminating \0 } /** * 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 > buf.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( buf, 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 b1 = buf[pos++] & 0xFF; // No swap, Java order b1 <<= 8; b1 |= (buf[pos++] & 0xFF); b1 <<= 8; b1 |= (buf[pos++] & 0xFF); b1 <<=8; b1 |= (buf[pos++] & 0xFF); return b1; } /** * 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(Channel ch, Endpoint ep) throws IOException { this.end(); // Write the packet header if (dL > 5 ) d("send() " + len + " " + buf[4] ); ch.write( ep, buf, 0, len ); } public int receive(Channel ch, Endpoint ep) throws IOException { if (dL > 0) { d("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. int rd = ch.read(ep, buf, 0, 4 ); // XXX - connection closed (JK_AJP13_COMM_CLOSED) // - connection broken (JK_AJP13_COMM_BROKEN) // if(rd < 0) { // Most likely normal apache restart. return rd; } pos = 0; int mark = getInt(); len = getInt(); if( mark != 0x1234 ) { // XXX Logging System.out.println("BAD packet " + mark); dump( "In: " ); return -1; } if( dL > 5 ) d( "Received " + rd + " " + len + " " + buf[0] ); // XXX check if enough space - it's assert()-ed !!! int total_read = 0; total_read = ch.read(ep, buf, 4, len); if (total_read <= 0) { d("can't read body, waited #" + len); return -1; } if (total_read != len) { d( "incomplete read, waited #" + len + " got only " + total_read); return -2; } if (dL > 0) d("receive: total read = " + total_read); return total_read; } public void dump(String msg) { System.out.println( msg + ": " + buf + " " + pos +"/" + (len + 4)); int max=pos; if( len + 4 > pos ) max=len+4; for( int j=0; j < max; j+=16 ) hexLine( buf, j, len ); System.out.println(); } /* -------------------- Utilities -------------------- */ // XXX Move to util package public static void hexLine( byte buf[], int start, int len ) { for( int i=start; i< start+16 ; i++ ) { if( i < len + 4) System.out.print( hex( buf[i] ) + " "); else System.out.print( " " ); } System.out.print(" | "); for( int i=start; i < start+16 && i < len + 4; i++ ) { if( Character.isLetterOrDigit( (char)buf[i] )) System.out.print( new Character((char)buf[i]) ); else System.out.print( "." ); } System.out.println(); } private static 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 static final int dL=10; private static void d(String s ) { System.err.println( "MsgAjp: " + s ); } } 1.1 jakarta-tomcat-connectors/jk/java/org/apache/jk/common/WorkerDummy.java Index: WorkerDummy.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 [EMAIL PROTECTED] * * 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.common; import java.io.*; import java.net.*; import java.util.*; import org.apache.jk.core.*; import org.apache.tomcat.util.net.*; import org.apache.tomcat.util.buf.*; import org.apache.tomcat.util.log.*; import org.apache.tomcat.util.http.*; import org.apache.tomcat.util.threads.*; /** A dummy worker, will just send back a dummy response. * Used for testing and tunning. */ public class WorkerDummy extends Worker { public WorkerDummy() { String msg="HelloWorld"; byte b[]=msg.getBytes(); body.setBytes(b, 0, b.length); } /* ==================== Start/stop ==================== */ /** Configuration. We'll extract and check the settings. * XXX We should be able to get info from the same * properties file as the C side, so port, etc could be * configured in only one place */ public void validate( Properties p ) { } /** Initialize the worker. After this call the worker will be * ready to accept new requests. */ public void init(WorkerEnv we) throws IOException { headersMsgNote=we.getNoteId( WorkerEnv.ENDPOINT_NOTE, "headerMsg" ); } /** Clean up and stop the worker */ public void destroy() throws IOException { } MessageBytes body=new MessageBytes(); private int headersMsgNote; public void service( BaseRequest req, Channel ch, Endpoint ep ) throws IOException { MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote ); if( msg==null ) { msg=new MsgAjp(); ep.setNote( headersMsgNote, msg ); } msg.reset(); msg.appendByte(HandlerRequest.JK_AJP13_SEND_HEADERS); msg.appendInt(200); msg.appendBytes(null); msg.appendInt(0); msg.send( ch, ep ); msg.dump("out:" ); msg.reset(); msg.appendByte( HandlerRequest.JK_AJP13_SEND_BODY_CHUNK); msg.appendInt( 10 ); msg.appendBytes( body ); msg.send(ch, ep); msg.reset(); msg.appendByte( HandlerRequest.JK_AJP13_END_RESPONSE ); msg.appendInt( 1 ); msg.send(ch, ep ); } private static final int dL=10; private static void d(String s ) { System.err.println( "WorkerDummy: " + s ); } }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>