View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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