/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.multiplexer.net.http;
import org.mortbay.util.ajax.Continuation;
/**
* Represents one HTTP connection with a client using the HTTP Binding service. The client will wait
* on {@link #getResponse()} until the server forwards a message to it or the wait time on the
* session timesout.
*
* @author Alexander Wenckus
*/
public class HttpConnection {
private long requestId;
private String body;
private HttpSession session;
private Continuation continuation;
private boolean isClosed;
private boolean isSecure = false;
private boolean isDelivered;
private static final String CONNECTION_CLOSED = "connection closed";
/**
* Constructs an HTTP Connection.
*
* @param requestId the ID which uniquely identifies this request.
* @param isSecure true if this connection is using HTTPS
*/
public HttpConnection(long requestId, boolean isSecure) {
this.requestId = requestId;
this.isSecure = isSecure;
this.isDelivered = false;
}
/**
* The connection should be closed without delivering a stanza to the requestor.
*/
public void close() {
if (isClosed) {
return;
}
try {
deliverBody(CONNECTION_CLOSED);
}
catch (HttpConnectionClosedException e) {
/* Shouldn't happen */
}
}
/**
* Returns true if this connection has been closed, either a response was delivered to the
* client or the server closed the connection aburbtly.
*
* @return true if this connection has been closed.
*/
public boolean isClosed() {
return isClosed;
}
/**
* Returns true if this connection is using HTTPS.
*
* @return true if this connection is using HTTPS.
*/
public boolean isSecure() {
return isSecure;
}
public boolean isDelivered() {
return isDelivered;
}
/**
* Delivers content to the client. The content should be valid XMPP wrapped inside of a body.
* A <i>null</i> value for body indicates that the connection should be closed and the client
* sent an empty body.
*
* @param body the XMPP content to be forwarded to the client inside of a body tag.
*
* @throws HttpConnectionClosedException when this connection to the client has already recieved
* a deliverable to forward to the client
*/
public void deliverBody(String body) throws HttpConnectionClosedException {
if(body == null) {
throw new IllegalArgumentException("Body cannot be null!");
}
// We only want to use this function once so we will close it when the body is delivered.
if (isClosed) {
throw new HttpConnectionClosedException("The http connection is no longer " +
"available to deliver content");
}
else {
isClosed = true;
}
if (continuation != null) {
continuation.setObject(body);
continuation.resume();
}
else {
this.body = body;
}
}
/**
* A call that will cause a wait, or in the case of Jetty the thread to be freed, if there is no
* deliverable currently available. Once the response becomes available, it is returned.
*
* @return the deliverable to send to the client
* @throws HttpBindTimeoutException to indicate that the maximum wait time requested by the
* client has been surpassed and an empty response should be returned.
*/
public String getResponse() throws HttpBindTimeoutException {
if (body == null && continuation != null) {
try {
body = waitForResponse();
}
catch (HttpBindTimeoutException e) {
this.isClosed = true;
throw e;
}
}
else if (body == null) {
throw new IllegalStateException("Continuation not set, cannot wait for deliverable.");
}
return body;
}
/**
* Returns the ID which uniquely identifies this connection.
*
* @return the ID which uniquely identifies this connection.
*/
public long getRequestId() {
return requestId;
}
/**
* Set the session that this connection belongs to.
*
* @param session the session that this connection belongs to.
*/
void setSession(HttpSession session) {
this.session = session;
}
/**
* Returns the session that this connection belongs to.
*
* @return the session that this connection belongs to.
*/
public HttpSession getSession() {
return session;
}
void setContinuation(Continuation continuation) {
this.continuation = continuation;
}
private String waitForResponse() throws HttpBindTimeoutException {
if (continuation.suspend(session.getWait() * 1000)) {
String deliverable = (String) continuation.getObject();
// This will occur when the hold attribute of a session has been exceded.
this.isDelivered = true;
if (deliverable == null) {
throw new HttpBindTimeoutException();
}
else if(CONNECTION_CLOSED.equals(deliverable)) {
return null;
}
return deliverable;
}
this.isDelivered = true;
throw new HttpBindTimeoutException("Request " + requestId + " exceeded response time from " +
"server of " + session.getWait() + " seconds.");
}
}