Path: blob/master/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java
41161 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.www.http;2627import java.io.*;2829import sun.net.ProgressSource;30import sun.net.www.MeteredStream;31import jdk.internal.misc.InnocuousThread;3233/**34* A stream that has the property of being able to be kept alive for35* multiple downloads from the same server.36*37* @author Stephen R. Pietrowicz (NCSA)38* @author Dave Brown39*/40public41class KeepAliveStream extends MeteredStream implements Hurryable {4243// instance variables44HttpClient hc;4546boolean hurried;4748// has this KeepAliveStream been put on the queue for asynchronous cleanup.49// This flag is read from within KeepAliveCleanerEntry outside of any lock.50protected volatile boolean queuedForCleanup = false;5152private static final KeepAliveStreamCleaner queue = new KeepAliveStreamCleaner();53private static Thread cleanerThread; // null5455/**56* Constructor57*/58public KeepAliveStream(InputStream is, ProgressSource pi, long expected, HttpClient hc) {59super(is, pi, expected);60this.hc = hc;61}6263/**64* Attempt to cache this connection65*/66public void close() throws IOException {67// If the inputstream is queued for cleanup, just return.68if (queuedForCleanup) return;6970// Skip past the data that's left in the Inputstream because71// some sort of error may have occurred.72// Do this ONLY if the skip won't block. The stream may have73// been closed at the beginning of a big file and we don't want74// to hang around for nothing. So if we can't skip without blocking75// we just close the socket and, therefore, terminate the keepAlive76// NOTE: Don't close super class77// For consistency, access to `expected` and `count` should be78// protected by readLock79lock();80try {81// If the inputstream is closed already, or if this stream82// has already been queued for cleanup, just return.83if (closed || queuedForCleanup) return;84try {85if (expected > count) {86long nskip = expected - count;87if (nskip <= available()) {88do {89} while ((nskip = (expected - count)) > 0L90&& skip(Math.min(nskip, available())) > 0L);91} else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {92//put this KeepAliveStream on the queue so that the data remaining93//on the socket can be cleanup asyncronously.94queueForCleanup(new KeepAliveCleanerEntry(this, hc));95} else {96hc.closeServer();97}98}99if (!closed && !hurried && !queuedForCleanup) {100hc.finished();101}102} finally {103if (pi != null)104pi.finishTracking();105106if (!queuedForCleanup) {107// nulling out the underlying inputstream as well as108// httpClient to let gc collect the memories faster109in = null;110hc = null;111closed = true;112}113}114} finally {115unlock();116}117}118119/* we explicitly do not support mark/reset */120121public boolean markSupported() {122return false;123}124125public void mark(int limit) {}126127public void reset() throws IOException {128throw new IOException("mark/reset not supported");129}130131public boolean hurry() {132lock();133try {134/* CASE 0: we're actually already done */135if (closed || count >= expected) {136return false;137} else if (in.available() < (expected - count)) {138/* CASE I: can't meet the demand */139return false;140} else {141/* CASE II: fill our internal buffer142* Remind: possibly check memory here143*/144int size = (int) (expected - count);145byte[] buf = new byte[size];146DataInputStream dis = new DataInputStream(in);147dis.readFully(buf);148in = new ByteArrayInputStream(buf);149hurried = true;150return true;151}152} catch (IOException e) {153// e.printStackTrace();154return false;155} finally {156unlock();157}158}159160@SuppressWarnings("removal")161private static void queueForCleanup(KeepAliveCleanerEntry kace) {162queue.lock();163try {164if(!kace.getQueuedForCleanup()) {165if (!queue.offer(kace)) {166kace.getHttpClient().closeServer();167return;168}169170kace.setQueuedForCleanup();171queue.signalAll();172}173174boolean startCleanupThread = (cleanerThread == null);175if (!startCleanupThread) {176if (!cleanerThread.isAlive()) {177startCleanupThread = true;178}179}180181if (startCleanupThread) {182java.security.AccessController.doPrivileged(183new java.security.PrivilegedAction<Void>() {184public Void run() {185cleanerThread = InnocuousThread.newSystemThread("Keep-Alive-SocketCleaner", queue);186cleanerThread.setDaemon(true);187cleanerThread.setPriority(Thread.MAX_PRIORITY - 2);188cleanerThread.start();189return null;190}191});192}193} finally {194queue.unlock();195}196}197198// Only called from KeepAliveStreamCleaner199protected long remainingToRead() {200assert isLockHeldByCurrentThread();201return expected - count;202}203204// Only called from KeepAliveStreamCleaner205protected void setClosed() {206assert isLockHeldByCurrentThread();207in = null;208hc = null;209closed = true;210}211}212213214