Package winstone.ajp13

Source Code of winstone.ajp13.Ajp13OutputStream

/*
* 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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.Cookie;

import winstone.Logger;
import winstone.WinstoneException;
import winstone.WinstoneOutputStream;

/**
* Extends the winstone output stream, so that the ajp13 protocol requirements
* can be fulfilled.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: Ajp13OutputStream.java,v 1.7 2007/05/05 00:52:50 rickknowles Exp $
*/
public class Ajp13OutputStream extends WinstoneOutputStream {
    // Container originated packet types
    byte CONTAINER_SEND_BODY_CHUNK = 0x03;
    byte CONTAINER_SEND_HEADERS = 0x04;
    byte CONTAINER_END_RESPONSE = 0x05;

    // byte CONTAINER_GET_BODY_CHUNK = 0x06;
    // byte CONTAINER_CPONG_REPLY = 0x09;

    static Map headerCodes = null;

    static {
        headerCodes = new Hashtable();
        headerCodes.put("content-type", new byte[] { (byte) 0xA0, 0x01 });
        headerCodes.put("content-language", new byte[] { (byte) 0xA0, 0x02 });
        headerCodes.put("content-length", new byte[] { (byte) 0xA0, 0x03 });
        headerCodes.put("date", new byte[] { (byte) 0xA0, 0x04 });
        headerCodes.put("last-modified", new byte[] { (byte) 0xA0, 0x05 });
        headerCodes.put("location", new byte[] { (byte) 0xA0, 0x06 });
        headerCodes.put("set-cookie", new byte[] { (byte) 0xA0, 0x07 });
        headerCodes.put("set-cookie2", new byte[] { (byte) 0xA0, 0x08 });
        headerCodes.put("servlet-engine", new byte[] { (byte) 0xA0, 0x09 });
        headerCodes.put("server", new byte[] { (byte) 0xA0, 0x09 });
        headerCodes.put("status", new byte[] { (byte) 0xA0, 0x0A });
        headerCodes.put("www-authenticate", new byte[] { (byte) 0xA0, 0x0B });
    }

    private String headerEncoding;

    public Ajp13OutputStream(OutputStream outStream, String headerEncoding) {
        super(outStream, false);
        this.headerEncoding = headerEncoding;
    }

    public void commit() throws IOException {
        Logger.log(Logger.FULL_DEBUG, Ajp13Listener.AJP_RESOURCES,
                "Ajp13OutputStream.CommittedBytes", "" + this.bytesCommitted);
        this.buffer.flush();

        // If we haven't written the headers yet, write them out
        if (!this.committed) {
            this.owner.validateHeaders();
            this.committed = true;

            ByteArrayOutputStream headerArrayStream = new ByteArrayOutputStream();
            for (Iterator i = this.owner.getHeaders().iterator(); i.hasNext();) {
                String header = (String) i.next();
                int colonPos = header.indexOf(':');
                if (colonPos == -1)
                    throw new WinstoneException(Ajp13Listener.AJP_RESOURCES.getString(
                            "Ajp13OutputStream.NoColonHeader", header));
                String headerName = header.substring(0, colonPos).trim();
                String headerValue = header.substring(colonPos + 1).trim();
                byte headerCode[] = (byte[]) headerCodes.get(headerName
                        .toLowerCase());
                if (headerCode == null) {
                    headerArrayStream.write(getStringBlock(headerName));
                } else {
                    headerArrayStream.write(headerCode);
                }
                headerArrayStream.write(getStringBlock(headerValue));
            }

            for (Iterator i = this.owner.getCookies().iterator(); i.hasNext();) {
                Cookie cookie = (Cookie) i.next();
                String cookieText = this.owner.writeCookie(cookie);
                int colonPos = cookieText.indexOf(':');
                if (colonPos == -1)
                    throw new WinstoneException(Ajp13Listener.AJP_RESOURCES.getString(
                            "Ajp13OutputStream.NoColonHeader", cookieText));
                String headerName = cookieText.substring(0, colonPos).trim();
                String headerValue = cookieText.substring(colonPos + 1).trim();
                byte headerCode[] = (byte[]) headerCodes.get(headerName.toLowerCase());
                if (headerCode == null) {
                    headerArrayStream.write(getStringBlock(headerName));
                } else {
                    headerArrayStream.write(headerCode);
                }
                headerArrayStream.write(getStringBlock(headerValue));
            }

            // Write packet header + prefix + status code + status msg + header
            // count
            byte headerArray[] = headerArrayStream.toByteArray();
            byte headerPacket[] = new byte[12];
            headerPacket[0] = (byte) 0x41;
            headerPacket[1] = (byte) 0x42;
            setIntBlock(headerArray.length + 8, headerPacket, 2);
            headerPacket[4] = CONTAINER_SEND_HEADERS;
            setIntBlock(this.owner.getStatus(), headerPacket, 5);
            setIntBlock(0, headerPacket, 7); // empty msg
            headerPacket[9] = (byte) 0x00;
            setIntBlock(this.owner.getHeaders().size()
                    + this.owner.getCookies().size(), headerPacket, 10);

            // Ajp13Listener.packetDump(headerPacket, headerPacket.length);
            // Ajp13Listener.packetDump(headerArray, headerArray.length);

            this.outStream.write(headerPacket);
            this.outStream.write(headerArray);
        }

        // Write out the contents of the buffer in max 8k chunks
        byte bufferContents[] = this.buffer.toByteArray();
        int position = 0;
        while (position < bufferContents.length) {
            int packetLength = Math.min(bufferContents.length - position, 8184);
            byte responsePacket[] = new byte[packetLength + 8];
            responsePacket[0] = 0x41;
            responsePacket[1] = 0x42;
            setIntBlock(packetLength + 4, responsePacket, 2);
            responsePacket[4] = CONTAINER_SEND_BODY_CHUNK;
            setIntBlock(packetLength, responsePacket, 5);
            System.arraycopy(bufferContents, position, responsePacket, 7, packetLength);
            responsePacket[packetLength + 7] = 0x00;
            position += packetLength;

            // Ajp13Listener.packetDump(responsePacket, responsePacket.length);
            this.outStream.write(responsePacket);
        }

        this.buffer.reset();
        this.bufferPosition = 0;
    }

    public void finishResponse() throws IOException {
        // Send end response packet
        byte endResponse[] = new byte[] { 0x41, 0x42, 0x00, 0x02,
                CONTAINER_END_RESPONSE, 1 };
        // Ajp13Listener.packetDump(endResponse, endResponse.length);
        this.outStream.write(endResponse);
    }

    /**
     * Useful generic method for getting ajp13 format integers in a packet.
     */
    public byte[] getIntBlock(int integer) {
        byte hi = (byte) (0xFF & (integer >> 8));
        byte lo = (byte) (0xFF & (integer - (hi << 8)));
        return new byte[] { hi, lo };
    }

    /**
     * Useful generic method for setting ajp13 format integers in a packet.
     */
    public static void setIntBlock(int integer, byte packet[], int offset) {
        byte hi = (byte) (0xFF & (integer >> 8));
        byte lo = (byte) (0xFF & (integer - (hi << 8)));
        packet[offset] = hi;
        packet[offset + 1] = lo;
    }

    /**
     * Useful generic method for getting ajp13 format strings in a packet.
     */
    public byte[] getStringBlock(String text)
            throws UnsupportedEncodingException {
        byte textBytes[] = text.getBytes(headerEncoding);
        byte outArray[] = new byte[textBytes.length + 3];
        System.arraycopy(getIntBlock(textBytes.length), 0, outArray, 0, 2);
        System.arraycopy(textBytes, 0, outArray, 2, textBytes.length);
        outArray[textBytes.length + 2] = 0x00;
        return outArray;
    }

}
TOP

Related Classes of winstone.ajp13.Ajp13OutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.