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

BOSH updated to version 1.6. JM-1033. Thanks to Safa Sofuoğlu.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10657 b35dd754-fafc-0310-a699-88a17e54d16e
parent c41ff947
...@@ -45,7 +45,7 @@ public enum BoshBindingError { ...@@ -45,7 +45,7 @@ public enum BoshBindingError {
/** /**
* (1) 'sid' is not valid, (2) 'stream' is not valid, (3) 'rid' is larger than the upper limit * (1) 'sid' is not valid, (2) 'stream' is not valid, (3) 'rid' is larger than the upper limit
* of the expected window, (4) connection manager is unable to resend response, (5) 'key' * of the expected window, (4) connection manager is unable to resend response, (5) 'key'
* sequence is invalid * sequence is invalid (6) script syntax is not enabled
*/ */
itemNotFound(Type.terminal, "item-not-found", HttpServletResponse.SC_NOT_FOUND), itemNotFound(Type.terminal, "item-not-found", HttpServletResponse.SC_NOT_FOUND),
/** /**
......
...@@ -19,8 +19,8 @@ import org.dom4j.Element; ...@@ -19,8 +19,8 @@ import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader; import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.net.MXParser; import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.mortbay.util.ajax.ContinuationSupport; import org.mortbay.util.ajax.ContinuationSupport;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
...@@ -182,7 +182,8 @@ public class HttpBindServlet extends HttpServlet { ...@@ -182,7 +182,8 @@ public class HttpBindServlet extends HttpServlet {
System.out.println(new Date()+": HTTP ERR("+session.getStreamID().getID() + "): " + bindingError.getErrorType().getType() + ", " + bindingError.getCondition() + "."); System.out.println(new Date()+": HTTP ERR("+session.getStreamID().getID() + "): " + bindingError.getErrorType().getType() + ", " + bindingError.getCondition() + ".");
} }
try { try {
if (session.getVersion() >= 1.6) { if ((session.getMajorVersion() == 1 && session.getMinorVersion() >= 6) ||
session.getMajorVersion() > 1) {
respond(session, response, createErrorBody(bindingError.getErrorType().getType(), respond(session, response, createErrorBody(bindingError.getErrorType().getType(),
bindingError.getCondition()), request.getMethod()); bindingError.getCondition()), request.getMethod());
} }
...@@ -241,11 +242,19 @@ public class HttpBindServlet extends HttpServlet { ...@@ -241,11 +242,19 @@ public class HttpBindServlet extends HttpServlet {
} }
String type = rootNode.attributeValue("type"); String type = rootNode.attributeValue("type");
int pauseDuration = getIntAttribue(rootNode.attributeValue("pause"), -1);
if ("terminate".equals(type)) { if ("terminate".equals(type)) {
session.close(); session.close();
respond(session, response, createEmptyBody(), request.getMethod()); respond(session, response, createEmptyBody(), request.getMethod());
} }
else if (pauseDuration > 0 && pauseDuration <= session.getMaxPause()) {
session.pause(pauseDuration);
respond(session, response, createEmptyBody(), request.getMethod());
session.setLastResponseEmpty(true);
}
else { else {
session.resetInactivityTimeout();
connection.setContinuation(ContinuationSupport.getContinuation(request, connection)); connection.setContinuation(ContinuationSupport.getContinuation(request, connection));
request.setAttribute("request-session", connection.getSession()); request.setAttribute("request-session", connection.getSession());
request.setAttribute("request", connection.getRequestId()); request.setAttribute("request", connection.getRequestId());
...@@ -301,6 +310,7 @@ public class HttpBindServlet extends HttpServlet { ...@@ -301,6 +310,7 @@ public class HttpBindServlet extends HttpServlet {
} }
catch (HttpBindTimeoutException e) { catch (HttpBindTimeoutException e) {
content = createEmptyBody(); content = createEmptyBody();
connection.getSession().setLastResponseEmpty(true);
} }
respond(connection.getSession(), response, content, method); respond(connection.getSession(), response, content, method);
...@@ -313,6 +323,10 @@ public class HttpBindServlet extends HttpServlet { ...@@ -313,6 +323,10 @@ public class HttpBindServlet extends HttpServlet {
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
if ("GET".equals(method)) { if ("GET".equals(method)) {
// Prevent caching of responses
response.addHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("Pragma", "no-cache");
content = "_BOSH_(\"" + StringEscapeUtils.escapeJavaScript(content) + "\")"; content = "_BOSH_(\"" + StringEscapeUtils.escapeJavaScript(content) + "\")";
} }
...@@ -343,6 +357,18 @@ public class HttpBindServlet extends HttpServlet { ...@@ -343,6 +357,18 @@ public class HttpBindServlet extends HttpServlet {
} }
} }
private int getIntAttribue(String value, int defaultValue) {
if (value == null || "".equals(value)) {
return defaultValue;
}
try {
return Integer.valueOf(value);
}
catch (Exception ex) {
return defaultValue;
}
}
private XMPPPacketReader getPacketReader() { private XMPPPacketReader getPacketReader() {
// Reader is associated with a new XMPPPacketReader // Reader is associated with a new XMPPPacketReader
XMPPPacketReader reader = localReader.get(); XMPPPacketReader reader = localReader.get();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
package org.jivesoftware.openfire.http; package org.jivesoftware.openfire.http;
import org.jivesoftware.util.JiveConstants;
import org.mortbay.util.ajax.Continuation; import org.mortbay.util.ajax.Continuation;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
...@@ -185,7 +186,7 @@ public class HttpConnection { ...@@ -185,7 +186,7 @@ public class HttpConnection {
} }
private String waitForResponse() throws HttpBindTimeoutException { private String waitForResponse() throws HttpBindTimeoutException {
if (continuation.suspend(session.getWait() * 1000)) { if (continuation.suspend(session.getWait() * JiveConstants.SECOND)) {
String deliverable = (String) continuation.getObject(); String deliverable = (String) continuation.getObject();
// This will occur when the hold attribute of a session has been exceded. // This will occur when the hold attribute of a session has been exceded.
this.isDelivered = true; this.isDelivered = true;
......
...@@ -26,6 +26,7 @@ import org.jivesoftware.openfire.net.MXParser; ...@@ -26,6 +26,7 @@ import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.net.SASLAuthentication; import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.VirtualConnection; import org.jivesoftware.openfire.net.VirtualConnection;
import org.jivesoftware.openfire.session.LocalClientSession; import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
...@@ -86,11 +87,15 @@ public class HttpSession extends LocalClientSession { ...@@ -86,11 +87,15 @@ public class HttpSession extends LocalClientSession {
private Set<SessionListener> listeners = new CopyOnWriteArraySet<SessionListener>(); private Set<SessionListener> listeners = new CopyOnWriteArraySet<SessionListener>();
private volatile boolean isClosed; private volatile boolean isClosed;
private int inactivityTimeout; private int inactivityTimeout;
private int defaultInactivityTimeout;
private long lastActivity; private long lastActivity;
private long lastRequestID; private long lastRequestID;
private boolean lastResponseEmpty;
private int maxRequests; private int maxRequests;
private int maxPause;
private PacketDeliverer backupDeliverer; private PacketDeliverer backupDeliverer;
private Double version = Double.NaN; private int majorVersion = -1;
private int minorVersion = -1;
private X509Certificate[] sslCertificates; private X509Certificate[] sslCertificates;
private final Queue<Collection<Element>> packetsToSend = new LinkedList<Collection<Element>>(); private final Queue<Collection<Element>> packetsToSend = new LinkedList<Collection<Element>>();
...@@ -228,7 +233,7 @@ public class HttpSession extends LocalClientSession { ...@@ -228,7 +233,7 @@ public class HttpSession extends LocalClientSession {
* *
* @param language the language this session is using. * @param language the language this session is using.
*/ */
public void setLanaguage(String language) { public void setLanguage(String language) {
this.language = language; this.language = language;
} }
...@@ -284,6 +289,28 @@ public class HttpSession extends LocalClientSession { ...@@ -284,6 +289,28 @@ public class HttpSession extends LocalClientSession {
return this.maxRequests; return this.maxRequests;
} }
/**
* Sets the maximum length of a temporary session pause (in seconds) that the client MAY
* request.
*
* @param maxPause the maximum length of a temporary session pause (in seconds) that the client
* MAY request.
*/
public void setMaxPause(int maxPause) {
this.maxPause = maxPause;
}
/**
* Returns the maximum length of a temporary session pause (in seconds) that the client MAY
* request.
*
* @return the maximum length of a temporary session pause (in seconds) that the client MAY
* request.
*/
public int getMaxPause() {
return this.maxPause;
}
/** /**
* Returns true if all connections on this session should be secured, and false if they should * Returns true if all connections on this session should be secured, and false if they should
* not. * not.
...@@ -296,11 +323,11 @@ public class HttpSession extends LocalClientSession { ...@@ -296,11 +323,11 @@ public class HttpSession extends LocalClientSession {
} }
/** /**
* Returns true if this session is a polling session. Some clients may be restricted to open * Returns true if this session is a polling session. Some clients may be restricted to open
* only one connection to the server. In this case the client SHOULD inform the server by * only one connection to the server. In this case the client SHOULD inform the server by
* setting the values of the 'wait' and/or 'hold' attributes in its session creation request * setting the values of the 'wait' and/or 'hold' attributes in its session creation request
* to "0", and then "poll" the server at regular intervals throughout the session for stanzas * to "0", and then "poll" the server at regular intervals throughout the session for stanzas
* it may have received from the server. * it may have received from the server.
* *
* @return true if this session is a polling session. * @return true if this session is a polling session.
*/ */
...@@ -328,6 +355,18 @@ public class HttpSession extends LocalClientSession { ...@@ -328,6 +355,18 @@ public class HttpSession extends LocalClientSession {
listeners.remove(listener); listeners.remove(listener);
} }
/**
* Sets the default inactivity timeout of this session. A session's inactivity timeout can
* be temporarily changed using session pause requests.
*
* @see #pause(int)
*
* @param defaultInactivityTimeout the default inactivity timeout of this session.
*/
public void setDefaultInactivityTimeout(int defaultInactivityTimeout) {
this.defaultInactivityTimeout = defaultInactivityTimeout;
}
/** /**
* Sets the time, in seconds, after which this session will be considered inactive and be be * Sets the time, in seconds, after which this session will be considered inactive and be be
* terminated. * terminated.
...@@ -339,6 +378,16 @@ public class HttpSession extends LocalClientSession { ...@@ -339,6 +378,16 @@ public class HttpSession extends LocalClientSession {
this.inactivityTimeout = inactivityTimeout; this.inactivityTimeout = inactivityTimeout;
} }
/**
* Resets the inactivity timeout of this session to default. A session's inactivity timeout can
* be temporarily changed using session pause requests.
*
* @see #pause(int)
*/
public void resetInactivityTimeout() {
this.inactivityTimeout = this.defaultInactivityTimeout;
}
/** /**
* Returns the time, in seconds, after which this session will be considered inactive and * Returns the time, in seconds, after which this session will be considered inactive and
* terminated. * terminated.
...@@ -350,6 +399,27 @@ public class HttpSession extends LocalClientSession { ...@@ -350,6 +399,27 @@ public class HttpSession extends LocalClientSession {
return inactivityTimeout; return inactivityTimeout;
} }
/**
* Pauses the session for the given amount of time. If a client encounters an exceptional
* temporary situation during which it will be unable to send requests to the connection
* manager for a period of time greater than the maximum inactivity period, then the client MAY
* request a temporary increase to the maximum inactivity period by including a 'pause'
* attribute in a request.
*
* @param duration the time, in seconds, after which this session will be considered inactive
* and terminated.
*/
public void pause(int duration) {
// Respond immediately to all pending requests
for (HttpConnection toClose : connectionQueue) {
if (!toClose.isClosed()) {
toClose.close();
lastRequestID = toClose.getRequestId();
}
}
setInactivityTimeout(duration);
}
/** /**
* Returns the time in milliseconds since the epoch that this session was last active. Activity * Returns the time in milliseconds since the epoch that this session was last active. Activity
* is a request was either made or responded to. If the session is currently active, meaning * is a request was either made or responded to. If the session is currently active, meaning
...@@ -375,41 +445,101 @@ public class HttpSession extends LocalClientSession { ...@@ -375,41 +445,101 @@ public class HttpSession extends LocalClientSession {
} }
/** /**
* Sets the version of BOSH which the client implements. Currently, the only versions supported * Returns the highest 'rid' attribute the server has received where it has also received
* by Openfire are 1.5 and 1.6. Any versions less than or equal to 1.5 will be interpreted as * all requests with lower 'rid' values. When responding to a request that it has been
* 1.5 and any values greater than or equal to 1.6 will be interpreted as 1.6. * holding, if the server finds it has already received another request with a higher 'rid'
* attribute (typically while it was holding the first request), then it MAY acknowledge the
* reception to the client.
* *
* @param version the version of BOSH which the client implements, represented as a Double, * @return the highest 'rid' attribute the server has received where it has also received
* {major version}.{minor version}. * all requests with lower 'rid' values.
*/ */
public void setVersion(double version) { public long getLastAcknowledged() {
if(version <= 1.5) { long ack = lastRequestID;
Collections.sort(connectionQueue, connectionComparator);
for (HttpConnection connection : connectionQueue) {
if (connection.getRequestId() == ack + 1) {
ack++;
}
}
return ack;
}
/**
* Sets the major version of BOSH which the client implements. Currently, the only versions
* supported by Openfire are 1.5 and 1.6.
*
* @param majorVersion the major version of BOSH which the client implements.
*/
public void setMajorVersion(int majorVersion) {
if(majorVersion != 1) {
return; return;
} }
else if(version >= 1.6) { this.majorVersion = majorVersion;
version = 1.6; }
/**
* Returns the major version of BOSH which this session utilizes. The version refers to the
* version of the XEP which the connecting client implements. If the client did not specify
* a version 1 is returned as 1.5 is the last version of the <a
* href="http://www.xmpp.org/extensions/xep-0124.html">XEP</a> that the client was not
* required to pass along its version information when creating a session.
*
* @return the major version of the BOSH XEP which the client is utilizing.
*/
public int getMajorVersion() {
if (this.majorVersion != -1) {
return this.majorVersion;
}
else {
return 1;
} }
this.version = version;
} }
/** /**
* Returns the BOSH version which this session utilizes. The version refers to the * Sets the minor version of BOSH which the client implements. Currently, the only versions
* supported by Openfire are 1.5 and 1.6. Any versions less than or equal to 5 will be
* interpreted as 5 and any values greater than or equal to 6 will be interpreted as 6.
*
* @param minorVersion the minor version of BOSH which the client implements.
*/
public void setMinorVersion(int minorVersion) {
if(minorVersion <= 5) {
this.minorVersion = 5;
}
else if(minorVersion >= 6) {
this.minorVersion = 6;
}
}
/**
* Returns the major version of BOSH which this session utilizes. The version refers to the
* version of the XEP which the connecting client implements. If the client did not specify * version of the XEP which the connecting client implements. If the client did not specify
* a version 1.5 is returned as this is the last version of the <a * a version 5 is returned as 1.5 is the last version of the <a
* href="http://www.xmpp.org/extensions/xep-0124.html">XEP</a> that the client was not * href="http://www.xmpp.org/extensions/xep-0124.html">XEP</a> that the client was not
* required to pass along its version information when creating a session. * required to pass along its version information when creating a session.
* *
* @return the version of the BOSH XEP which the client is utilizing. * @return the minor version of the BOSH XEP which the client is utilizing.
*/ */
public double getVersion() { public int getMinorVersion() {
if (!Double.isNaN(this.version)) { if (this.minorVersion != -1) {
return this.version; return this.minorVersion;
} }
else { else {
return 1.5; return 5;
} }
} }
/**
* lastResponseEmpty true if last response of this session is an empty body element. This
* is used in overactivity checking.
*
* @param lastResponseEmpty true if last response of this session is an empty body element.
*/
public void setLastResponseEmpty(boolean lastResponseEmpty) {
this.lastResponseEmpty = lastResponseEmpty;
}
public String getResponse(long requestID) throws HttpBindException { public String getResponse(long requestID) throws HttpBindException {
for (HttpConnection connection : connectionQueue) { for (HttpConnection connection : connectionQueue) {
if (connection.getRequestId() == requestID) { if (connection.getRequestId() == requestID) {
...@@ -441,6 +571,7 @@ public class HttpSession extends LocalClientSession { ...@@ -441,6 +571,7 @@ public class HttpSession extends LocalClientSession {
} }
if (response == null) { if (response == null) {
response = createEmptyBody(); response = createEmptyBody();
setLastResponseEmpty(true);
} }
return response; return response;
} }
...@@ -488,10 +619,10 @@ public class HttpSession extends LocalClientSession { ...@@ -488,10 +619,10 @@ public class HttpSession extends LocalClientSession {
} }
} }
} }
/** /**
* Return the X509Certificates associated with this session. * Return the X509Certificates associated with this session.
* *
* @return the X509Certificate associated with this session. * @return the X509Certificate associated with this session.
*/ */
public X509Certificate[] getPeerCertificates() { public X509Certificate[] getPeerCertificates() {
...@@ -529,9 +660,8 @@ public class HttpSession extends LocalClientSession { ...@@ -529,9 +660,8 @@ public class HttpSession extends LocalClientSession {
connection.deliverBody(createDeliverable(deliverable.deliverables)); connection.deliverBody(createDeliverable(deliverable.deliverables));
return connection; return connection;
} }
else if (rid > (lastRequestID + hold + 1)) { else if (rid > (lastRequestID + maxRequests)) {
// TODO handle the case of greater RID which basically has it wait Log.warn("Request " + rid + " > " + (lastRequestID + maxRequests) + ", ending session.");
Log.warn("Request " + rid + " > " + (lastRequestID + hold + 1) + ", ending session.");
throw new HttpBindException("Unexpected RID error.", throw new HttpBindException("Unexpected RID error.",
BoshBindingError.itemNotFound); BoshBindingError.itemNotFound);
} }
...@@ -558,9 +688,7 @@ public class HttpSession extends LocalClientSession { ...@@ -558,9 +688,7 @@ public class HttpSession extends LocalClientSession {
throw new IllegalArgumentException("Connection cannot be null."); throw new IllegalArgumentException("Connection cannot be null.");
} }
if (isPoll) { checkOveractivity(isPoll);
checkPollingInterval();
}
if (isSecure && !connection.isSecure()) { if (isSecure && !connection.isSecure()) {
throw new HttpBindException("Session was started from secure connection, all " + throw new HttpBindException("Session was started from secure connection, all " +
...@@ -568,7 +696,7 @@ public class HttpSession extends LocalClientSession { ...@@ -568,7 +696,7 @@ public class HttpSession extends LocalClientSession {
} }
sslCertificates = connection.getPeerCertificates(); sslCertificates = connection.getPeerCertificates();
connection.setSession(this); connection.setSession(this);
// We aren't supposed to hold connections open or we already have some packets waiting // We aren't supposed to hold connections open or we already have some packets waiting
// to be sent to the client. // to be sent to the client.
...@@ -585,15 +713,15 @@ public class HttpSession extends LocalClientSession { ...@@ -585,15 +713,15 @@ public class HttpSession extends LocalClientSession {
connectionQueue.add(connection); connectionQueue.add(connection);
Collections.sort(connectionQueue, connectionComparator); Collections.sort(connectionQueue, connectionComparator);
int connectionsToClose; int connectionsToClose;
if(connectionQueue.get(connectionQueue.size() - 1) != connection) { if(connectionQueue.get(connectionQueue.size() - 1) != connection) {
// Current connection does not have the greatest rid. That means // Current connection does not have the greatest rid. That means
// requests were received out of order, respond to all. // requests were received out of order, respond to all.
connectionsToClose = connectionQueue.size(); connectionsToClose = connectionQueue.size();
} }
else { else {
// Everything's fine, number of current connections open tells us // Everything's fine, number of current connections open tells us
// how many that we need to close. // how many that we need to close.
connectionsToClose = getOpenConnectionCount() - hold; connectionsToClose = getOpenConnectionCount() - hold;
} }
...@@ -646,18 +774,53 @@ public class HttpSession extends LocalClientSession { ...@@ -646,18 +774,53 @@ public class HttpSession extends LocalClientSession {
} }
} }
private void checkPollingInterval() throws HttpBindException { /**
long time = System.currentTimeMillis(); * Check that the client SHOULD NOT make more simultaneous requests than specified
if (((time - lastPoll) / 1000) < maxPollingInterval) { * by the 'requests' attribute in the connection manager's Session Creation Response.
Log.debug("Too frequent polling minimum interval is " * However the client MAY make one additional request if it is to pause or terminate a session.
+ maxPollingInterval + ", current interval " + ((time - lastPoll) / 1000)); *
if (!JiveGlobals.getBooleanProperty("xmpp.httpbind.client.requests.ignorePollingCap", false)) { * @see <a href="http://www.xmpp.org/extensions/xep-0124.html#overactive">overactive</a>.
throw new HttpBindException("Too frequent polling minimum interval is " * @param isPoll true if the session is using polling.
+ maxPollingInterval + ", current interval " + ((time - lastPoll) / 1000), * @throws HttpBindException if the connection has violated a facet of the HTTP binding
BoshBindingError.policyViolation); * protocol.
*/
private void checkOveractivity(boolean isPoll) throws HttpBindException {
int pendingConnections = 0;
boolean overactivity = false;
String errorMessage = "Overactivity detected";
for (HttpConnection conn : connectionQueue) {
if (!conn.isClosed()) {
pendingConnections++;
}
}
if(pendingConnections >= maxRequests) {
overactivity = true;
errorMessage += ", too many simultaneous requests.";
}
else if(isPoll) {
long time = System.currentTimeMillis();
if (time - lastPoll < maxPollingInterval * JiveConstants.SECOND) {
if(isPollingSession()) {
overactivity = lastResponseEmpty;
}
else {
overactivity = (pendingConnections >= maxRequests - 1);
}
}
lastPoll = time;
errorMessage += ", minimum polling interval is "
+ maxPollingInterval + ", current interval " + ((time - lastPoll) / 1000);
}
setLastResponseEmpty(false);
if(overactivity) {
Log.debug(errorMessage);
if (!JiveGlobals.getBooleanProperty("xmpp.httpbind.client.requests.ignoreOveractivity", false)) {
throw new HttpBindException(errorMessage, BoshBindingError.policyViolation);
} }
} }
lastPoll = time;
} }
private synchronized void deliver(String text) { private synchronized void deliver(String text) {
...@@ -703,7 +866,15 @@ public class HttpSession extends LocalClientSession { ...@@ -703,7 +866,15 @@ public class HttpSession extends LocalClientSession {
private String createDeliverable(Collection<Deliverable> elements) { private String createDeliverable(Collection<Deliverable> elements) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("<body xmlns='" + "http://jabber.org/protocol/httpbind" + "'>"); builder.append("<body xmlns='" + "http://jabber.org/protocol/httpbind" + "'");
long ack = getLastAcknowledged();
if(ack > lastRequestID)
builder.append(" ack='").append(ack).append("'");
builder.append(">");
setLastResponseEmpty(elements.size() == 0);
for (Deliverable child : elements) { for (Deliverable child : elements) {
builder.append(child.getDeliverable()); builder.append(child.getDeliverable());
} }
...@@ -768,9 +939,12 @@ public class HttpSession extends LocalClientSession { ...@@ -768,9 +939,12 @@ public class HttpSession extends LocalClientSession {
} }
private static String createEmptyBody() { private String createEmptyBody() {
Element body = DocumentHelper.createElement("body"); Element body = DocumentHelper.createElement("body");
body.addNamespace("", "http://jabber.org/protocol/httpbind"); body.addNamespace("", "http://jabber.org/protocol/httpbind");
long ack = getLastAcknowledged();
if(ack > lastRequestID)
body.addAttribute("ack", String.valueOf(ack));
return body.asXML(); return body.asXML();
} }
...@@ -813,7 +987,7 @@ public class HttpSession extends LocalClientSession { ...@@ -813,7 +987,7 @@ public class HttpSession extends LocalClientSession {
public void deliverRawText(String text) { public void deliverRawText(String text) {
((HttpSession) session).deliver(text); ((HttpSession) session).deliver(text);
} }
public Certificate[] getPeerCertificates() { public Certificate[] getPeerCertificates() {
return ((HttpSession) session).getPeerCertificates(); return ((HttpSession) session).getPeerCertificates();
} }
......
...@@ -119,7 +119,11 @@ public class HttpSessionManager { ...@@ -119,7 +119,11 @@ public class HttpSessionManager {
int wait = getIntAttribute(rootNode.attributeValue("wait"), 60); int wait = getIntAttribute(rootNode.attributeValue("wait"), 60);
int hold = getIntAttribute(rootNode.attributeValue("hold"), 1); int hold = getIntAttribute(rootNode.attributeValue("hold"), 1);
double version = getDoubleAttribute(rootNode.attributeValue("ver"), 1.5);
String version = rootNode.attributeValue("ver");
if (version == null || "".equals(version)) {
version = "1.5";
}
HttpSession session = createSession(connection.getRequestId(), address, connection); HttpSession session = createSession(connection.getRequestId(), address, connection);
session.setWait(Math.min(wait, getMaxWait())); session.setWait(Math.min(wait, getMaxWait()));
...@@ -127,10 +131,23 @@ public class HttpSessionManager { ...@@ -127,10 +131,23 @@ public class HttpSessionManager {
session.setSecure(connection.isSecure()); session.setSecure(connection.isSecure());
session.setMaxPollingInterval(getPollingInterval()); session.setMaxPollingInterval(getPollingInterval());
session.setMaxRequests(getMaxRequests()); session.setMaxRequests(getMaxRequests());
session.setInactivityTimeout(getInactivityTimeout()); session.setMaxPause(getMaxPause());
if(session.isPollingSession()) {
session.setDefaultInactivityTimeout(getPollingInactivityTimeout());
}
else {
session.setDefaultInactivityTimeout(getInactivityTimeout());
}
session.resetInactivityTimeout();
// Store language and version information in the connection. // Store language and version information in the connection.
session.setLanaguage(language); session.setLanguage(language);
session.setVersion(version);
String [] versionString = version.split("\\.");
session.setMajorVersion(Integer.parseInt(versionString[0]));
session.setMinorVersion(Integer.parseInt(versionString[1]));
try { try {
connection.deliverBody(createSessionCreationResponse(session)); connection.deliverBody(createSessionCreationResponse(session));
} }
...@@ -146,6 +163,17 @@ public class HttpSessionManager { ...@@ -146,6 +163,17 @@ public class HttpSessionManager {
} }
/**
* Returns the maximum length of a temporary session pause (in seconds) that the client MAY
* request.
*
* @return the maximum length of a temporary session pause (in seconds) that the client MAY
* request.
*/
public int getMaxPause() {
return JiveGlobals.getIntProperty("xmpp.httpbind.client.maxpause", 300);
}
/** /**
* Returns the longest time (in seconds) that Openfire is allowed to wait before responding to * Returns the longest time (in seconds) that Openfire is allowed to wait before responding to
* any request during the session. This enables the client to prevent its TCP connection from * any request during the session. This enables the client to prevent its TCP connection from
...@@ -188,7 +216,7 @@ public class HttpSessionManager { ...@@ -188,7 +216,7 @@ public class HttpSessionManager {
} }
/** /**
* Seconds a session has to be idle to be closed. Default is 30 minutes. Sending stanzas to the * Seconds a session has to be idle to be closed. Default is 30. Sending stanzas to the
* client is not considered as activity. We are only considering the connection active when 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 * 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 * that sending data will fail if the connection is closed. And if the thread is blocked while
...@@ -201,6 +229,20 @@ public class HttpSessionManager { ...@@ -201,6 +229,20 @@ public class HttpSessionManager {
return JiveGlobals.getIntProperty("xmpp.httpbind.client.idle", 30); return JiveGlobals.getIntProperty("xmpp.httpbind.client.idle", 30);
} }
/**
* Seconds a polling session has to be idle to be closed. Default is 60. 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.
*
* @return Seconds a polling session has to be idle to be closed.
*/
public int getPollingInactivityTimeout() {
return JiveGlobals.getIntProperty("xmpp.httpbind.client.idle.polling", 60);
}
/** /**
* Forwards a client request, which is related to a session, to the server. A connection is * Forwards a client request, which is related to a session, to the server. A connection is
* created and queued up in the provided session. When a connection reaches the top of a queue * created and queued up in the provided session. When a connection reaches the top of a queue
...@@ -278,8 +320,13 @@ public class HttpSessionManager { ...@@ -278,8 +320,13 @@ public class HttpSessionManager {
response.addAttribute("inactivity", String.valueOf(session.getInactivityTimeout())); response.addAttribute("inactivity", String.valueOf(session.getInactivityTimeout()));
response.addAttribute("polling", String.valueOf(session.getMaxPollingInterval())); response.addAttribute("polling", String.valueOf(session.getMaxPollingInterval()));
response.addAttribute("wait", String.valueOf(session.getWait())); response.addAttribute("wait", String.valueOf(session.getWait()));
if(session.getVersion() >= 1.6) { if ((session.getMajorVersion() == 1 && session.getMinorVersion() >= 6) ||
response.addAttribute("ver", String.valueOf(session.getVersion())); session.getMajorVersion() > 1) {
response.addAttribute("hold", String.valueOf(session.getHold()));
response.addAttribute("ack", String.valueOf(session.getLastAcknowledged()));
response.addAttribute("maxpause", String.valueOf(session.getMaxPause()));
response.addAttribute("ver", String.valueOf(session.getMajorVersion())
+ "." + String.valueOf(session.getMinorVersion()));
} }
Element features = response.addElement("stream:features"); Element features = response.addElement("stream:features");
...@@ -295,8 +342,8 @@ public class HttpSessionManager { ...@@ -295,8 +342,8 @@ public class HttpSessionManager {
public void run() { public void run() {
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
for (HttpSession session : sessionMap.values()) { for (HttpSession session : sessionMap.values()) {
long lastActive = (currentTime - session.getLastActivity()) / 1000; long lastActive = currentTime - session.getLastActivity();
if (lastActive > session.getInactivityTimeout()) { if (lastActive > session.getInactivityTimeout() * JiveConstants.SECOND) {
session.close(); session.close();
} }
} }
...@@ -304,7 +351,7 @@ public class HttpSessionManager { ...@@ -304,7 +351,7 @@ public class HttpSessionManager {
} }
/** /**
* A runner that gurantees that the packets per a session will be sent and * A runner that guarantees that the packets per a session will be sent and
* processed in the order in which they were received. * processed in the order in which they were received.
*/ */
private class HttpPacketSender implements Runnable { private class HttpPacketSender implements Runnable {
......
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