001: // ========================================================================
002: // Copyright 2002-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty;
016:
017: import java.util.Enumeration;
018: import java.util.List;
019: import java.util.StringTokenizer;
020:
021: import org.mortbay.log.Log;
022: import org.mortbay.util.LazyList;
023:
024: /* ------------------------------------------------------------ */
025: /** Byte range inclusive of end points.
026: * <PRE>
027: *
028: * parses the following types of byte ranges:
029: *
030: * bytes=100-499
031: * bytes=-300
032: * bytes=100-
033: * bytes=1-2,2-3,6-,-2
034: *
035: * given an entity length, converts range to string
036: *
037: * bytes 100-499/500
038: *
039: * </PRE>
040: *
041: * Based on RFC2616 3.12, 14.16, 14.35.1, 14.35.2
042: * @version $version$
043: * @author Helmut Hissen
044: */
045: public class InclusiveByteRange {
046: long first = 0;
047: long last = 0;
048:
049: public InclusiveByteRange(long first, long last) {
050: this .first = first;
051: this .last = last;
052: }
053:
054: public long getFirst() {
055: return first;
056: }
057:
058: public long getLast() {
059: return last;
060: }
061:
062: /* ------------------------------------------------------------ */
063: /**
064: * @param headers Enumeration of Range header fields.
065: * @param size Size of the resource.
066: * @return LazyList of satisfiable ranges
067: */
068: public static List satisfiableRanges(Enumeration headers, long size) {
069: Object satRanges = null;
070:
071: // walk through all Range headers
072: headers: while (headers.hasMoreElements()) {
073: String header = (String) headers.nextElement();
074: StringTokenizer tok = new StringTokenizer(header, "=,",
075: false);
076: String t = null;
077: try {
078: // read all byte ranges for this header
079: while (tok.hasMoreTokens()) {
080: t = tok.nextToken().trim();
081:
082: long first = -1;
083: long last = -1;
084: int d = t.indexOf('-');
085: if (d < 0 || t.indexOf("-", d + 1) >= 0) {
086: if ("bytes".equals(t))
087: continue;
088: Log.warn("Bad range format: {}", t);
089: continue headers;
090: } else if (d == 0) {
091: if (d + 1 < t.length())
092: last = Long.parseLong(t.substring(d + 1)
093: .trim());
094: else {
095: Log.warn("Bad range format: {}", t);
096: continue headers;
097: }
098: } else if (d + 1 < t.length()) {
099: first = Long
100: .parseLong(t.substring(0, d).trim());
101: last = Long
102: .parseLong(t.substring(d + 1).trim());
103: } else
104: first = Long
105: .parseLong(t.substring(0, d).trim());
106:
107: if (first == -1 && last == -1)
108: continue headers;
109:
110: if (first != -1 && last != -1 && (first > last))
111: continue headers;
112:
113: if (first < size) {
114: InclusiveByteRange range = new InclusiveByteRange(
115: first, last);
116: satRanges = LazyList.add(satRanges, range);
117: }
118: }
119: } catch (Exception e) {
120: Log.warn("Bad range format: " + t);
121: Log.ignore(e);
122: }
123: }
124: return LazyList.getList(satRanges, true);
125: }
126:
127: /* ------------------------------------------------------------ */
128: public long getFirst(long size) {
129: if (first < 0) {
130: long tf = size - last;
131: if (tf < 0)
132: tf = 0;
133: return tf;
134: }
135: return first;
136: }
137:
138: /* ------------------------------------------------------------ */
139: public long getLast(long size) {
140: if (first < 0)
141: return size - 1;
142:
143: if (last < 0 || last >= size)
144: return size - 1;
145: return last;
146: }
147:
148: /* ------------------------------------------------------------ */
149: public long getSize(long size) {
150: return getLast(size) - getFirst(size) + 1;
151: }
152:
153: /* ------------------------------------------------------------ */
154: public String toHeaderRangeString(long size) {
155: StringBuffer sb = new StringBuffer(40);
156: sb.append("bytes ");
157: sb.append(getFirst(size));
158: sb.append('-');
159: sb.append(getLast(size));
160: sb.append("/");
161: sb.append(size);
162: return sb.toString();
163: }
164:
165: /* ------------------------------------------------------------ */
166: public static String to416HeaderRangeString(long size) {
167: StringBuffer sb = new StringBuffer(40);
168: sb.append("bytes */");
169: sb.append(size);
170: return sb.toString();
171: }
172:
173: /* ------------------------------------------------------------ */
174: public String toString() {
175: StringBuffer sb = new StringBuffer(60);
176: sb.append(Long.toString(first));
177: sb.append(":");
178: sb.append(Long.toString(last));
179: return sb.toString();
180: }
181:
182: }
|