Home | History | Annotate | Download | only in patch
      1 diff --git com/kenai/jbosh/ApacheHTTPResponse.java com/kenai/jbosh/ApacheHTTPResponse.java
      2 new file mode 100644
      3 index 0000000..9f6731f
      4 --- /dev/null
      5 +++ com/kenai/jbosh/ApacheHTTPResponse.java
      6 @@ -0,0 +1,253 @@
      7 +/*
      8 + * Copyright 2009 Guenther Niess
      9 + *
     10 + * Licensed under the Apache License, Version 2.0 (the "License");
     11 + * you may not use this file except in compliance with the License.
     12 + * You may obtain a copy of the License at
     13 + *
     14 + *   http://www.apache.org/licenses/LICENSE-2.0
     15 + *
     16 + * Unless required by applicable law or agreed to in writing, software
     17 + * distributed under the License is distributed on an "AS IS" BASIS,
     18 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     19 + * See the License for the specific language governing permissions and
     20 + * limitations under the License.
     21 + */
     22 +
     23 +package com.kenai.jbosh;
     24 +
     25 +import java.io.IOException;
     26 +import java.util.concurrent.locks.Lock;
     27 +import java.util.concurrent.locks.ReentrantLock;
     28 +
     29 +import org.apache.http.HttpEntity;
     30 +import org.apache.http.HttpResponse;
     31 +import org.apache.http.client.HttpClient;
     32 +import org.apache.http.client.methods.HttpPost;
     33 +import org.apache.http.entity.ByteArrayEntity;
     34 +
     35 +import org.apache.http.protocol.BasicHttpContext;
     36 +import org.apache.http.protocol.HttpContext;
     37 +import org.apache.http.util.EntityUtils;
     38 +
     39 +final class ApacheHTTPResponse implements HTTPResponse {
     40 +
     41 +    ///////////////////////////////////////////////////////////////////////////
     42 +    // Constants:
     43 +
     44 +    /**
     45 +     * Name of the accept encoding header.
     46 +     */
     47 +    private static final String ACCEPT_ENCODING = "Accept-Encoding";
     48 +
     49 +    /**
     50 +     * Value to use for the ACCEPT_ENCODING header.
     51 +     */
     52 +    private static final String ACCEPT_ENCODING_VAL =
     53 +            ZLIBCodec.getID() + ", " + GZIPCodec.getID();
     54 +
     55 +    /**
     56 +     * Name of the character set to encode the body to/from.
     57 +     */
     58 +    private static final String CHARSET = "UTF-8";
     59 +
     60 +    /**
     61 +     * Content type to use when transmitting the body data.
     62 +     */
     63 +    private static final String CONTENT_TYPE = "text/xml; charset=utf-8";
     64 +
     65 +    ///////////////////////////////////////////////////////////////////////////
     66 +    // Class variables:
     67 +
     68 +    /**
     69 +     * Lock used for internal synchronization.
     70 +     */
     71 +    private final Lock lock = new ReentrantLock();
     72 +
     73 +    /**
     74 +     * The execution state of an HTTP process.
     75 +     */
     76 +    private final HttpContext context;
     77 +
     78 +    /**
     79 +     * HttpClient instance to use to communicate.
     80 +     */
     81 +    private final HttpClient client;
     82 +
     83 +    /**
     84 +     * The HTTP POST request is sent to the server.
     85 +     */
     86 +    private final HttpPost post;
     87 +
     88 +    /**
     89 +     * A flag which indicates if the transmission was already done.
     90 +     */
     91 +    private boolean sent;
     92 +
     93 +    /**
     94 +     * Exception to throw when the response data is attempted to be accessed,
     95 +     * or {@code null} if no exception should be thrown.
     96 +     */
     97 +    private BOSHException toThrow;
     98 +
     99 +    /**
    100 +     * The response body which was received from the server or {@code null}
    101 +     * if that has not yet happened.
    102 +     */
    103 +    private AbstractBody body;
    104 +
    105 +    /**
    106 +     * The HTTP response status code.
    107 +     */
    108 +    private int statusCode;
    109 +
    110 +    ///////////////////////////////////////////////////////////////////////////
    111 +    // Constructors:
    112 +
    113 +    /**
    114 +     * Create and send a new request to the upstream connection manager,
    115 +     * providing deferred access to the results to be returned.
    116 +     *
    117 +     * @param client client instance to use when sending the request
    118 +     * @param cfg client configuration
    119 +     * @param params connection manager parameters from the session creation
    120 +     *  response, or {@code null} if the session has not yet been established
    121 +     * @param request body of the client request
    122 +     */
    123 +    ApacheHTTPResponse(
    124 +            final HttpClient client,
    125 +            final BOSHClientConfig cfg,
    126 +            final CMSessionParams params,
    127 +            final AbstractBody request) {
    128 +        super();
    129 +        this.client = client;
    130 +        this.context = new BasicHttpContext();
    131 +        this.post = new HttpPost(cfg.getURI().toString());
    132 +        this.sent = false;
    133 +
    134 +        try {
    135 +            String xml = request.toXML();
    136 +            byte[] data = xml.getBytes(CHARSET);
    137 +
    138 +            String encoding = null;
    139 +            if (cfg.isCompressionEnabled() && params != null) {
    140 +                AttrAccept accept = params.getAccept();
    141 +                if (accept != null) {
    142 +                    if (accept.isAccepted(ZLIBCodec.getID())) {
    143 +                        encoding = ZLIBCodec.getID();
    144 +                        data = ZLIBCodec.encode(data);
    145 +                    } else if (accept.isAccepted(GZIPCodec.getID())) {
    146 +                        encoding = GZIPCodec.getID();
    147 +                        data = GZIPCodec.encode(data);
    148 +                    }
    149 +                }
    150 +            }
    151 +
    152 +            ByteArrayEntity entity = new ByteArrayEntity(data);
    153 +            entity.setContentType(CONTENT_TYPE);
    154 +            if (encoding != null) {
    155 +                entity.setContentEncoding(encoding);
    156 +            }
    157 +            post.setEntity(entity);
    158 +            if (cfg.isCompressionEnabled()) {
    159 +                post.setHeader(ACCEPT_ENCODING, ACCEPT_ENCODING_VAL);
    160 +            }
    161 +        } catch (Exception e) {
    162 +            toThrow = new BOSHException("Could not generate request", e);
    163 +        }
    164 +    }
    165 +
    166 +    ///////////////////////////////////////////////////////////////////////////
    167 +    // HTTPResponse interface methods:
    168 +
    169 +    /**
    170 +     * Abort the client transmission and response processing.
    171 +     */
    172 +    public void abort() {
    173 +        if (post != null) {
    174 +            post.abort();
    175 +            toThrow = new BOSHException("HTTP request aborted");
    176 +        }
    177 +    }
    178 +
    179 +    /**
    180 +     * Wait for and then return the response body.
    181 +     *
    182 +     * @return body of the response
    183 +     * @throws InterruptedException if interrupted while awaiting the response
    184 +     * @throws BOSHException on communication failure
    185 +     */
    186 +    public AbstractBody getBody() throws InterruptedException, BOSHException {
    187 +        if (toThrow != null) {
    188 +            throw(toThrow);
    189 +        }
    190 +        lock.lock();
    191 +        try {
    192 +            if (!sent) {
    193 +                awaitResponse();
    194 +            }
    195 +        } finally {
    196 +            lock.unlock();
    197 +        }
    198 +        return body;
    199 +    }
    200 +
    201 +    /**
    202 +     * Wait for and then return the response HTTP status code.
    203 +     *
    204 +     * @return HTTP status code of the response
    205 +     * @throws InterruptedException if interrupted while awaiting the response
    206 +     * @throws BOSHException on communication failure
    207 +     */
    208 +    public int getHTTPStatus() throws InterruptedException, BOSHException {
    209 +        if (toThrow != null) {
    210 +            throw(toThrow);
    211 +        }
    212 +        lock.lock();
    213 +        try {
    214 +            if (!sent) {
    215 +                awaitResponse();
    216 +            }
    217 +        } finally {
    218 +            lock.unlock();
    219 +        }
    220 +        return statusCode;
    221 +    }
    222 +
    223 +    ///////////////////////////////////////////////////////////////////////////
    224 +    // Package-private methods:
    225 +
    226 +    /**
    227 +     * Await the response, storing the result in the instance variables of
    228 +     * this class when they arrive.
    229 +     *
    230 +     * @throws InterruptedException if interrupted while awaiting the response
    231 +     * @throws BOSHException on communication failure
    232 +     */
    233 +    private synchronized void awaitResponse() throws BOSHException {
    234 +        HttpEntity entity = null;
    235 +        try {
    236 +            HttpResponse httpResp = client.execute(post, context);
    237 +            entity = httpResp.getEntity();
    238 +            byte[] data = EntityUtils.toByteArray(entity);
    239 +            String encoding = entity.getContentEncoding() != null ?
    240 +                    entity.getContentEncoding().getValue() :
    241 +                    null;
    242 +            if (ZLIBCodec.getID().equalsIgnoreCase(encoding)) {
    243 +                data = ZLIBCodec.decode(data);
    244 +            } else if (GZIPCodec.getID().equalsIgnoreCase(encoding)) {
    245 +                data = GZIPCodec.decode(data);
    246 +            }
    247 +            body = StaticBody.fromString(new String(data, CHARSET));
    248 +            statusCode = httpResp.getStatusLine().getStatusCode();
    249 +            sent = true;
    250 +        } catch (IOException iox) {
    251 +            abort();
    252 +            toThrow = new BOSHException("Could not obtain response", iox);
    253 +            throw(toThrow);
    254 +        } catch (RuntimeException ex) {
    255 +            abort();
    256 +            throw(ex);
    257 +        }
    258 +    }
    259 +}
    260 diff --git com/kenai/jbosh/ApacheHTTPSender.java com/kenai/jbosh/ApacheHTTPSender.java
    261 new file mode 100644
    262 index 0000000..2abb4ee
    263 --- /dev/null
    264 +++ com/kenai/jbosh/ApacheHTTPSender.java
    265 @@ -0,0 +1,156 @@
    266 +/*
    267 + * Copyright 2009 Guenther Niess
    268 + *
    269 + * Licensed under the Apache License, Version 2.0 (the "License");
    270 + * you may not use this file except in compliance with the License.
    271 + * You may obtain a copy of the License at
    272 + *
    273 + *   http://www.apache.org/licenses/LICENSE-2.0
    274 + *
    275 + * Unless required by applicable law or agreed to in writing, software
    276 + * distributed under the License is distributed on an "AS IS" BASIS,
    277 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    278 + * See the License for the specific language governing permissions and
    279 + * limitations under the License.
    280 + */
    281 +
    282 +package com.kenai.jbosh;
    283 +
    284 +import java.util.concurrent.locks.Lock;
    285 +import java.util.concurrent.locks.ReentrantLock;
    286 +
    287 +import org.apache.http.HttpHost;
    288 +import org.apache.http.HttpVersion;
    289 +import org.apache.http.client.HttpClient;
    290 +import org.apache.http.conn.ClientConnectionManager;
    291 +import org.apache.http.conn.params.ConnManagerParams;
    292 +import org.apache.http.conn.params.ConnRoutePNames;
    293 +import org.apache.http.conn.scheme.PlainSocketFactory;
    294 +import org.apache.http.conn.scheme.Scheme;
    295 +import org.apache.http.conn.scheme.SchemeRegistry;
    296 +import org.apache.http.conn.ssl.SSLSocketFactory;
    297 +import org.apache.http.impl.client.DefaultHttpClient;
    298 +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
    299 +import org.apache.http.params.BasicHttpParams;
    300 +import org.apache.http.params.HttpParams;
    301 +import org.apache.http.params.HttpProtocolParams;
    302 +
    303 +/**
    304 + * Implementation of the {@code HTTPSender} interface which uses the
    305 + * Apache HttpClient API to send messages to the connection manager.
    306 + */
    307 +final class ApacheHTTPSender implements HTTPSender {
    308 +
    309 +    /**
    310 +     * Lock used for internal synchronization.
    311 +     */
    312 +    private final Lock lock = new ReentrantLock();
    313 +
    314 +    /**
    315 +     * Session configuration.
    316 +     */
    317 +    private BOSHClientConfig cfg;
    318 +
    319 +    /**
    320 +     * HttpClient instance to use to communicate.
    321 +     */
    322 +    private HttpClient httpClient;
    323 +
    324 +    ///////////////////////////////////////////////////////////////////////////
    325 +    // Constructors:
    326 +
    327 +    /**
    328 +     * Prevent construction apart from our package.
    329 +     */
    330 +    ApacheHTTPSender() {
    331 +        // Load Apache HTTP client class
    332 +        HttpClient.class.getName();
    333 +    }
    334 +
    335 +    ///////////////////////////////////////////////////////////////////////////
    336 +    // HTTPSender interface methods:
    337 +
    338 +    /**
    339 +     * {@inheritDoc}
    340 +     */
    341 +    public void init(final BOSHClientConfig session) {
    342 +        lock.lock();
    343 +        try {
    344 +            cfg = session;
    345 +            httpClient = initHttpClient(session);
    346 +        } finally {
    347 +            lock.unlock();
    348 +        }
    349 +    }
    350 +
    351 +    /**
    352 +     * {@inheritDoc}
    353 +     */
    354 +    public void destroy() {
    355 +        lock.lock();
    356 +        try {
    357 +            if (httpClient != null) {
    358 +                httpClient.getConnectionManager().shutdown();
    359 +            }
    360 +        } finally {
    361 +            cfg = null;
    362 +            httpClient = null;
    363 +            lock.unlock();
    364 +        }
    365 +    }
    366 +
    367 +    /**
    368 +     * {@inheritDoc}
    369 +     */
    370 +    public HTTPResponse send(
    371 +            final CMSessionParams params,
    372 +            final AbstractBody body) {
    373 +        HttpClient mClient;
    374 +        BOSHClientConfig mCfg;
    375 +        lock.lock();
    376 +        try {
    377 +            if (httpClient == null) {
    378 +                httpClient = initHttpClient(cfg);
    379 +            }
    380 +            mClient = httpClient;
    381 +            mCfg = cfg;
    382 +        } finally {
    383 +            lock.unlock();
    384 +        }
    385 +        return new ApacheHTTPResponse(mClient, mCfg, params, body);
    386 +    }
    387 +
    388 +    ///////////////////////////////////////////////////////////////////////////
    389 +    // Package-private methods:
    390 +
    391 +    private synchronized HttpClient initHttpClient(final BOSHClientConfig config) {
    392 +        // Create and initialize HTTP parameters
    393 +        HttpParams params = new BasicHttpParams();
    394 +        ConnManagerParams.setMaxTotalConnections(params, 100);
    395 +        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    396 +        HttpProtocolParams.setUseExpectContinue(params, false);
    397 +        if (config != null &&
    398 +                config.getProxyHost() != null &&
    399 +                config.getProxyPort() != 0) {
    400 +            HttpHost proxy = new HttpHost(
    401 +                    config.getProxyHost(),
    402 +                    config.getProxyPort());
    403 +            params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
    404 +        }
    405 +
    406 +        // Create and initialize scheme registry 
    407 +        SchemeRegistry schemeRegistry = new SchemeRegistry();
    408 +        schemeRegistry.register(
    409 +                new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    410 +            SSLSocketFactory sslFactory = SSLSocketFactory.getSocketFactory();
    411 +            sslFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    412 +            schemeRegistry.register(
    413 +                    new Scheme("https", sslFactory, 443));
    414 +
    415 +        // Create an HttpClient with the ThreadSafeClientConnManager.
    416 +        // This connection manager must be used if more than one thread will
    417 +        // be using the HttpClient.
    418 +        ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
    419 +        return new DefaultHttpClient(cm, params);
    420 +    }
    421 +}
    422 diff --git com/kenai/jbosh/BodyParserXmlPull.java com/kenai/jbosh/BodyParserXmlPull.java
    423 index cc95236..5f23b06 100644
    424 --- com/kenai/jbosh/BodyParserXmlPull.java
    425 +++ com/kenai/jbosh/BodyParserXmlPull.java
    426 @@ -22,7 +22,6 @@ import java.lang.ref.SoftReference;
    427  import java.util.logging.Level;
    428  import java.util.logging.Logger;
    429  import javax.xml.XMLConstants;
    430 -import javax.xml.namespace.QName;
    431  import org.xmlpull.v1.XmlPullParser;
    432  import org.xmlpull.v1.XmlPullParserException;
    433  import org.xmlpull.v1.XmlPullParserFactory;
    434 diff --git com/kenai/jbosh/BodyQName.java com/kenai/jbosh/BodyQName.java
    435 index fc7ab0c..83acdf1 100644
    436 --- com/kenai/jbosh/BodyQName.java
    437 +++ com/kenai/jbosh/BodyQName.java
    438 @@ -16,8 +16,6 @@
    439  
    440  package com.kenai.jbosh;
    441  
    442 -import javax.xml.namespace.QName;
    443 -
    444  /**
    445   * Qualified name of an attribute of the wrapper element.  This class is
    446   * analagous to the {@code javax.xml.namespace.QName} class.
    447