@Nonnull
    synchronized HttpConnection createConnection(@Nonnull final HttpBindBody body, @Nonnull final AsyncContext context)
    {
        final HttpConnection connection = new HttpConnection(body, context);
        final StreamID streamID = getStreamID();
        final long rid = body.getRid();
        Log.debug( "Creating connection for rid: {} in session {}", rid, streamID );
        connection.setSession(this);
        context.setTimeout(getWait().toMillis());
        context.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) {
                Log.trace("Session {} Request ID {}, event complete: {}", streamID, rid, asyncEvent);
                synchronized (connectionQueue) {
                    if (connectionQueue.remove(connection) || !connection.isClosed()) {
                        Log.warn("Discovered a 'complete' event for a BOSH connection that has not been consumed (for session {} with Request ID {}, was closed: {}). This likely is a bug in Openfire.", streamID, rid, connection.isClosed());
                    }
                }
                lastActivity = Instant.now();
                SessionEventDispatcher.dispatchEvent( HttpSession.this, SessionEventDispatcher.EventType.connection_closed, connection, context );
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                Log.trace("Session {} Request ID {}, event timeout: {}. Returning an empty response.", streamID, rid, asyncEvent);

                try {
                    // If onTimeout does not result in a complete(), the container falls back to default behavior.
                    // This is why this body is to be delivered in a non-async fashion.
                    synchronized (connectionQueue) {
                        // Consume the connection that is timing out, by removing it from the queue and sending data to it.
                        connectionQueue.remove(connection);
                        deliver(connection, Collections.singletonList(new Deliverable("")), false);
                        setLastResponseEmpty(true);
                    }
                } catch (HttpConnectionClosedException e) {
                    Log.warn("Unexpected exception while processing connection timeout.", e);
                }

                // Note that 'onComplete' will be invoked.
            }

            @Override
            public void onError(AsyncEvent asyncEvent) {
                Log.trace("Session {} Request ID {}, event error: {}", streamID, rid, asyncEvent);
                Log.warn("For session {} an unhandled AsyncListener error occurred: ", streamID, asyncEvent.getThrowable());
                synchronized (connectionQueue) {
                    // There was an error with a connection. Make sure it cannot be consumed again.
                    connectionQueue.remove(connection);
                }
                SessionEventDispatcher.dispatchEvent( HttpSession.this, SessionEventDispatcher.EventType.connection_closed, connection, context );
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) {
                Log.trace("Session {} Request ID {}, event start: {}", streamID, rid, asyncEvent);
                lastActivity = Instant.now();
            }
        });

        return connection;
    }
Creates a new connection on this session.

@param body the body element that was sent containing the request for a new session.
@param context the context of the asynchronous servlet call leading up to this method call.
@return the created {@link HttpConnection} which represents the connection.
xmppserver/src/main/java/org/jivesoftware/openfire/http/HttpSession.java