Commit 0d75703d authored by Guus der Kinderen's avatar Guus der Kinderen

OF-883: Prevent sending data to known disconnected peers.

By closing a session when the new MINA inputClosed() handler is triggered, we run
the risk of sending data to the peer. As that peer is known to be dead, this is
pointless (and potentially dangerous - deadlocks have been observed that are likely
related to this scenario).

To prevent sending data during session closure, the close() method has been
overloaded with an argument that indicates if the peer is known to be dead. When
set, its implementation will not attempt to send data.
parent 1b2dd66e
...@@ -144,9 +144,29 @@ public interface Connection { ...@@ -144,9 +144,29 @@ public interface Connection {
* <li>Call notifyEvent all listeners that the channel is shutting down. * <li>Call notifyEvent all listeners that the channel is shutting down.
* <li>Close the socket. * <li>Close the socket.
* </ul> * </ul>
*
* An invocation of this method is equal to invoking {@link #close(boolean)} with a parameter
* that is false.
*/ */
public void close(); public void close();
/**
* Close this session including associated socket connection. The order of
* events for closing the session is:
* <ul>
* <li>Set closing flag to prevent redundant shutdowns.
* <li>Call notifyEvent all listeners that the channel is shutting down.
* <li>Close the socket.
* </ul>
*
* This method takes into account the connection state of the peer. Specifically,
* when the peer is known to be in a disconnected state, no data will be sent
* (otherwise, this method can trigger the delivery of an end-of-stream signal).
*
* @param peerIsKnownToBeDisconnected should be set to true when the peer is known to no longer be available.
*/
public void close( boolean peerIsKnownToBeDisconnected );
/** /**
* Notification message indicating that the server is being shutdown. Implementors * Notification message indicating that the server is being shutdown. Implementors
* should send a stream error whose condition is system-shutdown before closing * should send a stream error whose condition is system-shutdown before closing
......
...@@ -442,6 +442,10 @@ public class SocketConnection implements Connection { ...@@ -442,6 +442,10 @@ public class SocketConnection implements Connection {
} }
public void close() { public void close() {
close( false );
}
public void close( boolean peerIsKnownToBeDisconnected ) {
boolean wasClosed = false; boolean wasClosed = false;
synchronized (this) { synchronized (this) {
if (!isClosed()) { if (!isClosed()) {
...@@ -449,26 +453,29 @@ public class SocketConnection implements Connection { ...@@ -449,26 +453,29 @@ public class SocketConnection implements Connection {
if (session != null) { if (session != null) {
session.setStatus(Session.STATUS_CLOSED); session.setStatus(Session.STATUS_CLOSED);
} }
boolean allowedToWrite = false;
try { if ( !peerIsKnownToBeDisconnected ) {
requestWriting(); boolean allowedToWrite = false;
allowedToWrite = true; try {
// Register that we started sending data on the connection requestWriting();
writeStarted(); allowedToWrite = true;
writer.write("</stream:stream>"); // Register that we started sending data on the connection
if (flashClient) { writeStarted();
writer.write('\0'); writer.write("</stream:stream>");
if (flashClient) {
writer.write('\0');
}
writer.flush();
} }
writer.flush(); catch (IOException e) {
} // Do nothing
catch (IOException e) { }
// Do nothing finally {
} // Register that we finished sending data on the connection
finally { writeFinished();
// Register that we finished sending data on the connection if (allowedToWrite) {
writeFinished(); releaseWriting();
if (allowedToWrite) { }
releaseWriting();
} }
} }
} }
......
...@@ -169,6 +169,10 @@ public abstract class VirtualConnection implements Connection { ...@@ -169,6 +169,10 @@ public abstract class VirtualConnection implements Connection {
* has been closed. * has been closed.
*/ */
public void close() { public void close() {
close( false );
}
public void close(boolean peerIsKnownToBeDisconnected) {
boolean wasClosed = false; boolean wasClosed = false;
synchronized (this) { synchronized (this) {
if (!isClosed()) { if (!isClosed()) {
......
...@@ -111,7 +111,7 @@ public abstract class ConnectionHandler extends IoHandlerAdapter { ...@@ -111,7 +111,7 @@ public abstract class ConnectionHandler extends IoHandlerAdapter {
public void inputClosed( IoSession session ) throws Exception { public void inputClosed( IoSession session ) throws Exception {
final Connection connection = (Connection) session.getAttribute(CONNECTION); final Connection connection = (Connection) session.getAttribute(CONNECTION);
if ( connection != null ) { if ( connection != null ) {
connection.close(); connection.close( true );
} }
} }
......
...@@ -219,7 +219,11 @@ public class NIOConnection implements Connection { ...@@ -219,7 +219,11 @@ public class NIOConnection implements Connection {
return backupDeliverer; return backupDeliverer;
} }
public void close() public void close() {
close( false );
}
public void close( boolean peerIsKnownToBeDisconnected )
{ {
boolean notifyClose = false; boolean notifyClose = false;
synchronized ( this ) { synchronized ( this ) {
...@@ -234,13 +238,16 @@ public class NIOConnection implements Connection { ...@@ -234,13 +238,16 @@ public class NIOConnection implements Connection {
if ( state != State.CLOSING ) if ( state != State.CLOSING )
{ {
state = State.CLOSING; state = State.CLOSING;
try if ( !peerIsKnownToBeDisconnected )
{
deliverRawText( flashClient ? "</flash:stream>" : "</stream:stream>" );
}
catch ( Exception e )
{ {
// Ignore try
{
deliverRawText( flashClient ? "</flash:stream>" : "</stream:stream>" );
}
catch ( Exception e )
{
// Ignore
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment