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