blob: 68b9a192151d15696469876c5c9b376d3c58b5e5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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.
*/
package org.apache.harmony.xnet.provider.jsse;
import java.io.IOException;
import java.io.InputStream;
import javax.net.ssl.SSLException;
/**
* This class provides input data stream functionality
* for SSLSocket. It accumulates the application data
* received by SSL protocol.
*/
public final class SSLSocketInputStream
extends InputStream {
// The size of the internal data buffer.
// It should not be less than maximum data chunk enclosed
// in one ssl packet.
private final int size = SSLRecordProtocol.MAX_DATA_LENGTH;
// Internal buffer accumulating the received application data
private byte[] buffer = new byte[size];
// position of the next byte to read from the buffer
private int pos;
// position of the last byte to read + 1
private int end;
// the ssl socket owning the stream
private final SSLSocketImpl owner;
// the flag indicating that the end of the (owner's) input stream
// has been reached
private boolean end_reached = false;
/**
* Creates the application data input stream for specified socket.
* @param owner the socket which will provide this input stream
* to client applications.
*/
protected SSLSocketInputStream(SSLSocketImpl owner) {
this.owner = owner;
}
// The helper delivering the application data from the record layer
protected Adapter dataPoint = new Adapter();
/**
* Tells to the stream that the end of the income data has
* been reached.
*/
protected void setEnd() {
end_reached = true;
}
// ------------------ InputStream implementetion -------------------
/**
* Returns the number of bytes available for reading without blocking.
* @return the number of available bytes.
* @throws IOException
*/
@Override
public int available() throws IOException {
return end - pos;
}
/**
* Closes the stream
* @throws IOException
*/
@Override
public void close() throws IOException {
buffer = null;
}
/**
* Reads one byte. If there is no data in the underlying buffer,
* this operation can block until the data will be
* available.
* @return read value.
* @throws IOException
*/
@Override
public int read() throws IOException {
if (buffer == null) {
throw new IOException("Stream was closed.");
}
while (pos == end) {
if (end_reached) {
return -1;
}
// If there is no data in the buffer
// - will block untill the data will be provided by
// record layer
owner.needAppData();
}
return buffer[pos++] & 0xFF;
}
/**
* Method acts as described in spec for superclass.
* @see java.io.InputStream#read(byte[])
*/
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/**
* Method acts as described in spec for superclass.
* @see java.io.InputStream#read(byte[],int,int)
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
int read_b;
int i = 0;
do {
if ((read_b = read()) == -1) {
return (i == 0) ? -1 : i;
}
b[off+i] = (byte) read_b;
i++;
} while ((available() != 0) && (i<len));
return i;
}
/**
* Method acts as described in spec for superclass.
* @see java.io.InputStream#skip(long)
*/
@Override
public long skip(long n) throws IOException {
long i = 0;
int av = available();
if (av < n) {
n = av;
}
while ((i < n) && (read() != -1)) {
i++;
}
return i;
}
// The helper class devivering the application data from the record layer
// to this input stream.
// It 'adapts' the InputStream interface to Appendable, which is used for
// transmition of income data from the record protocol to its clients.
private class Adapter implements org.apache.harmony.xnet.provider.jsse.Appendable {
/**
* Appends the data to the stream.
* This method could be implemented in the outer class
* itself, but it could be insecure.
*/
public void append(byte[] src) {
int length = src.length;
if (size - (end - pos) < length) {
// If the size of the buffer is greater than or equals to
// SSLRecordProtocol.MAX_DATA_LENGTH this situation will
// happen iff:
// 1. the length of received data fragment is greater
// than allowed by the spec
// 2. it is rehandhaking stage and we have got several
// extra app data messages.
// In any case it is better to throw alert exception.
throw new AlertException(AlertProtocol.INTERNAL_ERROR,
new SSLException("Could not accept income app data."));
}
if (end + length > size) {
// move the content of the buffer to the beginnig
System.arraycopy(buffer, pos, buffer, 0, end-pos);
end -= pos;
pos = 0;
}
System.arraycopy(src, 0, buffer, end, length);
end = end + length;
}
}
}