001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.xnet.provider.jsse;
022:
023: import org.apache.harmony.xnet.provider.jsse.AlertException;
024: import org.apache.harmony.xnet.provider.jsse.SSLSocketImpl;
025:
026: import java.io.IOException;
027: import java.io.InputStream;
028: import javax.net.ssl.SSLException;
029:
030: /**
031: * This class provides input data stream functionality
032: * for SSLSocket. It accumulates the application data
033: * received by SSL protocol.
034: */
035: public final class SSLSocketInputStream extends InputStream {
036:
037: // The size of the internal data buffer.
038: // It should not be less than maximum data chunk enclosed
039: // in one ssl packet.
040: private int size = SSLRecordProtocol.MAX_DATA_LENGTH;
041:
042: // Internal buffer accumulating the received application data
043: private byte[] buffer = new byte[size];
044:
045: // position of the next byte to read from the buffer
046: private int pos;
047:
048: // position of the last byte to read + 1
049: private int end;
050:
051: // the ssl socket owning the stream
052: private final SSLSocketImpl owner;
053:
054: // the flag indicating that the end of the (owner's) input stream
055: // has been reached
056: private boolean end_reached = false;
057:
058: /**
059: * Creates the application data input stream for specified socket.
060: * @param owner the socket which will provide this input stream
061: * to client applications.
062: */
063: protected SSLSocketInputStream(SSLSocketImpl owner) {
064: this .owner = owner;
065: }
066:
067: // The helper delivering the application data from the record layer
068: protected Adapter dataPoint = new Adapter();
069:
070: /**
071: * Tells to the stream that the end of the income data has
072: * been reached.
073: */
074: protected void setEnd() {
075: end_reached = true;
076: }
077:
078: // ------------------ InputStream implementetion -------------------
079:
080: /**
081: * Returns the number of bytes available for reading without blocking.
082: * @return the number of available bytes.
083: * @throws IOException
084: */
085: public int available() throws IOException {
086: return end - pos;
087: }
088:
089: /**
090: * Closes the stream
091: * @throws IOException
092: */
093: public void close() throws IOException {
094: buffer = null;
095: }
096:
097: /**
098: * Reads one byte. If there is no data in the underlying buffer,
099: * this operation can block until the data will be
100: * available.
101: * @return read value.
102: * @throws IOException
103: */
104: public int read() throws IOException {
105: if (buffer == null) {
106: throw new IOException("Stream was closed.");
107: }
108: while (pos == end) {
109: if (end_reached) {
110: return -1;
111: }
112: // If there is no data in the buffer
113: // - will block untill the data will be provided by
114: // record layer
115: owner.needAppData();
116: }
117: return buffer[pos++] & 0xFF;
118: }
119:
120: /**
121: * Method acts as described in spec for superclass.
122: * @see java.io.InputStream#read(byte[])
123: */
124: public int read(byte[] b) throws IOException {
125: return read(b, 0, b.length);
126: }
127:
128: /**
129: * Method acts as described in spec for superclass.
130: * @see java.io.InputStream#read(byte[],int,int)
131: */
132: public int read(byte[] b, int off, int len) throws IOException {
133: int read_b;
134: int i = 0;
135: do {
136: if ((read_b = read()) == -1) {
137: return (i == 0) ? -1 : i;
138: }
139: b[off + i] = (byte) read_b;
140: i++;
141: } while ((available() != 0) && (i < len));
142: return i;
143: }
144:
145: /**
146: * Method acts as described in spec for superclass.
147: * @see java.io.InputStream#skip(long)
148: */
149: public long skip(long n) throws IOException {
150: long i = 0;
151: int av = available();
152: if (av < n) {
153: n = av;
154: }
155: while ((i < n) && (read() != -1)) {
156: i++;
157: }
158: return i;
159: }
160:
161: // The helper class devivering the application data from the record layer
162: // to this input stream.
163: // It 'adapts' the InputStream interface to Appendable, which is used for
164: // transmition of income data from the record protocol to its clients.
165: private class Adapter implements
166: org.apache.harmony.xnet.provider.jsse.Appendable {
167: /**
168: * Appends the data to the stream.
169: * This method could be implemented in the outer class
170: * itself, but it could be insecure.
171: */
172: public void append(byte[] src) {
173: int length = src.length;
174: if (size - (end - pos) < length) {
175: // If the size of the buffer is greater than or equals to
176: // SSLRecordProtocol.MAX_DATA_LENGTH this situation will
177: // happen iff:
178: // 1. the length of received data fragment is greater
179: // than allowed by the spec
180: // 2. it is rehandhaking stage and we have got several
181: // extra app data messages.
182: // In any case it is better to throw alert exception.
183: throw new AlertException(AlertProtocol.INTERNAL_ERROR,
184: new SSLException(
185: "Could not accept income app data."));
186: }
187: if (end + length > size) {
188: // move the content of the buffer to the beginnig
189: System.arraycopy(buffer, pos, buffer, 0, end - pos);
190: end -= pos;
191: pos = 0;
192: }
193: System.arraycopy(src, 0, buffer, end, length);
194: end = end + length;
195: }
196: }
197: }
|