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