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

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

There is a newer version: 11.0.0.beta1
Show newest version
//
//  ========================================================================
//  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.io.EofException;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
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 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() { if (!_closed) { _closed = true; try { _channel.getResponse().closeOutput(); } catch(IOException e) { _channel.failed(); LOG.ignore(e); } 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.failed(); 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 isAllContentWritten() { Response response=_channel.getResponse(); return response.isAllContentWritten(_written); } public void closeOutput() throws IOException { _channel.getResponse().closeOutput(); } @Override public void write(byte[] b, int off, int len) throws IOException { if (isClosed()) throw new EofException("Closed"); _written+=len; boolean complete=_channel.getResponse().isAllContentWritten(_written); int capacity = getBufferSize(); // Should we aggregate? if (!complete && len<=capacity/4) { if (_aggregate == null) _aggregate = _channel.getByteBufferPool().acquire(capacity, false); // YES - fill the aggregate with content from the buffer int filled = BufferUtil.fill(_aggregate, b, off, len); // return if we are not complete, not full and filled all the content if (!complete && filled == len && !BufferUtil.isFull(_aggregate)) return; // adjust offset/length off += filled; len -= filled; } // flush any content from the aggregate if (BufferUtil.hasContent(_aggregate)) { _channel.write(_aggregate, complete && len==0); // should we fill aggregate again from the buffer? if (len>0 && !complete && len<=_aggregate.capacity()/4) { BufferUtil.append(_aggregate, b, off, len); return; } } // write any remaining content in the buffer directly if (len>0) // pass as readonly to avoid space stealing optimisation in HttpConnection _channel.write(ByteBuffer.wrap(b, off, len).asReadOnlyBuffer(), complete); else if (complete) _channel.write(BufferUtil.EMPTY_BUFFER,complete); if (complete) closed(); } @Override public void write(int b) throws IOException { if (isClosed()) throw new EOFException("Closed"); // Allocate an aggregate buffer. // Never direct as it is slow to do little writes to a direct buffer. if (_aggregate == null) _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false); BufferUtil.append(_aggregate, (byte)b); _written++; boolean complete=_channel.getResponse().isAllContentWritten(_written); // Check if all written or full if (complete || BufferUtil.isFull(_aggregate)) { BlockingCallback callback = _channel.getWriteBlockingCallback(); _channel.write(_aggregate, false, callback); callback.block(); if (complete) closed(); } } @Override public void print(String s) throws IOException { if (isClosed()) throw new IOException("Closed"); write(s.getBytes(_channel.getResponse().getCharacterEncoding())); } /* ------------------------------------------------------------ */ /** Set headers and send content. * @deprecated Use {@link Response#setHeaders(HttpContent)} and {@link #sendContent(HttpContent)} instead. * @param content * @throws IOException */ @Deprecated public void sendContent(Object content) throws IOException { final BlockingCallback callback =_channel.getWriteBlockingCallback(); if (content instanceof HttpContent) { _channel.getResponse().setHeaders((HttpContent)content); sendContent((HttpContent)content,callback); } else if (content instanceof Resource) { Resource resource = (Resource)content; _channel.getResponse().getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, resource.lastModified()); ReadableByteChannel in=((Resource)content).getReadableByteChannel(); if (in!=null) sendContent(in,callback); else sendContent(resource.getInputStream(),callback); } else if (content instanceof ByteBuffer) { sendContent((ByteBuffer)content,callback); } else if (content instanceof ReadableByteChannel) { sendContent((ReadableByteChannel)content,callback); } else if (content instanceof InputStream) { sendContent((InputStream)content,callback); } else callback.failed(new IllegalArgumentException("unknown content type "+content.getClass())); callback.block(); } /* ------------------------------------------------------------ */ /** Blocking send of content. * @param content The content to send. * @throws IOException */ public void sendContent(ByteBuffer content) throws IOException { final BlockingCallback callback =_channel.getWriteBlockingCallback(); if (content.hasArray()&&content.limit()




© 2015 - 2024 Weber Informatics LLC | Privacy Policy