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