Jean-Francois Arcand avatar Jean-Francois Arcand committed 84b593d

First drop

Comments (0)

Files changed (17)

+*.class
+*~
+.*.swp
+.*.swo
+.loadpath
+.buildpath
+.classpath
+.project
+.settings
+.idea
+*.iml
+*.ipr
+*.iws
+nbproject
+.DS_Store
+target
+test-output
+/META-INF/MANIFEST.MF
+An AsyncHttpClient transport for the Jersey Client API.
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ Copyright (c) 2011 Sonatype, Inc.
+ ~ All rights reserved. This program and the accompanying materials
+ ~ are made available under the terms of the Eclipse Public License v1.0
+ ~ and Apache License v2.0 which accompanies this distribution.
+ ~ The Eclipse Public License is available at
+ ~   http://www.eclipse.org/legal/epl-v10.html
+ ~ The Apache License v2.0 is available at
+ ~   http://www.apache.org/licenses/LICENSE-2.0.html
+ ~ You may elect to redistribute this code under either of these licenses.
+-->
+        
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.sonatype.oss</groupId>
+        <artifactId>oss-parent</artifactId>
+        <version>5</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.sonatype</groupId>
+    <artifactId>jersey-ahc-client</artifactId>
+    <name>jersey-ahc-client</name>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <description>
+        Async Http Client implementation for the Jersey Client API.
+    </description>
+    <url>http://github.com/AsyncHttpClient/async-http-client</url>
+    <scm>
+        <connection>scm:git@github.com:sonatype/jersey-ahc-client.git</connection>
+        <url>https://github.com/sonatype/jersey-ahc-client</url>
+        <developerConnection>scm:git@github.com:sonatype/jersey-ahc-client.git</developerConnection>
+    </scm>
+    <prerequisites>
+        <maven>2.0.9</maven>
+    </prerequisites>
+    <developers>
+        <developer>
+            <id>jfarcand</id>
+            <name>Jeanfrancois Arcand</name>
+            <email>jfarcand@apache.org</email>
+        </developer>
+    </developers>
+    <licenses>
+        <license>
+            <name>Apache License 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    <dependencies>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning</groupId>
+            <artifactId>async-http-client</artifactId>
+            <version>1.6.2-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <version>2.2.3-1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>0.9.26</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.grizzly</groupId>
+            <artifactId>grizzly-servlet-webserver</artifactId>
+            <version>1.9.18-i</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.8.2</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <extensions>
+            <!-- Enabling the use of SSH -->
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh-external</artifactId>
+                <version>1.0-beta-6</version>
+            </extension>
+        </extensions>
+        <defaultGoal>install</defaultGoal>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>animal-sniffer-maven-plugin</artifactId>
+                <version>1.6</version>
+                <configuration>
+                    <signature>
+                        <groupId>org.codehaus.mojo.signature</groupId>
+                        <artifactId>java15</artifactId>
+                        <version>1.0</version>
+                    </signature>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>check-java-1.5-compat</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.0.1</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <manifestLocation>META-INF</manifestLocation>
+                    <instructions>
+                        <Bundle-Vendor>Sonatype</Bundle-Vendor>
+                        <Import-Package>
+                            com.sun.jersey.*;resolution:=optional,
+                            *
+                        </Import-Package>
+                        <Export-Package>
+                            com.ning.http.*;version="1.6.1"
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>osgi-bundle</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>bundle</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>1.0-beta-1</version>
+                <executions>
+                    <execution>
+                        <id>enforce-versions</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireMavenVersion>
+                                    <version>2.0.9</version>
+                                </requireMavenVersion>
+                                <requireJavaVersion>
+                                    <version>1.5</version>
+                                </requireJavaVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                    <encoding>UTF-8</encoding>
+                    <maxmem>1024m</maxmem>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.4.3</version>
+                <configuration>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.3.1</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.1.2</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.7</version>
+                <configuration>
+                    <source>1.6</source>
+                    <encoding>UTF-8</encoding>
+                    <maxmemory>1g</maxmemory>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>1.2.1</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>shaded</shadedClassifierName>
+                            <artifactSet>
+                                <excludes>
+                                    <exclude>commons-codec:commons-codec</exclude>
+                                    <exclude>commons-lang:commons-lang</exclude>
+                                    <exclude>commons-logging:commons-logging</exclude>
+                                    <exclude>junit:junit</exclude>
+                                    <exclude>log4j:log4j</exclude>
+                                    <exclude>commons-httpclient:commons-httpclient</exclude>
+                                </excludes>
+                            </artifactSet>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer"/>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>release-sign-artifacts</id>
+            <activation>
+                <property>
+                    <name>performRelease</name>
+                    <value>true</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>offline-testing</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <groups>standalone</groups>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>online-testing</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <groups>standalone, online</groups>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+    <distributionManagement>
+        <repository>
+            <id>sonatype-nexus-staging</id>
+            <name>Sonatype Release</name>
+            <url>http://oss.sonatype.org/service/local/staging/deploy/maven2</url>
+        </repository>
+        <snapshotRepository>
+            <id>sonatype-nexus-snapshots</id>
+            <name>sonatype-nexus-snapshots</name>
+            <url>${distMgmtSnapshotsUrl}</url>
+        </snapshotRepository>
+    </distributionManagement>
+    <repositories>
+        <repository>
+            <id>repository.jboss.org</id>
+            <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+    <properties>
+        <distMgmtSnapshotsUrl>http://oss.sonatype.org/content/repositories/snapshots</distMgmtSnapshotsUrl>
+    </properties>
+
+</project>

src/main/java/org/sonatype/jersey/client/ahc/AhcClientHandler.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+package org.sonatype.jersey.client.ahc;
+
+import com.ning.http.client.AsyncHttpClient;
+import com.ning.http.client.Cookie;
+import com.ning.http.client.FluentCaseInsensitiveStringsMap;
+import com.ning.http.client.RequestBuilder;
+import com.ning.http.client.Response;
+import com.sun.jersey.api.client.ClientHandler;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
+import org.sonatype.jersey.client.ahc.config.AhcConfig;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+import com.sun.jersey.core.header.InBoundHeaders;
+import com.sun.jersey.spi.MessageBodyWorkers;
+
+import javax.ws.rs.core.Context;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A root handler with Sonatype AsyncHttpClient acting as a backend.
+ * <p/>
+ * Client operations are thread safe, the HTTP connection may
+ * be shared between different threads.
+ * <p/>
+ * If a response entity is obtained that is an instance of {@link java.io.Closeable}
+ * then the instance MUST be closed after processing the entity to release
+ * connection-based resources.
+ * <p/>
+ * If a {@link ClientResponse} is obtained and an entity is not read from the
+ * response then {@link ClientResponse#close() } MUST be called after processing
+ * the response to release connection-based resources.
+ * <p/>
+ * The following methods are currently supported: HEAD, GET, POST, PUT, DELETE, TRACE
+ * OPTIONS as well as custom methods.
+ * <p/>
+ *
+ * @author Jeanfrancois Arcand
+ */
+public final class AhcClientHandler implements ClientHandler {
+
+    private final AsyncHttpClient client;
+
+    private final AhcConfig config;
+
+    private final AhcRequestWriter requestWriter = new AhcRequestWriter();
+
+    private List<Cookie> cookies = new ArrayList<Cookie>();
+
+    @Context
+    private MessageBodyWorkers workers;
+
+    /**
+     * Create a new root handler with an {@link AsyncHttpClient}.
+     *
+     * @param client the {@link AsyncHttpClient}.
+     */
+    public AhcClientHandler(AsyncHttpClient client) {
+        this(client, new DefaultAhcConfig());
+    }
+
+    /**
+     * Create a new root handler with an {@link AsyncHttpClient}.
+     *
+     * @param client the {@link AsyncHttpClient}.
+     * @param config the client configuration.
+     */
+    public AhcClientHandler(AsyncHttpClient client, AhcConfig config) {
+        this.client = client;
+        this.config = config;
+    }
+
+    /**
+     * Get the client config.
+     *
+     * @return the client config.
+     */
+    public AhcConfig getConfig() {
+        return config;
+    }
+
+    /**
+     * Get the {@link AsyncHttpClient}.
+     *
+     * @return the {@link AsyncHttpClient}.
+     */
+    public AsyncHttpClient getHttpClient() {
+        return client;
+    }
+
+    /**
+     * Translate the {@link ClientRequest} into a AsyncHttpClient request, and execute it.
+     * @param cr the HTTP request.
+     * @return the {@link ClientResponse}
+     * @throws ClientHandlerException
+     */
+    @Override
+    public ClientResponse handle(final ClientRequest cr)
+            throws ClientHandlerException {
+
+        try {
+            final RequestBuilder requestBuilder = getRequestBuilder(cr);
+            handleCookie(requestBuilder);
+            requestWriter.configureRequest(requestBuilder, cr, allowBody(cr.getMethod()));
+
+            final Response response = client.executeRequest(requestBuilder.build()).get();
+
+            cookies = response.getCookies();
+
+            ClientResponse r = new ClientResponse(response.getStatusCode(),
+                    getInBoundHeaders(response),
+                    response.getResponseBodyAsStream(),
+                    workers);
+            if (!r.hasEntity()) {
+                r.bufferEntity();
+                r.close();
+            }
+            return r;
+        } catch (Exception e) {
+            throw new ClientHandlerException(e);
+        }
+    }
+
+    /**
+     * Check if a body needs to be constructed based on a method's name.
+     * @param method An HTTP method
+     * @return true if s body can be allowed.
+     */
+    private boolean allowBody(String method) {
+        if ( method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("OPTIONS")
+                    && method.equalsIgnoreCase("TRACE")
+                    && method.equalsIgnoreCase("HEAD")) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Return the {@link RequestBuilder} based on a method 
+     * @param cr the HTTP request.
+     * @return {@link RequestBuilder}
+     */
+    private RequestBuilder getRequestBuilder(ClientRequest cr) {
+        final String strMethod = cr.getMethod();
+        final String uri = cr.getURI().toString();
+
+        if (strMethod.equals("GET")) {
+            return new RequestBuilder("GET").setUrl(uri);
+        } else if (strMethod.equals("POST")) {
+            return new RequestBuilder("POST").setUrl(uri);
+        } else if (strMethod.equals("PUT")) {
+            return new RequestBuilder("PUT").setUrl(uri);
+        } else if (strMethod.equals("DELETE")) {
+            return new RequestBuilder("DELETE").setUrl(uri);
+        } else if (strMethod.equals("HEAD")) {
+            return new RequestBuilder("HEAD").setUrl(uri);
+        } else if (strMethod.equals("OPTIONS")) {
+            return new RequestBuilder("OPTIONS").setUrl(uri);
+        } else {
+            return new RequestBuilder(strMethod).setUrl(uri);
+        }
+    }
+
+    private InBoundHeaders getInBoundHeaders(Response response) {
+        InBoundHeaders headers = new InBoundHeaders();
+        FluentCaseInsensitiveStringsMap respHeaders = response.getHeaders();
+        for (Map.Entry<String, List<String>> header : respHeaders) {
+            headers.put(header.getKey(), header.getValue());
+        }
+        return headers;
+    }
+
+    /**
+     * Return the instance of {@link com.sun.jersey.api.client.RequestWriter}. This instance will be injected
+     * within Jersey so it cannot be null.
+     * @return the instance of {@link com.sun.jersey.api.client.RequestWriter}.
+     */
+    public AhcRequestWriter getAhcRequestWriter(){
+        return requestWriter;
+    }
+
+    private void handleCookie(RequestBuilder requestBuilder) {
+        for (Cookie c : cookies) {
+            requestBuilder.addCookie(c);
+        }
+    }
+    
+}

src/main/java/org/sonatype/jersey/client/ahc/AhcHttpClient.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+package org.sonatype.jersey.client.ahc;
+
+import com.ning.http.client.AsyncHttpClient;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.config.ClientConfig;
+import org.sonatype.jersey.client.ahc.config.AhcConfig;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+import com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory;
+
+/**
+ * A {@link Client} that utilizes the AsyncHttpClient to send and receive
+ * HTTP request and responses.
+ * <p>
+ * If an {@link AhcClientHandler} is not explicitly passed as a
+ * constructor or method parameter then by default an instance is created with
+ * an {@link AsyncHttpClient} constructed
+ * <p>
+ * <p>
+ * If a response entity is obtained that is an instance of
+ * {@link java.io.Closeable}
+ * then the instance MUST be closed after processing the entity to release
+ * connection-based resources.
+ * <p>
+ * If a {@link com.sun.jersey.api.client.ClientResponse} is obtained and an
+ * entity is not read from the response then
+ * {@link com.sun.jersey.api.client.ClientResponse#close() } MUST be called
+ * after processing the response to release connection-based resources.
+ *
+ * @author Jeanfrancois Arcand
+ */
+public class AhcHttpClient extends Client {
+
+    private AhcClientHandler clientHandler;
+    
+    /**
+     * Create a new client instance.
+     *
+     */
+    public AhcHttpClient() {
+        this(createDefaultClientHander(new DefaultAhcConfig()));
+    }
+
+    /**
+     * Create a new client instance.
+     *
+     * @param root the root client handler for dispatching a request and
+     *        returning a response.
+     */
+    public AhcHttpClient(AhcClientHandler root) {
+        this(root, null);
+    }
+
+    /**
+     * Create a new instance with a client configuration and a
+     * component provider.
+     *
+     * @param root the root client handler for dispatching a request and
+     *        returning a response.
+     * @param config the client configuration.
+     * @param provider the IoC component provider factory.
+     * @deprecated the config parameter is no longer utilized and instead
+     *             the config obtained from the {@link AhcClientHandler#getConfig() }
+     *             is utilized instead.
+     */
+    @Deprecated
+    public AhcHttpClient(AhcClientHandler root, ClientConfig config,
+            IoCComponentProviderFactory provider) {
+        this(root, provider);
+    }
+
+    /**
+     * Create a new instance with a client configuration and a
+     * component provider.
+     *
+     * @param root the root client handler for dispatching a request and
+     *        returning a response.
+     * @param provider the IoC component provider factory.
+     */
+    public AhcHttpClient(AhcClientHandler root,
+            IoCComponentProviderFactory provider) {
+        super(root, root.getConfig(), provider);
+
+        this.clientHandler = root;
+        inject(this.clientHandler.getAhcRequestWriter());
+    }
+
+    /**
+     * Get the AsyncHttpClient client handler.
+     * 
+     * @return the AsyncHttpClient client handler.
+     */
+    public AhcClientHandler getClientHandler() {
+        return clientHandler;
+    }
+
+    /**
+     * Create a default client.
+     *
+     * @return a default client.
+     */
+    public static AhcHttpClient create() {
+        return create(new DefaultAhcConfig());
+    }
+
+    /**
+     * Create a default client with client configuration.
+     *
+     * @param cc the client configuration.
+     * @return a default client.
+     */
+    public static AhcHttpClient create(ClientConfig cc) {
+        return create(cc, null);
+    }
+
+    /**
+     * Create a default client with client configuration and component provider.
+     *
+     * @param cc the client configuration.
+     * @param provider the IoC component provider factory.
+     * @return a default client.
+     */
+    public static AhcHttpClient create(ClientConfig cc, IoCComponentProviderFactory provider) {
+        return new AhcHttpClient(createDefaultClientHander(cc), provider);
+    }
+
+    /**
+     * Create a default AsyncHttpClient client handler.
+     *
+     * @return a default AsyncHttpClient client handler.
+     */
+    private static AhcClientHandler createDefaultClientHander(ClientConfig cc) {
+
+        if (AhcConfig.class.isAssignableFrom(cc.getClass()) || DefaultAhcConfig.class.isAssignableFrom(cc.getClass())) {
+            AhcConfig c = AhcConfig.class.cast(cc);
+            return new AhcClientHandler(new AsyncHttpClient(c.getAsyncHttpClientConfigBuilder().build()), c);
+        } else {
+            throw new IllegalStateException("Client Config Type not supported");
+        }
+    }
+
+    @Override
+    public void setFollowRedirects(Boolean redirect) {
+        clientHandler.getConfig().getAsyncHttpClientConfigBuilder().setFollowRedirects(redirect);
+    }
+
+    @Override
+    public void setReadTimeout(Integer interval) {
+        clientHandler.getConfig().getAsyncHttpClientConfigBuilder().setRequestTimeoutInMs(interval);
+    }
+
+    @Override
+    public void setConnectTimeout(Integer interval) {
+        clientHandler.getConfig().getAsyncHttpClientConfigBuilder().setConnectionTimeoutInMs(interval);
+    }
+}

src/main/java/org/sonatype/jersey/client/ahc/AhcRequestWriter.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+package org.sonatype.jersey.client.ahc;
+
+import com.ning.http.client.PerRequestConfig;
+import com.ning.http.client.Request;
+import com.ning.http.client.RequestBuilder;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.CommittingOutputStream;
+import com.sun.jersey.api.client.RequestWriter;
+import org.sonatype.jersey.client.ahc.config.AhcConfig;
+
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An implementation of {@link RequestWriter} that also configure the AHC {@link RequestBuilder}
+ *
+ * @author Jeanfrancois Arcand
+ */
+public class AhcRequestWriter extends RequestWriter {
+
+    public void configureRequest(final RequestBuilder requestBuilder, final ClientRequest cr, boolean needsBody) {
+        final Map<String, Object> props = cr.getProperties();
+
+        // Set the read timeout
+        final Integer readTimeout = (Integer) props.get(AhcConfig.PROPERTY_READ_TIMEOUT);
+        if (readTimeout != null) {
+            PerRequestConfig c = new PerRequestConfig();
+            c.setRequestTimeoutInMs(readTimeout);
+            requestBuilder.setPerRequestConfig(c);
+        }
+        configureHeaders(cr.getMetadata(), requestBuilder);
+        if (cr.getEntity() != null && needsBody) {
+            final RequestEntityWriter re = getRequestEntityWriter(cr);
+
+            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            try {
+                re.writeRequestEntity(new CommittingOutputStream(baos) {
+                    @Override
+                    protected void commit() throws IOException {
+                        configureHeaders(cr.getMetadata(), requestBuilder);
+                    }
+                });
+            } catch (IOException ex) {
+                throw new ClientHandlerException(ex);
+            }
+
+            final byte[] content = baos.toByteArray();
+            requestBuilder.setBody(new Request.EntityWriter() {
+                @Override
+                public void writeEntity(OutputStream out) throws IOException {
+                    out.write(content);
+                }
+            });
+        }
+    }
+
+    private void configureHeaders(MultivaluedMap<String, Object> metadata, RequestBuilder requestBuilder) {
+        for (Map.Entry<String, List<Object>> e : metadata.entrySet()) {
+            List<Object> vs = e.getValue();
+            for (Object o : vs) {
+                requestBuilder.addHeader(e.getKey(), headerValueToString(o));
+            }
+        }
+    }
+}

src/main/java/org/sonatype/jersey/client/ahc/config/AhcConfig.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+package org.sonatype.jersey.client.ahc.config;
+
+import com.ning.http.client.AsyncHttpClientConfig;
+import com.sun.jersey.api.client.config.ClientConfig;
+
+public interface AhcConfig extends ClientConfig {
+
+    /**
+     * Get the {@link com.ning.http.client.AsyncHttpClientConfig.Builder} config object. Credentials may be set on the it.
+     * <p>
+     * @return the {@link com.ning.http.client.AsyncHttpClientConfig.Builder}
+     */
+    public AsyncHttpClientConfig.Builder getAsyncHttpClientConfigBuilder();
+}

src/main/java/org/sonatype/jersey/client/ahc/config/DefaultAhcConfig.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+package org.sonatype.jersey.client.ahc.config;
+
+import com.ning.http.client.AsyncHttpClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+public class DefaultAhcConfig extends DefaultClientConfig implements AhcConfig{
+
+    private AsyncHttpClientConfig.Builder config;
+
+    public AsyncHttpClientConfig.Builder getAsyncHttpClientConfigBuilder() {
+
+        if (config == null) {
+            config = new AsyncHttpClientConfig.Builder();
+        }
+
+        return config;
+    }
+}

src/test/java/org/sonatype/jersey/client/ahc/tests/AbstractGrizzlyServerTester.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+package org.sonatype.jersey.client.ahc.tests;
+
+import com.sun.grizzly.http.SelectorThread;
+import com.sun.grizzly.tcp.Adapter;
+import com.sun.jersey.api.container.ContainerFactory;
+import com.sun.jersey.api.container.grizzly.GrizzlyServerFactory;
+import com.sun.jersey.api.core.ResourceConfig;
+import junit.framework.TestCase;
+
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ *
+ * @author Paul.Sandoz@Sun.Com
+ */
+public abstract class AbstractGrizzlyServerTester extends TestCase {
+    public static final String CONTEXT = "";
+
+    private SelectorThread selectorThread;
+
+    private int port = getEnvVariable("JERSEY_HTTP_PORT", 9997);
+    
+    private static int getEnvVariable(final String varName, int defaultValue) {
+        if (null == varName) {
+            return defaultValue;
+        }
+        String varValue = System.getenv(varName);
+        if (null != varValue) {
+            try {
+                return Integer.parseInt(varValue);
+            }catch (NumberFormatException e) {
+                // will return default value bellow
+            }
+        }
+        return defaultValue;
+    }
+
+    public AbstractGrizzlyServerTester(String name) {
+        super(name);
+    }
+    
+    public UriBuilder getUri() {
+        return UriBuilder.fromUri("http://localhost").port(port).path(CONTEXT);
+    }
+    
+    public void startServer(Class... resources) {
+        start(ContainerFactory.createContainer(Adapter.class, resources));
+    }
+    
+    public void startServer(ResourceConfig config) {
+        start(ContainerFactory.createContainer(Adapter.class, config));
+    }
+    
+    private void start(Adapter adapter) {
+        if (selectorThread != null && selectorThread.isRunning()){
+            stopServer();
+        }
+
+        System.out.println("Starting GrizzlyServer port number = " + port);
+        
+        URI u = UriBuilder.fromUri("http://localhost").port(port).build();
+        try {
+            selectorThread = GrizzlyServerFactory.create(u, adapter);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        System.out.println("Started GrizzlyServer");
+
+        int timeToSleep = getEnvVariable("JERSEY_HTTP_SLEEP", 0);
+        if (timeToSleep > 0) {
+            System.out.println("Sleeping for " + timeToSleep + " ms");
+            try {
+                // Wait for the server to start
+                Thread.sleep(timeToSleep);
+            } catch (InterruptedException ex) {
+                System.out.println("Sleeping interrupted: " + ex.getLocalizedMessage());
+            }
+        }
+    }
+    
+    public void stopServer() {
+        if (selectorThread.isRunning()) {
+            selectorThread.stopEndpoint();
+        }
+    }
+    
+    @Override
+    public void tearDown() {
+        stopServer();
+    }
+}

src/test/java/org/sonatype/jersey/client/ahc/tests/AuthTest.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+package org.sonatype.jersey.client.ahc.tests;
+
+import com.ning.http.client.Realm;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+import com.sun.jersey.api.container.filter.LoggingFilter;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.api.core.ResourceConfig;
+import com.sun.jersey.spi.resource.Singleton;
+import org.sonatype.jersey.client.ahc.AhcHttpClient;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+/**
+ *
+ * @author Paul.Sandoz@Sun.Com
+ */
+public class AuthTest extends AbstractGrizzlyServerTester {
+
+    public AuthTest(String testName) {
+        super(testName);
+    }
+    
+    @Path("/")
+    public static class PreemptiveAuthResource {
+        @GET
+        public String get(@Context HttpHeaders h) {
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            assertNotNull(value);
+            return "GET";
+        }
+
+        @POST
+        public String post(@Context HttpHeaders h, String e) {
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            assertNotNull(value);
+            return e;
+        }
+    }
+        
+    public void testPreemptiveAuth() {
+        ResourceConfig rc = new DefaultResourceConfig(PreemptiveAuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(true).setPrincipal("name").setPassword("password").build());
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().build());
+        assertEquals("GET", r.get(String.class));
+    }
+
+    public void testPreemptiveAuthPost() {
+        ResourceConfig rc = new DefaultResourceConfig(PreemptiveAuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(true).setPrincipal("name").setPassword("password").build());
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().build());
+        assertEquals("POST", r.post(String.class, "POST"));
+    }
+
+    @Path("/test")
+    @Singleton
+    public static class AuthResource {
+        int requestCount = 0;
+        @GET
+        public String get(@Context HttpHeaders h) {
+            requestCount++;
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                assertEquals(1, requestCount);
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            } else {
+                assertTrue(requestCount > 1);
+            }
+
+            return "GET";
+        }
+
+        @GET
+        @Path("filter")
+        public String getFilter(@Context HttpHeaders h) {
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            }
+
+            return "GET";
+        }
+
+        @POST
+        public String post(@Context HttpHeaders h, String e) {
+            requestCount++;
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                assertEquals(1, requestCount);
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            } else {
+                assertTrue(requestCount > 1);
+            }
+
+            return e;
+        }
+
+        @POST
+        @Path("filter")
+        public String postFilter(@Context HttpHeaders h, String e) {
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            }
+
+            return e;
+        }
+
+        @DELETE
+        public void delete(@Context HttpHeaders h) {
+            requestCount++;
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                assertEquals(1, requestCount);
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            } else {
+                assertTrue(requestCount > 1);
+            }
+        }
+
+        @DELETE
+        @Path("filter")
+        public void deleteFilter(@Context HttpHeaders h) {
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            }
+        }
+
+        @DELETE
+        @Path("filter/withEntity")
+        public String deleteFilterWithEntity(@Context HttpHeaders h, String e) {
+            String value = h.getRequestHeaders().getFirst("Authorization");
+            if (value == null) {
+                throw new WebApplicationException(Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+            }
+
+            return e;
+        }
+
+
+        
+    }
+
+    public void testAuthGet() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(false).setPrincipal("name").setPassword("password").build());
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+        assertEquals("GET", r.get(String.class));
+    }
+
+    public void testAuthGetWithClientFilter() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+        AhcHttpClient c = AhcHttpClient.create();
+        c.addFilter(new HTTPBasicAuthFilter("name", "password"));
+
+        WebResource r = c.resource(getUri().path("test/filter").build());
+        assertEquals("GET", r.get(String.class));
+    }
+
+    public void testAuthPost() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+//        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+//                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(false).setPrincipal("name").setPassword("password").build());
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+        assertEquals("POST", r.post(String.class, "POST"));
+    }
+
+    public void testAuthPostWithClientFilter() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+        AhcHttpClient c = AhcHttpClient.create();
+        c.addFilter(new HTTPBasicAuthFilter("name", "password"));
+
+        WebResource r = c.resource(getUri().path("test/filter").build());
+        assertEquals("POST", r.post(String.class, "POST"));
+    }
+
+    public void testAuthDelete() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(false).setPrincipal("name").setPassword("password").build());
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+        ClientResponse response = r.delete(ClientResponse.class);
+        assertEquals(response.getStatus(), 204);
+    }
+
+    public void testAuthDeleteWithClientFilter() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+        AhcHttpClient c = AhcHttpClient.create();
+        c.addFilter(new HTTPBasicAuthFilter("name", "password"));
+
+        WebResource r = c.resource(getUri().path("test/filter").build());
+        ClientResponse response = r.delete(ClientResponse.class);
+        assertEquals(204, response.getStatus());
+    }
+
+    public void testAuthDeleteWithEntityUsingClientFilter() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+        AhcHttpClient c = AhcHttpClient.create();
+        c.addFilter(new HTTPBasicAuthFilter("name", "password"));
+
+        WebResource r = c.resource(getUri().path("test/filter/withEntity").build());
+        ClientResponse response = r.delete(ClientResponse.class, "DELETE");
+        assertEquals(200, response.getStatus());
+        assertEquals("DELETE", response.getEntity(String.class));
+    }
+
+    public void testAuthInteractiveGet() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(false).setPrincipal("name").setPassword("password").build());
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+        assertEquals("GET", r.get(String.class));
+    }
+
+    public void testAuthInteractivePost() {
+        ResourceConfig rc = new DefaultResourceConfig(AuthResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getAsyncHttpClientConfigBuilder().setRealm(new Realm.RealmBuilder().setUsePreemptiveAuth(false).setPrincipal("name").setPassword("password").build());
+
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+        assertEquals("POST", r.post(String.class, "POST"));
+    }
+}

src/test/java/org/sonatype/jersey/client/ahc/tests/CookieTest.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * http://glassfish.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.sonatype.jersey.client.ahc.tests;
+
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.container.filter.LoggingFilter;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.api.core.ResourceConfig;
+import org.sonatype.jersey.client.ahc.AhcHttpClient;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+
+/**
+ *
+ * @author Paul.Sandoz@Sun.Com
+ */
+public class CookieTest extends AbstractGrizzlyServerTester {
+    @Path("/")
+    public static class CookieResource {
+        @GET
+        public Response get(@Context HttpHeaders h) {
+            Cookie c = h.getCookies().get("name");
+            String e = (c == null) ? "NO-COOKIE" : c.getValue();
+            return Response.ok(e).
+                    cookie(new NewCookie("name", "value")).build();
+        }
+    }
+        
+    public CookieTest(String testName) {
+        super(testName);
+    }
+    
+    public void testCookie() {
+        ResourceConfig rc = new DefaultResourceConfig(CookieResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().build());
+
+        assertEquals("NO-COOKIE", r.get(String.class));
+        assertEquals("value", r.get(String.class));
+    }
+
+    public void testCookieWithState() {
+        ResourceConfig rc = new DefaultResourceConfig(CookieResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().build());
+
+        assertEquals("NO-COOKIE", r.get(String.class));
+        assertEquals("value", r.get(String.class));
+
+    }
+}

src/test/java/org/sonatype/jersey/client/ahc/tests/GZIPContentEncodingTest.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+package org.sonatype.jersey.client.ahc.tests;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.api.core.ResourceConfig;
+import org.sonatype.jersey.client.ahc.AhcHttpClient;
+import org.sonatype.jersey.client.ahc.config.AhcConfig;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import java.util.Arrays;
+
+/**
+ *
+ * @author Paul.Sandoz@Sun.Com
+ */
+public class GZIPContentEncodingTest extends AbstractGrizzlyServerTester {
+
+    @Path("/")
+    public static class Resource {
+        @POST
+        public byte[] post(byte[] content) { return content; }
+    }
+    
+    public GZIPContentEncodingTest(String testName) {
+        super(testName);
+    }
+
+
+    public void testPost() {
+        ResourceConfig rc = new DefaultResourceConfig(Resource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                GZIPContentEncodingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                GZIPContentEncodingFilter.class.getName());
+        startServer(rc);
+
+        AhcHttpClient c = AhcHttpClient.create();
+        c.addFilter(new com.sun.jersey.api.client.filter.GZIPContentEncodingFilter());
+
+        WebResource r = c.resource(getUri().path("/").build());
+        byte[] content = new byte[1024 * 1024];
+        assertTrue(Arrays.equals(content, r.post(byte[].class, content)));
+
+        ClientResponse cr = r.post(ClientResponse.class, content);
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPostChunked() {
+        ResourceConfig rc = new DefaultResourceConfig(Resource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                GZIPContentEncodingFilter.class.getName());
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS,
+                GZIPContentEncodingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getProperties().put(AhcConfig.PROPERTY_CHUNKED_ENCODING_SIZE, 1024);
+        AhcHttpClient c = AhcHttpClient.create(config);
+        c.addFilter(new com.sun.jersey.api.client.filter.GZIPContentEncodingFilter());
+
+        WebResource r = c.resource(getUri().path("/").build());
+        byte[] content = new byte[1024 * 1024];
+        assertTrue(Arrays.equals(content, r.post(byte[].class, content)));
+
+        ClientResponse cr = r.post(ClientResponse.class, "POST");
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+}

src/test/java/org/sonatype/jersey/client/ahc/tests/HttpHeadersTest.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+package org.sonatype.jersey.client.ahc.tests;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.container.filter.LoggingFilter;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.api.core.ResourceConfig;
+import org.sonatype.jersey.client.ahc.AhcHttpClient;
+import org.sonatype.jersey.client.ahc.config.AhcConfig;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+/**
+ *
+ * @author Paul.Sandoz@Sun.Com
+ */
+public class HttpHeadersTest extends AbstractGrizzlyServerTester {
+    @Path("/test")
+    public static class HttpMethodResource {
+        @POST
+        public String post(
+                @HeaderParam("Transfer-Encoding") String transferEncoding,
+                @HeaderParam("X-CLIENT") String xClient,
+                @HeaderParam("X-WRITER") String xWriter,
+                String entity) {
+            assertEquals("client", xClient);
+            if (transferEncoding == null || !transferEncoding.equals("chunked"))
+                assertEquals("writer", xWriter);
+            return entity;
+        }
+    }
+
+    @Provider
+    @Produces("text/plain")
+    public static class HeaderWriter implements MessageBodyWriter<String> {
+
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        public long getSize(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        public void writeTo(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
+            httpHeaders.add("X-WRITER", "writer");
+            entityStream.write(t.getBytes());
+        }
+    }
+
+    public HttpHeadersTest(String testName) {
+        super(testName);
+    }
+
+    public void testPost() {
+        startServer(HttpMethodResource.class);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getClasses().add(HeaderWriter.class);
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+
+        ClientResponse cr = r.header("X-CLIENT", "client").post(ClientResponse.class, "POST");
+        assertEquals(200, cr.getStatus());
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPostChunked() {
+        ResourceConfig rc = new DefaultResourceConfig(HttpMethodResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getClasses().add(HeaderWriter.class);
+        config.getProperties().put(AhcConfig.PROPERTY_CHUNKED_ENCODING_SIZE, 1024);
+        AhcHttpClient c = AhcHttpClient.create(config);
+
+        WebResource r = c.resource(getUri().path("test").build());
+
+        ClientResponse cr = r.header("X-CLIENT", "client").post(ClientResponse.class, "POST");
+        assertEquals(200, cr.getStatus());
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+
+
+
+}

src/test/java/org/sonatype/jersey/client/ahc/tests/HttpMethodTest.java

+/*******************************************************************************
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * The Apache License v2.0 is available at
+ *   http://www.apache.org/licenses/LICENSE-2.0.html
+ * You may elect to redistribute this code under either of these licenses.
+ *******************************************************************************/
+
+package org.sonatype.jersey.client.ahc.tests;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.container.filter.LoggingFilter;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.api.core.ResourceConfig;
+import org.sonatype.jersey.client.ahc.AhcHttpClient;
+import org.sonatype.jersey.client.ahc.config.AhcConfig;
+import org.sonatype.jersey.client.ahc.config.DefaultAhcConfig;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author Paul.Sandoz@Sun.Com
+ */
+public class HttpMethodTest extends AbstractGrizzlyServerTester {
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @HttpMethod("PATCH")
+    public @interface PATCH {
+    }
+
+    @Path("/test")
+    public static class HttpMethodResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @POST
+        public String post(String entity) {
+            return entity;
+        }
+
+        @PUT
+        public String put(String entity) {
+            return entity;
+        }
+
+        @DELETE
+        public String delete() {
+            return "DELETE";
+        }
+
+        @DELETE
+        @Path("withentity")
+        public String delete(String entity) {
+            return entity;
+        }
+
+        @POST
+        @Path("noproduce")
+        public void postNoProduce(String entity) {
+        }
+
+        @POST
+        @Path("noconsumeproduce")
+        public void postNoConsumeProduce() {
+        }
+
+        @PATCH
+        public String patch(String entity) {
+            return entity;
+        }
+    }
+
+    public HttpMethodTest(String testName) {
+        super(testName);
+    }
+
+    protected AhcHttpClient createClient() {
+        return AhcHttpClient.create();
+    }
+
+    protected AhcHttpClient createClient(AhcConfig cc) {
+        return AhcHttpClient.create(cc);
+    }
+
+    public void testHead() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        ClientResponse cr = r.head();
+        assertFalse(cr.hasEntity());
+    }
+
+    public void testOptions() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        ClientResponse cr = r.options(ClientResponse.class);
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testGet() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        assertEquals("GET", r.get(String.class));
+
+        ClientResponse cr = r.get(ClientResponse.class);
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPost() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        assertEquals("POST", r.post(String.class, "POST"));
+
+        ClientResponse cr = r.post(ClientResponse.class, "POST");
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPostChunked() {
+        ResourceConfig rc = new DefaultResourceConfig(HttpMethodResource.class);
+        rc.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
+                LoggingFilter.class.getName());
+        startServer(rc);
+
+        DefaultAhcConfig config = new DefaultAhcConfig();
+        config.getProperties().put(AhcConfig.PROPERTY_CHUNKED_ENCODING_SIZE, 1024);
+        AhcHttpClient c = createClient(config);
+
+        WebResource r = c.resource(getUri().path("test").build());        
+        assertEquals("POST", r.post(String.class, "POST"));
+
+        ClientResponse cr = r.post(ClientResponse.class, "POST");
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPostVoid() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+
+        // This test will lock up if ClientResponse is not closed by WebResource.
+        // TODO need a better way to detect this.
+        for (int i = 0; i < 100; i++) {
+            r.post("POST");
+        }
+    }
+
+    public void testPostNoProduce() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        assertEquals(204, r.path("noproduce").post(ClientResponse.class, "POST").getStatus());
+
+        ClientResponse cr = r.path("noproduce").post(ClientResponse.class, "POST");
+        assertFalse(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPostNoConsumeProduce() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        assertEquals(204, r.path("noconsumeproduce").post(ClientResponse.class).getStatus());
+
+        ClientResponse cr = r.path("noconsumeproduce").post(ClientResponse.class, "POST");
+        assertFalse(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPut() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        assertEquals("PUT", r.put(String.class, "PUT"));
+
+        ClientResponse cr = r.put(ClientResponse.class, "PUT");
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testDelete() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        assertEquals("DELETE", r.delete(String.class));
+
+        ClientResponse cr = r.delete(ClientResponse.class);
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testDeleteWithEntity() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test/withentity").build());
+        r.addFilter(new com.sun.jersey.api.client.filter.LoggingFilter());
+        assertEquals("DELETE with entity", r.delete(String.class, "DELETE with entity"));
+
+        ClientResponse cr = r.delete(ClientResponse.class, "DELETE with entity");
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testPatch() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+        r.addFilter(new com.sun.jersey.api.client.filter.LoggingFilter());
+        assertEquals("PATCH", r.method("PATCH", String.class, "PATCH"));
+
+        ClientResponse cr = r.method("PATCH", ClientResponse.class, "PATCH");
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+
+    public void testAll() {
+        startServer(HttpMethodResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+
+        assertEquals("GET", r.get(String.class));
+
+        assertEquals("POST", r.post(String.class, "POST"));
+
+        assertEquals(204, r.path("noproduce").post(ClientResponse.class, "POST").getStatus());
+
+        assertEquals(204, r.path("noconsumeproduce").post(ClientResponse.class).getStatus());
+
+        assertEquals("PUT", r.post(String.class, "PUT"));
+
+        assertEquals("DELETE", r.delete(String.class));
+    }
+
+
+    @Path("/test")
+    public static class ErrorResource {
+        @POST
+        public Response post(String entity) {
+            return Response.serverError().build();
+        }
+
+        @Path("entity")
+        @POST
+        public Response postWithEntity(String entity) {
+            return Response.serverError().entity("error").build();
+        }
+    }
+
+    public void testPostError() {
+        startServer(ErrorResource.class);
+        WebResource r = createClient().resource(getUri().path("test").build());
+
+        // This test will lock up if ClientResponse is not closed by WebResource.
+        // TODO need a better way to detect this.
+        for (int i = 0; i < 100; i++) {
+            try {
+                r.post("POST");
+            } catch (UniformInterfaceException ex) {
+            }
+        }
+    }
</