1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2017 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 module hunt.http.codec.http.model.InclusiveByteRange; 20 21 22 import hunt.logging; 23 24 import hunt.lang.exception; 25 import hunt.string; 26 27 import hunt.container.List; 28 import hunt.container.ArrayList; 29 30 import std.conv; 31 import std.format; 32 import std.string; 33 34 35 /** 36 * Byte range inclusive of end points. 37 * <PRE> 38 * parses the following types of byte ranges: 39 * bytes=100-499 40 * bytes=-300 41 * bytes=100- 42 * bytes=1-2,2-3,6-,-2 43 * given an entity length, converts range to string 44 * bytes 100-499/500 45 * </PRE> 46 * Based on RFC2616 3.12, 14.16, 14.35.1, 14.35.2 47 * 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. 48 * 49 * @version $version$ 50 */ 51 class InclusiveByteRange { 52 53 54 long first = 0; 55 long last = 0; 56 57 this(long first, long last) { 58 this.first = first; 59 this.last = last; 60 } 61 62 long getFirst() { 63 return first; 64 } 65 66 long getLast() { 67 return last; 68 } 69 70 /** 71 * @param headers Enumeration of Range header fields. 72 * @param size Size of the resource. 73 * @return LazyList of satisfiable ranges 74 */ 75 static List!InclusiveByteRange satisfiableRanges(List!string headers, long size) { 76 Object satRanges = null; 77 78 // walk through all Range headers 79 bool isContinue = false; 80 do{ 81 isContinue = false; 82 foreach (string header ; headers) { 83 StringTokenizer tok = new StringTokenizer(header, "=,", false); 84 string t = null; 85 try { 86 // read all byte ranges for this header 87 while (tok.hasMoreTokens()) { 88 try { 89 t = tok.nextToken().strip(); 90 91 long first = -1; 92 long last = -1; 93 int d = cast(int)t.indexOf("-"); 94 if (d < 0 || t.indexOf("-", d + 1) >= 0) { 95 if ("bytes" == (t)) 96 continue; 97 warningf("Bad range format: %s", t); 98 isContinue = true; 99 break; 100 } else if (d == 0) { 101 if (d + 1 < t.length) 102 last = to!long(t.substring(d + 1).strip()); 103 else { 104 warningf("Bad range format: %s", t); 105 continue; 106 } 107 } else if (d + 1 < t.length) { 108 first = to!long(t.substring(0, d).strip()); 109 last = to!long(t.substring(d + 1).strip()); 110 } else 111 first = to!long(t.substring(0, d).strip()); 112 113 if (first == -1 && last == -1) 114 { 115 isContinue = true; 116 break; 117 } 118 119 if (first != -1 && last != -1 && (first > last)) 120 { 121 isContinue = true; 122 break; 123 } 124 125 if (first < size) { 126 InclusiveByteRange range = new InclusiveByteRange(first, last); 127 satRanges = LazyList.add(satRanges, range); 128 } 129 } catch (NumberFormatException e) { 130 warningf("Bad range format: %s", t); 131 continue; 132 } 133 } 134 } catch (Exception e) { 135 warningf("Bad range format: %s", t); 136 } 137 } 138 139 140 } while(isContinue); 141 return LazyList.getList!(InclusiveByteRange)(satRanges, true); 142 } 143 144 long getFirst(long size) { 145 if (first < 0) { 146 long tf = size - last; 147 if (tf < 0) 148 tf = 0; 149 return tf; 150 } 151 return first; 152 } 153 154 long getLast(long size) { 155 if (first < 0) 156 return size - 1; 157 158 if (last < 0 || last >= size) 159 return size - 1; 160 return last; 161 } 162 163 long getSize(long size) { 164 return getLast(size) - getFirst(size) + 1; 165 } 166 167 string toHeaderRangeString(long size) { 168 StringBuilder sb = new StringBuilder(40); 169 sb.append("bytes "); 170 sb.append(getFirst(size).to!string); 171 sb.append('-'); 172 sb.append(getLast(size).to!string); 173 sb.append("/"); 174 sb.append(to!string(size)); 175 return sb.toString(); 176 } 177 178 static string to416HeaderRangeString(long size) { 179 StringBuilder sb = new StringBuilder(40); 180 sb.append("bytes */"); 181 sb.append(to!string(size)); 182 return sb.toString(); 183 } 184 185 override 186 string toString() { 187 return format("%d:%d", first, last); 188 } 189 190 } 191 192 193