1 /* 2 * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.net.www; 27 28 import java.io.*; 29 import java.util.concurrent.locks.ReentrantLock; 30 import sun.net.ProgressSource; 31 import sun.net.www.http.ChunkedInputStream; 32 33 34 public class MeteredStream extends FilterInputStream { 35 36 // Instance variables. 37 /* if expected != -1, after we've read >= expected, we're "closed" and return -1 38 * from subsequest read() 's 39 */ 40 protected boolean closed = false; 41 protected long expected; 42 protected long count = 0; 43 protected long markedCount = 0; 44 protected int markLimit = -1; 45 protected ProgressSource pi; 46 private final ReentrantLock readLock = new ReentrantLock(); 47 MeteredStream(InputStream is, ProgressSource pi, long expected)48 public MeteredStream(InputStream is, ProgressSource pi, long expected) 49 { 50 super(is); 51 52 this.pi = pi; 53 this.expected = expected; 54 55 if (pi != null) { 56 pi.updateProgress(0, expected); 57 } 58 } 59 justRead(long n)60 private final void justRead(long n) throws IOException { 61 assert isLockHeldByCurrentThread(); 62 63 if (n == -1) { 64 65 /* 66 * don't close automatically when mark is set and is valid; 67 * cannot reset() after close() 68 */ 69 if (!isMarked()) { 70 close(); 71 } 72 return; 73 } 74 75 count += n; 76 77 /** 78 * If read beyond the markLimit, invalidate the mark 79 */ 80 if (count - markedCount > markLimit) { 81 markLimit = -1; 82 } 83 84 if (pi != null) 85 pi.updateProgress(count, expected); 86 87 if (isMarked()) { 88 return; 89 } 90 91 // if expected length is known, we could determine if 92 // read overrun. 93 if (expected > 0) { 94 if (count >= expected) { 95 close(); 96 } 97 } 98 } 99 100 /** 101 * Returns true if the mark is valid, false otherwise 102 */ isMarked()103 private boolean isMarked() { 104 assert isLockHeldByCurrentThread(); 105 106 if (markLimit < 0) { 107 return false; 108 } 109 110 // mark is set, but is not valid anymore 111 if (count - markedCount > markLimit) { 112 return false; 113 } 114 115 // mark still holds 116 return true; 117 } 118 read()119 public int read() throws java.io.IOException { 120 lock(); 121 try { 122 if (closed) return -1; 123 int c = in.read(); 124 if (c != -1) { 125 justRead(1); 126 } else { 127 justRead(c); 128 } 129 return c; 130 } finally { 131 unlock(); 132 } 133 } 134 read(byte b[], int off, int len)135 public int read(byte b[], int off, int len) 136 throws java.io.IOException { 137 lock(); 138 try { 139 if (closed) return -1; 140 141 int n = in.read(b, off, len); 142 justRead(n); 143 return n; 144 } finally { 145 unlock(); 146 } 147 } 148 skip(long n)149 public long skip(long n) throws IOException { 150 lock(); 151 try { 152 // REMIND: what does skip do on EOF???? 153 if (closed) return 0; 154 155 if (in instanceof ChunkedInputStream) { 156 n = in.skip(n); 157 } else { 158 // just skip min(n, num_bytes_left) 159 long min = (n > expected - count) ? expected - count : n; 160 n = in.skip(min); 161 } 162 justRead(n); 163 return n; 164 } finally { 165 unlock(); 166 } 167 } 168 close()169 public void close() throws IOException { 170 lock(); 171 try { 172 if (closed) return; 173 if (pi != null) 174 pi.finishTracking(); 175 176 closed = true; 177 in.close(); 178 } finally { 179 unlock(); 180 } 181 } 182 available()183 public int available() throws IOException { 184 lock(); 185 try { 186 return closed ? 0 : in.available(); 187 } finally { 188 unlock(); 189 } 190 } 191 mark(int readLimit)192 public void mark(int readLimit) { 193 lock(); 194 try { 195 if (closed) return; 196 super.mark(readLimit); 197 198 /* 199 * mark the count to restore upon reset 200 */ 201 markedCount = count; 202 markLimit = readLimit; 203 } finally { 204 unlock(); 205 } 206 } 207 reset()208 public void reset() throws IOException { 209 lock(); 210 try { 211 if (closed) return; 212 if (!isMarked()) { 213 throw new IOException("Resetting to an invalid mark"); 214 } 215 216 count = markedCount; 217 super.reset(); 218 } finally { 219 unlock(); 220 } 221 } 222 markSupported()223 public boolean markSupported() { 224 lock(); 225 try { 226 if (closed) return false; 227 return super.markSupported(); 228 } finally { 229 unlock(); 230 } 231 } 232 lock()233 public final void lock() { 234 readLock.lock(); 235 } 236 unlock()237 public final void unlock() { 238 readLock.unlock(); 239 } 240 isLockHeldByCurrentThread()241 public final boolean isLockHeldByCurrentThread() { 242 return readLock.isHeldByCurrentThread(); 243 } 244 245 @SuppressWarnings("deprecation") finalize()246 protected void finalize() throws Throwable { 247 try { 248 close(); 249 if (pi != null) 250 pi.close(); 251 } 252 finally { 253 // Call super class 254 super.finalize(); 255 } 256 } 257 } 258