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