summaryrefslogtreecommitdiffstats
path: root/simple/simple-transport/src/main/java/org/simpleframework/transport/SecureTransport.java
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-transport/src/main/java/org/simpleframework/transport/SecureTransport.java')
-rw-r--r--simple/simple-transport/src/main/java/org/simpleframework/transport/SecureTransport.java428
1 files changed, 428 insertions, 0 deletions
diff --git a/simple/simple-transport/src/main/java/org/simpleframework/transport/SecureTransport.java b/simple/simple-transport/src/main/java/org/simpleframework/transport/SecureTransport.java
new file mode 100644
index 0000000..2083873
--- /dev/null
+++ b/simple/simple-transport/src/main/java/org/simpleframework/transport/SecureTransport.java
@@ -0,0 +1,428 @@
+/*
+ * 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;
+
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * 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 certificate associated with this SSL connection.
+ */
+ private Certificate certificate;
+
+ /**
+ * 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 the trace that is used to monitor socket activity.
+ */
+ private Trace trace;
+
+ /**
+ * 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 certificate this is the certificate for the connection
+ * @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, Certificate certificate, ByteBuffer input, ByteBuffer swap) {
+ this(transport, certificate, 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 certificate this is the certificate for the connection
+ * @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, Certificate certificate, ByteBuffer input, ByteBuffer swap, int size) {
+ this.output = ByteBuffer.allocate(size);
+ this.engine = transport.getEngine();
+ this.trace = transport.getTrace();
+ this.certificate = certificate;
+ this.transport = transport;
+ this.input = input;
+ this.swap = swap;
+ }
+
+ /**
+ * This is used to acquire the SSL certificate used when the
+ * server is using a HTTPS connection. For plain text connections
+ * or connections that use a security mechanism other than SSL
+ * this will be null. This is only available when the connection
+ * makes specific use of an SSL engine to secure the connection.
+ *
+ * @return this returns the associated SSL certificate if any
+ */
+ public Certificate getCertificate() {
+ return certificate;
+ }
+
+ /**
+ * This is used to acquire the trace object that is associated
+ * with the socket. A trace object is used to collection details
+ * on what operations are being performed on the socket. For
+ * instance it may contain information relating to I/O events
+ * or more application specific events such as errors.
+ *
+ * @return this returns the trace associated with this socket
+ */
+ public Trace getTrace() {
+ return trace;
+ }
+
+ /**
+ * 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;
+ }
+ }
+}