net.paoding.spdy.server.tomcat.impl.supports.SpdyOutputBuffer.java Source code

Java tutorial

Introduction

Here is the source code for net.paoding.spdy.server.tomcat.impl.supports.SpdyOutputBuffer.java

Source

/*
 * Copyright 2010-2011 the original author or authors.
 *
 * 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 i 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 net.paoding.spdy.server.tomcat.impl.supports;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import net.paoding.spdy.common.frame.frames.DataFrame;
import net.paoding.spdy.common.frame.frames.SpdyFrame;
import net.paoding.spdy.common.frame.frames.SynStream;
import net.paoding.spdy.server.tomcat.impl.hook.ClientFlush;
import net.paoding.spdy.server.tomcat.impl.hook.Close;

import org.apache.catalina.connector.CoyoteAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.coyote.ActionCode;
import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
import org.apache.tomcat.util.buf.ByteChunk;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.Channels;

/**
 * 
 * @author qieqie.wang@gmail.com
 * 
 */
public class SpdyOutputBuffer implements OutputBuffer {

    private static Log logger = LogFactory.getLog(SpdyOutputBuffer.class);

    private ChannelBuffer delay;

    private boolean fin = false;

    private final boolean debugEnabled = logger.isDebugEnabled();

    public SpdyOutputBuffer() {
    }

    @Override
    public int doWrite(ByteChunk chunk, Response coyoteResponse) throws IOException {
        int chunkLength = chunk.getLength();
        if (chunkLength <= 0) {
            return 0;
        }
        // @see org.apache.catalina.connector.CoyoteAdapter#service
        HttpServletResponse response = (HttpServletResponse) coyoteResponse.getNote(CoyoteAdapter.ADAPTER_NOTES);
        // chunk.getBuffer()byte?write
        if (response != null && chunk.getStart() + chunkLength == response.getBufferSize()) {
            byte[] copied = new byte[chunkLength];
            System.arraycopy(chunk.getBuffer(), chunk.getStart(), copied, 0, chunkLength);
            delay = ChannelBuffers.wrappedBuffer(copied);
            if (debugEnabled) {
                logger.debug("writing copied buffer: offset=" + chunk.getStart() + " len=" + chunkLength
                        + "  outputBufferSize=" + response.getBufferSize());
            }
            flush(coyoteResponse);
        }
        // ????write??chunk.bufferbyte
        else {
            if (fin) {
                throw new Error("spdy buffer error: buffer closed");
            }
            if (debugEnabled) {
                logger.debug("writing writing buffer: offset=" + chunk.getStart() + " len=" + chunkLength);
            }
            delay = ChannelBuffers.wrappedBuffer(chunk.getBuffer(), chunk.getStart(), chunkLength);
            flushAndClose(coyoteResponse);
        }
        return chunk.getLength();
    }

    /**
     * {@link ClientFlush}
     * 
     * @param response
     */
    public ChannelFuture flush(Response response) {
        return flush(response, false);
    }

    private ChannelFuture flush(Response response, boolean last) {
        if (delay != null) {
            if (!response.isCommitted()) {
                response.action(ActionCode.ACTION_COMMIT, null);
            }
            ChannelBuffer data = this.delay;
            this.delay = null;
            DataFrame frame = createDataFrame(response, data);
            if (last) {
                frame.setFlags(SpdyFrame.FLAG_FIN);
                fin = true;
                if (logger.isInfoEnabled()) {
                    logger.info("fin response (by last): " + response.getRequest());
                }
            }
            if (debugEnabled) {
                logger.debug("flush buffer: " + frame);
            }
            return Channels.write(frame.getChannel(), frame);
        }
        return null;
    }

    /**
     * {@link Close}
     * 
     * @param response
     */
    public void flushAndClose(Response response) {
        if (fin) {
            return;
        }
        if (delay == null) {
            delay = ChannelBuffers.EMPTY_BUFFER;
        }
        flush(response, true);
    }

    public void reset() {
        this.delay = null;
    }

    private DataFrame createDataFrame(Response response, ChannelBuffer data) {
        DataFrame frame = new DataFrame();
        SynStream synStream = CoyoteAttributes.getSynStream(response.getRequest());
        frame.setStreamId(synStream.getStreamId());
        frame.setChannel(synStream.getChannel());
        frame.setData(data);
        return frame;
    }

}