1 // ========================================================================
2 // Copyright 2002-2005 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 // ========================================================================
14
15 package org.mortbay.jetty;
16
17 import java.util.Enumeration;
18 import java.util.List;
19 import java.util.StringTokenizer;
20
21 import org.mortbay.log.Log;
22 import org.mortbay.util.LazyList;
23
24 /* ------------------------------------------------------------ */
25 /** Byte range inclusive of end points.
26 * <PRE>
27 *
28 * parses the following types of byte ranges:
29 *
30 * bytes=100-499
31 * bytes=-300
32 * bytes=100-
33 * bytes=1-2,2-3,6-,-2
34 *
35 * given an entity length, converts range to string
36 *
37 * bytes 100-499/500
38 *
39 * </PRE>
40 *
41 * Based on RFC2616 3.12, 14.16, 14.35.1, 14.35.2
42 * @version $version$
43 * @author Helmut Hissen
44 */
45 public class InclusiveByteRange
46 {
47 long first = 0;
48 long last = 0;
49
50 public InclusiveByteRange(long first, long last)
51 {
52 this.first = first;
53 this.last = last;
54 }
55
56 public long getFirst()
57 {
58 return first;
59 }
60
61 public long getLast()
62 {
63 return last;
64 }
65
66 /* ------------------------------------------------------------ */
67 /**
68 * @param headers Enumeration of Range header fields.
69 * @param size Size of the resource.
70 * @return LazyList of satisfiable ranges
71 */
72 public static List satisfiableRanges(Enumeration headers,long size)
73 {
74 Object satRanges=null;
75
76 // walk through all Range headers
77 headers:
78 while (headers.hasMoreElements())
79 {
80 String header = (String) headers.nextElement();
81 StringTokenizer tok = new StringTokenizer(header,"=,",false);
82 String t=null;
83 try
84 {
85 // read all byte ranges for this header
86 while (tok.hasMoreTokens())
87 {
88 t=tok.nextToken().trim();
89
90 long first = -1;
91 long last = -1;
92 int d=t.indexOf('-');
93 if (d<0 || t.indexOf("-",d+1)>=0)
94 {
95 if ("bytes".equals(t))
96 continue;
97 Log.warn("Bad range format: {}",t);
98 continue headers;
99 }
100 else if (d==0)
101 {
102 if (d+1<t.length())
103 last = Long.parseLong(t.substring(d+1).trim());
104 else
105 {
106 Log.warn("Bad range format: {}",t);
107 continue headers;
108 }
109 }
110 else if (d+1<t.length())
111 {
112 first = Long.parseLong(t.substring(0,d).trim());
113 last = Long.parseLong(t.substring(d+1).trim());
114 }
115 else
116 first = Long.parseLong(t.substring(0,d).trim());
117
118 if (first == -1 && last == -1)
119 continue headers;
120
121 if (first != -1 && last != -1 && (first > last))
122 continue headers;
123
124 if (first<size)
125 {
126 InclusiveByteRange range = new
127 InclusiveByteRange(first, last);
128 satRanges=LazyList.add(satRanges,range);
129 }
130 }
131 }
132 catch(Exception e)
133 {
134 Log.warn("Bad range format: "+t);
135 Log.ignore(e);
136 }
137 }
138 return LazyList.getList(satRanges,true);
139 }
140
141 /* ------------------------------------------------------------ */
142 public long getFirst(long size)
143 {
144 if (first<0)
145 {
146 long tf=size-last;
147 if (tf<0)
148 tf=0;
149 return tf;
150 }
151 return first;
152 }
153
154 /* ------------------------------------------------------------ */
155 public long getLast(long size)
156 {
157 if (first<0)
158 return size-1;
159
160 if (last<0 ||last>=size)
161 return size-1;
162 return last;
163 }
164
165 /* ------------------------------------------------------------ */
166 public long getSize(long size)
167 {
168 return getLast(size)-getFirst(size)+1;
169 }
170
171
172 /* ------------------------------------------------------------ */
173 public String toHeaderRangeString(long size)
174 {
175 StringBuffer sb = new StringBuffer(40);
176 sb.append("bytes ");
177 sb.append(getFirst(size));
178 sb.append('-');
179 sb.append(getLast(size));
180 sb.append("/");
181 sb.append(size);
182 return sb.toString();
183 }
184
185 /* ------------------------------------------------------------ */
186 public static String to416HeaderRangeString(long size)
187 {
188 StringBuffer sb = new StringBuffer(40);
189 sb.append("bytes */");
190 sb.append(size);
191 return sb.toString();
192 }
193
194
195 /* ------------------------------------------------------------ */
196 public String toString()
197 {
198 StringBuffer sb = new StringBuffer(60);
199 sb.append(Long.toString(first));
200 sb.append(":");
201 sb.append(Long.toString(last));
202 return sb.toString();
203 }
204
205
206 }
207
208
209