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, 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 InclusiveByteRange range = new InclusiveByteRange(first,last);
130 satRanges = LazyList.add(satRanges,range);
131 }
132 }
133 catch (NumberFormatException e)
134 {
135 Log.warn("Bad range format: {}",t);
136 Log.ignore(e);
137 continue;
138 }
139 }
140 }
141 catch(Exception e)
142 {
143 Log.warn("Bad range format: {}",t);
144 Log.ignore(e);
145 }
146 }
147 return LazyList.getList(satRanges,true);
148 }
149
150 /* ------------------------------------------------------------ */
151 public long getFirst(long size)
152 {
153 if (first<0)
154 {
155 long tf=size-last;
156 if (tf<0)
157 tf=0;
158 return tf;
159 }
160 return first;
161 }
162
163 /* ------------------------------------------------------------ */
164 public long getLast(long size)
165 {
166 if (first<0)
167 return size-1;
168
169 if (last<0 ||last>=size)
170 return size-1;
171 return last;
172 }
173
174 /* ------------------------------------------------------------ */
175 public long getSize(long size)
176 {
177 return getLast(size)-getFirst(size)+1;
178 }
179
180
181 /* ------------------------------------------------------------ */
182 public String toHeaderRangeString(long size)
183 {
184 StringBuilder sb = new StringBuilder(40);
185 sb.append("bytes ");
186 sb.append(getFirst(size));
187 sb.append('-');
188 sb.append(getLast(size));
189 sb.append("/");
190 sb.append(size);
191 return sb.toString();
192 }
193
194 /* ------------------------------------------------------------ */
195 public static String to416HeaderRangeString(long size)
196 {
197 StringBuilder sb = new StringBuilder(40);
198 sb.append("bytes */");
199 sb.append(size);
200 return sb.toString();
201 }
202
203
204 /* ------------------------------------------------------------ */
205 @Override
206 public String toString()
207 {
208 StringBuilder sb = new StringBuilder(60);
209 sb.append(Long.toString(first));
210 sb.append(":");
211 sb.append(Long.toString(last));
212 return sb.toString();
213 }
214
215
216 }
217
218
219