org.scilla.util.PartialContentHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.scilla.util.PartialContentHandler.java

Source

/*
 * scilla
 *
 * Copyright (C) 2001  R.W. van 't Veer
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

package org.scilla.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Handle (partial) content requests.
 *
 * @version $Revision: 1.5 $
 * @author R.W. van 't Veer
 */
public class PartialContentHandler {
    private static final Log log = LogFactory.getLog(PartialContentHandler.class);
    private static final int BUFFER_SIZE = 4096;

    /** name of range request header */
    public static final String RANGE_HEADER = "range";
    /** byte range spec */
    public static final String BYTE_RANGE = "bytes=";
    /** name of range response header */
    public static final String CONTENT_RANGE_HEADER = "Content-Range";
    /** bytes-unit for content-range header */
    public static final String BYTES_UNIT = "bytes";

    /**
     * Process servlet request.  Determine if request is a
     * request for partial content and write the needed headers
     * and status back to the client and write the request bytes
     * range.  If the request is not for partial content, just
     * write all data.
     * @param request HTTP request object
     * @param response HTTP response object
     * @param in stream to read data from
     * @param len length of data to be write or <tt>-1</tt> if unknown,
     * in which case partial content request are handled as normal requests
     * @throws IOException when reading or writing fails
     */
    public static void process(HttpServletRequest request, HttpServletResponse response, InputStream in, long len)
            throws IOException {
        OutputStream out = response.getOutputStream();

        // can only do partial content when length is unknown
        if (len != -1) {
            String rangeHeader = request.getHeader(RANGE_HEADER);
            // was partial content requested?
            if (rangeHeader != null && rangeHeader.startsWith(BYTE_RANGE)) {
                String byteSpec = rangeHeader.substring(BYTE_RANGE.length());
                int sepPos = byteSpec.indexOf('-');
                // does the byte spec describe a range?
                if (sepPos != -1) {
                    long offset = 0;
                    long endpoint = -1;

                    // determine offset
                    if (sepPos > 0) {
                        String s = byteSpec.substring(0, sepPos).trim();
                        offset = Integer.parseInt(s);
                    }
                    // determine endpoint
                    if (sepPos != byteSpec.length() - 1) {
                        String s = byteSpec.substring(sepPos + 1).trim();
                        endpoint = Integer.parseInt(s);
                    } else {
                        endpoint = len - 1;
                    }

                    // notify receiver this is partial content
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    String contentRange = BYTES_UNIT + " " + offset + "-" + endpoint + "/" + len;
                    response.setHeader(CONTENT_RANGE_HEADER, contentRange);
                    log.debug("send range header: " + CONTENT_RANGE_HEADER + ": " + contentRange);
                    response.setContentLength((int) (endpoint - offset + 1));

                    // skip till offset
                    if (offset > 0) {
                        in.skip(offset);
                    }

                    // write partial data
                    int n;
                    byte[] b = new byte[BUFFER_SIZE];
                    while ((n = in.read(b)) != -1) {
                        if (endpoint != -1) {
                            if (offset + n > endpoint) {
                                n = (int) (endpoint - offset) + 1;
                                if (n > 0) {
                                    out.write(b, 0, n);
                                }
                                break;
                            }
                            offset += n;
                        }
                        out.write(b, 0, n);
                    }

                    // done
                    return;
                }

                log.info("didn't understand request.. treat as normal request");
                if (log.isDebugEnabled()) {
                    logHeaders(request);
                }
            }

            // inform client of data size
            response.setContentLength((int) len);
        }

        // write all content to client
        int n;
        byte[] b = new byte[BUFFER_SIZE];
        while ((n = in.read(b)) != -1) {
            out.write(b, 0, n);
        }
    }

    private static void logHeaders(HttpServletRequest request) {
        for (Enumeration en = request.getHeaderNames(); en.hasMoreElements();) {
            String key = (String) en.nextElement();
            String val = request.getHeader(key);
            log.debug("header: " + key + "=" + val);
        }
    }
}