|
|
|
|
@@ -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)
|
|
|
|
|
|