1 /*
   2  * Copyright (c) 1994, 2017, 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.net.URL;
  29 import java.util.*;
  30 import java.io.*;
  31 import sun.net.ProgressSource;
  32 import sun.net.www.http.ChunkedInputStream;
  33 
  34 
  35 public class MeteredStream extends FilterInputStream {
  36 
  37     // Instance variables.
  38     /* if expected != -1, after we've read >= expected, we're "closed" and return -1
  39      * from subsequest read() 's
  40      */
  41     protected boolean closed = false;
  42     protected long expected;
  43     protected long count = 0;
  44     protected long markedCount = 0;
  45     protected int markLimit = -1;
  46     protected ProgressSource pi;
  47 
  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 
  60     private final void justRead(long n) throws IOException   {
  61         if (n == -1) {
  62 
  63             /*
  64              * don't close automatically when mark is set and is valid;
  65              * cannot reset() after close()
  66              */
  67             if (!isMarked()) {
  68                 close();
  69             }
  70             return;
  71         }
  72 
  73         count += n;
  74 
  75         /**
  76          * If read beyond the markLimit, invalidate the mark
  77          */
  78         if (count - markedCount > markLimit) {
  79             markLimit = -1;
  80         }
  81 
  82         if (pi != null)
  83             pi.updateProgress(count, expected);
  84 
  85         if (isMarked()) {
  86             return;
  87         }
  88 
  89         // if expected length is known, we could determine if
  90         // read overrun.
  91         if (expected > 0)   {
  92             if (count >= expected) {
  93                 close();
  94             }
  95         }
  96     }
  97 
  98     /**
  99      * Returns true if the mark is valid, false otherwise
 100      */
 101     private boolean isMarked() {
 102 
 103         if (markLimit < 0) {
 104             return false;
 105         }
 106 
 107         // mark is set, but is not valid anymore
 108         if (count - markedCount > markLimit) {
 109            return false;
 110         }
 111 
 112         // mark still holds
 113         return true;
 114     }
 115 
 116     public synchronized int read() throws java.io.IOException {
 117         if (closed) {
 118             return -1;
 119         }
 120         int c = in.read();
 121         if (c != -1) {
 122             justRead(1);
 123         } else {
 124             justRead(c);
 125         }
 126         return c;
 127     }
 128 
 129     public synchronized int read(byte b[], int off, int len)
 130                 throws java.io.IOException {
 131         if (closed) {
 132             return -1;
 133         }
 134         int n = in.read(b, off, len);
 135         justRead(n);
 136         return n;
 137     }
 138 
 139     public synchronized long skip(long n) throws IOException {
 140 
 141         // REMIND: what does skip do on EOF????
 142         if (closed) {
 143             return 0;
 144         }
 145 
 146         if (in instanceof ChunkedInputStream) {
 147             n = in.skip(n);
 148         }
 149         else {
 150             // just skip min(n, num_bytes_left)
 151             long min = (n > expected - count) ? expected - count: n;
 152             n = in.skip(min);
 153         }
 154         justRead(n);
 155         return n;
 156     }
 157 
 158     public void close() throws IOException {
 159         if (closed) {
 160             return;
 161         }
 162         if (pi != null)
 163             pi.finishTracking();
 164 
 165         closed = true;
 166         in.close();
 167     }
 168 
 169     public synchronized int available() throws IOException {
 170         return closed ? 0: in.available();
 171     }
 172 
 173     public synchronized void mark(int readLimit) {
 174         if (closed) {
 175             return;
 176         }
 177         super.mark(readLimit);
 178 
 179         /*
 180          * mark the count to restore upon reset
 181          */
 182         markedCount = count;
 183         markLimit = readLimit;
 184     }
 185 
 186     public synchronized void reset() throws IOException {
 187         if (closed) {
 188             return;
 189         }
 190 
 191         if (!isMarked()) {
 192             throw new IOException ("Resetting to an invalid mark");
 193         }
 194 
 195         count = markedCount;
 196         super.reset();
 197     }
 198 
 199     public boolean markSupported() {
 200         if (closed) {
 201             return false;
 202         }
 203         return super.markSupported();
 204     }
 205 
 206     @SuppressWarnings("deprecation")
 207     protected void finalize() throws Throwable {
 208         try {
 209             close();
 210             if (pi != null)
 211                 pi.close();
 212         }
 213         finally {
 214             // Call super class
 215             super.finalize();
 216         }
 217     }
 218 }