Package org.simpleframework.transport

Source Code of org.simpleframework.transport.SecureTransport

/*
* SecureTransport.java February 2007
*
* Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
*
* Licensed 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.simpleframework.transport;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Map;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.Status;

/**
* The <code>SecureTransport</code> object provides an implementation
* of a transport used to send and receive data over SSL. Data read
* from this transport is decrypted using an <code>SSLEngine</code>.
* Also, all data is written is encrypted with the same engine. This
* ensures that data can be send and received in a transparent way.
*
* @author Niall Gallagher
*/
class SecureTransport implements Transport {  

   /**
    * This is the transport used to send data over the socket.
    */
   private Transport transport;
  
   /**
    * This buffer is used to output the data for the SSL sent.
    */
   private ByteBuffer output;
  
   /**
    * This is the internal buffer used to exchange the SSL data.
    */
   private ByteBuffer input;

   /**
    * This is the internal buffer used to exchange the SSL data.
    */
   private ByteBuffer swap;
  
   /**
    * This is the SSL engine used to encrypt and decrypt data.
    */
   private SSLEngine engine;

   /**
    * This is used to determine if the transport was closed.
    */
   private boolean closed;
  
   /**
    * This is used to determine if the end of stream was reached.
    */
   private boolean finished;
  
   /**
    * Constructor for the <code>SecureTransport</code> object. This
    * is used to create a transport for sending and receiving data
    * over SSL. This must be created with a pipeline that has already
    * performed the SSL handshake and is read to used.
    *
    * @param transport this is the transport to delegate operations to
    * @param input this is the input buffer used to read the data
    * @param swap this is the swap buffer to be used for reading
    */
   public SecureTransport(Transport transport, ByteBuffer input, ByteBuffer swap) {
      this(transport, input, swap, 20480);
   }
  
   /**
    * Constructor for the <code>SecureTransport</code> object. This
    * is used to create a transport for sending and receiving data
    * over SSL. This must be created with a pipeline that has already
    * performed the SSL handshake and is read to used.
    *
    * @param transport this is the transport to delegate operations to
    * @param input this is the input buffer used to read the data
    * @param swap this is the swap buffer to be used for reading
    * @param size this is the size of the buffers to be allocated
    */
   public SecureTransport(Transport transport, ByteBuffer input, ByteBuffer swap, int size) {
      this.output = ByteBuffer.allocate(size);
      this.engine = transport.getEngine();
      this.transport = transport;
      this.input = input;   
      this.swap = swap;
   }
  
   /**
    * This is used to acquire the SSL engine used for HTTPS. If the
    * pipeline is connected to an SSL transport this returns an SSL
    * engine which can be used to establish the secure connection
    * and send and receive content over that connection. If this is
    * null then the pipeline represents a normal transport.
    * 
    * @return the SSL engine used to establish a secure transport
    */  
   public SSLEngine getEngine() {
      return engine;
   }
  
   /**
    * This method is used to get the <code>Map</code> of attributes
    * by this pipeline. The attributes map is used to maintain details
    * about the connection. Information such as security credentials
    * to client details can be placed within the attribute map.
    *
    * @return this returns the map of attributes for this pipeline
    */  
   public Map getAttributes() {
      return transport.getAttributes();
   }
  
   /**
    * This method is used to acquire the <code>SocketChannel</code>
    * for the connection. This allows the server to acquire the input
    * and output streams with which to communicate. It can also be
    * used to configure the connection and perform various network
    * operations that could otherwise not be performed.
    *
    * @return this returns the socket used by this HTTP pipeline
    */
   public SocketChannel getChannel() {
      return transport.getChannel();
   }  
  
   /**
    * This is used to perform a non-blocking read on the transport.
    * If there are no bytes available on the input buffers then
    * this method will return zero and the buffer will remain the
    * same. If there is data and the buffer can be filled then this
    * will return the number of bytes read. Finally if the socket
    * is closed this will return a -1 value.
    *
    * @param buffer this is the buffer to append the bytes to
    *
    * @return this returns the number of bytes that have been read 
    */
   public int read(ByteBuffer buffer) throws IOException {
      if(closed) {
         throw new TransportException("Transport is closed");             
      }  
      if(finished) {
         return -1;
      }
      int count = fill(buffer);
     
      if(count <= 0) {
         return process(buffer);
      }
      return count;
   }
  
   /**
    * This is used to perform a non-blocking read on the transport.
    * If there are no bytes available on the input buffers then
    * this method will return zero and the buffer will remain the
    * same. If there is data and the buffer can be filled then this
    * will return the number of bytes read.
    *
    * @param buffer this is the buffer to append the bytes to
    *
    * @return this returns the number of bytes that have been read 
    */
   private int process(ByteBuffer buffer) throws IOException {
      int size = swap.position()
     
      if(size >= 0) {
         swap.compact();
      }
      int space = swap.remaining();
     
      if(space > 0) {
         size = transport.read(swap);
        
         if(size < 0) {
            finished = true;
         }
      }
      if(size > 0 || space > 0) {
         swap.flip();
         receive();
      }
      return fill(buffer);
   }    
  
   /**
    * This is used to fill the provided buffer with data that has
    * been read from the secure socket channel. This enables reading
    * of the decrypted data in chunks that are smaller than the
    * size of the input buffer used to contain the plain text data.
    *
    * @param buffer this is the buffer to append the bytes to
    *
    * @return this returns the number of bytes that have been read
    */
   private int fill(ByteBuffer buffer) throws IOException {
      int space = buffer.remaining();
      int count = input.position();
     
      if(count > 0) {
         if(count > space) {
            count = space;
         }
      }
      return fill(buffer, count);
     
   }

   /**
    * This is used to fill the provided buffer with data that has
    * been read from the secure socket channel. This enables reading
    * of the decrypted data in chunks that are smaller than the
    * size of the input buffer used to contain the plain text data.
    *
    * @param buffer this is the buffer to append the bytes to
    * @param count this is the number of bytes that are to be read
    *
    * @return this returns the number of bytes that have been read
    */
   private int fill(ByteBuffer buffer, int count) throws IOException {
      input.flip();
     
      if(count > 0) {
         count = append(buffer, count);
      }
      input.compact();   
      return count;
   }
  
   /**
    * This will append bytes within the transport to the given buffer.
    * Once invoked the buffer will contain the transport bytes, which
    * will have been drained from the buffer. This effectively moves
    * the bytes in the buffer to the end of the packet instance.
    *
    * @param buffer this is the buffer containing the bytes
    * @param count this is the number of bytes that should be used
    *
    * @return returns the number of bytes that have been moved
    */ 
   private int append(ByteBuffer buffer, int count) throws IOException {
      ByteBuffer segment = input.slice();

      if(closed) {
         throw new TransportException("Transport is closed");              
      }
      int mark = input.position();
      int size = mark + count;
     
      if(count > 0) {
         input.position(size);
         segment.limit(count);        
         buffer.put(segment);
      }
      return count;
   }
  
   /**
    * This is used to perform a non-blocking read on the transport.
    * If there are no bytes available on the input buffers then
    * this method will return zero and the buffer will remain the
    * same. If there is data and the buffer can be filled then this
    * will return the number of bytes read. Finally if the socket
    * is closed this will return a -1 value.
    */   
   private void receive() throws IOException {
      int count = swap.remaining();
     
      while(count > 0) {
         SSLEngineResult result = engine.unwrap(swap, input);
         Status status = result.getStatus();
        
         switch(status) {
         case BUFFER_OVERFLOW:
         case BUFFER_UNDERFLOW:
            return;
         case CLOSED:    
            throw new TransportException("Transport error " + result);      
         }   
         count = swap.remaining();
        
         if(count <= 0) {
            break;
         }
      }
   }
  
   /**
    * This method is used to deliver the provided buffer of bytes to
    * the underlying transport. Depending on the connection type the
    * array may be encoded for SSL transport or send directly. Any
    * implementation may choose to buffer the bytes for performance.
    *
    * @param buffer this is the array of bytes to send to the client
    */
   public void write(ByteBuffer buffer) throws IOException {   
      if(closed) {
        throw new TransportException("Transport is closed");             
      }           
      int capacity = output.capacity();
      int ready = buffer.remaining();
      int length = ready;
     
      while(ready > 0) {
         int size = Math.min(ready, capacity / 2);
         int mark = buffer.position();
        
         if(length * 2 > capacity) {                     
            buffer.limit(mark + size);        
         }
         send(buffer);
         output.clear();
         ready -= size;
      }
   }
  
   /**
    * This method is used to deliver the provided buffer of bytes to
    * the underlying transport. Depending on the connection type the
    * array may be encoded for SSL transport or send directly. Any
    * implementation may choose to buffer the bytes for performance.
    *
    * @param buffer this is the array of bytes to send to the client
    */
   private void send(ByteBuffer buffer) throws IOException {  
      SSLEngineResult result = engine.wrap(buffer, output);
      Status status = result.getStatus();
    
      switch(status){
      case BUFFER_OVERFLOW:
      case BUFFER_UNDERFLOW:
      case CLOSED:
         throw new TransportException("Transport error " + status);
      default:
         output.flip();
      }    
      transport.write(output);
   }
  
   /**
    * This method is used to flush the contents of the buffer to
    * the client. This method will block until such time as all of
    * the data has been sent to the client. If at any point there
    * is an error sending the content an exception is thrown.   
    */    
   public void flush() throws IOException {
      if(closed) {
         throw new TransportException("Transport is closed");             
       }          
       transport.flush();
   }
  
   /**
    * This is used to close the sender and the underlying transport.
    * If a close is performed on the sender then no more bytes can
    * be read from or written to the transport and the client will
    * received a connection close on their side.
    */ 
   public void close() throws IOException {
      if(!closed) {
         transport.close();
         closed = true;
      }
   }
}
TOP

Related Classes of org.simpleframework.transport.SecureTransport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.