All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.grizzly.http.jk.common.JkInputStream Maven / Gradle / Ivy

There is a newer version: 1.9.65
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.grizzly.http.jk.common;

import com.sun.grizzly.http.jk.core.Msg;
import com.sun.grizzly.http.jk.core.MsgContext;
import java.io.IOException;

import com.sun.grizzly.tcp.OutputBuffer;
import com.sun.grizzly.tcp.InputBuffer;
import com.sun.grizzly.tcp.Request;
import com.sun.grizzly.tcp.Response;

import com.sun.grizzly.util.LoggerUtils;

import com.sun.grizzly.util.buf.ByteChunk;
import com.sun.grizzly.util.buf.MessageBytes;
import com.sun.grizzly.util.buf.C2BConverter;
import com.sun.grizzly.util.http.HttpMessages;
import com.sun.grizzly.util.http.MimeHeaders;
import java.util.logging.Level;

/** Generic input stream impl on top of ajp
 */
public class JkInputStream implements InputBuffer, OutputBuffer {

    private Msg bodyMsg;
    private Msg outputMsg;
    private MsgContext mc;
    // Holds incoming chunks of request body data
    private MessageBytes bodyBuff = MessageBytes.newInstance();
    private MessageBytes tempMB = MessageBytes.newInstance();
    private boolean end_of_stream = false;
    private boolean isEmpty = true;
    private boolean isFirst = true;
    private boolean isReplay = false;
    private boolean isReadRequired = false;

    static {
        // Make certain HttpMessages is loaded for SecurityManager
        try {
            Class.forName("com.sun.grizzly.util.http.HttpMessages");
        } catch (Exception ex) {
        // ignore
        }
    }

    public JkInputStream(MsgContext context, int bsize) {
        mc = context;
        bodyMsg = new MsgAjp(bsize);
        outputMsg = new MsgAjp(bsize);
    }

    /**
     * @deprecated
     */
    public JkInputStream(MsgContext context) {
        this(context, 8 * 1024);
    }

    // -------------------- Jk specific methods --------------------
    /**
     * Set the flag saying that the server is sending a body
     */
    public void setIsReadRequired(boolean irr) {
        isReadRequired = irr;
    }

    /**
     * Return the flag saying that the server is sending a body
     */
    public boolean isReadRequired() {
        return isReadRequired;
    }

    /** Must be called before or after each request
     */
    public void recycle() {
        if (isReadRequired && isFirst) {
            // The Servlet never read the request body, so we need to junk it
            try {
                receive();
            } catch (IOException iex) {
                LoggerUtils.getLogger().log(Level.FINEST, "Error consuming request body", iex);
            }
        }

        end_of_stream = false;
        isEmpty = true;
        isFirst = true;
        isReplay = false;
        isReadRequired = false;
        bodyBuff.recycle();
        tempMB.recycle();
    }

    public void endMessage() throws IOException {
        outputMsg.reset();
        outputMsg.appendByte(AjpConstants.JK_AJP13_END_RESPONSE);
        outputMsg.appendByte(1);
        mc.getSource().send(outputMsg, mc);
        mc.getSource().flush(outputMsg, mc);
    }


    // -------------------- OutputBuffer implementation --------------------
    public int doWrite(ByteChunk chunk, Response res)
            throws IOException {
        if (!res.isCommitted()) {
            // Send the connector a request for commit. The connector should
            // then validate the headers, send them (using sendHeader) and 
            // set the filters accordingly.
            res.sendHeaders();
        }

        int len = chunk.getLength();
        byte buf[] = outputMsg.getBuffer();
        // 4 - hardcoded, byte[] marshalling overhead 
        int chunkSize = buf.length - outputMsg.getHeaderLength() - 4;
        int off = 0;
        while (len > 0) {
            int thisTime = len;
            if (thisTime > chunkSize) {
                thisTime = chunkSize;
            }
            len -= thisTime;

            outputMsg.reset();
            outputMsg.appendByte(AjpConstants.JK_AJP13_SEND_BODY_CHUNK);
            outputMsg.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
            off += thisTime;
            mc.getSource().send(outputMsg, mc);
        }
        return 0;
    }

    public int doRead(ByteChunk responseChunk, Request req)
            throws IOException {

        if (LoggerUtils.getLogger().isLoggable(Level.FINEST)) {
            LoggerUtils.getLogger().log(Level.FINEST, "doRead " + end_of_stream +
                    " " + responseChunk.getOffset() + " " + responseChunk.getLength());
        }
        if (end_of_stream) {
            return -1;
        }

        if (isFirst && isReadRequired) {
            // Handle special first-body-chunk, but only if httpd expects it.
            if (!receive()) {
                return 0;
            }
        } else if (isEmpty) {
            if (!refillReadBuffer()) {
                return -1;
            }
        }
        ByteChunk bc = bodyBuff.getByteChunk();
        responseChunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
        isEmpty = true;
        return responseChunk.getLength();
    }

    /** Receive a chunk of data. Called to implement the
     *  'special' packet in ajp13 and to receive the data
     *  after we send a GET_BODY packet
     */
    public boolean receive() throws IOException {
        isFirst = false;
        bodyMsg.reset();
        int err = mc.getSource().receive(bodyMsg, mc);
        if (LoggerUtils.getLogger().isLoggable(Level.FINEST)) {
            LoggerUtils.getLogger().info("Receiving: getting request body chunk " + err + " " + bodyMsg.getLen());
        }

        if (err < 0) {
            throw new IOException();
        }

        // No data received.
        if (bodyMsg.getLen() == 0) { // just the header
            // Don't mark 'end of stream' for the first chunk.
            // end_of_stream = true;
            return false;
        }
        int blen = bodyMsg.peekInt();

        if (blen == 0) {
            return false;
        }

        bodyMsg.getBytes(bodyBuff);
        isEmpty = false;
        return true;
    }

    /**
     * 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.    
     */
    private boolean refillReadBuffer() throws IOException {
        // If the server returns an empty packet, assume that that end of
        // the stream has been reached (yuck -- fix protocol??).
        if (isReplay) {
            end_of_stream = true; // we've read everything there is
        }
        if (end_of_stream) {
            if (LoggerUtils.getLogger().isLoggable(Level.FINEST)) {
                LoggerUtils.getLogger().log(Level.FINEST, "refillReadBuffer: end of stream ");
            }
            return false;
        }

        // Why not use outBuf??
        bodyMsg.reset();
        bodyMsg.appendByte(AjpConstants.JK_AJP13_GET_BODY_CHUNK);
        bodyMsg.appendInt(AjpConstants.MAX_READ_SIZE);

        if (LoggerUtils.getLogger().isLoggable(Level.FINEST)) {
            LoggerUtils.getLogger().log(Level.FINEST, "refillReadBuffer " + Thread.currentThread());
        }

        mc.getSource().send(bodyMsg, mc);
        mc.getSource().flush(bodyMsg, mc); // Server needs to get it

        // In JNI mode, response will be in bodyMsg. In TCP mode, response need to be
        // read

        boolean moreData = receive();
        if (!moreData) {
            end_of_stream = true;
        }
        return moreData;
    }

    public void appendHead(Response res) throws IOException {
        if (LoggerUtils.getLogger().isLoggable(Level.FINEST)) {
            LoggerUtils.getLogger().log(Level.FINEST, "COMMIT sending headers " + res + " " + res.getMimeHeaders());
        }

        C2BConverter c2b = mc.getConverter();

        outputMsg.reset();
        outputMsg.appendByte(AjpConstants.JK_AJP13_SEND_HEADERS);
        outputMsg.appendInt(res.getStatus());

        String message = null;
      //  if (com.sun.grizzly.tcp.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
        message = res.getMessage();
    //    }
        if (message == null) {
            message = HttpMessages.getMessage(res.getStatus());
        } else {
            message = message.replace('\n', ' ').replace('\r', ' ');
        }
        tempMB.setString(message);
        c2b.convert(tempMB);
        outputMsg.appendBytes(tempMB);

        // XXX add headers

        MimeHeaders headers = res.getMimeHeaders();
        String contentType = res.getContentType();
        if (contentType != null) {
            headers.setValue("Content-Type").setString(contentType);
        }
        String contentLanguage = res.getContentLanguage();
        if (contentLanguage != null) {
            headers.setValue("Content-Language").setString(contentLanguage);
        }
        long contentLength = res.getContentLengthLong();
        if (contentLength >= 0) {
            headers.setValue("Content-Length").setLong(contentLength);
        }
        int numHeaders = headers.size();
        outputMsg.appendInt(numHeaders);
        for (int i = 0; i < numHeaders; i++) {
            MessageBytes hN = headers.getName(i);
            // no header to sc conversion - there's little benefit
            // on this direction
            c2b.convert(hN);
            outputMsg.appendBytes(hN);

            MessageBytes hV = headers.getValue(i);
            c2b.convert(hV);
            outputMsg.appendBytes(hV);
        }
        mc.getSource().send(outputMsg, mc);
    }

    /**
     * Set the replay buffer for Form auth
     */
    public void setReplay(ByteChunk replay) {
        isFirst = false;
        isEmpty = false;
        isReplay = true;
        bodyBuff.setBytes(replay.getBytes(), replay.getStart(), replay.getLength());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy