1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.apache.harmony.luni.internal.net.www.protocol.http; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.io.OutputStream; 22 import java.net.CacheRequest; 23 24 /** 25 * An input stream for the body of an HTTP response. 26 * 27 * <p>Since a single socket's input stream may be used to read multiple HTTP 28 * responses from the same server, subclasses shouldn't close the socket stream. 29 * 30 * <p>A side effect of reading an HTTP response is that the response cache 31 * is populated. If the stream is closed early, that cache entry will be 32 * invalidated. 33 */ 34 abstract class AbstractHttpInputStream extends InputStream { 35 protected final InputStream in; 36 protected final HttpURLConnectionImpl httpURLConnection; 37 protected final CacheRequest cacheRequest; 38 protected final OutputStream cacheOut; 39 protected boolean closed; 40 private byte[] skipBuffer; 41 42 AbstractHttpInputStream(InputStream in, HttpURLConnectionImpl httpURLConnection, 43 CacheRequest cacheRequest) throws IOException { 44 this.in = in; 45 this.httpURLConnection = httpURLConnection; 46 this.cacheRequest = cacheRequest; 47 this.cacheOut = cacheRequest != null ? cacheRequest.getBody() : null; 48 } 49 50 /** 51 * read() is implemented using read(byte[], int, int) so subclasses only 52 * need to override the latter. 53 */ 54 @Override public final int read() throws IOException { 55 byte[] buffer = new byte[1]; 56 int count = read(buffer, 0, 1); 57 return count == -1 ? -1 : buffer[0] & 0xff; 58 } 59 60 /** 61 * skip(long) is implemented using read(byte[], int, int) so subclasses 62 * only need to override the latter. 63 */ 64 @Override public final long skip(long n) throws IOException { 65 if (skipBuffer == null) { 66 skipBuffer = new byte[4096]; 67 } 68 long total = 0; 69 while (total < n) { 70 // Calling read() ensures the skipped bytes make it into the response cache. 71 int count = read(skipBuffer, 0, (int) Math.min(n - total, skipBuffer.length)); 72 if (count == -1) { 73 break; 74 } 75 total += count; 76 } 77 return total; 78 } 79 80 protected final void checkBounds(byte[] buffer, int offset, int count) { 81 if (offset < 0 || offset > buffer.length || count < 0 || buffer.length - offset < count) { 82 throw new ArrayIndexOutOfBoundsException( 83 "offset=" + offset + ", buffer.length=" + buffer.length + ", count=" + count); 84 } 85 } 86 87 protected final void checkNotClosed() throws IOException { 88 if (closed) { 89 throw new IOException("stream closed"); 90 } 91 } 92 93 protected final void cacheWrite(byte[] buffer, int offset, int count) throws IOException { 94 if (cacheOut != null) { 95 cacheOut.write(buffer, offset, count); 96 } 97 } 98 99 /** 100 * Closes the cache entry and makes the socket available for reuse. This 101 * should be invoked when the end of the body has been reached. 102 */ 103 protected final void endOfInput(boolean reuseSocket) throws IOException { 104 if (cacheRequest != null) { 105 cacheOut.close(); 106 } 107 httpURLConnection.releaseSocket(reuseSocket); 108 } 109 110 /** 111 * Calls abort on the cache entry and disconnects the socket. This 112 * should be invoked when the connection is closed unexpectedly to 113 * invalidate the cache entry and to prevent the HTTP connection from 114 * being reused. HTTP messages are sent in serial so whenever a message 115 * cannot be read to completion, subsequent messages cannot be read 116 * either and the connection must be discarded. 117 * 118 * <p>An earlier implementation skipped the remaining bytes, but this 119 * requires that the entire transfer be completed. If the intention was 120 * to cancel the transfer, closing the connection is the only solution. 121 */ 122 protected final void unexpectedEndOfInput() { 123 if (cacheRequest != null) { 124 cacheRequest.abort(); 125 } 126 httpURLConnection.releaseSocket(false); 127 } 128 } 129