aboutsummaryrefslogtreecommitdiff
path: root/src/com/kenai/jbosh/ApacheHTTPResponse.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/kenai/jbosh/ApacheHTTPResponse.java')
-rw-r--r--src/com/kenai/jbosh/ApacheHTTPResponse.java253
1 files changed, 253 insertions, 0 deletions
diff --git a/src/com/kenai/jbosh/ApacheHTTPResponse.java b/src/com/kenai/jbosh/ApacheHTTPResponse.java
new file mode 100644
index 0000000..9f6731f
--- /dev/null
+++ b/src/com/kenai/jbosh/ApacheHTTPResponse.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2009 Guenther Niess
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kenai.jbosh;
+
+import java.io.IOException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+final class ApacheHTTPResponse implements HTTPResponse {
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Constants:
+
+ /**
+ * Name of the accept encoding header.
+ */
+ private static final String ACCEPT_ENCODING = "Accept-Encoding";
+
+ /**
+ * Value to use for the ACCEPT_ENCODING header.
+ */
+ private static final String ACCEPT_ENCODING_VAL =
+ ZLIBCodec.getID() + ", " + GZIPCodec.getID();
+
+ /**
+ * Name of the character set to encode the body to/from.
+ */
+ private static final String CHARSET = "UTF-8";
+
+ /**
+ * Content type to use when transmitting the body data.
+ */
+ private static final String CONTENT_TYPE = "text/xml; charset=utf-8";
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Class variables:
+
+ /**
+ * Lock used for internal synchronization.
+ */
+ private final Lock lock = new ReentrantLock();
+
+ /**
+ * The execution state of an HTTP process.
+ */
+ private final HttpContext context;
+
+ /**
+ * HttpClient instance to use to communicate.
+ */
+ private final HttpClient client;
+
+ /**
+ * The HTTP POST request is sent to the server.
+ */
+ private final HttpPost post;
+
+ /**
+ * A flag which indicates if the transmission was already done.
+ */
+ private boolean sent;
+
+ /**
+ * Exception to throw when the response data is attempted to be accessed,
+ * or {@code null} if no exception should be thrown.
+ */
+ private BOSHException toThrow;
+
+ /**
+ * The response body which was received from the server or {@code null}
+ * if that has not yet happened.
+ */
+ private AbstractBody body;
+
+ /**
+ * The HTTP response status code.
+ */
+ private int statusCode;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Constructors:
+
+ /**
+ * Create and send a new request to the upstream connection manager,
+ * providing deferred access to the results to be returned.
+ *
+ * @param client client instance to use when sending the request
+ * @param cfg client configuration
+ * @param params connection manager parameters from the session creation
+ * response, or {@code null} if the session has not yet been established
+ * @param request body of the client request
+ */
+ ApacheHTTPResponse(
+ final HttpClient client,
+ final BOSHClientConfig cfg,
+ final CMSessionParams params,
+ final AbstractBody request) {
+ super();
+ this.client = client;
+ this.context = new BasicHttpContext();
+ this.post = new HttpPost(cfg.getURI().toString());
+ this.sent = false;
+
+ try {
+ String xml = request.toXML();
+ byte[] data = xml.getBytes(CHARSET);
+
+ String encoding = null;
+ if (cfg.isCompressionEnabled() && params != null) {
+ AttrAccept accept = params.getAccept();
+ if (accept != null) {
+ if (accept.isAccepted(ZLIBCodec.getID())) {
+ encoding = ZLIBCodec.getID();
+ data = ZLIBCodec.encode(data);
+ } else if (accept.isAccepted(GZIPCodec.getID())) {
+ encoding = GZIPCodec.getID();
+ data = GZIPCodec.encode(data);
+ }
+ }
+ }
+
+ ByteArrayEntity entity = new ByteArrayEntity(data);
+ entity.setContentType(CONTENT_TYPE);
+ if (encoding != null) {
+ entity.setContentEncoding(encoding);
+ }
+ post.setEntity(entity);
+ if (cfg.isCompressionEnabled()) {
+ post.setHeader(ACCEPT_ENCODING, ACCEPT_ENCODING_VAL);
+ }
+ } catch (Exception e) {
+ toThrow = new BOSHException("Could not generate request", e);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // HTTPResponse interface methods:
+
+ /**
+ * Abort the client transmission and response processing.
+ */
+ public void abort() {
+ if (post != null) {
+ post.abort();
+ toThrow = new BOSHException("HTTP request aborted");
+ }
+ }
+
+ /**
+ * Wait for and then return the response body.
+ *
+ * @return body of the response
+ * @throws InterruptedException if interrupted while awaiting the response
+ * @throws BOSHException on communication failure
+ */
+ public AbstractBody getBody() throws InterruptedException, BOSHException {
+ if (toThrow != null) {
+ throw(toThrow);
+ }
+ lock.lock();
+ try {
+ if (!sent) {
+ awaitResponse();
+ }
+ } finally {
+ lock.unlock();
+ }
+ return body;
+ }
+
+ /**
+ * Wait for and then return the response HTTP status code.
+ *
+ * @return HTTP status code of the response
+ * @throws InterruptedException if interrupted while awaiting the response
+ * @throws BOSHException on communication failure
+ */
+ public int getHTTPStatus() throws InterruptedException, BOSHException {
+ if (toThrow != null) {
+ throw(toThrow);
+ }
+ lock.lock();
+ try {
+ if (!sent) {
+ awaitResponse();
+ }
+ } finally {
+ lock.unlock();
+ }
+ return statusCode;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Package-private methods:
+
+ /**
+ * Await the response, storing the result in the instance variables of
+ * this class when they arrive.
+ *
+ * @throws InterruptedException if interrupted while awaiting the response
+ * @throws BOSHException on communication failure
+ */
+ private synchronized void awaitResponse() throws BOSHException {
+ HttpEntity entity = null;
+ try {
+ HttpResponse httpResp = client.execute(post, context);
+ entity = httpResp.getEntity();
+ byte[] data = EntityUtils.toByteArray(entity);
+ String encoding = entity.getContentEncoding() != null ?
+ entity.getContentEncoding().getValue() :
+ null;
+ if (ZLIBCodec.getID().equalsIgnoreCase(encoding)) {
+ data = ZLIBCodec.decode(data);
+ } else if (GZIPCodec.getID().equalsIgnoreCase(encoding)) {
+ data = GZIPCodec.decode(data);
+ }
+ body = StaticBody.fromString(new String(data, CHARSET));
+ statusCode = httpResp.getStatusLine().getStatusCode();
+ sent = true;
+ } catch (IOException iox) {
+ abort();
+ toThrow = new BOSHException("Could not obtain response", iox);
+ throw(toThrow);
+ } catch (RuntimeException ex) {
+ abort();
+ throw(ex);
+ }
+ }
+}