1 // ========================================================================
2 // Copyright (c) 2002-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.server;
15
16 import java.util.Enumeration;
17 import java.util.List;
18 import java.util.StringTokenizer;
19
20 import org.eclipse.jetty.util.LazyList;
21 import org.eclipse.jetty.util.log.Log;
22
23 /* ------------------------------------------------------------ */
24 /** Byte range inclusive of end points.
25 * <PRE>
26 *
27 * parses the following types of byte ranges:
28 *
29 * bytes=100-499
30 * bytes=-300
31 * bytes=100-
32 * bytes=1-2,2-3,6-,-2
33 *
34 * given an entity length, converts range to string
35 *
36 * bytes 100-499/500
37 *
38 * </PRE>
39 *
40 * Based on RFC2616 3.12, 14.16, 14.35.1, 14.35.2
41 * @version $version$
42 *
43 */
44 public class InclusiveByteRange
45 {
46 long first = 0;
47 long last = 0;
48
49 public InclusiveByteRange(long first, long last)
50 {
51 this.first = first;
52 this.last = last;
53 }
54
55 public long getFirst()
56 {
57 return first;
58 }
59
60 public long getLast()
61 {
62 return last;
63 }
64
65
66
67 /* ------------------------------------------------------------ */
68 /**
69 * @param headers Enumeration of Range header fields.
70 * @param size Size of the resource.
71 * @return LazyList of satisfiable ranges
72 */
73 public static List satisfiableRanges(Enumeration headers, boolean allowRelativeRange, long size)
74 {
75 Object satRanges=null;
76
77 // walk through all Range headers
78 headers:
79 while (headers.hasMoreElements())
80 {
81 String header = (String) headers.nextElement();
82 StringTokenizer tok = new StringTokenizer(header,"=,",false);
83 String t=null;
84 try
85 {
86 // read all byte ranges for this header
87 while (tok.hasMoreTokens())
88 {
89 try
90 {
91 t = tok.nextToken().trim();
92
93 long first = -1;
94 long last = -1;
95 int d = t.indexOf('-');
96 if (d < 0 || t.indexOf("-",d + 1) >= 0)
97 {
98 if ("bytes".equals(t))
99 continue;
100 Log.warn("Bad range format: {}",t);
101 continue headers;
102 }
103 else if (d == 0)
104 {
105 if (d + 1 < t.length())
106 last = Long.parseLong(t.substring(d + 1).trim());
107 else
108 {
109 Log.warn("Bad range format: {}",t);
110 continue;
111 }
112 }
113 else if (d + 1 < t.length())
114 {
115 first = Long.parseLong(t.substring(0,d).trim());
116 last = Long.parseLong(t.substring(d + 1).trim());
117 }
118 else
119 first = Long.parseLong(t.substring(0,d).trim());
120
121 if (first == -1 && last == -1)
122 continue headers;
123
124 if (first != -1 && last != -1 && (first > last))
125 continue headers;
126
127 if (first < size)
128 {
129 // Relative range end points not allowed (in some cases)
130 if ((!allowRelativeRange) && ((first < 0) || (last < 0)))
131 {
132 continue headers;
133 }
134 InclusiveByteRange range = new InclusiveByteRange(first,last);
135 satRanges = LazyList.add(satRanges,range);
136 }
137 }
138 catch (NumberFormatException e)
139 {
140 Log.warn("Bad range format: {}",t);
141 Log.ignore(e);
142 continue;
143 }
144 }
145 }
146 catch(Exception e)
147 {
148 Log.warn("Bad range format: {}",t);
149 Log.ignore(e);
150 }
151 }
152 return LazyList.getList(satRanges,true);
153 }
154
155 /* ------------------------------------------------------------ */
156 public long getFirst(long size)
157 {
158 if (first<0)
159 {
160 long tf=size-last;
161 if (tf<0)
162 tf=0;
163 return tf;
164 }
165 return first;
166 }
167
168 /* ------------------------------------------------------------ */
169 public long getLast(long size)
170 {
171 if (first<0)
172 return size-1;
173
174 if (last<0 ||last>=size)
175 return size-1;
176 return last;
177 }
178
179 /* ------------------------------------------------------------ */
180 public long getSize(long size)
181 {
182 return getLast(size)-getFirst(size)+1;
183 }
184
185
186 /* ------------------------------------------------------------ */
187 public String toHeaderRangeString(long size)
188 {
189 StringBuilder sb = new StringBuilder(40);
190 sb.append("bytes ");
191 sb.append(getFirst(size));
192 sb.append('-');
193 sb.append(getLast(size));
194 sb.append("/");
195 sb.append(size);
196 return sb.toString();
197 }
198
199 /* ------------------------------------------------------------ */
200 public static String to416HeaderRangeString(long size)
201 {
202 StringBuilder sb = new StringBuilder(40);
203 sb.append("bytes */");
204 sb.append(size);
205 return sb.toString();
206 }
207
208
209 /* ------------------------------------------------------------ */
210 @Override
211 public String toString()
212 {
213 StringBuilder sb = new StringBuilder(60);
214 sb.append(Long.toString(first));
215 sb.append(":");
216 sb.append(Long.toString(last));
217 return sb.toString();
218 }
219
220
221 }
222
223
224