Search in sources :

Example 1 with MultiPartOutputStream

use of org.eclipse.jetty.util.MultiPartOutputStream in project jetty.project by eclipse.

the class ResourceService method sendData.

/* ------------------------------------------------------------ */
protected boolean sendData(HttpServletRequest request, HttpServletResponse response, boolean include, final HttpContent content, Enumeration<String> reqRanges) throws IOException {
    final long content_length = content.getContentLengthValue();
    // Get the output stream (or writer)
    OutputStream out = null;
    boolean written;
    try {
        out = response.getOutputStream();
        // has something already written to the response?
        written = out instanceof HttpOutput ? ((HttpOutput) out).isWritten() : true;
    } catch (IllegalStateException e) {
        out = new WriterOutputStream(response.getWriter());
        // there may be data in writer buffer, so assume written
        written = true;
    }
    if (LOG.isDebugEnabled())
        LOG.debug(String.format("sendData content=%s out=%s async=%b", content, out, request.isAsyncSupported()));
    if (reqRanges == null || !reqRanges.hasMoreElements() || content_length < 0) {
        //  if there were no ranges, send entire entity
        if (include) {
            // write without headers
            content.getResource().writeTo(out, 0, content_length);
        } else // else if we can't do a bypass write because of wrapping
        if (written || !(out instanceof HttpOutput)) {
            // write normally
            putHeaders(response, content, written ? -1 : 0);
            ByteBuffer buffer = content.getIndirectBuffer();
            if (buffer != null)
                BufferUtil.writeTo(buffer, out);
            else
                content.getResource().writeTo(out, 0, content_length);
        } else // else do a bypass write
        {
            // write the headers
            putHeaders(response, content, 0);
            // write the content asynchronously if supported
            if (request.isAsyncSupported() && content.getContentLengthValue() > response.getBufferSize()) {
                final AsyncContext context = request.startAsync();
                context.setTimeout(0);
                ((HttpOutput) out).sendContent(content, new Callback() {

                    @Override
                    public void succeeded() {
                        context.complete();
                        content.release();
                    }

                    @Override
                    public void failed(Throwable x) {
                        if (x instanceof IOException)
                            LOG.debug(x);
                        else
                            LOG.warn(x);
                        context.complete();
                        content.release();
                    }

                    @Override
                    public String toString() {
                        return String.format("ResourceService@%x$CB", ResourceService.this.hashCode());
                    }
                });
                return false;
            }
            // otherwise write content blocking
            ((HttpOutput) out).sendContent(content);
        }
    } else {
        // Parse the satisfiable ranges
        List<InclusiveByteRange> ranges = InclusiveByteRange.satisfiableRanges(reqRanges, content_length);
        //  if there are no satisfiable ranges, send 416 response
        if (ranges == null || ranges.size() == 0) {
            putHeaders(response, content, 0);
            response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            response.setHeader(HttpHeader.CONTENT_RANGE.asString(), InclusiveByteRange.to416HeaderRangeString(content_length));
            content.getResource().writeTo(out, 0, content_length);
            return true;
        }
        //  since were here now), send that range with a 216 response
        if (ranges.size() == 1) {
            InclusiveByteRange singleSatisfiableRange = ranges.get(0);
            long singleLength = singleSatisfiableRange.getSize(content_length);
            putHeaders(response, content, singleLength);
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            if (!response.containsHeader(HttpHeader.DATE.asString()))
                response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
            response.setHeader(HttpHeader.CONTENT_RANGE.asString(), singleSatisfiableRange.toHeaderRangeString(content_length));
            content.getResource().writeTo(out, singleSatisfiableRange.getFirst(content_length), singleLength);
            return true;
        }
        //  multiple non-overlapping valid ranges cause a multipart
        //  216 response which does not require an overall
        //  content-length header
        //
        putHeaders(response, content, -1);
        String mimetype = (content == null ? null : content.getContentTypeValue());
        if (mimetype == null)
            LOG.warn("Unknown mimetype for " + request.getRequestURI());
        MultiPartOutputStream multi = new MultiPartOutputStream(out);
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        if (!response.containsHeader(HttpHeader.DATE.asString()))
            response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
        // If the request has a "Request-Range" header then we need to
        // send an old style multipart/x-byteranges Content-Type. This
        // keeps Netscape and acrobat happy. This is what Apache does.
        String ctp;
        if (request.getHeader(HttpHeader.REQUEST_RANGE.asString()) != null)
            ctp = "multipart/x-byteranges; boundary=";
        else
            ctp = "multipart/byteranges; boundary=";
        response.setContentType(ctp + multi.getBoundary());
        InputStream in = content.getResource().getInputStream();
        long pos = 0;
        // calculate the content-length
        int length = 0;
        String[] header = new String[ranges.size()];
        for (int i = 0; i < ranges.size(); i++) {
            InclusiveByteRange ibr = ranges.get(i);
            header[i] = ibr.toHeaderRangeString(content_length);
            length += ((i > 0) ? 2 : 0) + 2 + multi.getBoundary().length() + 2 + (mimetype == null ? 0 : HttpHeader.CONTENT_TYPE.asString().length() + 2 + mimetype.length()) + 2 + HttpHeader.CONTENT_RANGE.asString().length() + 2 + header[i].length() + 2 + 2 + (ibr.getLast(content_length) - ibr.getFirst(content_length)) + 1;
        }
        length += 2 + 2 + multi.getBoundary().length() + 2 + 2;
        response.setContentLength(length);
        for (int i = 0; i < ranges.size(); i++) {
            InclusiveByteRange ibr = ranges.get(i);
            multi.startPart(mimetype, new String[] { HttpHeader.CONTENT_RANGE + ": " + header[i] });
            long start = ibr.getFirst(content_length);
            long size = ibr.getSize(content_length);
            if (in != null) {
                // Handle non cached resource
                if (start < pos) {
                    in.close();
                    in = content.getResource().getInputStream();
                    pos = 0;
                }
                if (pos < start) {
                    in.skip(start - pos);
                    pos = start;
                }
                IO.copy(in, multi, size);
                pos += size;
            } else
                // Handle cached resource
                content.getResource().writeTo(multi, start, size);
        }
        if (in != null)
            in.close();
        multi.close();
    }
    return true;
}
Also used : InputStream(java.io.InputStream) OutputStream(java.io.OutputStream) MultiPartOutputStream(org.eclipse.jetty.util.MultiPartOutputStream) WriterOutputStream(org.eclipse.jetty.io.WriterOutputStream) AsyncContext(javax.servlet.AsyncContext) IOException(java.io.IOException) WriterOutputStream(org.eclipse.jetty.io.WriterOutputStream) ByteBuffer(java.nio.ByteBuffer) MultiPartOutputStream(org.eclipse.jetty.util.MultiPartOutputStream) Callback(org.eclipse.jetty.util.Callback)

Aggregations

IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 OutputStream (java.io.OutputStream)1 ByteBuffer (java.nio.ByteBuffer)1 AsyncContext (javax.servlet.AsyncContext)1 WriterOutputStream (org.eclipse.jetty.io.WriterOutputStream)1 Callback (org.eclipse.jetty.util.Callback)1 MultiPartOutputStream (org.eclipse.jetty.util.MultiPartOutputStream)1