Commit ce1a4eff authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Close channels when using TLS instead of closing the socket directly. JM-422

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@2906 b35dd754-fafc-0310-a699-88a17e54d16e
parent e3bb3c01
...@@ -78,7 +78,7 @@ public class SocketConnection implements Connection { ...@@ -78,7 +78,7 @@ public class SocketConnection implements Connection {
this.socket = socket; this.socket = socket;
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), CHARSET)); writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), CHARSET));
this.deliverer = deliverer; this.deliverer = deliverer;
xmlSerializer = new XMLSocketWriter(writer, socket); xmlSerializer = new XMLSocketWriter(writer, this);
} }
/** /**
...@@ -103,7 +103,7 @@ public class SocketConnection implements Connection { ...@@ -103,7 +103,7 @@ public class SocketConnection implements Connection {
secure = true; secure = true;
tlsStreamHandler = new TLSStreamHandler(socket, clientMode); tlsStreamHandler = new TLSStreamHandler(socket, clientMode);
writer = new BufferedWriter(new OutputStreamWriter(tlsStreamHandler.getOutputStream(), CHARSET)); writer = new BufferedWriter(new OutputStreamWriter(tlsStreamHandler.getOutputStream(), CHARSET));
xmlSerializer = new XMLSocketWriter(writer, socket); xmlSerializer = new XMLSocketWriter(writer, this);
} }
} }
...@@ -114,7 +114,7 @@ public class SocketConnection implements Connection { ...@@ -114,7 +114,7 @@ public class SocketConnection implements Connection {
try { try {
synchronized (writer) { synchronized (writer) {
// Register that we started sending data on the connection // Register that we started sending data on the connection
SocketSendingTracker.getInstance().socketStartedSending(socket); SocketSendingTracker.getInstance().socketStartedSending(this);
writer.write(" "); writer.write(" ");
writer.flush(); writer.flush();
} }
...@@ -125,7 +125,7 @@ public class SocketConnection implements Connection { ...@@ -125,7 +125,7 @@ public class SocketConnection implements Connection {
} }
finally { finally {
// Register that we finished sending data on the connection // Register that we finished sending data on the connection
SocketSendingTracker.getInstance().socketFinishedSending(socket); SocketSendingTracker.getInstance().socketFinishedSending(this);
} }
return !isClosed(); return !isClosed();
} }
...@@ -229,7 +229,7 @@ public class SocketConnection implements Connection { ...@@ -229,7 +229,7 @@ public class SocketConnection implements Connection {
synchronized (writer) { synchronized (writer) {
try { try {
// Register that we started sending data on the connection // Register that we started sending data on the connection
SocketSendingTracker.getInstance().socketStartedSending(socket); SocketSendingTracker.getInstance().socketStartedSending(this);
writer.write("</stream:stream>"); writer.write("</stream:stream>");
if (flashClient) { if (flashClient) {
writer.write('\0'); writer.write('\0');
...@@ -239,7 +239,7 @@ public class SocketConnection implements Connection { ...@@ -239,7 +239,7 @@ public class SocketConnection implements Connection {
catch (IOException e) {} catch (IOException e) {}
finally { finally {
// Register that we finished sending data on the connection // Register that we finished sending data on the connection
SocketSendingTracker.getInstance().socketFinishedSending(socket); SocketSendingTracker.getInstance().socketFinishedSending(this);
} }
} }
} }
...@@ -247,19 +247,49 @@ public class SocketConnection implements Connection { ...@@ -247,19 +247,49 @@ public class SocketConnection implements Connection {
Log.error(LocaleUtils.getLocalizedString("admin.error.close") Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e); + "\n" + this.toString(), e);
} }
closeConnection();
wasClosed = true;
}
}
if (wasClosed) {
notifyCloseListeners();
}
}
/**
* Forces the connection to be closed immediately no matter if closing the socket takes
* a long time. This method should only be called from {@link SocketSendingTracker} when
* sending data over the socket has taken a long time and we need to close the socket, discard
* the connection and its session.
*/
void forceClose() {
if (session != null) {
// Set that the session is closed. This will prevent threads from trying to
// deliver packets to this session thus preventing future locks.
session.setStatus(Session.STATUS_CLOSED);
}
closeConnection();
// Notify the close listeners so that the SessionManager can send unavailable
// presences if required.
notifyCloseListeners();
}
private void closeConnection() {
try { try {
if (tlsStreamHandler == null) {
socket.close(); socket.close();
} }
else {
// Close the channels since we are using TLS (i.e. NIO). If the channels implement
// the InterruptibleChannel interface then any other thread that was blocked in
// an I/O operation will be interrupted and an exception thrown
tlsStreamHandler.close();
}
}
catch (Exception e) { catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.close") Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e); + "\n" + this.toString(), e);
} }
wasClosed = true;
}
}
if (wasClosed) {
notifyCloseListeners();
}
} }
public void deliver(Packet packet) throws UnauthorizedException, PacketException { public void deliver(Packet packet) throws UnauthorizedException, PacketException {
...@@ -308,7 +338,7 @@ public class SocketConnection implements Connection { ...@@ -308,7 +338,7 @@ public class SocketConnection implements Connection {
synchronized (writer) { synchronized (writer) {
try { try {
// Register that we started sending data on the connection // Register that we started sending data on the connection
SocketSendingTracker.getInstance().socketStartedSending(socket); SocketSendingTracker.getInstance().socketStartedSending(this);
writer.write(text); writer.write(text);
if (flashClient) { if (flashClient) {
writer.write('\0'); writer.write('\0');
...@@ -321,7 +351,7 @@ public class SocketConnection implements Connection { ...@@ -321,7 +351,7 @@ public class SocketConnection implements Connection {
} }
finally { finally {
// Register that we finished sending data on the connection // Register that we finished sending data on the connection
SocketSendingTracker.getInstance().socketFinishedSending(socket); SocketSendingTracker.getInstance().socketFinishedSending(this);
} }
} }
if (errorDelivering) { if (errorDelivering) {
......
...@@ -3,8 +3,6 @@ package org.jivesoftware.messenger.net; ...@@ -3,8 +3,6 @@ package org.jivesoftware.messenger.net;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import java.io.IOException;
import java.net.Socket;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
...@@ -32,10 +30,10 @@ public class SocketSendingTracker { ...@@ -32,10 +30,10 @@ public class SocketSendingTracker {
private static SocketSendingTracker instance = new SocketSendingTracker(); private static SocketSendingTracker instance = new SocketSendingTracker();
/** /**
* Map that holds the sockets that are currently sending information together with the date * Map that holds the connections that are currently sending information together with the date
* when the sending operation started. * when the sending operation started.
*/ */
private Map<Socket, Date> sockets = new ConcurrentHashMap<Socket, Date>(); private Map<SocketConnection, Date> sockets = new ConcurrentHashMap<SocketConnection, Date>();
/** /**
* Flag that indicates if the tracket should shutdown the tracking process. * Flag that indicates if the tracket should shutdown the tracking process.
...@@ -70,7 +68,7 @@ public class SocketSendingTracker { ...@@ -70,7 +68,7 @@ public class SocketSendingTracker {
* *
* @param socket the socket that started sending data. * @param socket the socket that started sending data.
*/ */
public void socketStartedSending(Socket socket) { public void socketStartedSending(SocketConnection socket) {
sockets.put(socket, new Date()); sockets.put(socket, new Date());
} }
...@@ -80,7 +78,7 @@ public class SocketSendingTracker { ...@@ -80,7 +78,7 @@ public class SocketSendingTracker {
* *
* @param socket the socket that finished sending data. * @param socket the socket that finished sending data.
*/ */
public void socketFinishedSending(Socket socket) { public void socketFinishedSending(SocketConnection socket) {
sockets.remove(socket); sockets.remove(socket);
} }
...@@ -131,25 +129,22 @@ public class SocketSendingTracker { ...@@ -131,25 +129,22 @@ public class SocketSendingTracker {
* quite small. * quite small.
*/ */
private void checkHealth() { private void checkHealth() {
for (Socket socket : sockets.keySet()) { for (SocketConnection connection : sockets.keySet()) {
Date startDate = sockets.get(socket); Date startDate = sockets.get(connection);
if (startDate != null && if (startDate != null &&
System.currentTimeMillis() - startDate.getTime() > System.currentTimeMillis() - startDate.getTime() >
JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) { JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) {
// Check that the sending operation is still active // Check that the sending operation is still active
if (sockets.get(socket) != null) { if (sockets.get(connection) != null) {
// Close the socket // Close the socket
try { try {
Log.debug("Closing socket: " + socket + " that started sending data at: " + Log.debug("Closing connection: " + connection +
startDate); " that started sending data at: " + startDate);
socket.close(); connection.forceClose();
}
catch (IOException e) {
Log.error("Error closing socket", e);
} }
finally { finally {
// Remove tracking on this socket // Remove tracking on this socket
sockets.remove(socket); sockets.remove(connection);
} }
} }
} }
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
package org.jivesoftware.messenger.net; package org.jivesoftware.messenger.net;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
...@@ -21,10 +24,6 @@ import java.nio.channels.ReadableByteChannel; ...@@ -21,10 +24,6 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
/** /**
* TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating * TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating
* a new instance of this class the plain connection will be secured. * a new instance of this class the plain connection will be secured.
...@@ -280,4 +279,15 @@ public class TLSStreamHandler { ...@@ -280,4 +279,15 @@ public class TLSStreamHandler {
return tlsEngine.getHandshakeStatus(); return tlsEngine.getHandshakeStatus();
} }
/**
* Closes the channels that will end up closing the input and output streams of the connection.
* The channels implement the InterruptibleChannel interface so any other thread that was
* blocked in an I/O operation will be interrupted and will get an exception.
*
* @throws IOException if an I/O error occurs.
*/
public void close() throws IOException {
wbc.close();
rbc.close();
}
} }
...@@ -2,9 +2,8 @@ package org.jivesoftware.messenger.net; ...@@ -2,9 +2,8 @@ package org.jivesoftware.messenger.net;
import org.jivesoftware.util.XMLWriter; import org.jivesoftware.util.XMLWriter;
import java.net.Socket;
import java.io.Writer;
import java.io.IOException; import java.io.IOException;
import java.io.Writer;
/** /**
* XMLWriter whose writer is actually sending data on a socket connection. Since sending data over * XMLWriter whose writer is actually sending data on a socket connection. Since sending data over
...@@ -12,11 +11,11 @@ import java.io.IOException; ...@@ -12,11 +11,11 @@ import java.io.IOException;
*/ */
public class XMLSocketWriter extends XMLWriter { public class XMLSocketWriter extends XMLWriter {
private Socket socket; private SocketConnection connection;
public XMLSocketWriter(Writer writer, Socket socket) { public XMLSocketWriter(Writer writer, SocketConnection connection) {
super( writer, DEFAULT_FORMAT ); super( writer, DEFAULT_FORMAT );
this.socket = socket; this.connection = connection;
} }
/** /**
...@@ -27,13 +26,13 @@ public class XMLSocketWriter extends XMLWriter { ...@@ -27,13 +26,13 @@ public class XMLSocketWriter extends XMLWriter {
*/ */
public void flush() throws IOException { public void flush() throws IOException {
// Register that we have started sending data // Register that we have started sending data
SocketSendingTracker.getInstance().socketStartedSending(socket); SocketSendingTracker.getInstance().socketStartedSending(connection);
try { try {
super.flush(); super.flush();
} }
finally { finally {
// Register that we have finished sending data // Register that we have finished sending data
SocketSendingTracker.getInstance().socketFinishedSending(socket); SocketSendingTracker.getInstance().socketFinishedSending(connection);
} }
} }
} }
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