org.sonatype.nexus.repository.partial.RangeParser.java Source code

Java tutorial

Introduction

Here is the source code for org.sonatype.nexus.repository.partial.RangeParser.java

Source

/*
 * Sonatype Nexus (TM) Open Source Version
 * Copyright (c) 2008-2015 Sonatype, Inc.
 * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
 *
 * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
 * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
 *
 * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
 * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
 * Eclipse Foundation. All other trademarks are the property of their respective owners.
 */
package org.sonatype.nexus.repository.partial;

import java.util.Collections;
import java.util.List;

import org.sonatype.sisu.goodies.common.ComponentSupport;

import com.google.common.base.Strings;
import com.google.common.collect.Range;

import static java.util.Collections.singletonList;

/**
 * Parses the "Range" request header.
 *
 * Defined by <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35">RFC 2616 14.35</a>
 *
 * @since 3.0
 */
class RangeParser extends ComponentSupport {
    public static final List<Range<Long>> UNSATISFIABLE = null;

    public static final List<Range<Long>> WHOLE_RANGE = Collections.emptyList();

    /**
     * Returns a list of {@link Range}s, each indicating a range of byte indices (inclusive).
     *
     * Range: bytes=0-10 (from byte 0 to byte 10)
     * Range: bytes=500-999 (from byte 500 to byte 999)
     * Range: bytes=500- (from byte 500 to the end)
     * Range: bytes=-500 (the last 500 bytes, per the RFC)
     *
     * @return {@code null} if the requested range cannot be satisfied given the size of the content, or an empty list in
     * the case of parsing errors
     */
    public List<Range<Long>> parseRangeSpec(final String rangeHeader, long size) {
        Range content = Range.closed(0L, size - 1L);

        // TODO: Current limitation: only one Range of bytes supported in forms of "-X", "X-Y" (where X<Y) and "X-".
        if (!Strings.isNullOrEmpty(rangeHeader)) {
            try {
                if (rangeHeader.startsWith("bytes=") && rangeHeader.length() > 6 && !rangeHeader.contains(",")) {
                    final String rangeSpec = rangeHeader.substring(6, rangeHeader.length());
                    if (rangeSpec.startsWith("-")) {
                        final long byteCount = Long.parseLong(rangeSpec.substring(1));
                        if (byteCount > size) {
                            return UNSATISFIABLE;
                        }
                        final Range<Long> suffix = Range.atLeast(size - byteCount);
                        return ensureSatisfiable(suffix, content);
                    } else if (rangeSpec.endsWith("-")) {
                        final Range<Long> requested = Range
                                .atLeast(Long.parseLong(rangeSpec.substring(0, rangeSpec.length() - 1)));
                        return ensureSatisfiable(requested, content);
                    } else if (rangeSpec.contains("-")) {
                        final String[] parts = rangeSpec.split("-");
                        return ensureSatisfiable(Range.closed(Long.parseLong(parts[0]), Long.parseLong(parts[1])),
                                content);
                    } else {
                        log.warn("Malformed HTTP Range value: {}, ignoring it", rangeHeader);
                    }
                } else {
                    log.warn(
                            "Nexus does not support non-byte or multiple HTTP Ranges, sending complete content: Range value {}",
                            rangeHeader);
                }
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug("Problem parsing Range value: {}, ignoring it", rangeHeader, e);
                } else {
                    log.info("Problem parsing Range value: {}, ignoring it", rangeHeader);
                }
            }
        }

        return WHOLE_RANGE;
    }

    private List<Range<Long>> ensureSatisfiable(Range<Long> requested, Range content) {
        if (requested.isConnected(content)) {
            return singletonList(requested.intersection(content));
        } else {
            return UNSATISFIABLE;
        }
    }

    private boolean isSatisfiable(final Range<Long> range, final long contentSize) {
        if (!range.hasLowerBound()) {
            return true;
        }
        // Per RFC 2616, a requested range is satisfiable as long as its lower bound is within the content size.
        // Requests for ranges that extend beyond the content size are okay.
        return range.lowerEndpoint() < contentSize - 1;
    }
}