Revert "Modified SocketContext to use a queue and an active reference to the outbound messages instead of multiple linked lists."

This reverts commit 06cbff7cf0.
This commit is contained in:
wcrisman
2014-12-28 14:38:27 -08:00
parent 06cbff7cf0
commit 7f4d6702a0
5 changed files with 502 additions and 488 deletions

View File

@@ -1,28 +0,0 @@
package com.foundation.web.server;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.common.debug.Debug;
import com.common.util.LiteHashMap;
import com.common.util.LiteList;
import com.foundation.web.interfaces.IContent;
import com.foundation.web.interfaces.IMimeType;
/**
* A message buffer for HTTP messages.
*/
public class HttpMessageBuffer extends MessageBuffer {
protected final WebServer webServer;
private Response response;
private Request request;
public HttpMessageBuffer(WebServer webServer, Response response, Request request) {
this.webServer = webServer;
this.response = response;
this.request = request;
}//HttpMessageBuffer()//
}//HttpMessageBuffer//

View File

@@ -11,7 +11,7 @@ class MessageBuffer {
/** The actual underlying buffer containing the bytes to be sent. Will be null if the message buffer needs initializing or has finished. */
private ByteBuffer buffer = null;
/** The ability to chain message buffers into a linked list. */
// private MessageBuffer next = null;
private MessageBuffer next = null;
/** The optional response the message is based upon. */
private Response response = null;
@@ -119,10 +119,10 @@ public boolean loadBuffer() {
return result;
}//loadBuffer()//
///** Gets the next message buffer (only used for pass through sockets). */
//public MessageBuffer getNext() {return next;}
///** Sets the next message buffer (only used for pass through sockets). */
//public void setNext(MessageBuffer next) {this.next = next;}
/** Gets the next message buffer (only used for pass through sockets). */
public MessageBuffer getNext() {return next;}
/** Sets the next message buffer (only used for pass through sockets). */
public void setNext(MessageBuffer next) {this.next = next;}
/** Gets the response object that created the message. This will be null for pass through sockets. */
public Response getResponse() {return response;}
}//MessageBuffer//

View File

@@ -5,8 +5,6 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import com.common.util.Queue;
import com.foundation.web.server.WebServer.RegisterKeyRunnable;
/**
@@ -14,8 +12,8 @@ import com.foundation.web.server.WebServer.RegisterKeyRunnable;
* Allows the web server to act as an SSL front to another web server or service.
*/
public class PassThroughSocketContext extends AbstractSocketContext {
private MessageBuffer outboundMessage = null;
private Queue outboundMessages = new Queue(8);
private MessageBuffer pendingMessageBuffer = null;
private MessageBuffer lastAddedMessageBuffer = null;
/** The byte buffer used to read data from the socket. */
public ByteBuffer socketReadBuffer = ByteBuffer.allocate(BUFFER_SIZE);
/**
@@ -61,35 +59,47 @@ protected Object getLock() {
*/
protected synchronized void writeOutgoingMessages() throws IOException {
//Actually this is called when a request is being sent via the pass through socket (sending the request to the remote server).//
// //Synchronized to avoid accessing the pendingMessageBuffer and lastAddedMessageBuffer at the same time as a thread that is calling passThrough(ByteBuffer) which also accesses these variables.//
// boolean result = true;
//
// if(result && pendingMessageBuffer != null) {
// //Check to see if the outbound message is prepared to send more content.//
// if(!pendingMessageBuffer.getBuffer().hasRemaining()) {
// //Wait until additional message bytes are available.//
// result = false;
// pendingMessageBuffer = null;
// lastAddedMessageBuffer = null;
// }//if//
//
// //Keep sending encrypted frames until the output buffer is full, or we run out of message to send.//
// while(result && (pendingMessageBuffer != null) && pendingMessageBuffer.getBuffer().hasRemaining()) {
// //Write the bytes to the stream.//
// ((SocketChannel) key.channel()).write(pendingMessageBuffer.getBuffer());
//
// //If not all the bytes could be written then we will need to wait until we can write more.//
// if(pendingMessageBuffer.getBuffer().hasRemaining()) {
// result = false;
// }//if//
// else {
// //Wait until additional message bytes are available.//
// result = false;
// pendingMessageBuffer = null;
// lastAddedMessageBuffer = null;
// }//else//
// }//while//
// }//if//
//Synchronized to avoid accessing the pendingMessageBuffer and lastAddedMessageBuffer at the same time as a thread that is calling passThrough(ByteBuffer) which also accesses these variables.//
boolean result = true;
if(result && pendingMessageBuffer != null) {
//Check to see if the outbound message is prepared to send more content.//
if(!pendingMessageBuffer.getBuffer().hasRemaining()) {
//Load the next pending outbound message in the chain.//
if(pendingMessageBuffer.getNext() != null) {
pendingMessageBuffer = pendingMessageBuffer.getNext();
}//if//
else {
//Wait until additional message bytes are available.//
result = false;
pendingMessageBuffer = null;
lastAddedMessageBuffer = null;
}//else//
}//if//
//Keep sending encrypted frames until the output buffer is full, or we run out of message to send.//
while(result && (pendingMessageBuffer != null) && pendingMessageBuffer.getBuffer().hasRemaining()) {
//Write the bytes to the stream.//
((SocketChannel) key.channel()).write(pendingMessageBuffer.getBuffer());
//If not all the bytes could be written then we will need to wait until we can write more.//
if(pendingMessageBuffer.getBuffer().hasRemaining()) {
result = false;
}//if//
else {
//Load the next pending outbound message in the chain.//
if(pendingMessageBuffer.getNext() != null) {
pendingMessageBuffer = pendingMessageBuffer.getNext();
}//if//
else {
//Wait until additional message bytes are available.//
result = false;
pendingMessageBuffer = null;
lastAddedMessageBuffer = null;
}//else//
}//else//
}//while//
}//if//
}//processResponses()//
/* (non-Javadoc)
* @see com.foundation.web.server.WebServer.AbstractSocketContext#processRequest()
@@ -126,25 +136,23 @@ protected void readIncomingMessages() throws IOException {
* @see com.foundation.web.server.WebServer.AbstractSocketContext#passThrough(java.nio.ByteBuffer)
*/
protected synchronized boolean passThrough(ByteBuffer buffer) {
// ByteBuffer messageBytes = ByteBuffer.allocate(buffer.remaining());
// MessageBuffer message;
//
// //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.put(buffer);
// message = new MessageBuffer(messageBytes);
//
//
// //Chain the message into the linked list.
// if(lastAddedMessageBuffer == null) {
// pendingMessageBuffer = lastAddedMessageBuffer = message;
// }//if//
// else {
// lastAddedMessageBuffer.setNext(message);
// lastAddedMessageBuffer = message;
// }//else//
//
// return true;
ByteBuffer messageBytes = ByteBuffer.allocate(buffer.remaining());
MessageBuffer message;
//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.put(buffer);
message = new MessageBuffer(messageBytes);
//Chain the message into the linked list.
if(lastAddedMessageBuffer == null) {
pendingMessageBuffer = lastAddedMessageBuffer = message;
}//if//
else {
lastAddedMessageBuffer.setNext(message);
lastAddedMessageBuffer = message;
}//else//
return true;
}//passThrough()//
protected synchronized void close() {
@@ -155,7 +163,6 @@ protected synchronized void close() {
* @see com.foundation.web.server.WebServer.AbstractSocketContext#hasPendingWrite()
*/
protected boolean hasPendingWrite() {
// return pendingMessageBuffer != null;
return false;
return pendingMessageBuffer != null;
}//hasPendingWrite()//
}//PassThroughSocketContext//

View File

@@ -26,6 +26,8 @@ import com.foundation.web.interfaces.*;
public class Response implements IResponse {
/** The sequence number for the request. */
private int requestNumber = 0;
/** Used to chain responses together when sending. */
private Response nextResponse = null;
/** The session associated with the response. */
private ISession session = null;
/** The application that is serving the response. */
@@ -240,6 +242,20 @@ public String getCustomHeader() {
public void setCustomHeader(String customHeader) {
this.customHeader = customHeader;
}//getCustomHeader()//
/**
* Gets the next response in the chain of responses.
* @return The next response to be sent.
*/
public Response getNextResponse() {
return nextResponse;
}//getNextResponse()//
/**
* Sets the next response in the chain of responses.
* @param nextResponse The response to send after this one.
*/
public void setNextResponse(Response nextResponse) {
this.nextResponse = nextResponse;
}//setNextResponse()//
/**
* Gets the ordered list of header field names.
* @return The optional field names for the header. If provided then the server shouldn't generate a header for the response.

View File

@@ -70,14 +70,10 @@ public class SocketContext extends AbstractSocketContext implements IWebApplicat
public ContentPart currentPart = null;
/** Whether the final part boundary has already been found. If this is true then the message bytes should keep getting read until the partReadCount == contentLength. */
public boolean endPartBoundaryFound = false;
// /** The bytes containing the unencrypted outbound message that is waiting for the socket to allow a write. */
// public MessageBuffer/*ByteBuffer*/ currentOutboundMessage = null;
// /** The last message buffer added to the pending outbound message chain (linked list). Used only for pass through contexts currently since locally handled messages link the reponses together into a list. */
// private MessageBuffer lastOutboundMessage = null;
/** The queue of ready to be sent outbound messages. */
private Queue outboundMessages = new Queue(6);
/** The currently active outbound message (pulled from the outboundMessages queue). */
private MessageBuffer outboundMessage = null;
/** The bytes containing the unencrypted outbound message that is waiting for the socket to allow a write. */
public MessageBuffer/*ByteBuffer*/ currentOutboundMessage = null;
/** The last message buffer added to the pending outbound message chain (linked list). Used only for pass through contexts currently since locally handled messages link the reponses together into a list. */
private MessageBuffer lastOutboundMessage = null;
/** The byte buffer used to read data from the socket. This must be null if a SSL engine is provided. */
public ByteBuffer socketReadBuffer = null;
/** The buffer used to store the initial data in a SSL/TLS connection. The buffering is necessary to allow us to pre-read the client's hello message to gather the domain the client is connecting to - allowing for the correct SSL engine to be used. */
@@ -98,10 +94,10 @@ public class SocketContext extends AbstractSocketContext implements IWebApplicat
public ByteBuffer encryptedWriteBuffer = null;
/** The last used request number which identifies the sequence for the requests. */
private int lastRequestNumber = 0;
// /** The response we are currently processing. */
// private Response currentResponse = null;
// /** The response we are will process last. */
// private Response lastResponse = null;
/** The response we are currently processing. */
private Response currentResponse = null;
/** The response we are will process last. */
private Response lastResponse = null;
/** Tracks the number of bytes sent from the current response. This is only used when debugging. */
private int sentBytes = 0;
/** Tracks the getWebServer().debug output for the current request/response cycle. This is only used when debugging. */
@@ -193,7 +189,7 @@ protected synchronized void close() {
try {if(key != null) key.cancel();} catch(Throwable e) {}
//Clean up after the response and request.//
//try {while(currentOutboundMessage != null) {currentOutboundMessage.close(); currentOutboundMessage = currentOutboundMessage.getNext();}} catch(Throwable e2) {}
// try {if(currentResponse != null) currentResponse.close();} catch(Throwable e2) {}
try {if(currentResponse != null) currentResponse.close();} catch(Throwable e2) {}
if(getPassThroughSocketContext() != null) {
getPassThroughSocketContext().close();
@@ -236,14 +232,22 @@ private void queueOutboundClientMessage(MessageBuffer messageBuffer) {
boolean notify = false;
synchronized(this) {
outboundMessages.enqueue(messageBuffer);
if(currentOutboundMessage == null) {
lastOutboundMessage = currentOutboundMessage = messageBuffer;
notify = true;
}//if//
else {
lastOutboundMessage.setNext(messageBuffer);
lastOutboundMessage = messageBuffer;
}//else//
}//synchronized()//
if(notify) {
notifyListenerOfPendingWrite();
}//if//
}//queueOutboundClientMessage()//
private void writeSessionCookies(Response response, PrintStream pout) {
private void writeSessionCookies(PrintStream pout) {
Response response = currentResponse;
ISession session = response.getSession();
if(session != null) {
@@ -267,7 +271,8 @@ private void writeSessionCookies(Response response, PrintStream pout) {
* <p>Note: The caller must synchronize on this context to prevent multiple threads from accessing the context at the same time.</p>
* @result Whether request is in a receive state. Will be false if the request generated a response that could not be completely transmitted.
*/
private void prepareResponse(Response response) {
private void prepareResponse() {
Response response = currentResponse;
Request request = (Request) response.getRequest();
byte[] headerBytes = null;
IContent content = null;
@@ -303,7 +308,7 @@ private void prepareResponse(Response response) {
}//for//
//Write out any cookies necessary to retain our session.//
writeSessionCookies(response, pout);
writeSessionCookies(pout);
//End the header.//
pout.print("\r\n");
@@ -326,7 +331,7 @@ private void prepareResponse(Response response) {
pout.print("HTTP/1.1 302 Moved Temporarily\r\n");
//}//else//
writeSessionCookies(response, pout);
writeSessionCookies(pout);
pout.print("Location: " + response.getForwardUri() + "\r\n");
@@ -418,7 +423,7 @@ private void prepareResponse(Response response) {
}//if//
}//if//
writeSessionCookies(response, pout);
writeSessionCookies(pout);
pout.print("Server: DE/1.0\r\n");
//TODO: IE has a problem with caching and forwarding/redirecting. A page that redirects to another page that was previously cached does not result in IE sending a request for the forwarded content.//
@@ -482,7 +487,7 @@ private void prepareResponse(Response response) {
pout.print("HTTP/1.1 200 OK\r\n");
}//else//
writeSessionCookies(response, pout);
writeSessionCookies(pout);
pout.print("Content-Length: 0\r\n");
pout.print("Server: DE/1.0\r\n");
pout.print("\r\n");
@@ -511,7 +516,7 @@ private void prepareResponse(Response response) {
// }//if//
//Save the buffer as the current pending outbound message for this socket context.//
queueOutboundClientMessage(new MessageBuffer(buffer, response, content != null && request.getRequestType() != Request.TYPE_HEAD ? content : null));
currentOutboundMessage = new MessageBuffer(buffer, response, content != null && request.getRequestType() != Request.TYPE_HEAD ? content : null);
}//try//
catch(Throwable e) {
Debug.log("Fatal Error: Failed to build and send the response message due to an exception.", e);
@@ -528,7 +533,18 @@ private void prepareResponse(Response response) {
* @result Whether request is in a receive state. Will be false if the request generated a response that could not be completely transmitted.
*/
public synchronized boolean sendHttpResponse(Response response) {
prepareResponse(response);
if(currentResponse != null) {
lastResponse.setNextResponse(response);
lastResponse = response;
}//if//
else {
lastResponse = currentResponse = response;
sentBytes = 0;
prepareResponse();
//Note: Not going to process the response on this thread. Allow the flag to be set for writing to the socket, and have the next thread in the network listener handle the write. This allows for cleaner code and pipelining without all the synchronizing.
// result = internalProcessResponses();
}//else//
request = null;
@@ -546,7 +562,14 @@ protected synchronized boolean passThrough(ByteBuffer buffer) {
messageBytes.put(buffer);
message = new MessageBuffer(messageBytes);
queueOutboundClientMessage(message);
//Chain the message into the linked list.
if(lastOutboundMessage == null || currentOutboundMessage == null) {
currentOutboundMessage = lastOutboundMessage = message;
}//if//
else {
lastOutboundMessage.setNext(message);
lastOutboundMessage = message;
}//else//
return true;
}//passThrough()//
@@ -561,12 +584,12 @@ protected void writeOutgoingMessages() throws IOException {
}//synchronized//
}//if//
else if(isWebsocket) {
// //Right after upgrading the socket we have one last HTTP response to process.//
// if(currentResponse != null) {
// internalProcessResponses();
// }//if//
//
// internalProcessWebsocketMessages();
//Right after upgrading the socket we have one last HTTP response to process.//
if(currentResponse != null) {
internalProcessResponses();
}//if//
internalProcessWebsocketMessages();
}//else if//
else {
//Go directly to writing the client response if we are just passing everything through to another process.//
@@ -578,30 +601,30 @@ protected void writeOutgoingMessages() throws IOException {
* If a message could only be partially sent then the next call will attempt to finish sending it.
*/
private void internalProcessWebsocketMessages() {
// if(websocketSendingMessage == null) {
// loadNextWebsocketMessage();
// }//if//
//
// while(websocketSendingMessage != null) {
// //If the socket is open then send the next buffer of data.//
// if(key.channel().isOpen()) {
// if(currentOutboundMessage != null) {
// //Put the sending message in a MessageBuffer (pendingOutboundMessage).//
// currentOutboundMessage = new MessageBuffer(websocketSendingMessage);
// }//if//
//
// //Write the pendingOutboundMessage to the socket.//
// if(writeClientBoundMessage()) {
// websocketSendingMessage = null;
// currentOutboundMessage = null;
// }//if//
// }//if//
//
// //If we finished sending the message then load the next one.//
// if(websocketSendingMessage == null) {
// loadNextWebsocketMessage();
// }//if//
// }//while//
if(websocketSendingMessage == null) {
loadNextWebsocketMessage();
}//if//
while(websocketSendingMessage != null) {
//If the socket is open then send the next buffer of data.//
if(key.channel().isOpen()) {
if(currentOutboundMessage != null) {
//Put the sending message in a MessageBuffer (pendingOutboundMessage).//
currentOutboundMessage = new MessageBuffer(websocketSendingMessage);
}//if//
//Write the pendingOutboundMessage to the socket.//
if(writeClientBoundMessage()) {
websocketSendingMessage = null;
currentOutboundMessage = null;
}//if//
}//if//
//If we finished sending the message then load the next one.//
if(websocketSendingMessage == null) {
loadNextWebsocketMessage();
}//if//
}//while//
}//internalProcessWebsocketMessages()//
/**
* Loads and prepares the next websocket message from the queue of pending messages.
@@ -703,46 +726,42 @@ private void loadNextWebsocketMessage() {
private synchronized void internalProcessResponses() {
boolean finishedSending = true;
while(finishedSending) {
//Keep sending responses while the buffers are not full and there is another response to send.//
while(finishedSending && currentResponse != null) {
//If the socket is open then send the next buffer of data.//
if(key.channel().isOpen()) {
//Send the pending response object's prepared buffer of data.//
finishedSending = writeClientBoundMessage();
}//if//
//Close the response if successfully sent, or if the socket is closed.//
if(finishedSending || !key.channel().isOpen()) {
try {currentResponse.close();} catch(Throwable e) {}
}//if//
//If we finished sending the current response then load the next one.//
if(finishedSending) {
currentResponse = currentResponse.getNextResponse();
if(currentResponse == null) {
lastResponse = null;
}//if//
else if(key.channel().isOpen()) {
//Prep the next response object for sending.//
prepareResponse();
}//else//
else {
//Clean up after all the left over responses.//
while(currentResponse != null) {
currentResponse.close();
currentResponse = currentResponse.getNextResponse();
}//while//
// //Keep sending responses while the buffers are not full and there is another response to send.//
// while(finishedSending && currentResponse != null) {
// //If the socket is open then send the next buffer of data.//
// if(key.channel().isOpen()) {
// //Send the pending response object's prepared buffer of data.//
// finishedSending = writeClientBoundMessage();
// }//if//
//
// //Close the response if successfully sent, or if the socket is closed.//
// if(finishedSending || !key.channel().isOpen()) {
// try {currentResponse.close();} catch(Throwable e) {}
// }//if//
//
// //If we finished sending the current response then load the next one.//
// if(finishedSending) {
// currentResponse = currentResponse.getNextResponse();
//
// if(currentResponse == null) {
// lastResponse = null;
// }//if//
// else if(key.channel().isOpen()) {
// //Prep the next response object for sending.//
// prepareResponse(currentResponse);
// }//else//
// else {
// //Clean up after all the left over responses.//
// while(currentResponse != null) {
// currentResponse.close();
// currentResponse = currentResponse.getNextResponse();
// }//while//
//
// currentResponse = null;
// lastResponse = null;
// }//else//
// }//if//
// }//while//
currentResponse = null;
lastResponse = null;
}//else//
}//if//
}//while//
}//processCurrentResponse()//
/**
* Sends a response to the client.
@@ -751,24 +770,13 @@ private synchronized void internalProcessResponses() {
private boolean writeClientBoundMessage() {
boolean sendMore = true;
//Process SSL output first.//
if(sslEngine != null) {
sendMore = writeClientBoundSslMessage();
}//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;
// if(getWebServer().debug) {
// debugBuffer.append("Starting a write cycle.\n");
// }//if//
try {
//Process SSL output first.//
if(sslEngine != null) {
//If we have part of an SSL frame then try to send it first.//
if(encryptedWriteBuffer.hasRemaining()) {
int remaining = encryptedWriteBuffer.remaining();
@@ -776,6 +784,10 @@ private boolean writeClientBoundSslMessage() {
//Write the bytes to the stream.//
((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;
@@ -847,31 +859,50 @@ private boolean writeClientBoundSslMessage() {
}//else//
}//while//
if(sendMore) {
//Prepare a new message for sending.//
if(outboundMessage == null && outboundMessages.getSize() > 0) {
outboundMessage = (MessageBuffer) outboundMessages.dequeue();
// if(getWebServer().debug) {
// debugBuffer.append("End Handshaking SSL\n");
// }//if//
}//if//
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(!outboundMessage.getBuffer().hasRemaining()) {
if(!outboundMessage.loadBuffer()) {
if(!currentOutboundMessage.getBuffer().hasRemaining()) {
if(!currentOutboundMessage.loadBuffer()) {
if(currentOutboundMessage.getBuffer() == null && currentOutboundMessage.getNext() != null) {
currentOutboundMessage = currentOutboundMessage.getNext();
}//if//
else {
sendMore = false;
}//else//
}//if//
if(outboundMessage.getBuffer() == null) {
outboundMessage = null;
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()) {
if(sslEngine != null) {
//Keep sending encrypted frames until the output buffer is full, or we run out of message to send.//
while(key.channel().isOpen() && sendMore && outboundMessage != null && outboundMessage.getBuffer().hasRemaining()) {
while(key.channel().isOpen() && sendMore && (currentOutboundMessage != null) && currentOutboundMessage.getBuffer().hasRemaining()) {
SSLEngineResult encryptResult;
// int offset = pendingOutboundMessage.getBuffer().position();
//TODO: Comment me.
//int rem = pendingOutboundMessage.getBuffer().remaining();
//Reset the encrypted write buffer.//
encryptedWriteBuffer.compact();
//Encrypt the next message frame.//
encryptResult = sslEngine.wrap(outboundMessage.getBuffer(), encryptedWriteBuffer);
encryptResult = sslEngine.wrap(currentOutboundMessage.getBuffer(), encryptedWriteBuffer);
encryptedWriteBuffer.flip();
//TODO: Comment me.
//Debug.log("Encrypting/Sending to client from Git " + (rem - pendingOutboundMessage.getBuffer().remaining()) + " bytes.");
// if(getWebServer().debug) {
// sentBytes += (pendingOutboundMessage.position() - offset);
// debugBuffer.append("Encrypted: " + (pendingOutboundMessage.position() - offset) + ". Total Encrypted: " + sentBytes + ". Encrypted size: " + encryptedWriteBuffer.limit() + ".\n");
// }//if//
if(encryptResult.getStatus() == Status.BUFFER_OVERFLOW) {
//Should never happen.//
@@ -882,6 +913,9 @@ private boolean writeClientBoundSslMessage() {
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."));
//TODO: Handle this closure without an infinate loop...
//Close the socket.//
try {key.channel().close();} catch(Throwable e2) {}
}//else if//
@@ -891,6 +925,10 @@ private boolean writeClientBoundSslMessage() {
int remaining = encryptedWriteBuffer.remaining();
((SocketChannel) key.channel()).write(encryptedWriteBuffer);
// if(getWebServer().debug) {
// debugBuffer.append("Sent " + (remaining - encryptedWriteBuffer.remaining()) + " encrypted bytes.\n");
// }//if//
}//try//
catch(IOException e) {
//Caught if the channel is forcably closed by the client. We will ignore it.//
@@ -900,6 +938,10 @@ private boolean writeClientBoundSslMessage() {
if(encryptedWriteBuffer.hasRemaining()) {
//Leave the data in the encrypted write buffer for the writing operation to send it.//
sendMore = false;
// if(getWebServer().debug) {
// debugBuffer.append("Pausing due to a partially sent packet. Bytes actually sent: " + encryptedWriteBuffer.position() + ". Bytes remaining: " + encryptedWriteBuffer.remaining() + ".\n");
// }//if//
}//if//
}//else if//
else {
@@ -909,85 +951,63 @@ private boolean writeClientBoundSslMessage() {
//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() && outboundMessage != null) {
if(!outboundMessage.loadBuffer()) {
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(outboundMessage.getBuffer() == null) {
outboundMessage = null;
if(currentOutboundMessage.getBuffer() == null) {
currentOutboundMessage = null;
lastOutboundMessage = null;
}//if//
}//if//
}//while//
}//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;
}//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 {
//Prepare a new message for sending.//
if(outboundMessage == null && outboundMessages.getSize() > 0) {
outboundMessage = (MessageBuffer) outboundMessages.dequeue();
}//if//
//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(!outboundMessage.getBuffer().hasRemaining()) {
if(!outboundMessage.loadBuffer()) {
sendMore = false;
}//if//
if(outboundMessage.getBuffer() == null) {
outboundMessage = null;
}//if//
}//if//
else {
//Keep sending encrypted frames until the output buffer is full, or we run out of message to send.//
while(sendMore && outboundMessage != null && outboundMessage.getBuffer().hasRemaining()) {
while(sendMore && (currentOutboundMessage != null) && currentOutboundMessage.getBuffer().hasRemaining()) {
//Write the bytes to the stream.//
((SocketChannel) key.channel()).write(outboundMessage.getBuffer());
((SocketChannel) key.channel()).write(currentOutboundMessage.getBuffer());
// if(getWebServer().debug) {
// sentBytes += pendingOutboundMessage.position();
// debugBuffer.append("Wrote " + pendingOutboundMessage.position() + " bytes to the client. Total sent: " + sentBytes + "\n");
// }//if//
//If not all the bytes could be written then we will need to wait until we can write more.//
if(outboundMessage.getBuffer().hasRemaining()) {
if(currentOutboundMessage.getBuffer().hasRemaining()) {
sendMore = false;
}//if//
else {
if(!outboundMessage.loadBuffer()) {
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(outboundMessage.getBuffer() == null) {
outboundMessage = null;
if(currentOutboundMessage.getBuffer() == null) {
currentOutboundMessage = null;
lastOutboundMessage = null;
}//if//
}//else//
}//while//
}//else//
}//if//
}//if//
}//try//
catch(ClosedChannelException e) {
close();
@@ -1008,7 +1028,7 @@ private boolean writeClientBoundPlainMessage() {
}//catch//
return sendMore;
}//writeClientBoundPlainMessage()//
}//writeClientBoundMessage()//
/* (non-Javadoc)
* @see com.foundation.web.server.WebServer.AbstractSocketContext#processRequest()
*/
@@ -2523,8 +2543,7 @@ private int indexOf(byte[] source, byte[] pattern, int fromOffset) {
* @see com.foundation.web.server.WebServer.AbstractSocketContext#hasPendingWrite()
*/
protected boolean hasPendingWrite() {
//return currentOutboundMessage != null || (encryptedWriteBuffer != null && encryptedWriteBuffer.hasRemaining());
return outboundMessage != null || outboundMessages.getSize() > 0 || (encryptedWriteBuffer != null && encryptedWriteBuffer.hasRemaining());
return currentOutboundMessage != null || (encryptedWriteBuffer != null && encryptedWriteBuffer.hasRemaining());
}//hasPendingWrite()//
/* (non-Javadoc)
* @see com.foundation.web.interfaces.IConnectionContext#upgradeToWebsocket(java.lang.String, long, com.foundation.web.interfaces.WebsocketHandler)