1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.harmony.xnet.provider.jsse; 19 20 import java.io.IOException; 21 import java.io.InputStream; 22 import javax.net.ssl.SSLException; 23 24 /** 25 * This class provides input data stream functionality 26 * for SSLSocket. It accumulates the application data 27 * received by SSL protocol. 28 */ 29 public final class SSLSocketInputStream extends InputStream { 30 31 // The size of the internal data buffer. 32 // It should not be less than maximum data chunk enclosed 33 // in one ssl packet. 34 private static final int BUFFER_SIZE = SSLRecordProtocol.MAX_DATA_LENGTH; 35 36 // Internal buffer accumulating the received application data 37 private byte[] buffer = new byte[BUFFER_SIZE]; 38 39 // position of the next byte to read from the buffer 40 private int pos; 41 42 // position of the last byte to read + 1 43 private int end; 44 45 // the ssl socket owning the stream 46 private final SSLSocketImpl owner; 47 48 // the flag indicating that the end of the (owner's) input stream 49 // has been reached 50 private boolean end_reached = false; 51 52 /** 53 * Creates the application data input stream for specified socket. 54 * @param owner the socket which will provide this input stream 55 * to client applications. 56 */ 57 protected SSLSocketInputStream(SSLSocketImpl owner) { 58 this.owner = owner; 59 } 60 61 // The helper delivering the application data from the record layer 62 protected Adapter dataPoint = new Adapter(); 63 64 /** 65 * Tells to the stream that the end of the income data has 66 * been reached. 67 */ 68 protected void setEnd() { 69 end_reached = true; 70 } 71 72 // ------------------ InputStream implementation ------------------- 73 74 /** 75 * Returns the number of bytes available for reading without blocking. 76 * @return the number of available bytes. 77 * @throws IOException 78 */ 79 @Override 80 public int available() throws IOException { 81 return end - pos; 82 } 83 84 /** 85 * Closes the stream 86 * @throws IOException 87 */ 88 @Override 89 public void close() throws IOException { 90 buffer = null; 91 } 92 93 /** 94 * Reads one byte. If there is no data in the underlying buffer, 95 * this operation can block until the data will be 96 * available. 97 * @return read value. 98 * @throws IOException 99 */ 100 @Override 101 public int read() throws IOException { 102 if (buffer == null) { 103 throw new IOException("Stream was closed."); 104 } 105 while (pos == end) { 106 if (end_reached) { 107 return -1; 108 } 109 // If there is no data in the buffer 110 // - will block until the data will be provided by 111 // record layer 112 owner.needAppData(); 113 } 114 return buffer[pos++] & 0xFF; 115 } 116 117 @Override public int read(byte[] b, int off, int len) throws IOException { 118 int read_b; 119 int i = 0; 120 do { 121 if ((read_b = read()) == -1) { 122 return (i == 0) ? -1 : i; 123 } 124 b[off+i] = (byte) read_b; 125 i++; 126 } while ((available() != 0) && (i<len)); 127 return i; 128 } 129 130 // The helper class delivering the application data from the record layer 131 // to this input stream. 132 // It 'adapts' the InputStream interface to Appendable, which is used for 133 // transmission of income data from the record protocol to its clients. 134 private class Adapter implements org.apache.harmony.xnet.provider.jsse.Appendable { 135 /** 136 * Appends the data to the stream. 137 * This method could be implemented in the outer class 138 * itself, but it could be insecure. 139 */ 140 public void append(byte[] src) { 141 int length = src.length; 142 if (BUFFER_SIZE - (end - pos) < length) { 143 // If the size of the buffer is greater than or equals to 144 // SSLRecordProtocol.MAX_DATA_LENGTH this situation will 145 // happen iff: 146 // 1. the length of received data fragment is greater 147 // than allowed by the spec 148 // 2. it is rehandshaking stage and we have got several 149 // extra app data messages. 150 // In any case it is better to throw alert exception. 151 throw new AlertException(AlertProtocol.INTERNAL_ERROR, 152 new SSLException("Could not accept income app data.")); 153 } 154 if (end + length > BUFFER_SIZE) { 155 // move the content of the buffer to the beginning 156 System.arraycopy(buffer, pos, buffer, 0, end-pos); 157 end -= pos; 158 pos = 0; 159 } 160 System.arraycopy(src, 0, buffer, end, length); 161 end = end + length; 162 } 163 } 164 } 165