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

org.eclipse.jetty.server.HttpOutput Maven / Gradle / Ivy

//
//  ========================================================================
//  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.server;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;

/**
 * 

{@link HttpOutput} implements {@link ServletOutputStream} * as required by the Servlet specification.

*

{@link HttpOutput} buffers content written by the application until a * further write will overflow the buffer, at which point it triggers a commit * of the response.

*

{@link HttpOutput} can be closed and reopened, to allow requests included * via {@link RequestDispatcher#include(ServletRequest, ServletResponse)} to * close the stream, to be reopened after the inclusion ends.

*/ public class HttpOutput extends ServletOutputStream { private static final boolean OUTPUT_BUFFER_DIRECT=false; private static final boolean CHANNEL_BUFFER_DIRECT=true; private static final boolean STREAM_BUFFER_DIRECT=false; private static Logger LOG = Log.getLogger(HttpOutput.class); private final HttpChannel _channel; private boolean _closed; private long _written; private ByteBuffer _aggregate; private int _bufferSize; public HttpOutput(HttpChannel channel) { _channel = channel; _bufferSize = _channel.getHttpConfiguration().getOutputBufferSize(); } public boolean isWritten() { return _written > 0; } public long getWritten() { return _written; } public void reset() { _written = 0; reopen(); } public void reopen() { _closed = false; } /** Called by the HttpChannel if the output was closed * externally (eg by a 500 exception handling). */ void closed() { _closed = true; releaseBuffer(); } @Override public void close() { if (!isClosed()) { try { if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, !_channel.getResponse().isIncluding()); else _channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding()); } catch(IOException e) { _channel.getEndPoint().shutdownOutput(); LOG.ignore(e); } } closed(); } private void releaseBuffer() { if (_aggregate != null) { _channel.getConnector().getByteBufferPool().release(_aggregate); _aggregate = null; } } public boolean isClosed() { return _closed; } @Override public void flush() throws IOException { if (isClosed()) return; if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, false); else _channel.write(BufferUtil.EMPTY_BUFFER, false); } public boolean closeIfAllContentWritten() throws IOException { return _channel.getResponse().closeIfAllContentWritten(_written); } @Override public void write(byte[] b, int off, int len) throws IOException { if (isClosed()) throw new EOFException("Closed"); // Do we have an aggregate buffer already ? if (_aggregate == null) { // What size should the aggregate be ? int size = getBufferSize(); // If this write would fill more than half the aggregate, just write it directly if (len > size / 2) { _channel.write(ByteBuffer.wrap(b, off, len), false); _written += len; return; } // Allocate an aggregate buffer _aggregate = _channel.getByteBufferPool().acquire(size, OUTPUT_BUFFER_DIRECT); } // Do we have space to aggregate ? int space = BufferUtil.space(_aggregate); if (len > space) { // No space so write the aggregate out if it is not empty if (BufferUtil.hasContent(_aggregate)) { _channel.write(_aggregate, false); space = BufferUtil.space(_aggregate); } } // Do we have space to aggregate now ? if (len > space) { // No space so write the content directly _channel.write(ByteBuffer.wrap(b, off, len), false); _written += len; return; } // Aggregate the content BufferUtil.append(_aggregate, b, off, len); _written += len; // Check if all written or full if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate)) _channel.write(_aggregate, false); } @Override public void write(int b) throws IOException { if (isClosed()) throw new EOFException("Closed"); if (_aggregate == null) _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), OUTPUT_BUFFER_DIRECT); BufferUtil.append(_aggregate, (byte)b); _written++; // Check if all written or full if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate)) _channel.write(_aggregate, false); } @Override public void print(String s) throws IOException { if (isClosed()) throw new IOException("Closed"); write(s.getBytes(_channel.getResponse().getCharacterEncoding())); } public void sendContent(Object content) throws IOException { if (isClosed()) throw new IOException("Closed"); if (content instanceof HttpContent) { HttpContent httpContent = (HttpContent)content; Response response = _channel.getResponse(); String contentType = httpContent.getContentType(); if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString())) response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType); if (httpContent.getContentLength() > 0) response.getHttpFields().putLongField(HttpHeader.CONTENT_LENGTH, httpContent.getContentLength()); String lm = httpContent.getLastModified(); if (lm != null) response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm); else if (httpContent.getResource() != null) { long lml = httpContent.getResource().lastModified(); if (lml != -1) response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml); } String etag=httpContent.getETag(); if (etag!=null) response.getHttpFields().put(HttpHeader.ETAG,etag); content = httpContent.getDirectBuffer(); if (content == null) content = httpContent.getIndirectBuffer(); if (content == null) content = httpContent.getReadableByteChannel(); if (content == null) content = httpContent.getInputStream(); } else if (content instanceof Resource) { Resource resource = (Resource)content; _channel.getResponse().getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, resource.lastModified()); content = resource.getInputStream(); } // Process content. if (content instanceof ByteBuffer) { _channel.write((ByteBuffer)content, true); _closed=true; } else if (content instanceof ReadableByteChannel) { ReadableByteChannel channel = (ReadableByteChannel)content; ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), CHANNEL_BUFFER_DIRECT); try { while(channel.isOpen()) { int pos = BufferUtil.flipToFill(buffer); int len=channel.read(buffer); if (len<0) break; BufferUtil.flipToFlush(buffer,pos); _channel.write(buffer,false); } } finally { close(); _channel.getByteBufferPool().release(buffer); } } else if (content instanceof InputStream) { InputStream in = (InputStream)content; ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), STREAM_BUFFER_DIRECT); byte[] array = buffer.array(); int offset=buffer.arrayOffset(); int size=array.length-offset; try { while(true) { int len=in.read(array,offset,size); if (len<0) break; buffer.position(0); buffer.limit(len); _channel.write(buffer,false); } } finally { close(); _channel.getByteBufferPool().release(buffer); } } else throw new IllegalArgumentException("unknown content type "+content.getClass()); } public int getBufferSize() { return _bufferSize; } public void setBufferSize(int size) { this._bufferSize = size; } public void resetBuffer() { if (BufferUtil.hasContent(_aggregate)) BufferUtil.clear(_aggregate); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy