/* * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net> * Distributed under the terms of either: * - the common development and distribution license (CDDL), v1.0; or * - the GNU Lesser General Public License, v2.1 or later */ package winstone.ajp13; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.Hashtable; import java.util.Map; import winstone.Logger; import winstone.RequestHandlerThread; import winstone.WinstoneException; /** * Models a single incoming ajp13 packet. * * Fixes by Cory Osborn 2007/4/3 - IIS related. Thanks * * @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a> * @version $Id: Ajp13IncomingPacket.java,v 1.6 2007/04/03 01:23:19 rickknowles Exp $ */ public class Ajp13IncomingPacket { // Server originated packet types byte SERVER_FORWARD_REQUEST = 0x02; // public static byte SERVER_SHUTDOWN = 0x07; //not implemented // public static byte SERVER_PING = 0x08; //not implemented // public static byte SERVER_CPING = 0x10; //not implemented private int packetLength; private byte packetBytes[]; private byte packetType; private String method; private String protocol; private String uri; private String remoteAddr; private String remoteHost; private String serverName; private int serverPort; private boolean isSSL; private String headers[]; private Map attributes; /** * Constructor */ public Ajp13IncomingPacket(InputStream in, RequestHandlerThread handler) throws IOException { // Get the incoming packet flag byte headerBuffer[] = new byte[4]; int headerBytesRead = in.read(headerBuffer); handler.setRequestStartTime(); if (headerBytesRead != 4) throw new WinstoneException(Ajp13Listener.AJP_RESOURCES .getString("Ajp13IncomingPacket.InvalidHeader")); else if ((headerBuffer[0] != 0x12) || (headerBuffer[1] != 0x34)) throw new WinstoneException(Ajp13Listener.AJP_RESOURCES .getString("Ajp13IncomingPacket.InvalidHeader")); // Read in the whole packet packetLength = ((headerBuffer[2] & 0xFF) << 8) + (headerBuffer[3] & 0xFF); packetBytes = new byte[packetLength]; int packetBytesRead = in.read(packetBytes); if (packetBytesRead < packetLength) throw new WinstoneException(Ajp13Listener.AJP_RESOURCES .getString("Ajp13IncomingPacket.ShortPacket")); // Ajp13Listener.packetDump(packetBytes, packetBytesRead); } public byte parsePacket(String encoding) throws IOException { int position = 0; this.packetType = packetBytes[position++]; if (this.packetType != SERVER_FORWARD_REQUEST) throw new WinstoneException(Ajp13Listener.AJP_RESOURCES.getString( "Ajp13IncomingPacket.UnknownPacketType", this.packetType + "")); // Check for terminator if (packetBytes[packetLength - 1] != (byte) 255) throw new WinstoneException(Ajp13Listener.AJP_RESOURCES .getString("Ajp13IncomingPacket.InvalidTerminator")); this.method = decodeMethodType(packetBytes[position++]); Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.Method", method); // Protocol int protocolLength = readInteger(position, packetBytes, true); position += 2; this.protocol = (protocolLength > -1) ? readString(position, packetBytes, encoding, protocolLength) : null; position += protocolLength + 1; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.Protocol", protocol); // URI int uriLength = readInteger(position, packetBytes, true); position += 2; this.uri = (uriLength > -1) ? readString(position, packetBytes, encoding, uriLength) : null; position += uriLength + 1; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.URI", uri); // Remote addr int remoteAddrLength = readInteger(position, packetBytes, true); position += 2; this.remoteAddr = (remoteAddrLength > -1) ? readString(position, packetBytes, encoding, remoteAddrLength) : null; position += remoteAddrLength + 1; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.RemoteAddress", remoteAddr); // Remote host int remoteHostLength = readInteger(position, packetBytes, true); position += 2; this.remoteHost = (remoteHostLength > -1) ? readString(position, packetBytes, encoding, remoteHostLength) : null; position += remoteHostLength + 1; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.RemoteHost", remoteHost); // Server name int serverNameLength = readInteger(position, packetBytes, true); position += 2; this.serverName = (serverNameLength > -1) ? readString(position, packetBytes, encoding, serverNameLength) : null; position += serverNameLength + 1; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.ServerName", serverName); this.serverPort = readInteger(position, packetBytes, false); position += 2; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.ServerPort", "" + serverPort); this.isSSL = readBoolean(position++, packetBytes); Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.SSL", "" + isSSL); // Read headers int headerCount = readInteger(position, packetBytes, false); Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.HeaderCount", "" + headerCount); position += 2; this.headers = new String[headerCount]; for (int n = 0; n < headerCount; n++) { // Header name int headerTypeOrLength = readInteger(position, packetBytes, false); position += 2; String headerName = null; if (packetBytes[position - 2] == (byte) 0xA0) headerName = decodeHeaderType(headerTypeOrLength); else { headerName = readString(position, packetBytes, encoding, headerTypeOrLength); position += headerTypeOrLength + 1; } // Header value int headerValueLength = readInteger(position, packetBytes, true); position += 2; this.headers[n] = headerName + ": " + ((headerValueLength > -1) ? readString(position, packetBytes, encoding, headerValueLength) : ""); position += headerValueLength + 1; Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.Header", this.headers[n]); } // Attribute parsing this.attributes = new Hashtable(); while (position < packetLength - 2) { String attName = decodeAttributeType(packetBytes[position++]); int attValueLength = readInteger(position, packetBytes, true); position += 2; String attValue = (attValueLength > -1) ? readString(position, packetBytes, encoding, attValueLength) : null; position += attValueLength + 1; this.attributes.put(attName, attValue); Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.Attribute", new String[] { attName, attValue }); } Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES, "Ajp13IncomingPacket.SuccessfullyReadRequest", "" + packetLength); return this.packetType; } public int getPacketLength() { return this.packetLength; } public String getMethod() { return this.method; } public String getProtocol() { return this.protocol; } public String getURI() { return this.uri; } public String getRemoteAddress() { return this.remoteAddr; } public String getRemoteHost() { return this.remoteHost; } public String getServerName() { return this.serverName; } public int getServerPort() { return this.serverPort; } public boolean isSSL() { return this.isSSL; } public String[] getHeaders() { return this.headers; } public Map getAttributes() { return this.attributes; } /** * Read a single integer from the stream */ private int readInteger(int position, byte packet[], boolean forStringLength) { if (forStringLength && (packet[position] == (byte) 0xFF) && (packet[position + 1] == (byte) 0xFF)) return -1; else return ((packet[position] & 0xFF) << 8) + (packet[position + 1] & 0xFF); } /** * Read a single boolean from the stream */ private boolean readBoolean(int position, byte packet[]) { return (packet[position] == (byte) 1); } /** * Read a single string from the stream */ private String readString(int position, byte packet[], String encoding, int length) throws UnsupportedEncodingException { // System.out.println("Reading string length: " + length + // " position=" + position + " packetLength=" + packet.length); return length == 0 ? "" : new String(packet, position, length, encoding); } /** * Decodes the method types into Winstone HTTP method strings */ private String decodeMethodType(byte methodType) { switch (methodType) { case 1: return "OPTIONS"; case 2: return "GET"; case 3: return "HEAD"; case 4: return "POST"; case 5: return "PUT"; case 6: return "DELETE"; case 7: return "TRACE"; case 8: return "PROPFIND"; case 9: return "PROPPATCH"; case 10: return "MKCOL"; case 11: return "COPY"; case 12: return "MOVE"; case 13: return "LOCK"; case 14: return "UNLOCK"; case 15: return "ACL"; case 16: return "REPORT"; case 17: return "VERSION-CONTROL"; case 18: return "CHECKIN"; case 19: return "CHECKOUT"; case 20: return "UNCHECKOUT"; case 21: return "SEARCH"; case 22: return "MKWORKSPACE"; case 23: return "UPDATE"; case 24: return "LABEL"; case 25: return "MERGE"; case 26: return "BASELINE_CONTROL"; case 27: return "MKACTIVITY"; default: return "UNKNOWN"; } } /** * Decodes the header types into Winstone HTTP header strings */ private String decodeHeaderType(int headerType) { switch (headerType) { case 0xA001: return "Accept"; case 0xA002: return "Accept-Charset"; case 0xA003: return "Accept-Encoding"; case 0xA004: return "Accept-Language"; case 0xA005: return "Authorization"; case 0xA006: return "Connection"; case 0xA007: return "Content-Type"; case 0xA008: return "Content-Length"; case 0xA009: return "Cookie"; case 0xA00A: return "Cookie2"; case 0xA00B: return "Host"; case 0xA00C: return "Pragma"; case 0xA00D: return "Referer"; case 0xA00E: return "User-Agent"; default: return null; } } /** * Decodes the header types into Winstone HTTP header strings */ private String decodeAttributeType(byte attributeType) { switch (attributeType) { case 0x01: return "context"; case 0x02: return "servlet_path"; case 0x03: return "remote_user"; case 0x04: return "auth_type"; case 0x05: return "query_string"; case 0x06: return "jvm_route"; case 0x07: return "ssl_cert"; case 0x08: return "ssl_cipher"; case 0x09: return "ssl_session"; case 0x0A: return "req_attribute"; default: return null; } } }