View Javadoc

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  import org.eclipse.jetty.util.log.Logger;
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   * 
44   */
45  public class InclusiveByteRange 
46  {
47      private static final Logger LOG = Log.getLogger(InclusiveByteRange.class);
48  
49      long first = 0;
50      long last  = 0;    
51  
52      public InclusiveByteRange(long first, long last)
53      {
54          this.first = first;
55          this.last = last;
56      }
57      
58      public long getFirst()
59      {
60          return first;
61      }
62  
63      public long getLast()
64      {
65          return last;
66      }    
67  
68  
69      
70      /* ------------------------------------------------------------ */
71      /** 
72       * @param headers Enumeration of Range header fields.
73       * @param size Size of the resource.
74       * @return LazyList of satisfiable ranges
75       */
76      public static List satisfiableRanges(Enumeration headers, long size)
77      {
78          Object satRanges=null;
79          
80          // walk through all Range headers
81      headers:
82          while (headers.hasMoreElements())
83          {
84              String header = (String) headers.nextElement();
85              StringTokenizer tok = new StringTokenizer(header,"=,",false);
86              String t=null;
87              try
88              {
89                  // read all byte ranges for this header 
90                  while (tok.hasMoreTokens())
91                  {
92                      try
93                      {
94                          t = tok.nextToken().trim();
95  
96                          long first = -1;
97                          long last = -1;
98                          int d = t.indexOf('-');
99                          if (d < 0 || t.indexOf("-",d + 1) >= 0)
100                         {
101                             if ("bytes".equals(t))
102                                 continue;
103                             LOG.warn("Bad range format: {}",t);
104                             continue headers;
105                         }
106                         else if (d == 0)
107                         {
108                             if (d + 1 < t.length())
109                                 last = Long.parseLong(t.substring(d + 1).trim());
110                             else
111                             {
112                                 LOG.warn("Bad range format: {}",t);
113                                 continue;
114                             }
115                         }
116                         else if (d + 1 < t.length())
117                         {
118                             first = Long.parseLong(t.substring(0,d).trim());
119                             last = Long.parseLong(t.substring(d + 1).trim());
120                         }
121                         else
122                             first = Long.parseLong(t.substring(0,d).trim());
123 
124                         if (first == -1 && last == -1)
125                             continue headers;
126 
127                         if (first != -1 && last != -1 && (first > last))
128                             continue headers;
129 
130                         if (first < size)
131                         {
132                             InclusiveByteRange range = new InclusiveByteRange(first,last);
133                             satRanges = LazyList.add(satRanges,range);
134                         }
135                     }
136                     catch (NumberFormatException e)
137                     {
138                         LOG.warn("Bad range format: {}",t);
139                         LOG.ignore(e);
140                         continue;
141                     }
142                 }
143             }
144             catch(Exception e)
145             {
146                 LOG.warn("Bad range format: {}",t);
147                 LOG.ignore(e);
148             }    
149         }
150         return LazyList.getList(satRanges,true);
151     }
152 
153     /* ------------------------------------------------------------ */
154     public long getFirst(long size)
155     {
156         if (first<0)
157         {
158             long tf=size-last;
159             if (tf<0)
160                 tf=0;
161             return tf;
162         }
163         return first;
164     }
165     
166     /* ------------------------------------------------------------ */
167     public long getLast(long size)
168     {
169         if (first<0)
170             return size-1;
171         
172         if (last<0 ||last>=size)
173             return size-1;
174         return last;
175     }
176     
177     /* ------------------------------------------------------------ */
178     public long getSize(long size)
179     {
180         return getLast(size)-getFirst(size)+1;
181     }
182 
183 
184     /* ------------------------------------------------------------ */
185     public String toHeaderRangeString(long size)
186     {
187         StringBuilder sb = new StringBuilder(40);
188         sb.append("bytes ");
189         sb.append(getFirst(size));
190         sb.append('-');
191         sb.append(getLast(size));
192         sb.append("/");
193         sb.append(size);
194         return sb.toString();
195     }
196 
197     /* ------------------------------------------------------------ */
198     public static String to416HeaderRangeString(long size)
199     {
200         StringBuilder sb = new StringBuilder(40);
201         sb.append("bytes */");
202         sb.append(size);
203         return sb.toString();
204     }
205 
206 
207     /* ------------------------------------------------------------ */
208     @Override
209     public String toString()
210     {
211         StringBuilder sb = new StringBuilder(60);
212         sb.append(Long.toString(first));
213         sb.append(":");
214         sb.append(Long.toString(last));
215         return sb.toString();
216     }
217 
218 
219 }
220 
221 
222