View Javadoc

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