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 {
this.socket = socket;
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), CHARSET));
this.deliverer = deliverer;
xmlSerializer = new XMLSocketWriter(writer, socket);
xmlSerializer = new XMLSocketWriter(writer, this);
}
/**
......@@ -103,7 +103,7 @@ public class SocketConnection implements Connection {
secure = true;
tlsStreamHandler = new TLSStreamHandler(socket, clientMode);
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 {
try {
synchronized (writer) {
// Register that we started sending data on the connection
SocketSendingTracker.getInstance().socketStartedSending(socket);
SocketSendingTracker.getInstance().socketStartedSending(this);
writer.write(" ");
writer.flush();
}
......@@ -125,7 +125,7 @@ public class SocketConnection implements Connection {
}
finally {
// Register that we finished sending data on the connection
SocketSendingTracker.getInstance().socketFinishedSending(socket);
SocketSendingTracker.getInstance().socketFinishedSending(this);
}
return !isClosed();
}
......@@ -229,7 +229,7 @@ public class SocketConnection implements Connection {
synchronized (writer) {
try {
// Register that we started sending data on the connection
SocketSendingTracker.getInstance().socketStartedSending(socket);
SocketSendingTracker.getInstance().socketStartedSending(this);
writer.write("</stream:stream>");
if (flashClient) {
writer.write('\0');
......@@ -239,7 +239,7 @@ public class SocketConnection implements Connection {
catch (IOException e) {}
finally {
// Register that we finished sending data on the connection
SocketSendingTracker.getInstance().socketFinishedSending(socket);
SocketSendingTracker.getInstance().socketFinishedSending(this);
}
}
}
......@@ -247,13 +247,7 @@ public class SocketConnection implements Connection {
Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e);
}
try {
socket.close();
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e);
}
closeConnection();
wasClosed = true;
}
}
......@@ -262,6 +256,42 @@ public class SocketConnection implements Connection {
}
}
/**
* 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 {
if (tlsStreamHandler == null) {
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) {
Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e);
}
}
public void deliver(Packet packet) throws UnauthorizedException, PacketException {
if (isClosed()) {
deliverer.deliver(packet);
......@@ -308,7 +338,7 @@ public class SocketConnection implements Connection {
synchronized (writer) {
try {
// Register that we started sending data on the connection
SocketSendingTracker.getInstance().socketStartedSending(socket);
SocketSendingTracker.getInstance().socketStartedSending(this);
writer.write(text);
if (flashClient) {
writer.write('\0');
......@@ -321,7 +351,7 @@ public class SocketConnection implements Connection {
}
finally {
// Register that we finished sending data on the connection
SocketSendingTracker.getInstance().socketFinishedSending(socket);
SocketSendingTracker.getInstance().socketFinishedSending(this);
}
}
if (errorDelivering) {
......
......@@ -3,8 +3,6 @@ package org.jivesoftware.messenger.net;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
......@@ -32,10 +30,10 @@ public class 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.
*/
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.
......@@ -70,7 +68,7 @@ public class SocketSendingTracker {
*
* @param socket the socket that started sending data.
*/
public void socketStartedSending(Socket socket) {
public void socketStartedSending(SocketConnection socket) {
sockets.put(socket, new Date());
}
......@@ -80,7 +78,7 @@ public class SocketSendingTracker {
*
* @param socket the socket that finished sending data.
*/
public void socketFinishedSending(Socket socket) {
public void socketFinishedSending(SocketConnection socket) {
sockets.remove(socket);
}
......@@ -131,25 +129,22 @@ public class SocketSendingTracker {
* quite small.
*/
private void checkHealth() {
for (Socket socket : sockets.keySet()) {
Date startDate = sockets.get(socket);
for (SocketConnection connection : sockets.keySet()) {
Date startDate = sockets.get(connection);
if (startDate != null &&
System.currentTimeMillis() - startDate.getTime() >
JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) {
// Check that the sending operation is still active
if (sockets.get(socket) != null) {
if (sockets.get(connection) != null) {
// Close the socket
try {
Log.debug("Closing socket: " + socket + " that started sending data at: " +
startDate);
socket.close();
}
catch (IOException e) {
Log.error("Error closing socket", e);
Log.debug("Closing connection: " + connection +
" that started sending data at: " + startDate);
connection.forceClose();
}
finally {
// Remove tracking on this socket
sockets.remove(socket);
sockets.remove(connection);
}
}
}
......
......@@ -11,6 +11,9 @@
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.InputStream;
import java.io.OutputStream;
......@@ -21,10 +24,6 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
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
* a new instance of this class the plain connection will be secured.
......@@ -280,4 +279,15 @@ public class TLSStreamHandler {
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;
import org.jivesoftware.util.XMLWriter;
import java.net.Socket;
import java.io.Writer;
import java.io.IOException;
import java.io.Writer;
/**
* XMLWriter whose writer is actually sending data on a socket connection. Since sending data over
......@@ -12,11 +11,11 @@ import java.io.IOException;
*/
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 );
this.socket = socket;
this.connection = connection;
}
/**
......@@ -27,13 +26,13 @@ public class XMLSocketWriter extends XMLWriter {
*/
public void flush() throws IOException {
// Register that we have started sending data
SocketSendingTracker.getInstance().socketStartedSending(socket);
SocketSendingTracker.getInstance().socketStartedSending(connection);
try {
super.flush();
}
finally {
// 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