Home | History | Annotate | Download | only in jsse
      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