com.qcloud.cos.internal.crypto.AdjustedRangeInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cos_api-bundle Show documentation
Show all versions of cos_api-bundle Show documentation
A single bundled dependency that includes all service and dependent JARs with third-party libraries
relocated to different namespaces.
/*
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
* According to cos feature, we modify some class,comment, field name, etc.
*/
package com.qcloud.cos.internal.crypto;
import java.io.IOException;
import java.io.InputStream;
import com.qcloud.cos.internal.SdkInputStream;
import com.qcloud.cos.utils.IOUtils;
/**
* Reads only a specific range of bytes from the underlying input stream.
*/
public class AdjustedRangeInputStream extends SdkInputStream {
private InputStream decryptedContents;
private long virtualAvailable;
private boolean closed;
/**
* Creates a new DecryptedContentsInputStream object.
*
* @param objectContents
* The input stream containing the object contents retrieved from COS
* @param rangeBeginning
* The position of the left-most byte desired by the user
* @param rangeEnd
* The position of the right-most byte desired by the user
* @throws IOException
* If there are errors skipping to the left-most byte desired by the user.
*/
public AdjustedRangeInputStream(InputStream objectContents, long rangeBeginning, long rangeEnd) throws IOException {
this.decryptedContents = objectContents;
this.closed = false;
initializeForRead(rangeBeginning, rangeEnd);
}
/**
* Skip to the start location of the range of bytes desired by the user.
*/
private void initializeForRead(long rangeBeginning, long rangeEnd) throws IOException {
// To get to the left-most byte desired by a user, we must skip over the 16 bytes of the
// preliminary cipher block, and then possibly skip a few more bytes into the next block
// to where the the left-most byte is located.
int numBytesToSkip;
if(rangeBeginning < JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE) {
numBytesToSkip = (int)rangeBeginning;
} else {
int offsetIntoBlock = (int)(rangeBeginning % JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE);
numBytesToSkip = JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE + offsetIntoBlock;
}
if(numBytesToSkip != 0) {
// Skip to the left-most desired byte. The read() method is used instead of the skip() method
// since the skip() method will not block if the underlying input stream is waiting for more input.
while(numBytesToSkip > 0) {
this.decryptedContents.read();
numBytesToSkip--;
}
}
// The number of bytes the user may read is equal to the number of the bytes in the range.
// Note that the range includes the endpoints.
this.virtualAvailable = (rangeEnd - rangeBeginning) + 1;
}
/* (non-Javadoc)
* @see java.io.InputStream#read()
*/
@Override
public int read() throws IOException {
abortIfNeeded();
int result;
// If there are no more available bytes, mark that we are at the end of the stream.
if (this.virtualAvailable <= 0) {
result = -1;
} else {
// Otherwise, read a byte.
result = this.decryptedContents.read();
}
// If we have not reached the end of the stream, decrement the number of available bytes.
if (result != -1) {
this.virtualAvailable--;
} else {
// If we are at the end of the stream, close it.
this.virtualAvailable = 0;
close();
}
return result;
}
/* (non-Javadoc)
* @see java.io.InputStream#read(byte[], int, int)
*/
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
abortIfNeeded();
int numBytesRead;
// If no more bytes are available, do not read any bytes into the buffer
if(this.virtualAvailable <= 0) {
numBytesRead = -1;
} else {
// If the desired read length is greater than the number of available bytes,
// shorten the read length to the number of available bytes.
if(length > this.virtualAvailable) {
// If the number of available bytes is greater than the maximum value of a 32 bit int, then
// read as many bytes as an int can.
length = (this.virtualAvailable < Integer.MAX_VALUE) ? (int)this.virtualAvailable : Integer.MAX_VALUE;
}
// Read bytes into the buffer.
numBytesRead = this.decryptedContents.read(buffer, offset, length);
}
// If we were able to read bytes, decrement the number of bytes available to be read.
if(numBytesRead != -1) {
this.virtualAvailable -= numBytesRead;
} else {
// If we've reached the end of the stream, close it
this.virtualAvailable = 0;
close();
}
return numBytesRead;
}
/* (non-Javadoc)
* @see java.io.InputStream#available()
*/
@Override
public int available() throws IOException {
abortIfNeeded();
int available = this.decryptedContents.available();
if(available < this.virtualAvailable) {
return available;
} else {
// Limit the number of bytes available to the number
// of bytes remaining in the range.
return (int)this.virtualAvailable;
}
}
/* (non-Javadoc)
* @see java.io.InputStream#close()
*/
@Override
public void close() throws IOException {
// If not already closed, then close the input stream.
if(!this.closed) {
this.closed = true;
// if the user read to the end of the virtual stream, then drain
// the wrapped stream so the HTTP client can keep this connection
// alive if possible.
// This should not have too much overhead since if we've reached the
// end of the virtual stream, there should be at most 31 bytes left
// (2 * JceEncryptionConstants.SYMMETRIC_CIPHER_BLOCK_SIZE - 1) in the
// stream.
// See: COSCryptoModuleBase#getCipherBlockUpperBound
if (this.virtualAvailable == 0) {
IOUtils.drainInputStream(decryptedContents);
}
this.decryptedContents.close();
}
abortIfNeeded();
}
@Override
protected InputStream getWrappedInputStream() {
return decryptedContents;
}
}