use of org.jivesoftware.openfire.StreamID in project Openfire by igniterealtime.
the class HttpSession method processConnection.
/**
* Schedules the connection for processing, ensuring that connections are processed in the order of their request ID
* values. Processing causes data delivered by the client to be routed in the server to their intended recipients,
* and allow the connection to be used to send back data to the client.
*
* @param connection The connection ready to be processed.
* @param context the context of the asynchronous servlet call leading up to this method call.
*/
private void processConnection(@Nonnull final HttpConnection connection, @Nonnull final AsyncContext context) throws HttpConnectionClosedException, IOException {
final long rid = connection.getRequestId();
final StreamID streamid = getStreamID();
if (Log.isDebugEnabled()) {
Log.debug("Adding connection to stream {} with rid {}", streamid, rid);
}
// Note that connections can be expected to arrive 'out of order'. The implementation should only use a connection
// that has a request ID value that's exactly one higher than the last request ID value in the 'gapless' sequence
// of request IDs. When a connection is being processed that has a higher value, it should go unused until another
// connection arrives that 'fills the gap' (and be used only _after_ that connection gets used).
boolean aConnectionAvailableForDelivery = false;
synchronized (connectionQueue) {
connectionQueue.add(connection);
// Order the connection queue, and determine if the sequence of consecutive request IDs can be updated.
connectionQueue.sort(connectionComparator);
for (final HttpConnection queuedConnection : connectionQueue) {
final long queuedRequestID = queuedConnection.getRequestId();
if (queuedRequestID <= lastSequentialRequestID) {
continue;
}
if (queuedRequestID == lastSequentialRequestID + 1) {
// There is a new connection available for consumption.
lastSequentialRequestID = queuedRequestID;
if (queuedConnection.isClosed()) {
// Unsure if this can happen - perhaps at edge-cases involving time-outs?
continue;
}
// The data that was provided by the client can now be processed by the server.
sendPendingPackets(queuedConnection.getInboundDataQueue());
// Evaluate edge-cases.
if (queuedConnection.isTerminate()) {
queuedConnection.deliverBody(createEmptyBody(true), true);
close();
} else if (queuedConnection.isRestart()) {
queuedConnection.deliverBody(createSessionRestartResponse(), true);
} else if (queuedConnection.getPause() != null && queuedConnection.getPause().compareTo(getMaxPause()) <= 0) {
// TODO shouldn't we error when the requested pause is higher than the allowed maximum?
pause(queuedConnection.getPause());
connection.deliverBody(createEmptyBody(false), true);
setLastResponseEmpty(true);
} else {
// At least one new connection has become available, and can be used to return data to the client.
aConnectionAvailableForDelivery = true;
}
// Note that when this connection fills a gap in the sequence, other connections might already be
// available that have the 'next' Request ID value. We need to keep iterating.
} else {
// when a gap is detected (subsequent connections will only have higher Request ID values).
break;
}
}
if (isPollingSession()) {
// guarantee that 'a new connection is now available for delivery').
assert aConnectionAvailableForDelivery;
}
if (isPollingSession() || (aConnectionAvailableForDelivery && !pendingElements.isEmpty())) {
lastActivity = Instant.now();
// TODO is this the right place to dispatch this event?
SessionEventDispatcher.dispatchEvent(this, SessionEventDispatcher.EventType.connection_opened, connection, context);
deliver(pendingElements);
pendingElements.clear();
}
// When a new connection has become available, older connections need to be released (allowing the client to
// send more data if it needs to).
final List<HttpConnection> openConnections = connectionQueue.stream().filter(c -> !c.isClosed()).filter(c -> c.getRequestId() <= lastSequentialRequestID).sorted(connectionComparator).collect(Collectors.toList());
while (openConnections.size() > hold) {
if (Log.isTraceEnabled()) {
Log.trace("Stream {}: releasing oldest connection (rid {}), as the amount of open connections ({}) is higher than the requested amount to hold ({}).", streamid, rid, openConnections.size(), hold);
}
final HttpConnection openConnection = openConnections.remove(0);
openConnection.deliverBody(createEmptyBody(false), true);
}
}
}
use of org.jivesoftware.openfire.StreamID in project Openfire by igniterealtime.
the class ConnectionMultiplexerManager method removeSession.
private void removeSession(Session session) {
// Remove trace indicating that a connection manager is hosting a connection
StreamID streamID = session.getStreamID();
String connectionManagerDomain = streamIDs.remove(streamID);
// Remove trace indicating that a connection manager is hosting a session
if (connectionManagerDomain != null) {
Map<StreamID, LocalClientSession> sessions = sessionsByManager.get(connectionManagerDomain);
if (sessions != null) {
sessions.remove(streamID);
}
}
}
use of org.jivesoftware.openfire.StreamID in project Openfire by igniterealtime.
the class ConnectionMultiplexerManager method multiplexerUnavailable.
/**
* A connection manager has gone unavailable. Close client sessions that were established
* to the specified connection manager.
*
* @param connectionManagerName the connection manager that is no longer available.
*/
public void multiplexerUnavailable(String connectionManagerName) {
// Remove the connection manager and the hosted sessions
Map<StreamID, LocalClientSession> sessions = sessionsByManager.remove(connectionManagerName);
if (sessions != null) {
for (StreamID streamID : sessions.keySet()) {
// Remove inverse track of connection manager hosting streamIDs
streamIDs.remove(streamID);
// Close the session
sessions.get(streamID).close();
}
}
}
use of org.jivesoftware.openfire.StreamID in project Openfire by igniterealtime.
the class ConnectionMultiplexerManager method closeClientSession.
/**
* Closes an existing client session that was established through a connection manager.
*
* @param connectionManagerDomain the connection manager that is handling the connection
* of the session.
* @param streamID the stream ID created by the connection manager for the session.
*/
public void closeClientSession(String connectionManagerDomain, StreamID streamID) {
Map<StreamID, LocalClientSession> sessions = sessionsByManager.get(connectionManagerDomain);
if (sessions != null) {
Session session = sessions.remove(streamID);
if (session != null) {
Log.debug("Closing session: {}", session);
session.close();
}
}
}
use of org.jivesoftware.openfire.StreamID in project Openfire by igniterealtime.
the class ConnectionMultiplexerManager method createClientSession.
/**
* Creates a new client session that was established to the specified connection manager.
* The new session will not be findable through its stream ID.
*
* @param connectionManagerDomain the connection manager that is handling the connection
* of the session.
* @param streamID the stream ID created by the connection manager for the new session.
* @param hostName the address's hostname of the client or null if using old connection manager.
* @param hostAddress the textual representation of the address of the client or null if using old CM.
* @return true if a session was created or false if the client should disconnect.
*/
public boolean createClientSession(String connectionManagerDomain, StreamID streamID, String hostName, String hostAddress) {
Connection connection = new ClientSessionConnection(connectionManagerDomain, hostName, hostAddress);
// Check if client is allowed to connect from the specified IP address. Ignore the checking if connection
// manager is old version and is not passing client's address
byte[] address = null;
try {
address = connection.getAddress();
} catch (UnknownHostException e) {
// Ignore
}
if (address == null || LocalClientSession.isAllowed(connection)) {
LocalClientSession session = SessionManager.getInstance().createClientSession(connection, streamID);
// Register that this streamID belongs to the specified connection manager
streamIDs.put(streamID, connectionManagerDomain);
// Register which sessions are being hosted by the speicifed connection manager
Map<StreamID, LocalClientSession> sessions = sessionsByManager.get(connectionManagerDomain);
if (sessions == null) {
synchronized (connectionManagerDomain.intern()) {
sessions = sessionsByManager.get(connectionManagerDomain);
if (sessions == null) {
sessions = new ConcurrentHashMap<>();
sessionsByManager.put(connectionManagerDomain, sessions);
}
}
}
sessions.put(streamID, session);
return true;
}
return false;
}
Aggregations