Split sending message code into SSL and plain.

This commit is contained in:
wcrisman
2014-12-28 14:48:03 -08:00
parent 7f4d6702a0
commit 7a75ba30de

View File

@@ -235,8 +235,8 @@ private void queueOutboundClientMessage(MessageBuffer messageBuffer) {
if(currentOutboundMessage == null) { if(currentOutboundMessage == null) {
lastOutboundMessage = currentOutboundMessage = messageBuffer; lastOutboundMessage = currentOutboundMessage = messageBuffer;
notify = true; notify = true;
}//if// }//if//
else { else {
lastOutboundMessage.setNext(messageBuffer); lastOutboundMessage.setNext(messageBuffer);
lastOutboundMessage = messageBuffer; lastOutboundMessage = messageBuffer;
}//else// }//else//
@@ -244,7 +244,7 @@ private void queueOutboundClientMessage(MessageBuffer messageBuffer) {
if(notify) { if(notify) {
notifyListenerOfPendingWrite(); notifyListenerOfPendingWrite();
}//if// }//if//
}//queueOutboundClientMessage()// }//queueOutboundClientMessage()//
private void writeSessionCookies(PrintStream pout) { private void writeSessionCookies(PrintStream pout) {
Response response = currentResponse; Response response = currentResponse;
@@ -554,22 +554,22 @@ public synchronized boolean sendHttpResponse(Response response) {
* @see com.foundation.web.server.WebServer.AbstractSocketContext#passThrough(java.nio.ByteBuffer) * @see com.foundation.web.server.WebServer.AbstractSocketContext#passThrough(java.nio.ByteBuffer)
*/ */
protected synchronized boolean passThrough(ByteBuffer buffer) { protected synchronized boolean passThrough(ByteBuffer buffer) {
ByteBuffer messageBytes = ByteBuffer.allocate(buffer.remaining()); // ByteBuffer messageBytes = ByteBuffer.allocate(buffer.remaining());
MessageBuffer message; // MessageBuffer message;
//
//Create a new buffer to hold the data so we don't modify the passed buffer (other than to update its position).// // //Create a new buffer to hold the data so we don't modify the passed buffer (other than to update its position).//
messageBytes = ByteBuffer.allocate(buffer.remaining()); // messageBytes = ByteBuffer.allocate(buffer.remaining());
messageBytes.put(buffer); // messageBytes.put(buffer);
message = new MessageBuffer(messageBytes); // message = new MessageBuffer(messageBytes);
//
//Chain the message into the linked list. // //Chain the message into the linked list.
if(lastOutboundMessage == null || currentOutboundMessage == null) { // if(lastOutboundMessage == null || currentOutboundMessage == null) {
currentOutboundMessage = lastOutboundMessage = message; // currentOutboundMessage = lastOutboundMessage = message;
}//if// // }//if//
else { // else {
lastOutboundMessage.setNext(message); // lastOutboundMessage.setNext(message);
lastOutboundMessage = message; // lastOutboundMessage = message;
}//else// // }//else//
return true; return true;
}//passThrough()// }//passThrough()//
@@ -578,18 +578,18 @@ protected synchronized boolean passThrough(ByteBuffer buffer) {
*/ */
protected void writeOutgoingMessages() throws IOException { protected void writeOutgoingMessages() throws IOException {
if(getPassThroughSocketContext() != null) { if(getPassThroughSocketContext() != null) {
//Synchronized to avoid multiple threads accessing the pendingOutboundMessage chain at one time and updating the write flag out of order (could happen if we enabled request chaining over a single socket).// // //Synchronized to avoid multiple threads accessing the pendingOutboundMessage chain at one time and updating the write flag out of order (could happen if we enabled request chaining over a single socket).//
synchronized(this) { // synchronized(this) {
writeClientBoundMessage(); // writeClientBoundMessage();
}//synchronized// // }//synchronized//
}//if// }//if//
else if(isWebsocket) { else if(isWebsocket) {
//Right after upgrading the socket we have one last HTTP response to process.// //Right after upgrading the socket we have one last HTTP response to process.//
if(currentResponse != null) { // if(currentResponse != null) {
internalProcessResponses(); // internalProcessResponses();
}//if// // }//if//
//
internalProcessWebsocketMessages(); // internalProcessWebsocketMessages();
}//else if// }//else if//
else { else {
//Go directly to writing the client response if we are just passing everything through to another process.// //Go directly to writing the client response if we are just passing everything through to another process.//
@@ -770,99 +770,92 @@ private synchronized void internalProcessResponses() {
private boolean writeClientBoundMessage() { private boolean writeClientBoundMessage() {
boolean sendMore = true; boolean sendMore = true;
// if(getWebServer().debug) { if(sslEngine != null) {
// debugBuffer.append("Starting a write cycle.\n"); sendMore = writeClientBoundSslMessage();
// }//if// }//if//
else {
sendMore = writeClientBoundPlainMessage();
}//else//
return sendMore;
}//writeClientBoundMessage()//
/**
* Sends a response to the client.
* @return Whether the response could be fully sent. This will be false if there is still more data to be written when the call returns.
*/
private boolean writeClientBoundSslMessage() {
boolean sendMore = true;
try { try {
//Process SSL output first.// //If we have part of an SSL frame then try to send it first.//
if(sslEngine != null) { if(encryptedWriteBuffer.hasRemaining()) {
//If we have part of an SSL frame then try to send it first.// int remaining = encryptedWriteBuffer.remaining();
if(encryptedWriteBuffer.hasRemaining()) {
int remaining = encryptedWriteBuffer.remaining();
//Write the bytes to the stream.// //Write the bytes to the stream.//
((SocketChannel) key.channel()).write(encryptedWriteBuffer); ((SocketChannel) key.channel()).write(encryptedWriteBuffer);
// if(getWebServer().debug) {
// debugBuffer.append("Wrote " + (remaining - encryptedWriteBuffer.remaining()) + " encrypted bytes to the stream. " + encryptedWriteBuffer.remaining() + " remain.\n");
// }//if//
//Check to see if we failed to send the whole frame.//
if(encryptedWriteBuffer.hasRemaining()) {
sendMore = false;
}//if//
}//if//
while(sendMore && sslNeedsWrap) { //Check to see if we failed to send the whole frame.//
SSLEngineResult handshakeResult; if(encryptedWriteBuffer.hasRemaining()) {
sendMore = false;
//Reset the encrypted write buffer - note that since we will never read while waiting to write data, this should always be empty.// }//if//
encryptedWriteBuffer.position(0); }//if//
encryptedWriteBuffer.limit(encryptedWriteBuffer.capacity());
//Generate the handshake message.// while(sendMore && sslNeedsWrap) {
handshakeResult = sslEngine.wrap(ByteBuffer.allocate(0), encryptedWriteBuffer); SSLEngineResult handshakeResult;
encryptedWriteBuffer.flip();
//Reset the encrypted write buffer - note that since we will never read while waiting to write data, this should always be empty.//
if(handshakeResult.getStatus() == Status.BUFFER_OVERFLOW) { encryptedWriteBuffer.position(0);
encryptedWriteBuffer.limit(encryptedWriteBuffer.capacity());
//Generate the handshake message.//
handshakeResult = sslEngine.wrap(ByteBuffer.allocate(0), encryptedWriteBuffer);
encryptedWriteBuffer.flip();
if(handshakeResult.getStatus() == Status.BUFFER_OVERFLOW) {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine buffer overflow."));
}//if//
else if(handshakeResult.getStatus() == Status.BUFFER_UNDERFLOW) {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine buffer underflow."));
}//else if//
else if(handshakeResult.getStatus() == Status.CLOSED) {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine closed."));
//TODO: Handle this closure without an infinate loop...
//Close the socket.//
try {key.channel().close();}catch(Throwable e2) {}
}//else if//
else if(handshakeResult.getStatus() == Status.OK) {
if(handshakeResult.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
//Should never happen.// //Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine buffer overflow.")); Debug.log(new RuntimeException("Unexpected ssl engine task."));
}//if// }//if//
else if(handshakeResult.getStatus() == Status.BUFFER_UNDERFLOW) { else if(encryptedWriteBuffer.hasRemaining()) {
//Should never happen.// int remaining = encryptedWriteBuffer.remaining();
Debug.log(new RuntimeException("Unexpected ssl engine buffer underflow."));
}//else if// //Write the bytes to the stream.//
else if(handshakeResult.getStatus() == Status.CLOSED) { ((SocketChannel) key.channel()).write(encryptedWriteBuffer);
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine closed.")); //If not all the bytes could be written then we will need to wait until we can write more.//
//TODO: Handle this closure without an infinate loop... if(encryptedWriteBuffer.hasRemaining()) {
//Close the socket.// //Leave the data in the encrypted write buffer for the writing operation to send it.//
try {key.channel().close();}catch(Throwable e2) {} sendMore = false;
}//else if//
else if(handshakeResult.getStatus() == Status.OK) {
if(handshakeResult.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine task."));
}//if// }//if//
else if(encryptedWriteBuffer.hasRemaining()) {
int remaining = encryptedWriteBuffer.remaining(); //Update the SSL needs wrap flag.//
if(handshakeResult.getHandshakeStatus() != HandshakeStatus.NEED_WRAP) {
//Write the bytes to the stream.//
((SocketChannel) key.channel()).write(encryptedWriteBuffer);
// if(getWebServer().debug) {
// debugBuffer.append("Sent " + (remaining - encryptedWriteBuffer.remaining()) + " encrypted bytes.\n");
// }//if//
//If not all the bytes could be written then we will need to wait until we can write more.//
if(encryptedWriteBuffer.hasRemaining()) {
// if(getWebServer().debug) {
// debugBuffer.append("Pausing due to a partially sent packet (while ssl handshaking). Bytes actually sent: " + encryptedWriteBuffer.position() + ". Bytes remaining: " + encryptedWriteBuffer.remaining() + ".\n");
// }//if//
//Leave the data in the encrypted write buffer for the writing operation to send it.//
sendMore = false;
}//if//
//Update the SSL needs wrap flag.//
if(handshakeResult.getHandshakeStatus() != HandshakeStatus.NEED_WRAP) {
sslNeedsWrap = false;
}//if//
}//else if//
else {
sslNeedsWrap = false; sslNeedsWrap = false;
}//else// }//if//
}//else if// }//else if//
else { else {
//Should never happen.// sslNeedsWrap = false;
Debug.log(new RuntimeException("Unexpected ssl engine status code."));
}//else// }//else//
}//while// }//else if//
else {
// if(getWebServer().debug) { //Should never happen.//
// debugBuffer.append("End Handshaking SSL\n"); Debug.log(new RuntimeException("Unexpected ssl engine status code."));
// }//if// }//else//
}//if// }//while//
if(sendMore && currentOutboundMessage != null) { if(sendMore && currentOutboundMessage != null) {
//Check to see if the outbound message is prepared to send more content. For chunked transfers the outbound message may be waiting for additional content from another stream and we should return later.// //Check to see if the outbound message is prepared to send more content. For chunked transfers the outbound message may be waiting for additional content from another stream and we should return later.//
@@ -884,128 +877,82 @@ private boolean writeClientBoundMessage() {
//If we have an application response pending then send it now.// //If we have an application response pending then send it now.//
if(sendMore && currentOutboundMessage.getBuffer().hasRemaining()) { if(sendMore && currentOutboundMessage.getBuffer().hasRemaining()) {
if(sslEngine != null) { //Keep sending encrypted frames until the output buffer is full, or we run out of message to send.//
//Keep sending encrypted frames until the output buffer is full, or we run out of message to send.// while(key.channel().isOpen() && sendMore && (currentOutboundMessage != null) && currentOutboundMessage.getBuffer().hasRemaining()) {
while(key.channel().isOpen() && sendMore && (currentOutboundMessage != null) && currentOutboundMessage.getBuffer().hasRemaining()) { SSLEngineResult encryptResult;
SSLEngineResult encryptResult;
// int offset = pendingOutboundMessage.getBuffer().position(); //Reset the encrypted write buffer.//
//TODO: Comment me. encryptedWriteBuffer.compact();
//int rem = pendingOutboundMessage.getBuffer().remaining(); //Encrypt the next message frame.//
//Reset the encrypted write buffer.// encryptResult = sslEngine.wrap(currentOutboundMessage.getBuffer(), encryptedWriteBuffer);
encryptedWriteBuffer.compact(); encryptedWriteBuffer.flip();
//Encrypt the next message frame.//
encryptResult = sslEngine.wrap(currentOutboundMessage.getBuffer(), encryptedWriteBuffer); if(encryptResult.getStatus() == Status.BUFFER_OVERFLOW) {
encryptedWriteBuffer.flip(); //Should never happen.//
//TODO: Comment me. Debug.log(new RuntimeException("Unexpected ssl engine buffer overflow."));
//Debug.log("Encrypting/Sending to client from Git " + (rem - pendingOutboundMessage.getBuffer().remaining()) + " bytes."); }//if//
else if(encryptResult.getStatus() == Status.BUFFER_UNDERFLOW) {
// if(getWebServer().debug) { //Should never happen.//
// sentBytes += (pendingOutboundMessage.position() - offset); Debug.log(new RuntimeException("Unexpected ssl engine buffer underflow."));
// debugBuffer.append("Encrypted: " + (pendingOutboundMessage.position() - offset) + ". Total Encrypted: " + sentBytes + ". Encrypted size: " + encryptedWriteBuffer.limit() + ".\n"); }//else if//
// }//if// else if(encryptResult.getStatus() == Status.CLOSED) {
//Should never happen.//
if(encryptResult.getStatus() == Status.BUFFER_OVERFLOW) {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine buffer overflow."));
}//if//
else if(encryptResult.getStatus() == Status.BUFFER_UNDERFLOW) {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine buffer underflow."));
}//else if//
else if(encryptResult.getStatus() == Status.CLOSED) {
//Should never happen.//
// Debug.log(new RuntimeException("Unexpected ssl engine closed.")); // Debug.log(new RuntimeException("Unexpected ssl engine closed."));
//TODO: Handle this closure without an infinate loop... //TODO: Handle this closure without an infinate loop...
//Close the socket.// //Close the socket.//
try {key.channel().close();} catch(Throwable e2) {} try {key.channel().close();} catch(Throwable e2) {}
}//else if// }//else if//
else if(encryptResult.getStatus() == Status.OK) { else if(encryptResult.getStatus() == Status.OK) {
//Write the bytes to the stream.// //Write the bytes to the stream.//
try { try {
int remaining = encryptedWriteBuffer.remaining(); int remaining = encryptedWriteBuffer.remaining();
((SocketChannel) key.channel()).write(encryptedWriteBuffer); ((SocketChannel) key.channel()).write(encryptedWriteBuffer);
// if(getWebServer().debug) { // if(getWebServer().debug) {
// debugBuffer.append("Sent " + (remaining - encryptedWriteBuffer.remaining()) + " encrypted bytes.\n"); // debugBuffer.append("Sent " + (remaining - encryptedWriteBuffer.remaining()) + " encrypted bytes.\n");
// }//if// // }//if//
}//try// }//try//
catch(IOException e) { catch(IOException e) {
//Caught if the channel is forcably closed by the client. We will ignore it.// //Caught if the channel is forcably closed by the client. We will ignore it.//
}//catch// }//catch//
//If not all the bytes could be written then we will need to wait until we can write more.//
if(encryptedWriteBuffer.hasRemaining()) {
//Leave the data in the encrypted write buffer for the writing operation to send it.//
sendMore = false;
//If not all the bytes could be written then we will need to wait until we can write more.//
if(encryptedWriteBuffer.hasRemaining()) {
//Leave the data in the encrypted write buffer for the writing operation to send it.//
sendMore = false;
// if(getWebServer().debug) { // if(getWebServer().debug) {
// debugBuffer.append("Pausing due to a partially sent packet. Bytes actually sent: " + encryptedWriteBuffer.position() + ". Bytes remaining: " + encryptedWriteBuffer.remaining() + ".\n"); // debugBuffer.append("Pausing due to a partially sent packet. Bytes actually sent: " + encryptedWriteBuffer.position() + ". Bytes remaining: " + encryptedWriteBuffer.remaining() + ".\n");
// }//if// // }//if//
}//if//
}//else if//
else {
//Should never happen.//
Debug.log(new RuntimeException("Unexpected ssl engine status code."));
}//else//
//Add more content to the buffer.//
//Note: Do this even if the last encrypted write buffer could not be fully sent - so that when it is sent there will be outbound message content.//
if(key.channel().isOpen() && currentOutboundMessage != null) {
if(!currentOutboundMessage.loadBuffer()) {
//Load the next pending outbound message in the chain. This is currently only used for content being passed through to another process via a second socket.//
if(currentOutboundMessage.getBuffer() == null && currentOutboundMessage.getNext() != null) {
currentOutboundMessage = currentOutboundMessage.getNext();
}//if//
else {
//Wait until additional message bytes are available.//
sendMore = false;
}//else//
}//if//
//If the message end has been reached then the buffer will be null.//
if(currentOutboundMessage.getBuffer() == null) {
currentOutboundMessage = null;
lastOutboundMessage = null;
}//if//
}//if// }//if//
}//while// }//else if//
}//if// else {
else { //Should never happen.//
//Keep sending encrypted frames until the output buffer is full, or we run out of message to send.// Debug.log(new RuntimeException("Unexpected ssl engine status code."));
while(sendMore && (currentOutboundMessage != null) && currentOutboundMessage.getBuffer().hasRemaining()) { }//else//
//Write the bytes to the stream.//
((SocketChannel) key.channel()).write(currentOutboundMessage.getBuffer()); //Add more content to the buffer.//
//Note: Do this even if the last encrypted write buffer could not be fully sent - so that when it is sent there will be outbound message content.//
// if(getWebServer().debug) { if(key.channel().isOpen() && currentOutboundMessage != null) {
// sentBytes += pendingOutboundMessage.position(); if(!currentOutboundMessage.loadBuffer()) {
// debugBuffer.append("Wrote " + pendingOutboundMessage.position() + " bytes to the client. Total sent: " + sentBytes + "\n"); //Load the next pending outbound message in the chain. This is currently only used for content being passed through to another process via a second socket.//
// }//if// if(currentOutboundMessage.getBuffer() == null && currentOutboundMessage.getNext() != null) {
currentOutboundMessage = currentOutboundMessage.getNext();
//If not all the bytes could be written then we will need to wait until we can write more.// }//if//
if(currentOutboundMessage.getBuffer().hasRemaining()) { else {
sendMore = false; //Wait until additional message bytes are available.//
sendMore = false;
}//else//
}//if// }//if//
else {
if(!currentOutboundMessage.loadBuffer()) { //If the message end has been reached then the buffer will be null.//
//Load the next pending outbound message in the chain. This is currently only used for content being passed through to another process via a second socket.// if(currentOutboundMessage.getBuffer() == null) {
if(currentOutboundMessage.getBuffer() == null && currentOutboundMessage.getNext() != null) { currentOutboundMessage = null;
currentOutboundMessage = currentOutboundMessage.getNext(); lastOutboundMessage = null;
}//if// }//if//
else { }//if//
//Wait until additional message bytes are available.// }//while//
sendMore = false;
}//else//
}//if//
//If the message end has been reached then the buffer will be null.//
if(currentOutboundMessage.getBuffer() == null) {
currentOutboundMessage = null;
lastOutboundMessage = null;
}//if//
}//else//
}//while//
}//else//
}//if// }//if//
}//if// }//if//
}//try// }//try//
@@ -1028,7 +975,86 @@ private boolean writeClientBoundMessage() {
}//catch// }//catch//
return sendMore; return sendMore;
}//writeClientBoundMessage()// }//writeClientBoundSslMessage()//
/**
* Sends a response to the client.
* @return Whether the response could be fully sent. This will be false if there is still more data to be written when the call returns.
*/
private boolean writeClientBoundPlainMessage() {
boolean sendMore = true;
try {
if(sendMore && currentOutboundMessage != null) {
//Check to see if the outbound message is prepared to send more content. For chunked transfers the outbound message may be waiting for additional content from another stream and we should return later.//
if(!currentOutboundMessage.getBuffer().hasRemaining()) {
if(!currentOutboundMessage.loadBuffer()) {
if(currentOutboundMessage.getBuffer() == null && currentOutboundMessage.getNext() != null) {
currentOutboundMessage = currentOutboundMessage.getNext();
}//if//
else {
sendMore = false;
}//else//
}//if//
if(currentOutboundMessage.getBuffer() == null) {
currentOutboundMessage = null;
lastOutboundMessage = null;
}//if//
}//if//
//If we have an application response pending then send it now.//
if(sendMore && currentOutboundMessage.getBuffer().hasRemaining()) {
//Keep sending encrypted frames until the output buffer is full, or we run out of message to send.//
while(sendMore && (currentOutboundMessage != null) && currentOutboundMessage.getBuffer().hasRemaining()) {
//Write the bytes to the stream.//
((SocketChannel) key.channel()).write(currentOutboundMessage.getBuffer());
//If not all the bytes could be written then we will need to wait until we can write more.//
if(currentOutboundMessage.getBuffer().hasRemaining()) {
sendMore = false;
}//if//
else {
if(!currentOutboundMessage.loadBuffer()) {
//Load the next pending outbound message in the chain. This is currently only used for content being passed through to another process via a second socket.//
if(currentOutboundMessage.getBuffer() == null && currentOutboundMessage.getNext() != null) {
currentOutboundMessage = currentOutboundMessage.getNext();
}//if//
else {
//Wait until additional message bytes are available.//
sendMore = false;
}//else//
}//if//
//If the message end has been reached then the buffer will be null.//
if(currentOutboundMessage.getBuffer() == null) {
currentOutboundMessage = null;
lastOutboundMessage = null;
}//if//
}//else//
}//while//
}//if//
}//if//
}//try//
catch(ClosedChannelException e) {
close();
}//catch//
catch(SSLException e) {
if(getWebServer().debug) {
Debug.log(e);
}//if//
close();
}//catch//
catch(IOException e) {
if(getWebServer().debug) {
Debug.log(e);
}//if//
close();
}//catch//
return sendMore;
}//writeClientBoundPlainMessage()//
/* (non-Javadoc) /* (non-Javadoc)
* @see com.foundation.web.server.WebServer.AbstractSocketContext#processRequest() * @see com.foundation.web.server.WebServer.AbstractSocketContext#processRequest()
*/ */