Newer
Older
Openfire-connectionmanager / src / java / org / jivesoftware / multiplexer / net / http / HttpSessionManager.java
/**
 * $RCSfile:  $
 * $Revision:  $
 * $Date:  $
 *
 * Copyright (C) 2006 Jive Software. All rights reserved.
 * This software is the proprietary information of Jive Software. Use is subject to license terms.
 */
package org.jivesoftware.multiplexer.net.http;

import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.multiplexer.ServerSurrogate;
import org.jivesoftware.multiplexer.ConnectionManager;
import org.jivesoftware.multiplexer.Session;
import org.dom4j.Element;

/**
 *
 */
public class HttpSessionManager {

    /**
     * Milliseconds a connection has to be idle to be closed. Default is 30 minutes. Sending
     * stanzas to the client is not considered as activity. We are only considering the connection
     * active when the client sends some data or hearbeats (i.e. whitespaces) to the server.
     * The reason for this is that sending data will fail if the connection is closed. And if
     * the thread is blocked while sending data (because the socket is closed) then the clean up
     * thread will close the socket anyway.
     */
    private static long inactivityTimeout;

    /**
     * The connection manager MAY limit the number of simultaneous requests the client makes with
     * the 'requests' attribute. The RECOMMENDED value is "2". Servers that only support polling
     * behavior MUST prevent clients from making simultaneous requests by setting the 'requests'
     * attribute to a value of "1" (however, polling is NOT RECOMMENDED). In any case, clients MUST
     * NOT make more simultaneous requests than specified by the connection manager.
     */
    private static int maxRequests;

    /**
     * The connection manager SHOULD include two additional attributes in the session creation
     * response element, specifying the shortest allowable polling interval and the longest
     * allowable inactivity period (both in seconds). Communication of these parameters enables
     * the client to engage in appropriate behavior (e.g., not sending empty request elements more
     * often than desired, and ensuring that the periods with no requests pending are
     * never too long).
     */
    private static int pollingInterval;

    private String serverName;

    static {
        // Set the default read idle timeout. If none was set then assume 30 minutes
        inactivityTimeout = JiveGlobals.getIntProperty("xmpp.httpbind.client.idle", 30);
        maxRequests = JiveGlobals.getIntProperty("xmpp.httpbind.client.requests.max", 2);
        pollingInterval = JiveGlobals.getIntProperty("xmpp.httpbind.client.requests.polling", 5);
    }

    public HttpSessionManager(String serverName) {
        this.serverName = serverName;
    }

    public HttpSession getSession(String streamID) {
        Session session = Session.getSession(streamID);
        if(session instanceof HttpSession) {
            return (HttpSession) session;
        }
        return null;
    }

    public HttpSession createSession(Element rootNode, HttpConnection connection) {
        // TODO Check if IP address is allowed to connect to the server

        // Default language is English ("en").
        String language = rootNode.attributeValue("xml:lang");
        if(language == null || "".equals(language)) {
            language = "en";
        }

        int wait = getIntAttribute(rootNode.attributeValue("wait"), 60);
        int hold = getIntAttribute(rootNode.attributeValue("hold"), 1);

        ServerSurrogate serverSurrogate = ConnectionManager.getInstance().getServerSurrogate();
        // Indicate the compression policy to use for this connection
        connection.setCompressionPolicy(serverSurrogate.getCompressionPolicy());

        HttpSession session = createSession(serverName);
        session.setWait(wait);
        session.setHold(hold);
        // Store language and version information in the connection.
        session.setLanaguage(language);
        connection.deliverBody(createSessionCreationResponse(session, serverSurrogate));

        return session;
    }

    private HttpSession createSession(String serverName) {
        ServerSurrogate serverSurrogate = ConnectionManager.getInstance().getServerSurrogate();
        // Create a ClientSession for this user.
        String streamID = Session.idFactory.createStreamID();
        HttpSession session = new HttpSession(serverName, streamID);
        // Register that the new session is associated with the specified stream ID
        HttpSession.addSession(streamID, session);
        // Send to the server that a new client session has been created
        serverSurrogate.clientSessionCreated(streamID);
        return session;
    }

    private static int getIntAttribute(String value, int defaultValue) {
        if(value == null || "".equals(value)) {
            return defaultValue;
        }
        try {
            return Integer.valueOf(value);
        }
        catch (Exception ex) {
            return defaultValue;
        }
    }

    private static String createSessionCreationResponse(HttpSession session,
                                                         ServerSurrogate serverSurrogate)
    {
        StringBuilder builder = new StringBuilder();
        builder.append("<body")
                .append(" xmlns='http://jabber.org/protocol/httpbind'").append(" authID='")
                .append(session.getStreamID()).append("'")
                .append(" sid='").append(session.getStreamID()).append("'")
                .append(" secure='true" + "'").append(" requests='")
                .append(String.valueOf(maxRequests)).append("'")
                .append(" inactivity='").append(String.valueOf(inactivityTimeout)).append("'")
                .append(" polling='").append(String.valueOf(pollingInterval)).append("'")
                .append(" wait='").append(String.valueOf(session.getWait())).append("'")
                .append(">");
        builder.append("<stream:features>");
        builder.append(serverSurrogate.getSASLMechanismsElement(session).asXML());
        builder.append("</stream:features>");
        builder.append("</body>");

        return builder.toString();
    }
}