diff --git a/pom.xml b/pom.xml index 045026284d..b65610df90 100644 --- a/pom.xml +++ b/pom.xml @@ -9,17 +9,17 @@ com.ning async-http-client Asynchronous Http Client - 1.7.6-SNAPSHOT + 1.7.22-SNAPSHOT jar Async Http Client library purpose is to allow Java applications to easily execute HTTP requests and asynchronously process the HTTP responses. - http://github.com/sonatype/async-http-client + http://github.com/AsyncHttpClient/async-http-client - scm:git:git@github.com:sonatype/async-http-client.git - https://github.com/sonatype/async-http-client - scm:git:git@github.com:sonatype/async-http-client.git + scm:git:git@github.com:AsyncHttpClient/async-http-client.git + https://github.com/AsyncHttpClient/async-http-client + scm:git:git@github.com:AsyncHttpClient/async-http-client.git jira @@ -58,6 +58,11 @@ neotyk Hubert Iwaniuk + + slandelle + Stephane Landelle + slandelle@excilys.com + @@ -76,38 +81,27 @@ io.netty netty - 3.4.4.Final - - - javax.servlet - servlet-api - - - commons-logging - commons-logging - - - org.slf4j - slf4j-api - - - log4j - log4j - - + 3.6.6.Final org.slf4j slf4j-api - 1.6.2 + 1.7.5 + + + + com.google.guava + guava + 11.0.2 + true ch.qos.logback logback-classic - 0.9.26 + 1.0.13 test @@ -252,6 +246,7 @@ maven-surefire-plugin ${surefire.version} + ${surefire.redirectTestOutputToFile} @@ -431,40 +426,6 @@ - - org.codehaus.mojo - clirr-maven-plugin - 2.3 - - - **/NettyAsyncHttpProvider$* - **/AsyncHandler$STATE - **/ProxyServer$Protocol - **/Realm$AuthScheme - **/SimpleAsyncHttpClient$ErrorDocumentBehaviour - **/SpnegoEngine - **/Request - **/Request$EntityWriter - **/RequestBuilderBase - **/Response - **/Response$* - **/FilterContext - **/FilterContext$* - **/NettyResponseFuture - **/**ResponseBodyPart - **/**WebSocket - - - - - check-api-compat - verify - - check-no-fork - - - - @@ -528,9 +489,15 @@ org.glassfish.grizzly grizzly-websockets - 2.2.10 + 2.3.6 true + + org.glassfish.grizzly + grizzly-http-server + 2.3.5 + test + @@ -609,7 +576,7 @@ github - gitsite:git@github.com/sonatype/async-http-client.git + gitsite:git@github.com/AsyncHttpClient/async-http-client.git diff --git a/src/main/java/com/ning/http/client/AsyncHttpClient.java b/src/main/java/com/ning/http/client/AsyncHttpClient.java index 90c6d5f2fc..ccc5285c17 100755 --- a/src/main/java/com/ning/http/client/AsyncHttpClient.java +++ b/src/main/java/com/ning/http/client/AsyncHttpClient.java @@ -25,10 +25,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; @@ -138,7 +142,7 @@ * An instance of this class will cache every HTTP 1.1 connections and close them when the {@link AsyncHttpClientConfig#getIdleConnectionTimeoutInMs()} * expires. This object can hold many persistent connections to different host. */ -public class AsyncHttpClient { +public class AsyncHttpClient implements Closeable { private final static String DEFAULT_PROVIDER = "com.ning.http.client.providers.netty.NettyAsyncHttpProvider"; private final AsyncHttpProvider httpProvider; @@ -369,11 +373,16 @@ public void close() { * Asynchronous close the {@link AsyncHttpProvider} by spawning a thread and avoid blocking. */ public void closeAsynchronously() { - config.applicationThreadPool.submit(new Runnable() { - + final ExecutorService e = Executors.newSingleThreadExecutor(); + e.submit(new Runnable() { public void run() { - httpProvider.close(); - isClosed.set(true); + try { + close(); + } catch (Throwable t) { + logger.warn("", t); + } finally { + e.shutdown(); + } } }); } @@ -546,7 +555,7 @@ private FilterContext preProcessRequest(FilterContext fc) throws IOException { } Request request = fc.getRequest(); - if (ResumableAsyncHandler.class.isAssignableFrom(fc.getAsyncHandler().getClass())) { + if (fc.getAsyncHandler() instanceof ResumableAsyncHandler) { request = ResumableAsyncHandler.class.cast(fc.getAsyncHandler()).adjustRequestRange(request); } @@ -568,6 +577,16 @@ private final static AsyncHttpProvider loadDefaultProvider(String className, Asy new Class[]{AsyncHttpClientConfig.class}).newInstance(new Object[]{config}); } catch (Throwable t) { + if (t instanceof InvocationTargetException) { + final InvocationTargetException ite = (InvocationTargetException) t; + if (logger.isErrorEnabled()) { + logger.error( + "Unable to instantiate provider {}. Trying other providers.", + className); + logger.error(ite.getCause().toString(), ite.getCause()); + } + } + // Let's try with another classloader try { Class providerClass = (Class) diff --git a/src/main/java/com/ning/http/client/AsyncHttpClientConfig.java b/src/main/java/com/ning/http/client/AsyncHttpClientConfig.java index b3fb5bac1c..e5b8a20736 100644 --- a/src/main/java/com/ning/http/client/AsyncHttpClientConfig.java +++ b/src/main/java/com/ning/http/client/AsyncHttpClientConfig.java @@ -67,7 +67,7 @@ public class AsyncHttpClientConfig { protected boolean allowPoolingConnection; protected ScheduledExecutorService reaper; protected ExecutorService applicationThreadPool; - protected ProxyServer proxyServer; + protected ProxyServerSelector proxyServerSelector; protected SSLContext sslContext; protected SSLEngineFactory sslEngineFactory; protected AsyncHttpProviderConfig providerConfig; @@ -84,6 +84,9 @@ public class AsyncHttpClientConfig { protected HostnameVerifier hostnameVerifier; protected int ioThreadMultiplier; protected boolean strict302Handling; + protected boolean useRelativeURIsWithSSLProxies; + protected int maxConnectionLifeTimeInMs; + protected boolean rfc6265CookieEncoding; protected AsyncHttpClientConfig() { } @@ -95,6 +98,7 @@ private AsyncHttpClientConfig(int maxTotalConnections, int idleConnectionInPoolTimeoutInMs, int idleConnectionTimeoutInMs, int requestTimeoutInMs, + int connectionMaxLifeTimeInMs, boolean redirectEnabled, int maxDefaultRedirects, boolean compressionEnabled, @@ -102,7 +106,7 @@ private AsyncHttpClientConfig(int maxTotalConnections, boolean keepAlive, ScheduledExecutorService reaper, ExecutorService applicationThreadPool, - ProxyServer proxyServer, + ProxyServerSelector proxyServerSelector, SSLContext sslContext, SSLEngineFactory sslEngineFactory, AsyncHttpProviderConfig providerConfig, @@ -117,7 +121,9 @@ private AsyncHttpClientConfig(int maxTotalConnections, boolean removeQueryParamOnRedirect, HostnameVerifier hostnameVerifier, int ioThreadMultiplier, - boolean strict302Handling) { + boolean strict302Handling, + boolean useRelativeURIsWithSSLProxies, + boolean rfc6265CookieEncoding) { this.maxTotalConnections = maxTotalConnections; this.maxConnectionPerHost = maxConnectionPerHost; @@ -126,6 +132,7 @@ private AsyncHttpClientConfig(int maxTotalConnections, this.idleConnectionInPoolTimeoutInMs = idleConnectionInPoolTimeoutInMs; this.idleConnectionTimeoutInMs = idleConnectionTimeoutInMs; this.requestTimeoutInMs = requestTimeoutInMs; + this.maxConnectionLifeTimeInMs = connectionMaxLifeTimeInMs; this.redirectEnabled = redirectEnabled; this.maxDefaultRedirects = maxDefaultRedirects; this.compressionEnabled = compressionEnabled; @@ -147,13 +154,15 @@ private AsyncHttpClientConfig(int maxTotalConnections, this.hostnameVerifier = hostnameVerifier; this.ioThreadMultiplier = ioThreadMultiplier; this.strict302Handling = strict302Handling; + this.useRelativeURIsWithSSLProxies = useRelativeURIsWithSSLProxies; + this.rfc6265CookieEncoding = rfc6265CookieEncoding; if (applicationThreadPool == null) { this.applicationThreadPool = Executors.newCachedThreadPool(); } else { this.applicationThreadPool = applicationThreadPool; } - this.proxyServer = proxyServer; + this.proxyServerSelector = proxyServerSelector; this.useRawUrl = useRawUrl; } @@ -301,8 +310,8 @@ public ExecutorService executorService() { * * @return instance of {@link com.ning.http.client.ProxyServer} */ - public ProxyServer getProxyServer() { - return proxyServer; + public ProxyServerSelector getProxyServerSelector() { + return proxyServerSelector; } /** @@ -438,9 +447,29 @@ public boolean isRemoveQueryParamOnRedirect() { * Return true if one of the {@link java.util.concurrent.ExecutorService} has been shutdown. * * @return true if one of the {@link java.util.concurrent.ExecutorService} has been shutdown. + * + * @deprecated use #isValid */ public boolean isClosed() { - return applicationThreadPool.isShutdown() || reaper.isShutdown(); + return !isValid(); + } + + /** + * @return true if both the application and reaper thread pools + * haven't yet been shutdown. + * + * @since 1.7.21 + */ + public boolean isValid() { + boolean atpRunning = true; + try { + atpRunning = applicationThreadPool.isShutdown(); + } catch (Exception ignore) { + // isShutdown() will thrown an exception in an EE7 environment + // when using a ManagedExecutorService. + // When this is the case, we assume it's running. + } + return (atpRunning && !reaper.isShutdown()); } /** @@ -476,6 +505,35 @@ public boolean isStrict302Handling() { return strict302Handling; } + /** + * @returntrue if AHC should use relative URIs instead of absolute ones when talking with a SSL proxy, + * otherwise false. + * + * @since 1.7.12 + */ + public boolean isUseRelativeURIsWithSSLProxies() { + return useRelativeURIsWithSSLProxies; + } + + /** + * Return the maximum time in millisecond an {@link com.ning.http.client.AsyncHttpClient} will keep connection in the pool, or -1 to keep connection while possible. + * + * @return the maximum time in millisecond an {@link com.ning.http.client.AsyncHttpClient} will keep connection in the pool, or -1 to keep connection while possible. + */ + public int getMaxConnectionLifeTimeInMs() { + return maxConnectionLifeTimeInMs; + } + + /** + * @returntrue if AHC should use rfc6265 for encoding client side cookies, + * otherwise false. + * + * @since 1.7.18 + */ + public boolean isRfc6265CookieEncoding() { + return rfc6265CookieEncoding; + } + /** * Builder for an {@link AsyncHttpClient} */ @@ -487,27 +545,18 @@ public static class Builder { private int defaultIdleConnectionInPoolTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultIdleConnectionInPoolTimeoutInMS", 60 * 1000); private int defaultIdleConnectionTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultIdleConnectionTimeoutInMS", 60 * 1000); private int defaultRequestTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultRequestTimeoutInMS", 60 * 1000); + private int defaultMaxConnectionLifeTimeInMs = Integer.getInteger(ASYNC_CLIENT + "defaultMaxConnectionLifeTimeInMs", -1); private boolean redirectEnabled = Boolean.getBoolean(ASYNC_CLIENT + "defaultRedirectsEnabled"); private int maxDefaultRedirects = Integer.getInteger(ASYNC_CLIENT + "defaultMaxRedirects", 5); private boolean compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled"); private String userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "NING/1.0"); private boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties"); + private boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector"); private boolean allowPoolingConnection = true; - private ScheduledExecutorService reaper = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { - public Thread newThread(Runnable r) { - Thread t = new Thread(r, "AsyncHttpClient-Reaper"); - t.setDaemon(true); - return t; - } - }); - private ExecutorService applicationThreadPool = Executors.newCachedThreadPool(new ThreadFactory() { - public Thread newThread(Runnable r) { - Thread t = new Thread(r, "AsyncHttpClient-Callback"); - t.setDaemon(true); - return t; - } - }); - private ProxyServer proxyServer = null; + private boolean useRelativeURIsWithSSLProxies = Boolean.getBoolean(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies"); + private ScheduledExecutorService reaper; + private ExecutorService applicationThreadPool; + private ProxyServerSelector proxyServerSelector = null; private SSLContext sslContext; private SSLEngineFactory sslEngineFactory; private AsyncHttpProviderConfig providerConfig; @@ -524,6 +573,7 @@ public Thread newThread(Runnable r) { private HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier(); private int ioThreadMultiplier = 2; private boolean strict302Handling; + private boolean rfc6265CookieEncoding; public Builder() { } @@ -684,7 +734,6 @@ public Builder setKeepAlive(boolean allowPoolingConnection) { * @return a {@link Builder} */ public Builder setScheduledExecutorService(ScheduledExecutorService reaper) { - if (this.reaper != null) this.reaper.shutdown(); this.reaper = reaper; return this; } @@ -698,19 +747,29 @@ public Builder setScheduledExecutorService(ScheduledExecutorService reaper) { * @return a {@link Builder} */ public Builder setExecutorService(ExecutorService applicationThreadPool) { - if (this.applicationThreadPool != null) this.applicationThreadPool.shutdown(); this.applicationThreadPool = applicationThreadPool; return this; } /** - * Set an instance of {@link com.ning.http.client.ProxyServer} used by an {@link AsyncHttpClient} + * Set an instance of {@link ProxyServerSelector} used by an {@link AsyncHttpClient} + * + * @param proxyServerSelector instance of {@link ProxyServerSelector} + * @return a {@link Builder} + */ + public Builder setProxyServerSelector(ProxyServerSelector proxyServerSelector) { + this.proxyServerSelector = proxyServerSelector; + return this; + } + + /** + * Set an instance of {@link ProxyServer} used by an {@link AsyncHttpClient} * * @param proxyServer instance of {@link com.ning.http.client.ProxyServer} * @return a {@link Builder} */ public Builder setProxyServer(ProxyServer proxyServer) { - this.proxyServer = proxyServer; + this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer); return this; } @@ -911,12 +970,27 @@ public Builder setRemoveQueryParamsOnRedirect(boolean removeQueryParamOnRedirect return this; } + /** + * Sets whether AHC should use the default JDK ProxySelector to select a proxy server. + *

+ * If useProxySelector is set to true but {@link #setProxyServer(ProxyServer)} + * was used to explicitly set a proxy server, the latter is preferred. + *

+ * See http://docs.oracle.com/javase/7/docs/api/java/net/ProxySelector.html + */ + public Builder setUseProxySelector(boolean useProxySelector) { + this.useProxySelector = useProxySelector; + return this; + } + /** * Sets whether AHC should use the default http.proxy* system properties - * to obtain proxy information. + * to obtain proxy information. This differs from {@link #setUseProxySelector(boolean)} + * in that AsyncHttpClient will use its own logic to handle the system properties, + * potentially supporting other protocols that the the JDK ProxySelector doesn't. *

- * If useProxyProperties is set to true but {@link #setProxyServer(ProxyServer)} was used - * to explicitly set a proxy server, the latter is preferred. + * If useProxyProperties is set to true but {@link #setUseProxySelector(boolean)} + * was also set to true, the latter is preferred. *

* See http://download.oracle.com/javase/1.4.2/docs/guide/net/properties.html */ @@ -955,6 +1029,43 @@ public Builder setStrict302Handling(final boolean strict302Handling) { this.strict302Handling = strict302Handling; return this; } + + /** + * Configures this AHC instance to use relative URIs instead of absolute ones when talking with a SSL proxy. + * + * @param useRelativeURIsWithSSLProxies + * @return this + * + * @since 1.7.2 + */ + public Builder setUseRelativeURIsWithSSLProxies(boolean useRelativeURIsWithSSLProxies) { + this.useRelativeURIsWithSSLProxies = useRelativeURIsWithSSLProxies; + return this; + } + + /** + * Set the maximum time in millisecond connection can be added to the pool for further reuse + * + * @param maxConnectionLifeTimeInMs the maximum time in millisecond connection can be added to the pool for further reuse + * @return a {@link Builder} + */ + public Builder setMaxConnectionLifeTimeInMs(int maxConnectionLifeTimeInMs) { + this.defaultMaxConnectionLifeTimeInMs = maxConnectionLifeTimeInMs; + return this; + } + + /** + * Configures this AHC instance to use RFC 6265 cookie encoding style + * + * @param rfc6265CookieEncoding + * @return this + * + * @since 1.7.18 + */ + public Builder setRfc6265CookieEncoding(boolean rfc6265CookieEncoding) { + this.rfc6265CookieEncoding = rfc6265CookieEncoding; + return this; + } /** * Create a config builder with values taken from the given prototype configuration. @@ -969,9 +1080,10 @@ public Builder(AsyncHttpClientConfig prototype) { defaultIdleConnectionInPoolTimeoutInMs = prototype.getIdleConnectionInPoolTimeoutInMs(); defaultIdleConnectionTimeoutInMs = prototype.getIdleConnectionTimeoutInMs(); defaultMaxConnectionPerHost = prototype.getMaxConnectionPerHost(); + defaultMaxConnectionLifeTimeInMs = prototype.getMaxConnectionLifeTimeInMs(); maxDefaultRedirects = prototype.getMaxRedirects(); defaultMaxTotalConnections = prototype.getMaxTotalConnections(); - proxyServer = prototype.getProxyServer(); + proxyServerSelector = prototype.getProxyServerSelector(); realm = prototype.getRealm(); defaultRequestTimeoutInMs = prototype.getRequestTimeoutInMs(); sslContext = prototype.getSSLContext(); @@ -998,6 +1110,7 @@ public Builder(AsyncHttpClientConfig prototype) { removeQueryParamOnRedirect = prototype.isRemoveQueryParamOnRedirect(); hostnameVerifier = prototype.getHostnameVerifier(); strict302Handling = prototype.isStrict302Handling(); + rfc6265CookieEncoding = prototype.isRfc6265CookieEncoding(); } /** @@ -1007,12 +1120,39 @@ public Builder(AsyncHttpClientConfig prototype) { */ public AsyncHttpClientConfig build() { - if (applicationThreadPool.isShutdown()) { - throw new IllegalStateException("ExecutorServices closed"); + if (reaper == null) { + reaper = Executors.newScheduledThreadPool(Runtime.getRuntime() + .availableProcessors(), new ThreadFactory() { + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "AsyncHttpClient-Reaper"); + t.setDaemon(true); + return t; + } + }); + } + + if (applicationThreadPool == null) { + applicationThreadPool = Executors + .newCachedThreadPool(new ThreadFactory() { + public Thread newThread(Runnable r) { + Thread t = new Thread(r, + "AsyncHttpClient-Callback"); + t.setDaemon(true); + return t; + } + }); + } + + if (proxyServerSelector == null && useProxySelector) { + proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector(); + } + + if (proxyServerSelector == null && useProxyProperties) { + proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties()); } - if (proxyServer == null && useProxyProperties) { - proxyServer = ProxyUtils.createProxy(System.getProperties()); + if (proxyServerSelector == null) { + proxyServerSelector = ProxyServerSelector.NO_PROXY_SELECTOR; } return new AsyncHttpClientConfig(defaultMaxTotalConnections, @@ -1022,6 +1162,7 @@ public AsyncHttpClientConfig build() { defaultIdleConnectionInPoolTimeoutInMs, defaultIdleConnectionTimeoutInMs, defaultRequestTimeoutInMs, + defaultMaxConnectionLifeTimeInMs, redirectEnabled, maxDefaultRedirects, compressionEnabled, @@ -1029,7 +1170,7 @@ public AsyncHttpClientConfig build() { allowPoolingConnection, reaper, applicationThreadPool, - proxyServer, + proxyServerSelector, sslContext, sslEngineFactory, providerConfig, @@ -1045,7 +1186,9 @@ public AsyncHttpClientConfig build() { removeQueryParamOnRedirect, hostnameVerifier, ioThreadMultiplier, - strict302Handling); + strict302Handling, + useRelativeURIsWithSSLProxies, + rfc6265CookieEncoding); } } } diff --git a/src/main/java/com/ning/http/client/AsyncHttpClientConfigBean.java b/src/main/java/com/ning/http/client/AsyncHttpClientConfigBean.java index 0924d4d760..8ea0b17459 100644 --- a/src/main/java/com/ning/http/client/AsyncHttpClientConfigBean.java +++ b/src/main/java/com/ning/http/client/AsyncHttpClientConfigBean.java @@ -54,10 +54,14 @@ void configureDefaults() { maxDefaultRedirects = Integer.getInteger(ASYNC_CLIENT + "defaultMaxRedirects", 5); compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled"); userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "NING/1.0"); + ioThreadMultiplier = Integer.getInteger(ASYNC_CLIENT + "ioThreadMultiplier", 2); + boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector"); boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties"); - if (useProxyProperties) { - proxyServer = ProxyUtils.createProxy(System.getProperties()); + if (useProxySelector) { + proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector(); + } else if (useProxyProperties) { + proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties()); } allowPoolingConnection = true; @@ -163,7 +167,12 @@ public AsyncHttpClientConfigBean setApplicationThreadPool(ExecutorService applic } public AsyncHttpClientConfigBean setProxyServer(ProxyServer proxyServer) { - this.proxyServer = proxyServer; + this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer); + return this; + } + + public AsyncHttpClientConfigBean setProxyServerSelector(ProxyServerSelector proxyServerSelector) { + this.proxyServerSelector = proxyServerSelector; return this; } diff --git a/src/main/java/com/ning/http/client/AsyncHttpProvider.java b/src/main/java/com/ning/http/client/AsyncHttpProvider.java index 1689bc137e..1dfdb0474c 100644 --- a/src/main/java/com/ning/http/client/AsyncHttpProvider.java +++ b/src/main/java/com/ning/http/client/AsyncHttpProvider.java @@ -16,7 +16,7 @@ package com.ning.http.client; import java.io.IOException; -import java.util.Collection; +import java.util.List; /** * Interface to be used when implementing custom asynchronous I/O HTTP client. @@ -48,6 +48,6 @@ public interface AsyncHttpProvider { */ public Response prepareResponse(HttpResponseStatus status, HttpResponseHeaders headers, - Collection bodyParts); + List bodyParts); } diff --git a/src/main/java/com/ning/http/client/ConnectionPoolKeyStrategy.java b/src/main/java/com/ning/http/client/ConnectionPoolKeyStrategy.java new file mode 100644 index 0000000000..d0e6643db1 --- /dev/null +++ b/src/main/java/com/ning/http/client/ConnectionPoolKeyStrategy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2010 Ning, Inc. + * + * Ning licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.ning.http.client; + +import java.net.URI; + +public interface ConnectionPoolKeyStrategy { + + String getKey(URI uri); +} \ No newline at end of file diff --git a/src/main/java/com/ning/http/client/Cookie.java b/src/main/java/com/ning/http/client/Cookie.java index 26fd46920f..b3be657d5a 100644 --- a/src/main/java/com/ning/http/client/Cookie.java +++ b/src/main/java/com/ning/http/client/Cookie.java @@ -20,35 +20,93 @@ import java.util.Set; import java.util.TreeSet; -public class Cookie { +public class Cookie implements Comparable { private final String domain; private final String name; private final String value; + private final String rawValue; private final String path; private final int maxAge; private final boolean secure; private final int version; + private final boolean httpOnly; + private final boolean discard; + private final String comment; + private final String commentUrl; + private Set ports = Collections.emptySet(); private Set unmodifiablePorts = ports; public Cookie(String domain, String name, String value, String path, int maxAge, boolean secure) { - this.domain = domain; - this.name = name; - this.value = value; - this.path = path; - this.maxAge = maxAge; - this.secure = secure; - this.version = 1; + this(domain, name, value, path, maxAge, secure, 1); } public Cookie(String domain, String name, String value, String path, int maxAge, boolean secure, int version) { - this.domain = domain; + this(domain, name, value, value, path, maxAge, secure, version, false, false, null, null, Collections. emptySet()); + } + + public Cookie(String domain, String name, String value, String rawValue, String path, int maxAge, boolean secure, int version, boolean httpOnly, boolean discard, String comment, String commentUrl, Iterable ports) { + + if (name == null) { + throw new NullPointerException("name"); + } + name = name.trim(); + if (name.length() == 0) { + throw new IllegalArgumentException("empty name"); + } + + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (c > 127) { + throw new IllegalArgumentException("name contains non-ascii character: " + name); + } + + // Check prohibited characters. + switch (c) { + case '\t': + case '\n': + case 0x0b: + case '\f': + case '\r': + case ' ': + case ',': + case ';': + case '=': + throw new IllegalArgumentException("name contains one of the following prohibited characters: " + "=,; \\t\\r\\n\\v\\f: " + name); + } + } + + if (name.charAt(0) == '$') { + throw new IllegalArgumentException("name starting with '$' not allowed: " + name); + } + + if (value == null) { + throw new NullPointerException("value"); + } + this.name = name; this.value = value; - this.path = path; + this.rawValue = rawValue; + this.domain = validateValue("domain", domain); + this.path = validateValue("path", path); this.maxAge = maxAge; this.secure = secure; this.version = version; + this.httpOnly = httpOnly; + + if (version > 0) { + this.comment = validateValue("comment", comment); + } else { + this.comment = null; + } + if (version > 1) { + this.discard = discard; + this.commentUrl = validateValue("commentUrl", commentUrl); + setPorts(ports); + } else { + this.discard = false; + this.commentUrl = null; + } } public String getDomain() { @@ -63,6 +121,10 @@ public String getValue() { return value == null ? "" : value; } + public String getRawValue() { + return rawValue; + } + public String getPath() { return path; } @@ -79,6 +141,22 @@ public int getVersion() { return version; } + public String getComment() { + return this.comment; + } + + public String getCommentUrl() { + return this.commentUrl; + } + + public boolean isHttpOnly() { + return httpOnly; + } + + public boolean isDiscard() { + return discard; + } + public Set getPorts() { if (unmodifiablePorts == null) { unmodifiablePorts = Collections.unmodifiableSet(ports); @@ -86,6 +164,8 @@ public Set getPorts() { return unmodifiablePorts; } + @Deprecated + // to be removed public void setPorts(int... ports) { if (ports == null) { throw new NullPointerException("ports"); @@ -107,6 +187,8 @@ public void setPorts(int... ports) { } } + @Deprecated + // to become private public void setPorts(Iterable ports) { Set newPorts = new TreeSet(); for (int p : ports) { @@ -128,4 +210,60 @@ public String toString() { return String.format("Cookie: domain=%s, name=%s, value=%s, path=%s, maxAge=%d, secure=%s", domain, name, value, path, maxAge, secure); } + + private String validateValue(String name, String value) { + if (value == null) { + return null; + } + value = value.trim(); + if (value.length() == 0) { + return null; + } + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + switch (c) { + case '\r': + case '\n': + case '\f': + case 0x0b: + case ';': + throw new IllegalArgumentException(name + " contains one of the following prohibited characters: " + ";\\r\\n\\f\\v (" + value + ')'); + } + } + return value; + } + + public int compareTo(Cookie c) { + int v; + v = getName().compareToIgnoreCase(c.getName()); + if (v != 0) { + return v; + } + + if (getPath() == null) { + if (c.getPath() != null) { + return -1; + } + } else if (c.getPath() == null) { + return 1; + } else { + v = getPath().compareTo(c.getPath()); + if (v != 0) { + return v; + } + } + + if (getDomain() == null) { + if (c.getDomain() != null) { + return -1; + } + } else if (c.getDomain() == null) { + return 1; + } else { + v = getDomain().compareToIgnoreCase(c.getDomain()); + return v; + } + + return 0; + } } diff --git a/src/main/java/com/ning/http/client/DefaultConnectionPoolStrategy.java b/src/main/java/com/ning/http/client/DefaultConnectionPoolStrategy.java new file mode 100644 index 0000000000..3d248e869f --- /dev/null +++ b/src/main/java/com/ning/http/client/DefaultConnectionPoolStrategy.java @@ -0,0 +1,29 @@ +/* + * Copyright 2010 Ning, Inc. + * + * Ning licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.ning.http.client; + +import java.net.URI; + +import com.ning.http.util.AsyncHttpProviderUtils; + +public enum DefaultConnectionPoolStrategy implements ConnectionPoolKeyStrategy { + + INSTANCE; + + public String getKey(URI uri) { + return AsyncHttpProviderUtils.getBaseUrl(uri); + } +} \ No newline at end of file diff --git a/src/main/java/com/ning/http/client/FluentCaseInsensitiveStringsMap.java b/src/main/java/com/ning/http/client/FluentCaseInsensitiveStringsMap.java index 009af7e43f..16ad85572a 100644 --- a/src/main/java/com/ning/http/client/FluentCaseInsensitiveStringsMap.java +++ b/src/main/java/com/ning/http/client/FluentCaseInsensitiveStringsMap.java @@ -16,6 +16,8 @@ */ package com.ning.http.client; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -24,6 +26,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -66,7 +69,7 @@ public FluentCaseInsensitiveStringsMap(Map> src) { * @return This object */ public FluentCaseInsensitiveStringsMap add(String key, String... values) { - if ((values != null) && (values.length > 0)) { + if (isNonEmpty(values)) { add(key, Arrays.asList(values)); } return this; @@ -103,7 +106,7 @@ public FluentCaseInsensitiveStringsMap add(String key, Collection values List nonNullValues = fetchValues(values); if (nonNullValues != null) { - String lcKey = key.toLowerCase(); + String lcKey = key.toLowerCase(Locale.ENGLISH); String realKey = keyLookup.get(lcKey); List curValues = null; @@ -175,7 +178,7 @@ public FluentCaseInsensitiveStringsMap replace(final String key, final String... public FluentCaseInsensitiveStringsMap replace(final String key, final Collection values) { if (key != null) { List nonNullValues = fetchValues(values); - String lcKkey = key.toLowerCase(); + String lcKkey = key.toLowerCase(Locale.ENGLISH); String realKey = keyLookup.get(lcKkey); if (nonNullValues == null) { @@ -257,7 +260,7 @@ public void putAll(Map> values) { */ public FluentCaseInsensitiveStringsMap delete(String key) { if (key != null) { - String lcKey = key.toLowerCase(); + String lcKey = key.toLowerCase(Locale.ENGLISH); String realKey = keyLookup.remove(lcKey); if (realKey != null) { @@ -366,7 +369,7 @@ public boolean isEmpty() { */ /* @Override */ public boolean containsKey(Object key) { - return key == null ? false : keyLookup.containsKey(key.toString().toLowerCase()); + return key == null ? false : keyLookup.containsKey(key.toString().toLowerCase(Locale.ENGLISH)); } /** @@ -431,7 +434,7 @@ public List get(Object key) { return null; } - String lcKey = key.toString().toLowerCase(); + String lcKey = key.toString().toLowerCase(Locale.ENGLISH); String realKey = keyLookup.get(lcKey); if (realKey == null) { diff --git a/src/main/java/com/ning/http/client/FluentStringsMap.java b/src/main/java/com/ning/http/client/FluentStringsMap.java index 5c71428614..22a2ccb6bb 100644 --- a/src/main/java/com/ning/http/client/FluentStringsMap.java +++ b/src/main/java/com/ning/http/client/FluentStringsMap.java @@ -16,6 +16,8 @@ */ package com.ning.http.client; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -61,30 +63,12 @@ public FluentStringsMap(Map> src) { * @return This object */ public FluentStringsMap add(String key, String... values) { - if ((values != null) && (values.length > 0)) { + if (isNonEmpty(values)) { add(key, Arrays.asList(values)); } return this; } - private List fetchValues(Collection values) { - List result = null; - - if (values != null) { - for (String value : values) { - if (value == null) { - value = ""; - } - if (result == null) { - // lazy initialization - result = new ArrayList(); - } - result.add(value); - } - } - return result; - } - /** * Adds the specified values and returns this object. * @@ -94,17 +78,13 @@ private List fetchValues(Collection values) { * @return This object */ public FluentStringsMap add(String key, Collection values) { - if (key != null) { - List nonNullValues = fetchValues(values); - - if (nonNullValues != null) { - List curValues = this.values.get(key); + if (key != null && isNonEmpty(values)) { + List curValues = this.values.get(key); - if (curValues == null) { - curValues = new ArrayList(); - this.values.put(key, curValues); - } - curValues.addAll(nonNullValues); + if (curValues == null) { + this.values.put(key, new ArrayList(values)); + } else { + curValues.addAll(values); } } return this; @@ -160,12 +140,10 @@ public FluentStringsMap replace(final String key, final String... values) { */ public FluentStringsMap replace(final String key, final Collection values) { if (key != null) { - List nonNullValues = fetchValues(values); - - if (nonNullValues == null) { + if (values == null) { this.values.remove(key); } else { - this.values.put(key, nonNullValues); + this.values.put(key, new ArrayList(values)); } } return this; diff --git a/src/main/java/com/ning/http/client/HttpResponseBodyPartsInputStream.java b/src/main/java/com/ning/http/client/HttpResponseBodyPartsInputStream.java index 1f6667cd2b..7b0f76db5b 100644 --- a/src/main/java/com/ning/http/client/HttpResponseBodyPartsInputStream.java +++ b/src/main/java/com/ning/http/client/HttpResponseBodyPartsInputStream.java @@ -14,26 +14,27 @@ import java.io.IOException; import java.io.InputStream; +import java.util.List; /** * An {@link InputStream} that reads all the elements in an array of {@link HttpResponseBodyPart}s. */ public class HttpResponseBodyPartsInputStream extends InputStream { - private final HttpResponseBodyPart[] parts; + private final List parts; private int currentPos = 0; private int bytePos = -1; private byte[] active; private int available = 0; - public HttpResponseBodyPartsInputStream(HttpResponseBodyPart[] parts) { + public HttpResponseBodyPartsInputStream(List parts) { this.parts = parts; - active = parts[0].getBodyPartBytes(); + active = parts.get(0).getBodyPartBytes(); computeLength(parts); } - private void computeLength(HttpResponseBodyPart[] parts) { + private void computeLength(List parts) { if (available == 0) { for (HttpResponseBodyPart p : parts) { available += p.getBodyPartBytes().length; @@ -50,12 +51,12 @@ public int available() throws IOException { public int read() throws IOException { if (++bytePos >= active.length) { // No more bytes, so step to the next array. - if (++currentPos >= parts.length) { + if (++currentPos >= parts.size()) { return -1; } bytePos = 0; - active = parts[currentPos].getBodyPartBytes(); + active = parts.get(currentPos).getBodyPartBytes(); } return active[bytePos] & 0xFF; diff --git a/src/main/java/com/ning/http/client/ListenableFuture.java b/src/main/java/com/ning/http/client/ListenableFuture.java index 74dfcb70f0..b119f26597 100755 --- a/src/main/java/com/ning/http/client/ListenableFuture.java +++ b/src/main/java/com/ning/http/client/ListenableFuture.java @@ -30,7 +30,6 @@ */ package com.ning.http.client; -import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.Future; @@ -42,11 +41,11 @@ public interface ListenableFuture extends Future { /** - * Execute a {@link Callable} and if there is no exception, mark this Future as done and release the internal lock. + * Terminate and if there is no exception, mark this Future as done and release the internal lock. * * @param callable */ - void done(Callable callable); + void done(); /** * Abort the current processing, and propagate the {@link Throwable} to the {@link AsyncHandler} or {@link Future} diff --git a/src/main/java/com/ning/http/client/ProxyServer.java b/src/main/java/com/ning/http/client/ProxyServer.java index 79cc5e1eee..784ba97e44 100644 --- a/src/main/java/com/ning/http/client/ProxyServer.java +++ b/src/main/java/com/ning/http/client/ProxyServer.java @@ -16,10 +16,13 @@ */ package com.ning.http.client; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.ning.http.util.AsyncHttpProviderUtils; + /** * Represents a proxy server. */ @@ -44,13 +47,14 @@ public String toString() { } } - private String encoding = "UTF-8"; private final List nonProxyHosts = new ArrayList(); private final Protocol protocol; private final String host; private final String principal; private final String password; - private int port; + private final int port; + private final URI uri; + private String encoding = "UTF-8"; private String ntlmDomain = System.getProperty("http.auth.ntlm.domain", ""); public ProxyServer(final Protocol protocol, final String host, final int port, String principal, String password) { @@ -59,6 +63,7 @@ public ProxyServer(final Protocol protocol, final String host, final int port, S this.port = port; this.principal = principal; this.password = password; + uri = AsyncHttpProviderUtils.createUri(toString()); } public ProxyServer(final String host, final int port, String principal, String password) { @@ -97,6 +102,10 @@ public String getPassword() { return password; } + public URI getURI() { + return uri; + } + public ProxyServer setEncoding(String encoding) { this.encoding = encoding; return this; @@ -131,7 +140,7 @@ public String getNtlmDomain() { @Override public String toString() { - return String.format("%s://%s:%d", protocol.toString(), host, port); + return protocol + "://" + host + ":" + port; } } diff --git a/src/main/java/com/ning/http/client/ProxyServerSelector.java b/src/main/java/com/ning/http/client/ProxyServerSelector.java new file mode 100644 index 0000000000..8544e7e617 --- /dev/null +++ b/src/main/java/com/ning/http/client/ProxyServerSelector.java @@ -0,0 +1,26 @@ +package com.ning.http.client; + +import java.net.URI; + +/** + * Selector for a proxy server + */ +public interface ProxyServerSelector { + + /** + * Select a proxy server to use for the given URI. + * + * @param uri The URI to select a proxy server for. + * @return The proxy server to use, if any. May return null. + */ + ProxyServer select(URI uri); + + /** + * A selector that always selects no proxy. + */ + static final ProxyServerSelector NO_PROXY_SELECTOR = new ProxyServerSelector() { + public ProxyServer select(URI uri) { + return null; + } + }; +} diff --git a/src/main/java/com/ning/http/client/Realm.java b/src/main/java/com/ning/http/client/Realm.java index 07c84bfa3a..8e68142c98 100644 --- a/src/main/java/com/ning/http/client/Realm.java +++ b/src/main/java/com/ning/http/client/Realm.java @@ -16,6 +16,8 @@ */ package com.ning.http.client; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -579,7 +581,7 @@ private static String toBase16(byte[] bytes) { public Realm build() { // Avoid generating - if (nonce != null && !nonce.equals("")) { + if (isNonEmpty(nonce)) { newCnonce(); try { newResponse(); diff --git a/src/main/java/com/ning/http/client/Request.java b/src/main/java/com/ning/http/client/Request.java index 10576405bb..8871dd6cc8 100644 --- a/src/main/java/com/ning/http/client/Request.java +++ b/src/main/java/com/ning/http/client/Request.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; +import java.net.URI; import java.util.Collection; import java.util.List; @@ -66,6 +67,10 @@ public static interface EntityWriter { */ public String getUrl(); + public URI getOriginalURI(); + public URI getURI(); + public URI getRawURI(); + /** * Return the InetAddress to override * @@ -232,4 +237,5 @@ public static interface EntityWriter { public boolean isUseRawUrl(); + ConnectionPoolKeyStrategy getConnectionPoolKeyStrategy(); } diff --git a/src/main/java/com/ning/http/client/RequestBuilder.java b/src/main/java/com/ning/http/client/RequestBuilder.java index 50aaad585f..7bf55ee1a5 100644 --- a/src/main/java/com/ning/http/client/RequestBuilder.java +++ b/src/main/java/com/ning/http/client/RequestBuilder.java @@ -23,6 +23,8 @@ /** * Builder for a {@link Request}. + * Warning: mutable and not thread-safe! Beware that it holds a reference on the Request instance it builds, + * so modifying the builder will modify the request even after it has been built. */ public class RequestBuilder extends RequestBuilderBase { diff --git a/src/main/java/com/ning/http/client/RequestBuilderBase.java b/src/main/java/com/ning/http/client/RequestBuilderBase.java index 9cc5ec40e0..6749cfa767 100644 --- a/src/main/java/com/ning/http/client/RequestBuilderBase.java +++ b/src/main/java/com/ning/http/client/RequestBuilderBase.java @@ -15,8 +15,12 @@ */ package com.ning.http.client; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.Request.EntityWriter; +import com.ning.http.util.AsyncHttpProviderUtils; import com.ning.http.util.UTF8UrlEncoder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,17 +40,21 @@ /** * Builder for {@link Request} - * + * * @param */ public abstract class RequestBuilderBase> { private final static Logger logger = LoggerFactory.getLogger(RequestBuilderBase.class); + private static final URI DEFAULT_REQUEST_URL = URI.create("http://localhost/"); + private static final class RequestImpl implements Request { private String method; - private String url = null; - private InetAddress address = null; - private InetAddress localAddress = null; + private URI originalUri; + private URI uri; + private URI rawUri; + private InetAddress address; + private InetAddress localAddress; private FluentCaseInsensitiveStringsMap headers = new FluentCaseInsensitiveStringsMap(); private Collection cookies = new ArrayList(); private byte[] byteData; @@ -64,9 +72,10 @@ private static final class RequestImpl implements Request { private File file; private Boolean followRedirects; private PerRequestConfig perRequestConfig; - private long rangeOffset = 0; + private long rangeOffset; public String charset; - private boolean useRawUrl = false; + private boolean useRawUrl; + private ConnectionPoolKeyStrategy connectionPoolKeyStrategy = DefaultConnectionPoolStrategy.INSTANCE; public RequestImpl(boolean useRawUrl) { this.useRawUrl = useRawUrl; @@ -75,8 +84,7 @@ public RequestImpl(boolean useRawUrl) { public RequestImpl(Request prototype) { if (prototype != null) { this.method = prototype.getMethod(); - int pos = prototype.getUrl().indexOf("?"); - this.url = pos > 0 ? prototype.getUrl().substring(0, pos) : prototype.getUrl(); + this.originalUri = prototype.getOriginalURI(); this.address = prototype.getInetAddress(); this.localAddress = prototype.getLocalAddress(); this.headers = new FluentCaseInsensitiveStringsMap(prototype.getHeaders()); @@ -86,19 +94,20 @@ public RequestImpl(Request prototype) { this.streamData = prototype.getStreamData(); this.entityWriter = prototype.getEntityWriter(); this.bodyGenerator = prototype.getBodyGenerator(); - this.params = (prototype.getParams() == null ? null : new FluentStringsMap(prototype.getParams())); - this.queryParams = (prototype.getQueryParams() == null ? null : new FluentStringsMap(prototype.getQueryParams())); - this.parts = (prototype.getParts() == null ? null : new ArrayList(prototype.getParts())); + this.params = prototype.getParams() == null ? null : new FluentStringsMap(prototype.getParams()); + this.queryParams = prototype.getQueryParams() == null ? null : new FluentStringsMap(prototype.getQueryParams()); + this.parts = prototype.getParts() == null ? null : new ArrayList(prototype.getParts()); this.virtualHost = prototype.getVirtualHost(); this.length = prototype.getContentLength(); this.proxyServer = prototype.getProxyServer(); this.realm = prototype.getRealm(); this.file = prototype.getFile(); - this.followRedirects = prototype.isRedirectOverrideSet()? prototype.isRedirectEnabled() : null; + this.followRedirects = prototype.isRedirectOverrideSet() ? prototype.isRedirectEnabled() : null; this.perRequestConfig = prototype.getPerRequestConfig(); this.rangeOffset = prototype.getRangeOffset(); this.charset = prototype.getBodyEncoding(); this.useRawUrl = prototype.isUseRawUrl(); + this.connectionPoolKeyStrategy = prototype.getConnectionPoolKeyStrategy(); } } @@ -112,12 +121,6 @@ public String getMethod() { return method; } - /* @Override */ - - public String getUrl() { - return toUrl(true); - } - public InetAddress getInetAddress() { return address; } @@ -125,42 +128,74 @@ public InetAddress getInetAddress() { public InetAddress getLocalAddress() { return localAddress; } - - private String toUrl(boolean encode) { - if (url == null) { + private String removeTrailingSlash(URI uri) { + String uriString = uri.toString(); + if (uriString.endsWith("/")) { + return uriString.substring(0, uriString.length() - 1); + } else { + return uriString; + } + } + + /* @Override */ + public String getUrl() { + return removeTrailingSlash(getURI()); + } + + /* @Override */ + public String getRawUrl() { + return removeTrailingSlash(getRawURI()); + } + + public URI getOriginalURI() { + return originalUri; + } + + public URI getURI() { + if (uri == null) + uri = toURI(true); + return uri; + } + + public URI getRawURI() { + if (rawUri == null) + rawUri = toURI(false); + return rawUri; + } + + private URI toURI(boolean encode) { + + if (originalUri == null) { logger.debug("setUrl hasn't been invoked. Using http://localhost"); - url = "http://localhost/"; + originalUri = DEFAULT_REQUEST_URL; } - String uri = url; - if (!uri.startsWith("ws")) { - try { - uri = URI.create(url).toURL().toString(); - } catch (Throwable e) { - throw new IllegalArgumentException("Illegal URL: " + url, e); - } + AsyncHttpProviderUtils.validateSupportedScheme(originalUri); + + StringBuilder builder = new StringBuilder(); + builder.append(originalUri.getScheme()).append("://").append(originalUri.getAuthority()); + if (isNonEmpty(originalUri.getRawPath())) { + builder.append(originalUri.getRawPath()); + } else { + builder.append("/"); } - if (queryParams != null && !queryParams.isEmpty()) { + if (isNonEmpty(queryParams)) { - StringBuilder builder = new StringBuilder(); - if (!url.substring(8).contains("/")) { // no other "/" than http[s]:// -> http://localhost:1234 - builder.append("/"); - } builder.append("?"); - for (Iterator>> i = queryParams.iterator(); i.hasNext(); ) { + for (Iterator>> i = queryParams.iterator(); i.hasNext();) { Map.Entry> param = i.next(); String name = param.getKey(); - for (Iterator j = param.getValue().iterator(); j.hasNext(); ) { + for (Iterator j = param.getValue().iterator(); j.hasNext();) { String value = j.next(); if (encode) { UTF8UrlEncoder.appendEncoded(builder, name); } else { builder.append(name); } - if (value != null && !value.equals("")) { + if (value != null) { builder.append('='); if (encode) { UTF8UrlEncoder.appendEncoded(builder, value); @@ -176,14 +211,9 @@ private String toUrl(boolean encode) { builder.append('&'); } } - uri += builder.toString(); } - return uri; - } - /* @Override */ - public String getRawUrl() { - return toUrl(false); + return URI.create(builder.toString()); } /* @Override */ @@ -270,7 +300,7 @@ public boolean isRedirectEnabled() { return (followRedirects != null && followRedirects); } - public boolean isRedirectOverrideSet(){ + public boolean isRedirectOverrideSet() { return followRedirects != null; } @@ -286,17 +316,33 @@ public String getBodyEncoding() { return charset; } + public ConnectionPoolKeyStrategy getConnectionPoolKeyStrategy() { + return connectionPoolKeyStrategy; + } + @Override public String toString() { - StringBuilder sb = new StringBuilder(url); + StringBuilder sb = new StringBuilder(getURI().toString()); sb.append("\t"); sb.append(method); - for (String name : headers.keySet()) { - sb.append("\t"); - sb.append(name); - sb.append(":"); - sb.append(headers.getJoinedValue(name, ", ")); + sb.append("\theaders:"); + if (isNonEmpty(headers)) { + for (String name : headers.keySet()) { + sb.append("\t"); + sb.append(name); + sb.append(":"); + sb.append(headers.getJoinedValue(name, ", ")); + } + } + if (isNonEmpty(params)) { + sb.append("\tparams:"); + for (String name : params.keySet()) { + sb.append("\t"); + sb.append(name); + sb.append(":"); + sb.append(params.getJoinedValue(name, ", ")); + } } return sb.toString(); @@ -325,47 +371,31 @@ protected RequestBuilderBase(Class derived, Request prototype) { } public T setUrl(String url) { - request.url = buildUrl(url); + return setURI(URI.create(url)); + } + + public T setURI(URI uri) { + if (uri.getPath() == null) + throw new IllegalArgumentException("Unsupported uri format: " + uri); + request.originalUri = uri; + addQueryParameters(request.originalUri); + request.uri = null; + request.rawUri = null; return derived.cast(this); } public T setInetAddress(InetAddress address) { - request.address = address; - return derived.cast(this); + request.address = address; + return derived.cast(this); } - + public T setLocalInetAddress(InetAddress address) { request.localAddress = address; return derived.cast(this); } - private String buildUrl(String url) { - URI uri = URI.create(url); - StringBuilder buildedUrl = new StringBuilder(); - - if (uri.getScheme() != null) { - buildedUrl.append(uri.getScheme()); - buildedUrl.append("://"); - } - - if (uri.getAuthority() != null) { - buildedUrl.append(uri.getAuthority()); - } - if (uri.getRawPath() != null) { - buildedUrl.append(uri.getRawPath()); - } else { - // AHC-96 - // Let's try to derive it - if (url.indexOf("://") == -1) { - String s = buildedUrl.toString(); - url = s + url.substring(uri.getScheme().length() + 1); - return buildUrl(url); - } else { - throw new IllegalArgumentException("Invalid url " + uri.toString()); - } - } - - if (uri.getRawQuery() != null && !uri.getRawQuery().equals("")) { + private void addQueryParameters(URI uri) { + if (isNonEmpty(uri.getRawQuery())) { String[] queries = uri.getRawQuery().split("&"); int pos; for (String query : queries) { @@ -374,7 +404,7 @@ private String buildUrl(String url) { addQueryParameter(query, null); } else { try { - if (this.useRawUrl) { + if (useRawUrl) { addQueryParameter(query.substring(0, pos), query.substring(pos + 1)); } else { addQueryParameter(URLDecoder.decode(query.substring(0, pos), "UTF-8"), URLDecoder.decode(query.substring(pos + 1), "UTF-8")); @@ -385,10 +415,8 @@ private String buildUrl(String url) { } } } - return buildedUrl.toString(); } - public T setVirtualHost(String virtualHost) { request.virtualHost = virtualHost; return derived.cast(this); @@ -446,8 +474,8 @@ private void resetMultipartData() { } private void checkIfBodyAllowed() { - if ("GET".equals(request.method) || "HEAD".equals(request.method)) { - throw new IllegalArgumentException("Can NOT set Body on HTTP Request Method GET nor HEAD."); + if ("HEAD".equals(request.method)) { + throw new IllegalArgumentException("Can NOT set Body on HTTP Request Method HEAD."); } } @@ -590,6 +618,11 @@ public T setBodyEncoding(String charset) { return derived.cast(this); } + public T setConnectionPoolKeyStrategy(ConnectionPoolKeyStrategy connectionPoolKeyStrategy) { + request.connectionPoolKeyStrategy = connectionPoolKeyStrategy; + return derived.cast(this); + } + public Request build() { if ((request.length < 0) && (request.streamData == null) && allowBody(request.getMethod())) { // can't concatenate content-length @@ -607,13 +640,7 @@ public Request build() { } private boolean allowBody(String method) { - if (method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("OPTIONS") - && method.equalsIgnoreCase("TRACE") - && method.equalsIgnoreCase("HEAD")) { - return false; - } else { - return true; - } + return !(method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("OPTIONS") || method.equalsIgnoreCase("TRACE") || method.equalsIgnoreCase("HEAD")); } public T addOrReplaceCookie(Cookie cookie) { diff --git a/src/main/java/com/ning/http/client/Response.java b/src/main/java/com/ning/http/client/Response.java index 17da422110..a4b98464b5 100644 --- a/src/main/java/com/ning/http/client/Response.java +++ b/src/main/java/com/ning/http/client/Response.java @@ -20,8 +20,8 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; +import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; @@ -51,6 +51,14 @@ public interface Response { */ public byte[] getResponseBodyAsBytes() throws IOException; + /** + * Return the entire response body as a ByteBuffer. + * + * @return the entire response body as a ByteBuffer. + * @throws IOException + */ + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException; + /** * Returns an input stream for the response body. Note that you should not try to get this more than once, * and that you should not close the stream. @@ -178,8 +186,8 @@ public interface Response { public static class ResponseBuilder { - private final Collection bodies = - Collections.synchronizedCollection(new ArrayList()); + private final List bodies = + Collections.synchronizedList(new ArrayList()); private HttpResponseStatus status; private HttpResponseHeaders headers; diff --git a/src/main/java/com/ning/http/client/SimpleAsyncHttpClient.java b/src/main/java/com/ning/http/client/SimpleAsyncHttpClient.java index ea03362bad..45eb19794f 100644 --- a/src/main/java/com/ning/http/client/SimpleAsyncHttpClient.java +++ b/src/main/java/com/ning/http/client/SimpleAsyncHttpClient.java @@ -68,8 +68,9 @@ public class SimpleAsyncHttpClient { private final ErrorDocumentBehaviour errorDocumentBehaviour; private final SimpleAHCTransferListener listener; private final boolean derived; + private String providerClass; - private SimpleAsyncHttpClient(AsyncHttpClientConfig config, RequestBuilder requestBuilder, ThrowableHandler defaultThrowableHandler, ErrorDocumentBehaviour errorDocumentBehaviour, boolean resumeEnabled, AsyncHttpClient ahc, SimpleAHCTransferListener listener) { + private SimpleAsyncHttpClient(AsyncHttpClientConfig config, RequestBuilder requestBuilder, ThrowableHandler defaultThrowableHandler, ErrorDocumentBehaviour errorDocumentBehaviour, boolean resumeEnabled, AsyncHttpClient ahc, SimpleAHCTransferListener listener, String providerClass) { this.config = config; this.requestBuilder = requestBuilder; this.defaultThrowableHandler = defaultThrowableHandler; @@ -77,6 +78,7 @@ private SimpleAsyncHttpClient(AsyncHttpClientConfig config, RequestBuilder reque this.errorDocumentBehaviour = errorDocumentBehaviour; this.asyncHttpClient = ahc; this.listener = listener; + this.providerClass = providerClass; this.derived = ahc != null; } @@ -287,7 +289,10 @@ private Future execute(RequestBuilder rb, BodyConsumer bodyConsumer, T private AsyncHttpClient asyncHttpClient() { synchronized (config) { if (asyncHttpClient == null) { - asyncHttpClient = new AsyncHttpClient(config); + if (providerClass == null) + asyncHttpClient = new AsyncHttpClient(config); + else + asyncHttpClient = new AsyncHttpClient(providerClass, config); } } return asyncHttpClient; @@ -400,6 +405,7 @@ public final static class Builder implements DerivedBuilder { private ErrorDocumentBehaviour errorDocumentBehaviour = ErrorDocumentBehaviour.WRITE; private AsyncHttpClient ahc = null; private SimpleAHCTransferListener listener = null; + private String providerClass = null; public Builder() { requestBuilder = new RequestBuilder("GET", false); @@ -659,6 +665,11 @@ public Builder setMaxRequestRetry(int maxRequestRetry) { return this; } + public Builder setProviderClass(String providerClass) { + this.providerClass = providerClass; + return this; + } + public SimpleAsyncHttpClient build() { if (realmBuilder != null) { @@ -671,7 +682,7 @@ public SimpleAsyncHttpClient build() { configBuilder.addIOExceptionFilter(new ResumableIOExceptionFilter()); - SimpleAsyncHttpClient sc = new SimpleAsyncHttpClient(configBuilder.build(), requestBuilder, defaultThrowableHandler, errorDocumentBehaviour, enableResumableDownload, ahc, listener); + SimpleAsyncHttpClient sc = new SimpleAsyncHttpClient(configBuilder.build(), requestBuilder, defaultThrowableHandler, errorDocumentBehaviour, enableResumableDownload, ahc, listener, providerClass); return sc; } diff --git a/src/main/java/com/ning/http/client/consumers/AppendableBodyConsumer.java b/src/main/java/com/ning/http/client/consumers/AppendableBodyConsumer.java index ef04f9b8da..a1e9dc5ade 100644 --- a/src/main/java/com/ning/http/client/consumers/AppendableBodyConsumer.java +++ b/src/main/java/com/ning/http/client/consumers/AppendableBodyConsumer.java @@ -41,7 +41,10 @@ public AppendableBodyConsumer(Appendable appendable) { */ /* @Override */ public void consume(ByteBuffer byteBuffer) throws IOException { - appendable.append(new String(byteBuffer.array(), encoding)); + appendable.append(new String(byteBuffer.array(), + byteBuffer.arrayOffset() + byteBuffer.position(), + byteBuffer.remaining(), + encoding)); } /** @@ -49,7 +52,7 @@ public void consume(ByteBuffer byteBuffer) throws IOException { */ /* @Override */ public void close() throws IOException { - if (Closeable.class.isAssignableFrom(appendable.getClass())) { + if (appendable instanceof Closeable) { Closeable.class.cast(appendable).close(); } } diff --git a/src/main/java/com/ning/http/client/consumers/FileBodyConsumer.java b/src/main/java/com/ning/http/client/consumers/FileBodyConsumer.java index ad8b7e288f..02a12d65fd 100644 --- a/src/main/java/com/ning/http/client/consumers/FileBodyConsumer.java +++ b/src/main/java/com/ning/http/client/consumers/FileBodyConsumer.java @@ -35,7 +35,9 @@ public FileBodyConsumer(RandomAccessFile file) { /* @Override */ public void consume(ByteBuffer byteBuffer) throws IOException { // TODO: Channel.transferFrom may be a good idea to investigate. - file.write(byteBuffer.array()); + file.write(byteBuffer.array(), + byteBuffer.arrayOffset() + byteBuffer.position(), + byteBuffer.remaining()); } /** diff --git a/src/main/java/com/ning/http/client/consumers/OutputStreamBodyConsumer.java b/src/main/java/com/ning/http/client/consumers/OutputStreamBodyConsumer.java index d1e806ca43..9f8c93aec1 100644 --- a/src/main/java/com/ning/http/client/consumers/OutputStreamBodyConsumer.java +++ b/src/main/java/com/ning/http/client/consumers/OutputStreamBodyConsumer.java @@ -34,7 +34,9 @@ public OutputStreamBodyConsumer(OutputStream outputStream) { */ /* @Override */ public void consume(ByteBuffer byteBuffer) throws IOException { - outputStream.write(byteBuffer.array()); + outputStream.write(byteBuffer.array(), + byteBuffer.arrayOffset() + byteBuffer.position(), + byteBuffer.remaining()); } /** diff --git a/src/main/java/com/ning/http/client/extra/ListenableFutureAdapter.java b/src/main/java/com/ning/http/client/extra/ListenableFutureAdapter.java new file mode 100644 index 0000000000..7d32343fca --- /dev/null +++ b/src/main/java/com/ning/http/client/extra/ListenableFutureAdapter.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.http.client.extra; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.ning.http.client.ListenableFuture; + +public final class ListenableFutureAdapter { + + /** + * @param future an AHC ListenableFuture + * @return a Guava ListenableFuture + */ + public static com.google.common.util.concurrent.ListenableFuture asGuavaFuture(final ListenableFuture future) { + + return new com.google.common.util.concurrent.ListenableFuture() { + + public boolean cancel(boolean mayInterruptIfRunning) { + return future.cancel(mayInterruptIfRunning); + } + + public V get() throws InterruptedException, ExecutionException { + return future.get(); + } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return future.get(timeout, unit); + } + + public boolean isCancelled() { + return future.isCancelled(); + } + + public boolean isDone() { + return future.isDone(); + } + + public void addListener(final Runnable runnable, final Executor executor) { + future.addListener(runnable, executor); + } + }; + } +} diff --git a/src/main/java/com/ning/http/client/generators/InputStreamBodyGenerator.java b/src/main/java/com/ning/http/client/generators/InputStreamBodyGenerator.java index 12660ca438..799a74615a 100644 --- a/src/main/java/com/ning/http/client/generators/InputStreamBodyGenerator.java +++ b/src/main/java/com/ning/http/client/generators/InputStreamBodyGenerator.java @@ -43,7 +43,7 @@ public InputStreamBodyGenerator(InputStream inputStream) { if (inputStream.markSupported()) { inputStream.mark(0); } else { - logger.warn("inputStream.markSupported() not supported. Some features will not works"); + logger.info("inputStream.markSupported() not supported. Some features will not work."); } } @@ -79,7 +79,7 @@ public long read(ByteBuffer buffer) throws IOException { if (patchNettyChunkingIssue) { if (read == -1) { - // Since we are chuncked, we must output extra bytes before considering the input stream closed. + // Since we are chunked, we must output extra bytes before considering the input stream closed. // chunking requires to end the chunking: // - A Terminating chunk of "0\r\n".getBytes(), // - Then a separate packet of "\r\n".getBytes() @@ -117,6 +117,10 @@ public long read(ByteBuffer buffer) throws IOException { } else { if (read > 0) { buffer.put(chunk, 0, read); + } else { + if (inputStream.markSupported()) { + inputStream.reset(); + } } } return read; diff --git a/src/main/java/com/ning/http/client/listenable/AbstractListenableFuture.java b/src/main/java/com/ning/http/client/listenable/AbstractListenableFuture.java index a0f9575e6a..16b2f94352 100644 --- a/src/main/java/com/ning/http/client/listenable/AbstractListenableFuture.java +++ b/src/main/java/com/ning/http/client/listenable/AbstractListenableFuture.java @@ -63,7 +63,7 @@ public ListenableFuture addListener(Runnable listener, Executor exec) { /* * Override the done method to execute the execution list. */ - protected void done() { + protected void runListeners() { executionList.run(); } } diff --git a/src/main/java/com/ning/http/client/listener/TransferCompletionHandler.java b/src/main/java/com/ning/http/client/listener/TransferCompletionHandler.java index 6d71cbd238..5088f12577 100644 --- a/src/main/java/com/ning/http/client/listener/TransferCompletionHandler.java +++ b/src/main/java/com/ning/http/client/listener/TransferCompletionHandler.java @@ -26,6 +26,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicLong; +import static com.ning.http.util.MiscUtil.isNonEmpty; + /** * A {@link com.ning.http.client.AsyncHandler} that can be used to notify a set of {@link com.ning.http.client.listener.TransferListener} *

@@ -144,7 +146,7 @@ public Response onCompleted(Response response) throws Exception { */ public STATE onHeaderWriteCompleted() { List list = transferAdapter.getHeaders().get("Content-Length"); - if (list != null && list.size() > 0 && list.get(0) != "") { + if (isNonEmpty(list) && list.get(0) != "") { totalBytesToTransfer.set(Long.valueOf(list.get(0))); } diff --git a/src/main/java/com/ning/http/client/ntlm/NTLMEngine.java b/src/main/java/com/ning/http/client/ntlm/NTLMEngine.java index a56c9bf141..00023369ef 100644 --- a/src/main/java/com/ning/http/client/ntlm/NTLMEngine.java +++ b/src/main/java/com/ning/http/client/ntlm/NTLMEngine.java @@ -38,16 +38,19 @@ package com.ning.http.client.ntlm; -import com.ning.http.util.Base64; +import static com.ning.http.util.MiscUtil.isNonEmpty; -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.Key; import java.security.MessageDigest; import java.util.Arrays; import java.util.Locale; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import com.ning.http.util.Base64; + /** * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM * authentication protocol. @@ -123,12 +126,12 @@ final String getResponseFor(String message, String username, String password, String host, String domain) throws NTLMEngineException { final String response; - if (message == null || message.trim().equals("")) { - response = getType1Message(host, domain); - } else { + if (isNonEmpty(message)) { Type2Message t2m = new Type2Message(message); response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m .getFlags(), t2m.getTarget(), t2m.getTargetInfo()); + } else { + response = getType1Message(host, domain); } return response; } diff --git a/src/main/java/com/ning/http/client/oauth/OAuthSignatureCalculator.java b/src/main/java/com/ning/http/client/oauth/OAuthSignatureCalculator.java index e33ad87c73..4e363745e0 100644 --- a/src/main/java/com/ning/http/client/oauth/OAuthSignatureCalculator.java +++ b/src/main/java/com/ning/http/client/oauth/OAuthSignatureCalculator.java @@ -16,7 +16,6 @@ */ package com.ning.http.client.oauth; - import com.ning.http.client.FluentStringsMap; import com.ning.http.client.Request; import com.ning.http.client.RequestBuilderBase; diff --git a/src/main/java/com/ning/http/client/oauth/ThreadSafeHMAC.java b/src/main/java/com/ning/http/client/oauth/ThreadSafeHMAC.java index 0157a9df77..7ba72dd1a6 100644 --- a/src/main/java/com/ning/http/client/oauth/ThreadSafeHMAC.java +++ b/src/main/java/com/ning/http/client/oauth/ThreadSafeHMAC.java @@ -17,6 +17,7 @@ package com.ning.http.client.oauth; import com.ning.http.util.UTF8Codec; +import com.ning.http.util.UTF8UrlEncoder; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -36,7 +37,7 @@ public class ThreadSafeHMAC { private final Mac mac; public ThreadSafeHMAC(ConsumerKey consumerAuth, RequestToken userAuth) { - byte[] keyBytes = UTF8Codec.toUTF8(consumerAuth.getSecret() + "&" + userAuth.getSecret()); + byte[] keyBytes = UTF8Codec.toUTF8(UTF8UrlEncoder.encode(consumerAuth.getSecret()) + "&" + UTF8UrlEncoder.encode(userAuth.getSecret())); SecretKeySpec signingKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM); // Get an hmac_sha1 instance and initialize with the signing key diff --git a/src/main/java/com/ning/http/client/providers/apache/ApacheAsyncHttpProvider.java b/src/main/java/com/ning/http/client/providers/apache/ApacheAsyncHttpProvider.java index 78abf16415..a125d1ba9d 100644 --- a/src/main/java/com/ning/http/client/providers/apache/ApacheAsyncHttpProvider.java +++ b/src/main/java/com/ning/http/client/providers/apache/ApacheAsyncHttpProvider.java @@ -12,6 +12,8 @@ */ package com.ning.http.client.providers.apache; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.AsyncHandler; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProvider; @@ -47,7 +49,6 @@ import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; @@ -102,7 +103,6 @@ import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -158,7 +158,7 @@ public ApacheAsyncHttpProvider(AsyncHttpClientConfig config) { params.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler()); AsyncHttpProviderConfig providerConfig = config.getAsyncHttpProviderConfig(); - if (providerConfig != null && ApacheAsyncHttpProvider.class.isAssignableFrom(providerConfig.getClass())) { + if (providerConfig instanceof ApacheAsyncHttpProvider) { configure(ApacheAsyncHttpProviderConfig.class.cast(providerConfig)); } } @@ -171,7 +171,7 @@ public ListenableFuture execute(Request request, AsyncHandler handler) throw new IOException("Closed"); } - if (ResumableAsyncHandler.class.isAssignableFrom(handler.getClass())) { + if (handler instanceof ResumableAsyncHandler) { request = ResumableAsyncHandler.class.cast(handler).adjustRequestRange(request); } @@ -224,7 +224,7 @@ public void close() { } } - public Response prepareResponse(HttpResponseStatus status, HttpResponseHeaders headers, Collection bodyParts) { + public Response prepareResponse(HttpResponseStatus status, HttpResponseHeaders headers, List bodyParts) { return new ApacheResponse(status, headers, bodyParts); } @@ -340,13 +340,12 @@ private HttpMethodBase createMethod(HttpClient client, Request request) throws I throw new IllegalStateException(String.format("Invalid Method", methodName)); } - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); - boolean avoidProxy = ProxyUtils.avoidProxy(proxyServer, request); - if (!avoidProxy) { + ProxyServer proxyServer = ProxyUtils.getProxyServer(config, request); + if (proxyServer != null) { if (proxyServer.getPrincipal() != null) { Credentials defaultcreds = new UsernamePasswordCredentials(proxyServer.getPrincipal(), proxyServer.getPassword()); - client.getState().setCredentials(new AuthScope(null, -1, AuthScope.ANY_REALM), defaultcreds); + client.getState().setProxyCredentials(new AuthScope(null, -1, AuthScope.ANY_REALM), defaultcreds); } ProxyHost proxyHost = proxyServer == null ? null : new ProxyHost(proxyServer.getHost(), proxyServer.getPort()); @@ -357,7 +356,7 @@ private HttpMethodBase createMethod(HttpClient client, Request request) throws I } method.setFollowRedirects(false); - if ((request.getCookies() != null) && !request.getCookies().isEmpty()) { + if (isNonEmpty(request.getCookies())) { for (Cookie cookie : request.getCookies()) { method.setRequestHeader("Cookie", AsyncHttpProviderUtils.encodeCookies(request.getCookies())); } @@ -461,7 +460,7 @@ public T call() { future.setReaperFuture(reaperFuture); } - if (TransferCompletionHandler.class.isAssignableFrom(asyncHandler.getClass())) { + if (asyncHandler instanceof TransferCompletionHandler) { throw new IllegalStateException(TransferCompletionHandler.class.getName() + "not supported by this provider"); } @@ -579,9 +578,10 @@ public T call() { } } - if (ProgressAsyncHandler.class.isAssignableFrom(asyncHandler.getClass())) { - ProgressAsyncHandler.class.cast(asyncHandler).onHeaderWriteCompleted(); - ProgressAsyncHandler.class.cast(asyncHandler).onContentWriteCompleted(); + if (asyncHandler instanceof ProgressAsyncHandler) { + ProgressAsyncHandler progressAsyncHandler = (ProgressAsyncHandler) asyncHandler; + progressAsyncHandler.onHeaderWriteCompleted(); + progressAsyncHandler.onContentWriteCompleted(); } try { @@ -593,7 +593,7 @@ public T call() { } } catch (Throwable t) { - if (IOException.class.isAssignableFrom(t.getClass()) && config.getIOExceptionFilters().size() > 0) { + if (t instanceof IOException && !config.getIOExceptionFilters().isEmpty()) { FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(asyncHandler) .request(future.getRequest()).ioException(IOException.class.cast(t)).build(); @@ -603,7 +603,7 @@ public T call() { if (config.getMaxTotalConnections() != -1) { maxConnections.decrementAndGet(); } - future.done(null); + future.done(); method.releaseConnection(); } @@ -629,7 +629,7 @@ public T call() { if (config.getMaxTotalConnections() != -1) { maxConnections.decrementAndGet(); } - future.done(null); + future.done(); // Crappy Apache HttpClient who blocks forever here with large files. config.executorService().submit(new Runnable() { @@ -644,20 +644,18 @@ public void run() { } private Throwable filterException(Throwable t) { - if (UnknownHostException.class.isAssignableFrom(t.getClass())) { + if (t instanceof UnknownHostException) { t = new ConnectException(t.getMessage()); - } - if (NoHttpResponseException.class.isAssignableFrom(t.getClass())) { + } else if (t instanceof NoHttpResponseException) { int responseTimeoutInMs = config.getRequestTimeoutInMs(); if (request.getPerRequestConfig() != null && request.getPerRequestConfig().getRequestTimeoutInMs() != -1) { responseTimeoutInMs = request.getPerRequestConfig().getRequestTimeoutInMs(); } t = new TimeoutException(String.format("No response received after %s", responseTimeoutInMs)); - } - if (SSLHandshakeException.class.isAssignableFrom(t.getClass())) { + } else if (t instanceof SSLHandshakeException) { Throwable t2 = new ConnectException(); t2.initCause(t); t = t2; diff --git a/src/main/java/com/ning/http/client/providers/apache/ApacheResponse.java b/src/main/java/com/ning/http/client/providers/apache/ApacheResponse.java index 9516f89ee4..988962dbdd 100644 --- a/src/main/java/com/ning/http/client/providers/apache/ApacheResponse.java +++ b/src/main/java/com/ning/http/client/providers/apache/ApacheResponse.java @@ -12,40 +12,40 @@ */ package com.ning.http.client.providers.apache; +import static com.ning.http.util.MiscUtil.isNonEmpty; + +import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder; import com.ning.http.client.Cookie; import com.ning.http.client.FluentCaseInsensitiveStringsMap; import com.ning.http.client.HttpResponseBodyPart; -import com.ning.http.client.HttpResponseBodyPartsInputStream; import com.ning.http.client.HttpResponseHeaders; import com.ning.http.client.HttpResponseStatus; import com.ning.http.client.Response; import com.ning.http.util.AsyncHttpProviderUtils; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; +import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; - +import java.util.Set; public class ApacheResponse implements Response { private final static String DEFAULT_CHARSET = "ISO-8859-1"; - private final static String HEADERS_NOT_COMPUTED = "Response's headers hasn't been computed by your AsyncHandler."; private final URI uri; - private final Collection bodyParts; + private final List bodyParts; private final HttpResponseHeaders headers; private final HttpResponseStatus status; - private final List cookies = new ArrayList(); + private List cookies; public ApacheResponse(HttpResponseStatus status, HttpResponseHeaders headers, - Collection bodyParts) { + List bodyParts) { this.bodyParts = bodyParts; this.headers = headers; @@ -71,31 +71,22 @@ public byte[] getResponseBodyAsBytes() throws IOException { return AsyncHttpProviderUtils.contentToByte(bodyParts); } + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException { + return ByteBuffer.wrap(getResponseBodyAsBytes()); + } + /* @Override */ public String getResponseBody() throws IOException { return getResponseBody(DEFAULT_CHARSET); } public String getResponseBody(String charset) throws IOException { - String contentType = getContentType(); - if (contentType != null && charset == null) { - charset = AsyncHttpProviderUtils.parseCharset(contentType); - } - - if (charset == null) { - charset = DEFAULT_CHARSET; - } - - return AsyncHttpProviderUtils.contentToString(bodyParts, charset); + return AsyncHttpProviderUtils.contentToString(bodyParts, computeCharset(charset)); } - + /* @Override */ public InputStream getResponseBodyAsStream() throws IOException { - if (bodyParts.size() > 0) { - return new HttpResponseBodyPartsInputStream(bodyParts.toArray(new HttpResponseBodyPart[bodyParts.size()])); - } else { - return new ByteArrayInputStream("".getBytes()); - } + return AsyncHttpProviderUtils.contentToInputStream(bodyParts); } /* @Override */ @@ -107,18 +98,20 @@ public String getResponseBodyExcerpt(int maxLength) throws IOException { /* @Override */ public String getResponseBodyExcerpt(int maxLength, String charset) throws IOException { - String contentType = getContentType(); - if (contentType != null && charset == null) { - charset = AsyncHttpProviderUtils.parseCharset(contentType); - } - - if (charset == null) { - charset = DEFAULT_CHARSET; - } + charset = computeCharset(charset); String response = AsyncHttpProviderUtils.contentToString(bodyParts, charset); return response.length() <= maxLength ? response : response.substring(0, maxLength); } + + private String computeCharset(String charset) { + if (charset == null) { + String contentType = getContentType(); + if (contentType != null) + charset = AsyncHttpProviderUtils.parseCharset(contentType); // parseCharset can return null + } + return charset != null? charset: DEFAULT_CHARSET; + } /* @Override */ @@ -129,64 +122,63 @@ public URI getUri() throws MalformedURLException { /* @Override */ public String getContentType() { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders().getFirstValue("Content-Type"); + return getHeader("Content-Type"); } /* @Override */ public String getHeader(String name) { - if (headers == null) { - throw new IllegalStateException(); - } - return headers.getHeaders().getFirstValue(name); + return headers != null? headers.getHeaders().getFirstValue(name): null; } /* @Override */ public List getHeaders(String name) { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders().get(name); + return headers != null? headers.getHeaders().get(name): Collections. emptyList(); } /* @Override */ public FluentCaseInsensitiveStringsMap getHeaders() { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders(); + return headers != null? headers.getHeaders(): new FluentCaseInsensitiveStringsMap(); } /* @Override */ public boolean isRedirected() { - return (status.getStatusCode() >= 300) && (status.getStatusCode() <= 399); + switch (status.getStatusCode()) { + case 301: + case 302: + case 303: + case 307: + case 308: + return true; + default: + return false; + } } /* @Override */ public List getCookies() { if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); + return Collections.emptyList(); } - if (cookies.isEmpty()) { + if (cookies == null) { + List localCookies = new ArrayList(); for (Map.Entry> header : headers.getHeaders().entrySet()) { if (header.getKey().equalsIgnoreCase("Set-Cookie")) { // TODO: ask for parsed header List v = header.getValue(); for (String value : v) { - Cookie cookie = AsyncHttpProviderUtils.parseCookie(value); - cookies.add(cookie); + Set cookies = CookieDecoder.decode(value); + localCookies.addAll(cookies); } } } + cookies = Collections.unmodifiableList(localCookies); } - return Collections.unmodifiableList(cookies); + return cookies; } /** @@ -194,7 +186,7 @@ public List getCookies() { */ /* @Override */ public boolean hasResponseStatus() { - return (bodyParts != null ? true : false); + return bodyParts != null; } /** @@ -202,7 +194,7 @@ public boolean hasResponseStatus() { */ /* @Override */ public boolean hasResponseHeaders() { - return (headers != null ? true : false); + return headers != null; } /** @@ -210,6 +202,6 @@ public boolean hasResponseHeaders() { */ /* @Override */ public boolean hasResponseBody() { - return (bodyParts != null && bodyParts.size() > 0 ? true : false); + return isNonEmpty(bodyParts); } } diff --git a/src/main/java/com/ning/http/client/providers/apache/ApacheResponseFuture.java b/src/main/java/com/ning/http/client/providers/apache/ApacheResponseFuture.java index 0b8abff3e9..cdbe106c92 100644 --- a/src/main/java/com/ning/http/client/providers/apache/ApacheResponseFuture.java +++ b/src/main/java/com/ning/http/client/providers/apache/ApacheResponseFuture.java @@ -12,6 +12,8 @@ */ package com.ning.http.client.providers.apache; +import static com.ning.http.util.DateUtil.millisTime; + import com.ning.http.client.AsyncHandler; import com.ning.http.client.Request; import com.ning.http.client.listenable.AbstractListenableFuture; @@ -19,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -41,7 +42,7 @@ public class ApacheResponseFuture extends AbstractListenableFuture { private final AtomicBoolean timedOut = new AtomicBoolean(false); private final AtomicBoolean isDone = new AtomicBoolean(false); private final AtomicReference exception = new AtomicReference(); - private final AtomicLong touch = new AtomicLong(System.currentTimeMillis()); + private final AtomicLong touch = new AtomicLong(millisTime()); private final AtomicBoolean contentProcessed = new AtomicBoolean(false); private final Request request; private final HttpMethodBase method; @@ -62,12 +63,12 @@ protected void setInnerFuture(Future innerFuture) { this.innerFuture = innerFuture; } - public void done(Callable callable) { + public void done() { isDone.set(true); if (reaperFuture != null) { reaperFuture.cancel(true); } - super.done(); + runListeners(); } /** @@ -123,7 +124,7 @@ public void abort(Throwable t) { logger.debug("asyncHandler.onThrowable", t2); } } - super.done(); + runListeners(); } public boolean cancel(boolean mayInterruptIfRunning) { @@ -138,10 +139,10 @@ public boolean cancel(boolean mayInterruptIfRunning) { if (reaperFuture != null) { reaperFuture.cancel(true); } - super.done(); + runListeners(); return innerFuture.cancel(mayInterruptIfRunning); } else { - super.done(); + runListeners(); return false; } } @@ -174,7 +175,7 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution content = innerFuture.get(timeout, unit); } } catch (TimeoutException t) { - if (!contentProcessed.get() && timeout != -1 && ((System.currentTimeMillis() - touch.get()) <= responseTimeoutInMs)) { + if (!contentProcessed.get() && timeout != -1 && ((millisTime() - touch.get()) <= responseTimeoutInMs)) { return get(timeout, unit); } @@ -197,11 +198,11 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution * @return true if response has expired and should be terminated. */ public boolean hasExpired() { - return responseTimeoutInMs != -1 && ((System.currentTimeMillis() - touch.get()) >= responseTimeoutInMs); + return responseTimeoutInMs != -1 && ((millisTime() - touch.get()) >= responseTimeoutInMs); } public void touch() { - touch.set(System.currentTimeMillis()); + touch.set(millisTime()); } public Request getRequest() { diff --git a/src/main/java/com/ning/http/client/providers/grizzly/FeedableBodyGenerator.java b/src/main/java/com/ning/http/client/providers/grizzly/FeedableBodyGenerator.java index 4e509964db..e9d826b651 100644 --- a/src/main/java/com/ning/http/client/providers/grizzly/FeedableBodyGenerator.java +++ b/src/main/java/com/ning/http/client/providers/grizzly/FeedableBodyGenerator.java @@ -14,73 +14,233 @@ import com.ning.http.client.Body; import com.ning.http.client.BodyGenerator; + import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ExecutionException; + import org.glassfish.grizzly.Buffer; +import org.glassfish.grizzly.CompletionHandler; +import org.glassfish.grizzly.Connection; +import org.glassfish.grizzly.WriteHandler; +import org.glassfish.grizzly.WriteResult; +import org.glassfish.grizzly.filterchain.FilterChain; import org.glassfish.grizzly.filterchain.FilterChainContext; import org.glassfish.grizzly.http.HttpContent; import org.glassfish.grizzly.http.HttpRequestPacket; +import org.glassfish.grizzly.impl.FutureImpl; +import org.glassfish.grizzly.nio.NIOConnection; +import org.glassfish.grizzly.nio.SelectorRunner; +import org.glassfish.grizzly.ssl.SSLBaseFilter; +import org.glassfish.grizzly.ssl.SSLFilter; +import org.glassfish.grizzly.utils.Futures; + +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider.getHttpTransactionContext; +import static java.lang.Boolean.TRUE; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.glassfish.grizzly.ssl.SSLUtils.getSSLEngine; +import static org.glassfish.grizzly.utils.Exceptions.*; /** - * {@link BodyGenerator} which may return just part of the payload at the time - * handler is requesting it. If it happens - PartialBodyGenerator becomes responsible - * for finishing payload transferring asynchronously. + * A Grizzly-specific {@link BodyGenerator} that allows data to be fed to the + * connection in blocking or non-blocking fashion via the use of a {@link Feeder}. + * + * This class provides two {@link Feeder} implementations for rapid prototyping. + * First is the {@link SimpleFeeder} which is simply a listener that asynchronous + * data transferring has been initiated. The second is the {@link NonBlockingFeeder} + * which allows reading and feeding data in a non-blocking fashion. * * @author The Grizzly Team * @since 1.7.0 */ public class FeedableBodyGenerator implements BodyGenerator { - private final Queue queue = new ConcurrentLinkedQueue(); - private final AtomicInteger queueSize = new AtomicInteger(); - + + /** + * There is no limit on bytes waiting to be written. This configuration + * value should be used with caution as it could lead to out-of-memory + * conditions. + */ + @SuppressWarnings("UnusedDeclaration") + public static final int UNBOUND = -1; + + /** + * Defer to whatever the connection has been configured for max pending bytes. + */ + public static final int DEFAULT = -2; + private volatile HttpRequestPacket requestPacket; private volatile FilterChainContext context; - + private volatile HttpContent.Builder contentBuilder; + + private final EmptyBody EMPTY_BODY = new EmptyBody(); + + private Feeder feeder; + private int origMaxPendingBytes; + private int configuredMaxPendingBytes = DEFAULT; + private boolean asyncTransferInitiated; + + + // ---------------------------------------------- Methods from BodyGenerator + + + /** + * {@inheritDoc} + */ @Override public Body createBody() throws IOException { - return new EmptyBody(); + return EMPTY_BODY; } - - public void feed(final Buffer buffer, final boolean isLast) - throws IOException { - queue.offer(new BodyPart(buffer, isLast)); - queueSize.incrementAndGet(); - - if (context != null) { - flushQueue(); + + + // ---------------------------------------------------------- Public Methods + + + /** + * Configured the maximum number of bytes that may be pending to be written + * to the wire. If not explicitly configured, the connection's current + * configuration will be used instead. + * + * Once all data has been fed, the connection's max pending bytes configuration + * will be restored to its original value. + * + * @param maxPendingBytes maximum number of bytes that may be queued to + * be written to the wire. + * + * @throws IllegalStateException if called after {@link #initializeAsynchronousTransfer(FilterChainContext, HttpRequestPacket)} + * has been called by the {@link GrizzlyAsyncHttpProvider}. + * @throws IllegalArgumentException if maxPendingBytes is less than zero and is + * not {@link #UNBOUND} or {@link #DEFAULT}. + */ + @SuppressWarnings("UnusedDeclaration") + public synchronized void setMaxPendingBytes(final int maxPendingBytes) { + if (maxPendingBytes < DEFAULT) { + throw new IllegalArgumentException("Invalid maxPendingBytes value: " + maxPendingBytes); + } + if (asyncTransferInitiated) { + throw new IllegalStateException("Unable to set max pending bytes after async data transfer has been initiated."); } + configuredMaxPendingBytes = maxPendingBytes; } + + + /** + * Add a {@link Feeder} implementation that will be invoked when writing + * without blocking is possible. This method must be set before dispatching + * the request this feeder is associated with. + * + * @param feeder the {@link Feeder} responsible for providing data. + * + * @throws IllegalStateException if called after {@link #initializeAsynchronousTransfer(FilterChainContext, HttpRequestPacket)} + * has been called by the {@link GrizzlyAsyncHttpProvider}. + * @throws IllegalArgumentException if feeder is null + */ + @SuppressWarnings("UnusedDeclaration") + public synchronized void setFeeder(final Feeder feeder) { + if (asyncTransferInitiated) { + throw new IllegalStateException("Unable to set Feeder after async data transfer has been initiated."); + } + if (feeder == null) { + throw new IllegalArgumentException("Feeder argument cannot be null."); + } + this.feeder = feeder; + } + + + // ------------------------------------------------- Package Private Methods + - void initializeAsynchronousTransfer(final FilterChainContext context, - final HttpRequestPacket requestPacket) throws IOException { - this.context = context; + synchronized void initializeAsynchronousTransfer(final FilterChainContext context, + final HttpRequestPacket requestPacket) + throws IOException { + + if (asyncTransferInitiated) { + throw new IllegalStateException("Async transfer has already been initiated."); + } + if (feeder == null) { + throw new IllegalStateException("No feeder available to perform the transfer."); + } + assert (context != null); + assert (requestPacket != null); + this.requestPacket = requestPacket; - flushQueue(); + this.contentBuilder = HttpContent.builder(requestPacket); + final Connection c = context.getConnection(); + origMaxPendingBytes = c.getMaxAsyncWriteQueueSize(); + if (configuredMaxPendingBytes != DEFAULT) { + c.setMaxAsyncWriteQueueSize(configuredMaxPendingBytes); + } + this.context = context; + asyncTransferInitiated = true; + final Runnable r = new Runnable() { + @Override + public void run() { + try { + if (requestPacket.isSecure() && + (getSSLEngine(context.getConnection()) == null)) { + flushOnSSLHandshakeComplete(); + } else { + feeder.flush(); + } + } catch (IOException ioe) { + GrizzlyAsyncHttpProvider.HttpTransactionContext ctx = + GrizzlyAsyncHttpProvider.getHttpTransactionContext( + c); + ctx.abort(ioe); + } + } + }; + + // If the current thread is a selector thread, we need to execute + // the remainder of the task on the worker thread to prevent + // it from being blocked. + if (isCurrentThreadSelectorRunner()) { + c.getTransport().getWorkerThreadPool().execute(r); + } else { + r.run(); + } } - private void flushQueue() throws IOException { - if (queueSize.get() > 0) { - synchronized(this) { - while(queueSize.get() > 0) { - final BodyPart bodyPart = queue.poll(); - queueSize.decrementAndGet(); - final HttpContent content = - requestPacket.httpContentBuilder() - .content(bodyPart.buffer) - .last(bodyPart.isLast) - .build(); - context.write(content, ((!requestPacket.isCommitted()) ? - context.getTransportContext().getCompletionHandler() : - null)); - + + // --------------------------------------------------------- Private Methods + + + private boolean isCurrentThreadSelectorRunner() { + final NIOConnection c = (NIOConnection) context.getConnection(); + final SelectorRunner runner = c.getSelectorRunner(); + return (Thread.currentThread() == runner.getRunnerThread()); + } + + + private void flushOnSSLHandshakeComplete() throws IOException { + final FilterChain filterChain = context.getFilterChain(); + final int idx = filterChain.indexOfType(SSLFilter.class); + assert (idx != -1); + final SSLFilter filter = (SSLFilter) filterChain.get(idx); + final Connection c = context.getConnection(); + filter.addHandshakeListener(new SSLBaseFilter.HandshakeListener() { + public void onStart(Connection connection) { + } + + public void onComplete(Connection connection) { + if (c.equals(connection)) { + filter.removeHandshakeListener(this); + try { + feeder.flush(); + } catch (IOException ioe) { + GrizzlyAsyncHttpProvider.HttpTransactionContext ctx = + GrizzlyAsyncHttpProvider.getHttpTransactionContext(c); + ctx.abort(ioe); + } } } - } + }); + filter.handshake(context.getConnection(), null); } - + + + // ----------------------------------------------------------- Inner Classes + + private final class EmptyBody implements Body { @Override @@ -98,16 +258,406 @@ public void close() throws IOException { context.completeAndRecycle(); context = null; requestPacket = null; + contentBuilder = null; } - } - - private final static class BodyPart { - private final boolean isLast; - private final Buffer buffer; - public BodyPart(final Buffer buffer, final boolean isLast) { - this.buffer = buffer; - this.isLast = isLast; + } // END EmptyBody + + + // ---------------------------------------------------------- Nested Classes + + + /** + * Specifies the functionality all Feeders must implement. Typically, + * developers need not worry about implementing this interface directly. + * It should be sufficient, for most use-cases, to simply use the {@link NonBlockingFeeder} + * or {@link SimpleFeeder} implementations. + */ + public interface Feeder { + + /** + * This method will be invoked when it's possible to begin feeding + * data downstream. Implementations of this method must use {@link #feed(Buffer, boolean)} + * to perform the actual write. + * + * @throws IOException if an I/O error occurs. + */ + void flush() throws IOException; + + /** + * This method will write the specified {@link Buffer} to the connection. + * Be aware that this method may block depending if data is being fed + * faster than it can write. How much data may be queued is dictated + * by {@link #setMaxPendingBytes(int)}. Once this threshold is exceeded, + * the method will block until the write queue length drops below the + * aforementioned threshold. + * + * @param buffer the {@link Buffer} to write. + * @param last flag indicating if this is the last buffer to send. + * + * @throws IOException if an I/O error occurs. + * @throws java.lang.IllegalArgumentException if buffer + * is null. + * @throws java.lang.IllegalStateException if this method is invoked + * before asynchronous transferring has been initiated. + * + * @see #setMaxPendingBytes(int) + */ + void feed(final Buffer buffer, final boolean last) throws IOException; + + } // END Feeder + + + /** + * Base class for {@link Feeder} implementations. This class provides + * an implementation for the contract defined by the {@link #feed} method. + */ + public static abstract class BaseFeeder implements Feeder { + + protected final FeedableBodyGenerator feedableBodyGenerator; + + + // -------------------------------------------------------- Constructors + + + protected BaseFeeder(FeedableBodyGenerator feedableBodyGenerator) { + this.feedableBodyGenerator = feedableBodyGenerator; } - } + + + // --------------------------------------------- Package Private Methods + + + /** + * {@inheritDoc} + */ + @SuppressWarnings("UnusedDeclaration") + public final synchronized void feed(final Buffer buffer, final boolean last) + throws IOException { + if (buffer == null) { + throw new IllegalArgumentException( + "Buffer argument cannot be null."); + } + if (!feedableBodyGenerator.asyncTransferInitiated) { + throw new IllegalStateException("Asynchronous transfer has not been initiated."); + } + blockUntilQueueFree(feedableBodyGenerator.context.getConnection()); + final HttpContent content = + feedableBodyGenerator.contentBuilder.content(buffer).last(last).build(); + final CompletionHandler handler = + ((last) ? new LastPacketCompletionHandler() : null); + feedableBodyGenerator.context.write(content, handler); + } + + /** + * This method will block if the async write queue is currently larger + * than the configured maximum. The amount of time that this method + * will block is dependent on the write timeout of the transport + * associated with the specified connection. + */ + private static void blockUntilQueueFree(final Connection c) { + if (!c.canWrite()) { + final FutureImpl future = + Futures.createSafeFuture(); + // Connection may be obtained by calling FilterChainContext.getConnection(). + c.notifyCanWrite(new WriteHandler() { + + @Override + public void onWritePossible() throws Exception { + future.result(TRUE); + } + + @Override + public void onError(Throwable t) { + future.failure(makeIOException(t)); + } + }); + + block(c, future); + } + } + + private static void block(final Connection c, + final FutureImpl future) { + try { + final long writeTimeout = + c.getTransport().getWriteTimeout(MILLISECONDS); + if (writeTimeout != -1) { + future.get(writeTimeout, MILLISECONDS); + } else { + future.get(); + } + } catch (ExecutionException e) { + GrizzlyAsyncHttpProvider.HttpTransactionContext httpCtx = + getHttpTransactionContext(c); + httpCtx.abort(e.getCause()); + } catch (Exception e) { + GrizzlyAsyncHttpProvider.HttpTransactionContext httpCtx = + getHttpTransactionContext(c); + httpCtx.abort(e); + } + } + + + // ------------------------------------------------------- Inner Classes + + + private final class LastPacketCompletionHandler + implements CompletionHandler { + + private final CompletionHandler delegate; + private final Connection c; + private final int origMaxPendingBytes; + + // -------------------------------------------------------- Constructors + + + @SuppressWarnings("unchecked") + private LastPacketCompletionHandler() { + delegate = ((!feedableBodyGenerator.requestPacket.isCommitted()) + ? feedableBodyGenerator.context.getTransportContext().getCompletionHandler() + : null); + c = feedableBodyGenerator.context.getConnection(); + origMaxPendingBytes = feedableBodyGenerator.origMaxPendingBytes; + } + + + // -------------------------------------- Methods from CompletionHandler + + + @Override + public void cancelled() { + c.setMaxAsyncWriteQueueSize(origMaxPendingBytes); + if (delegate != null) { + delegate.cancelled(); + } + } + + @Override + public void failed(Throwable throwable) { + c.setMaxAsyncWriteQueueSize(origMaxPendingBytes); + if (delegate != null) { + delegate.failed(throwable); + } + + } + + @Override + public void completed(WriteResult result) { + c.setMaxAsyncWriteQueueSize(origMaxPendingBytes); + if (delegate != null) { + delegate.completed(result); + } + + } + + @Override + public void updated(WriteResult result) { + if (delegate != null) { + delegate.updated(result); + } + } + + } // END LastPacketCompletionHandler + + } // END Feeder + + + /** + * Implementations of this class provide the framework to read data from + * some source and feed data to the {@link FeedableBodyGenerator} + * without blocking. + */ + @SuppressWarnings("UnusedDeclaration") + public static abstract class NonBlockingFeeder extends BaseFeeder { + + + // -------------------------------------------------------- Constructors + + + /** + * Constructs the NonBlockingFeeder with the associated + * {@link com.ning.http.client.providers.grizzly.FeedableBodyGenerator}. + */ + public NonBlockingFeeder(final FeedableBodyGenerator feedableBodyGenerator) { + super(feedableBodyGenerator); + } + + + // ------------------------------------------------------ Public Methods + + + /** + * Notification that it's possible to send another block of data via + * {@link #feed(org.glassfish.grizzly.Buffer, boolean)}. + * + * It's important to only invoke {@link #feed(Buffer, boolean)} + * once per invocation of {@link #canFeed()}. + */ + public abstract void canFeed(); + + /** + * @return true if all data has been fed by this feeder, + * otherwise returns false. + */ + public abstract boolean isDone(); + + /** + * @return true if data is available to be fed, otherwise + * returns false. When this method returns false, + * the {@link FeedableBodyGenerator} will call {@link #notifyReadyToFeed(ReadyToFeedListener)} + * by which this {@link NonBlockingFeeder} implementation may signal data is once + * again available to be fed. + */ + public abstract boolean isReady(); + + /** + * Callback registration to signal the {@link FeedableBodyGenerator} that + * data is available once again to continue feeding. Once this listener + * has been invoked, the NonBlockingFeeder implementation should no longer maintain + * a reference to the listener. + */ + public abstract void notifyReadyToFeed(final ReadyToFeedListener listener); + + + // ------------------------------------------------- Methods from Feeder + + + /** + * {@inheritDoc} + */ + @Override + public synchronized void flush() { + final Connection c = feedableBodyGenerator.context.getConnection(); + if (isReady()) { + writeUntilFullOrDone(c); + if (!isDone()) { + if (!isReady()) { + notifyReadyToFeed(new ReadyToFeedListenerImpl()); + } + if (!c.canWrite()) { + // write queue is full, leverage WriteListener to let us know + // when it is safe to write again. + c.notifyCanWrite(new WriteHandlerImpl()); + } + } + } else { + notifyReadyToFeed(new ReadyToFeedListenerImpl()); + } + } + + + // ----------------------------------------------------- Private Methods + + + private void writeUntilFullOrDone(final Connection c) { + while (c.canWrite()) { + if (isReady()) { + canFeed(); + } + if (!isReady()) { + break; + } + } + } + + + // ------------------------------------------------------- Inner Classes + + + /** + * Listener to signal that data is available to be fed. + */ + public interface ReadyToFeedListener { + + /** + * Data is once again ready to be fed. + */ + @SuppressWarnings("UnusedDeclaration") + void ready(); + + } // END ReadyToFeedListener + + + private final class WriteHandlerImpl implements WriteHandler { + + + private final Connection c; + + + // -------------------------------------------------------- Constructors + + + private WriteHandlerImpl() { + this.c = feedableBodyGenerator.context.getConnection(); + } + + + // ------------------------------------------ Methods from WriteListener + + @Override + public void onWritePossible() throws Exception { + writeUntilFullOrDone(c); + if (!isDone()) { + if (!isReady()) { + notifyReadyToFeed(new ReadyToFeedListenerImpl()); + } + if (!c.canWrite()) { + // write queue is full, leverage WriteListener to let us know + // when it is safe to write again. + c.notifyCanWrite(this); + } + } + } + + @Override + public void onError(Throwable t) { + c.setMaxAsyncWriteQueueSize(feedableBodyGenerator.origMaxPendingBytes); + GrizzlyAsyncHttpProvider.HttpTransactionContext ctx = + GrizzlyAsyncHttpProvider.getHttpTransactionContext(c); + ctx.abort(t); + } + + } // END WriteHandlerImpl + + + private final class ReadyToFeedListenerImpl + implements NonBlockingFeeder.ReadyToFeedListener { + + + // ------------------------------------ Methods from ReadyToFeedListener + + + @Override + public void ready() { + flush(); + } + + } // END ReadToFeedListenerImpl + + } // END NonBlockingFeeder + + + /** + * This simple {@link Feeder} implementation allows the implementation to + * feed data in whatever fashion is deemed appropriate. + */ + @SuppressWarnings("UnusedDeclaration") + public abstract static class SimpleFeeder extends BaseFeeder { + + + // -------------------------------------------------------- Constructors + + + /** + * Constructs the SimpleFeeder with the associated + * {@link com.ning.http.client.providers.grizzly.FeedableBodyGenerator}. + */ + public SimpleFeeder(FeedableBodyGenerator feedableBodyGenerator) { + super(feedableBodyGenerator); + } + + + } // END SimpleFeeder + } diff --git a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java index 3cfa92c9e3..66828f7c91 100644 --- a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java +++ b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java @@ -13,9 +13,14 @@ package com.ning.http.client.providers.grizzly; +import static com.ning.http.util.MiscUtil.isNonEmpty; + +import com.ning.http.client.AsyncHttpClient; +import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder; import com.ning.http.client.AsyncHandler; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProvider; +import com.ning.http.client.AsyncHttpProviderConfig; import com.ning.http.client.Body; import com.ning.http.client.BodyGenerator; import com.ning.http.client.ConnectionsPool; @@ -27,7 +32,6 @@ import com.ning.http.client.HttpResponseStatus; import com.ning.http.client.ListenableFuture; import com.ning.http.client.MaxRedirectException; -import com.ning.http.client.Part; import com.ning.http.client.PerRequestConfig; import com.ning.http.client.ProxyServer; import com.ning.http.client.Realm; @@ -51,7 +55,6 @@ import com.ning.http.util.AuthenticatorUtils; import com.ning.http.util.ProxyUtils; import com.ning.http.util.SslUtils; - import org.glassfish.grizzly.Buffer; import org.glassfish.grizzly.CompletionHandler; import org.glassfish.grizzly.Connection; @@ -59,9 +62,11 @@ import org.glassfish.grizzly.FileTransfer; import org.glassfish.grizzly.Grizzly; import org.glassfish.grizzly.WriteResult; +import org.glassfish.grizzly.asyncqueue.AsyncQueueWriter; import org.glassfish.grizzly.attributes.Attribute; import org.glassfish.grizzly.attributes.AttributeStorage; import org.glassfish.grizzly.filterchain.BaseFilter; +import org.glassfish.grizzly.filterchain.FilterChain; import org.glassfish.grizzly.filterchain.FilterChainBuilder; import org.glassfish.grizzly.filterchain.FilterChainContext; import org.glassfish.grizzly.filterchain.FilterChainEvent; @@ -77,13 +82,12 @@ import org.glassfish.grizzly.http.HttpResponsePacket; import org.glassfish.grizzly.http.Method; import org.glassfish.grizzly.http.Protocol; -import org.glassfish.grizzly.impl.FutureImpl; -import org.glassfish.grizzly.utils.Charsets; import org.glassfish.grizzly.http.util.CookieSerializerUtils; import org.glassfish.grizzly.http.util.DataChunk; import org.glassfish.grizzly.http.util.Header; import org.glassfish.grizzly.http.util.HttpStatus; import org.glassfish.grizzly.http.util.MimeHeaders; +import org.glassfish.grizzly.impl.FutureImpl; import org.glassfish.grizzly.impl.SafeFutureImpl; import org.glassfish.grizzly.memory.Buffers; import org.glassfish.grizzly.memory.MemoryManager; @@ -95,21 +99,24 @@ import org.glassfish.grizzly.strategies.SameThreadIOStrategy; import org.glassfish.grizzly.strategies.WorkerThreadIOStrategy; import org.glassfish.grizzly.utils.BufferOutputStream; +import org.glassfish.grizzly.utils.Charsets; import org.glassfish.grizzly.utils.DelayedExecutor; import org.glassfish.grizzly.utils.Futures; import org.glassfish.grizzly.utils.IdleTimeoutFilter; +import org.glassfish.grizzly.websockets.ClosingFrame; import org.glassfish.grizzly.websockets.DataFrame; -import org.glassfish.grizzly.websockets.DefaultWebSocket; import org.glassfish.grizzly.websockets.HandShake; +import org.glassfish.grizzly.websockets.HandshakeException; import org.glassfish.grizzly.websockets.ProtocolHandler; +import org.glassfish.grizzly.websockets.SimpleWebSocket; import org.glassfish.grizzly.websockets.Version; -import org.glassfish.grizzly.websockets.WebSocketEngine; import org.glassfish.grizzly.websockets.WebSocketFilter; -import org.glassfish.grizzly.websockets.draft06.ClosingFrame; +import org.glassfish.grizzly.websockets.WebSocketHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; +import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; @@ -121,11 +128,12 @@ import java.net.URISyntaxException; import java.net.URLEncoder; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; @@ -134,6 +142,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.BUFFER_WEBSOCKET_FRAGMENTS; +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.MAX_HTTP_PACKET_HEADER_SIZE; import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; /** @@ -147,9 +157,9 @@ public class GrizzlyAsyncHttpProvider implements AsyncHttpProvider { private final static Logger LOGGER = LoggerFactory.getLogger(GrizzlyAsyncHttpProvider.class); private static final boolean SEND_FILE_SUPPORT; static { - SEND_FILE_SUPPORT = configSendFileSupport(); + SEND_FILE_SUPPORT = /*configSendFileSupport()*/ false; } - private final Attribute REQUEST_STATE_ATTR = + private static final Attribute REQUEST_STATE_ATTR = Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(HttpTransactionContext.class.getName()); private final BodyHandlerFactory bodyHandlerFactory = new BodyHandlerFactory(); @@ -193,8 +203,11 @@ public GrizzlyAsyncHttpProvider(final AsyncHttpClientConfig clientConfig) { public ListenableFuture execute(final Request request, final AsyncHandler handler) throws IOException { - final GrizzlyResponseFuture future = - new GrizzlyResponseFuture(this, request, handler); + if (clientTransport.isStopped()) { + throw new IOException("AsyncHttpClient has been closed."); + } + final ProxyServer proxy = ProxyUtils.getProxyServer(clientConfig, request); + final GrizzlyResponseFuture future = new GrizzlyResponseFuture(this, request, handler, proxy); future.setDelegate(SafeFutureImpl.create()); final CompletionHandler connectHandler = new CompletionHandler() { @Override @@ -252,13 +265,14 @@ public void close() { try { connectionManager.destroy(); - clientTransport.stop(); + clientTransport.shutdownNow(); final ExecutorService service = clientConfig.executorService(); if (service != null) { service.shutdown(); } if (timeoutExecutor != null) { timeoutExecutor.stop(); + timeoutExecutor.getThreadPool().shutdownNow(); } } catch (IOException ignored) { } @@ -270,7 +284,7 @@ public void close() { */ public Response prepareResponse(HttpResponseStatus status, HttpResponseHeaders headers, - Collection bodyParts) { + List bodyParts) { return new GrizzlyResponse(status, headers, bodyParts); @@ -369,8 +383,12 @@ public void onTimeout(Connection connection) { false); final SwitchingSSLFilter filter = new SwitchingSSLFilter(configurator, defaultSecState); fcb.add(filter); + final GrizzlyAsyncHttpProviderConfig providerConfig = + clientConfig.getAsyncHttpProviderConfig() instanceof GrizzlyAsyncHttpProviderConfig ? + (GrizzlyAsyncHttpProviderConfig) clientConfig.getAsyncHttpProviderConfig() + : new GrizzlyAsyncHttpProviderConfig(); final AsyncHttpClientEventFilter eventFilter = new - AsyncHttpClientEventFilter(this); + AsyncHttpClientEventFilter(this, (Integer) providerConfig.getProperty(MAX_HTTP_PACKET_HEADER_SIZE)); final AsyncHttpClientFilter clientFilter = new AsyncHttpClientFilter(clientConfig); ContentEncoding[] encodings = eventFilter.getContentEncodings(); @@ -387,22 +405,17 @@ public void onTimeout(Connection connection) { } fcb.add(eventFilter); fcb.add(clientFilter); - - GrizzlyAsyncHttpProviderConfig providerConfig = - (GrizzlyAsyncHttpProviderConfig) clientConfig.getAsyncHttpProviderConfig(); - if (providerConfig != null) { - final TransportCustomizer customizer = (TransportCustomizer) - providerConfig.getProperty(TRANSPORT_CUSTOMIZER); - if (customizer != null) { - customizer.customize(clientTransport, fcb); - } else { - doDefaultTransportConfig(); - } + clientTransport.getAsyncQueueIO().getWriter() + .setMaxPendingBytesPerConnection(AsyncQueueWriter.AUTO_SIZE); + final TransportCustomizer customizer = (TransportCustomizer) + providerConfig.getProperty(TRANSPORT_CUSTOMIZER); + if (customizer != null) { + customizer.customize(clientTransport, fcb); } else { doDefaultTransportConfig(); } fcb.add(new WebSocketFilter()); - clientTransport.getAsyncQueueIO().getWriter().setMaxPendingBytesPerConnection(-1); + clientTransport.setProcessor(fcb.build()); } @@ -492,7 +505,7 @@ public void updated(WriteResult result) { } - void setHttpTransactionContext(final AttributeStorage storage, + static void setHttpTransactionContext(final AttributeStorage storage, final HttpTransactionContext httpTransactionState) { if (httpTransactionState == null) { @@ -503,7 +516,7 @@ void setHttpTransactionContext(final AttributeStorage storage, } - HttpTransactionContext getHttpTransactionContext(final AttributeStorage storage) { + static HttpTransactionContext getHttpTransactionContext(final AttributeStorage storage) { return REQUEST_STATE_ATTR.get(storage); @@ -521,7 +534,7 @@ void timeout(final Connection c) { static int getPort(final URI uri, final int p) { int port = p; if (port == -1) { - final String protocol = uri.getScheme().toLowerCase(); + final String protocol = uri.getScheme().toLowerCase(Locale.ENGLISH); if ("http".equals(protocol) || "ws".equals(protocol)) { port = 80; } else if ("https".equals(protocol) || "wss".equals(protocol)) { @@ -550,13 +563,17 @@ boolean sendRequest(final FilterChainContext ctx, handler = new ExpectHandler(handler); } context.bodyHandler = handler; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("REQUEST: " + requestPacket.toString()); + } isWriteComplete = handler.doHandle(ctx, request, requestPacket); } else { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("REQUEST: " + requestPacket.toString()); + } ctx.write(requestPacket, ctx.getTransportContext().getCompletionHandler()); } - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("REQUEST: " + requestPacket.toString()); - } + return isWriteComplete; } @@ -619,6 +636,7 @@ final class HttpTransactionContext { HandShake handshake; ProtocolHandler protocolHandler; WebSocket webSocket; + boolean establishingTunnel; // -------------------------------------------------------- Constructors @@ -663,9 +681,9 @@ void abort(final Throwable t) { } } - void done(final Callable c) { + void done() { if (future != null) { - future.done(c); + future.done(); } } @@ -673,10 +691,19 @@ void done(final Callable c) { void result(Object result) { if (future != null) { future.delegate.result(result); - future.done(null); + future.done(); } } + boolean isTunnelEstablished(final Connection c) { + return c.getAttributes().getAttribute("tunnel-established") != null; + } + + + void tunnelEstablished(final Connection c) { + c.getAttributes().setAttribute("tunnel-established", Boolean.TRUE); + } + } // END HttpTransactionContext @@ -796,24 +823,6 @@ public NextAction handleEvent(final FilterChainContext ctx, } -// @Override -// public NextAction handleRead(FilterChainContext ctx) throws IOException { -// Object message = ctx.getMessage(); -// if (HttpPacket.isHttp(message)) { -// final HttpPacket packet = (HttpPacket) message; -// HttpResponsePacket responsePacket; -// if (HttpContent.isContent(packet)) { -// responsePacket = (HttpResponsePacket) ((HttpContent) packet).getHttpHeader(); -// } else { -// responsePacket = (HttpResponsePacket) packet; -// } -// if (HttpStatus.SWITCHING_PROTOCOLS_101.statusMatches(responsePacket.getStatus())) { -// return ctx.getStopAction(); -// } -// } -// return super.handleRead(ctx); -// } - // ----------------------------------------------------- Private Methods @@ -826,7 +835,7 @@ private boolean sendAsGrizzlyRequest(final Request request, httpCtx.isWSRequest = true; convertToUpgradeRequest(httpCtx); } - final URI uri = AsyncHttpProviderUtils.createUri(httpCtx.requestUrl); + final URI uri = httpCtx.request.getURI(); final HttpRequestPacket.Builder builder = HttpRequestPacket.builder(); boolean secure = "https".equals(uri.getScheme()); builder.method(request.getMethod()); @@ -841,12 +850,16 @@ private boolean sendAsGrizzlyRequest(final Request request, builder.header(Header.Host, uri.getHost() + ':' + uri.getPort()); } } - final ProxyServer proxy = getProxyServer(request); - final boolean useProxy = (proxy != null); + final ProxyServer proxy = ProxyUtils.getProxyServer(config, request); + final boolean useProxy = proxy != null; if (useProxy) { - if (secure) { + if ((secure || httpCtx.isWSRequest) && !httpCtx.isTunnelEstablished(ctx.getConnection())) { + secure = false; + httpCtx.establishingTunnel = true; builder.method(Method.CONNECT); builder.uri(AsyncHttpProviderUtils.getAuthority(uri)); + } else if (secure && config.isUseRelativeURIsWithSSLProxies()){ + builder.uri(uri.getPath()); } else { builder.uri(uri.toString()); } @@ -864,10 +877,10 @@ private boolean sendAsGrizzlyRequest(final Request request, } HttpRequestPacket requestPacket; - if (httpCtx.isWSRequest) { + if (httpCtx.isWSRequest && !httpCtx.establishingTunnel) { try { final URI wsURI = new URI(httpCtx.wsRequestURI); - httpCtx.protocolHandler = Version.DRAFT17.createHandler(true); + httpCtx.protocolHandler = Version.RFC6455.createHandler(true); httpCtx.handshake = httpCtx.protocolHandler.createHandShake(wsURI); requestPacket = (HttpRequestPacket) httpCtx.handshake.composeHeaders().getHttpHeader(); @@ -877,7 +890,10 @@ private boolean sendAsGrizzlyRequest(final Request request, } else { requestPacket = builder.build(); } - requestPacket.setSecure(true); + requestPacket.setSecure(secure); + + ctx.notifyDownstream(new SwitchingSSLFilter.SSLSwitchingEvent(secure, ctx.getConnection())); + if (!useProxy && !httpCtx.isWSRequest) { addQueryString(request, requestPacket); } @@ -885,25 +901,20 @@ private boolean sendAsGrizzlyRequest(final Request request, addCookies(request, requestPacket); if (useProxy) { - boolean avoidProxy = ProxyUtils.avoidProxy(proxy, request); - if (!avoidProxy) { - if (!requestPacket.getHeaders().contains(Header.ProxyConnection)) { - requestPacket.setHeader(Header.ProxyConnection, "keep-alive"); - } + if (!requestPacket.getHeaders().contains(Header.ProxyConnection)) { + requestPacket.setHeader(Header.ProxyConnection, "keep-alive"); + } - if (proxy.getPrincipal() != null) { - requestPacket.setHeader(Header.ProxyAuthorization, - AuthenticatorUtils.computeBasicAuthentication(proxy)); - } + if (proxy.getPrincipal() != null) { + requestPacket.setHeader(Header.ProxyAuthorization, + AuthenticatorUtils.computeBasicAuthentication(proxy)); } } final AsyncHandler h = httpCtx.handler; - if (h != null) { - if (TransferCompletionHandler.class.isAssignableFrom(h.getClass())) { - final FluentCaseInsensitiveStringsMap map = - new FluentCaseInsensitiveStringsMap(request.getHeaders()); - TransferCompletionHandler.class.cast(h).transferAdapter(new GrizzlyTransferAdapter(map)); - } + if (h instanceof TransferCompletionHandler) { + final FluentCaseInsensitiveStringsMap map = + new FluentCaseInsensitiveStringsMap(request.getHeaders()); + TransferCompletionHandler.class.cast(h).transferAdapter(new GrizzlyTransferAdapter(map)); } return sendRequest(ctx, request, requestPacket); @@ -933,27 +944,15 @@ private void convertToUpgradeRequest(final HttpTransactionContext ctx) { ctx.requestUrl = sb.toString(); } - - private ProxyServer getProxyServer(Request request) { - - ProxyServer proxyServer = request.getProxyServer(); - if (proxyServer == null) { - proxyServer = config.getProxyServer(); - } - return proxyServer; - - } - - private void addHeaders(final Request request, final HttpRequestPacket requestPacket) { final FluentCaseInsensitiveStringsMap map = request.getHeaders(); - if (map != null && !map.isEmpty()) { + if (isNonEmpty(map)) { for (final Map.Entry> entry : map.entrySet()) { final String headerName = entry.getKey(); final List headerValues = entry.getValue(); - if (headerValues != null && !headerValues.isEmpty()) { + if (isNonEmpty(headerValues)) { for (final String headerValue : headerValues) { requestPacket.addHeader(headerName, headerValue); } @@ -982,7 +981,7 @@ private void addCookies(final Request request, final HttpRequestPacket requestPacket) { final Collection cookies = request.getCookies(); - if (cookies != null && !cookies.isEmpty()) { + if (isNonEmpty(cookies)) { StringBuilder sb = new StringBuilder(128); org.glassfish.grizzly.http.Cookie[] gCookies = new org.glassfish.grizzly.http.Cookie[cookies.size()]; @@ -1016,16 +1015,16 @@ private void addQueryString(final Request request, final HttpRequestPacket requestPacket) { final FluentStringsMap map = request.getQueryParams(); - if (map != null && !map.isEmpty()) { + if (isNonEmpty(map)) { StringBuilder sb = new StringBuilder(128); for (final Map.Entry> entry : map.entrySet()) { final String name = entry.getKey(); final List values = entry.getValue(); - if (values != null && !values.isEmpty()) { + if (isNonEmpty(values)) { try { for (int i = 0, len = values.size(); i < len; i++) { final String value = values.get(i); - if (value != null && value.length() > 0) { + if (isNonEmpty(value)) { sb.append(URLEncoder.encode(name, "UTF-8")).append('=') .append(URLEncoder.encode(values.get(i), "UTF-8")).append('&'); } else { @@ -1036,11 +1035,11 @@ private void addQueryString(final Request request, } } } - String queryString = sb.deleteCharAt((sb.length() - 1)).toString(); + sb.setLength(sb.length() - 1); + String queryString = sb.toString(); requestPacket.setQueryString(queryString); } - } } // END AsyncHttpClientFiler @@ -1053,15 +1052,15 @@ private static final class AsyncHttpClientEventFilter extends HttpClientFilter { private final GrizzlyAsyncHttpProvider provider; - // -------------------------------------------------------- Constructors - AsyncHttpClientEventFilter(final GrizzlyAsyncHttpProvider provider) { + AsyncHttpClientEventFilter(final GrizzlyAsyncHttpProvider provider, int maxHerdersSizeProperty) { + super(maxHerdersSizeProperty); this.provider = provider; HANDLER_MAP.put(HttpStatus.UNAUTHORIZED_401.getStatusCode(), - AuthorizationHandler.INSTANCE); + AuthorizationHandler.INSTANCE); HANDLER_MAP.put(HttpStatus.MOVED_PERMANENTLY_301.getStatusCode(), RedirectHandler.INSTANCE); HANDLER_MAP.put(HttpStatus.FOUND_302.getStatusCode(), @@ -1108,10 +1107,8 @@ protected void onHttpContentParsed(HttpContent content, protected void onHttpHeadersEncoded(HttpHeader httpHeader, FilterChainContext ctx) { final HttpTransactionContext context = provider.getHttpTransactionContext(ctx.getConnection()); final AsyncHandler handler = context.handler; - if (handler != null) { - if (TransferCompletionHandler.class.isAssignableFrom(handler.getClass())) { - ((TransferCompletionHandler) handler).onHeaderWriteCompleted(); - } + if (handler instanceof TransferCompletionHandler) { + ((TransferCompletionHandler) handler).onHeaderWriteCompleted(); } } @@ -1119,15 +1116,13 @@ protected void onHttpHeadersEncoded(HttpHeader httpHeader, FilterChainContext ct protected void onHttpContentEncoded(HttpContent content, FilterChainContext ctx) { final HttpTransactionContext context = provider.getHttpTransactionContext(ctx.getConnection()); final AsyncHandler handler = context.handler; - if (handler != null) { - if (TransferCompletionHandler.class.isAssignableFrom(handler.getClass())) { - final int written = content.getContent().remaining(); - final long total = context.totalBodyWritten.addAndGet(written); - ((TransferCompletionHandler) handler).onContentWriteProgress( - written, - total, - content.getHttpHeader().getContentLength()); - } + if (handler instanceof TransferCompletionHandler) { + final int written = content.getContent().remaining(); + final long total = context.totalBodyWritten.addAndGet(written); + ((TransferCompletionHandler) handler).onContentWriteProgress( + written, + total, + content.getHttpHeader().getContentLength()); } } @@ -1139,14 +1134,19 @@ protected void onInitialLineParsed(HttpHeader httpHeader, if (httpHeader.isSkipRemainder()) { return; } + final Connection connection = ctx.getConnection(); final HttpTransactionContext context = - provider.getHttpTransactionContext(ctx.getConnection()); + provider.getHttpTransactionContext(connection); final int status = ((HttpResponsePacket) httpHeader).getStatus(); + if (context.establishingTunnel && HttpStatus.OK_200.statusMatches(status)) { + return; + } if (HttpStatus.CONINTUE_100.statusMatches(status)) { ctx.notifyUpstream(new ContinueEvent(context)); return; } + if (context.statusHandler != null && !context.statusHandler.handlesStatus(status)) { context.statusHandler = null; context.invocationStatus = StatusHandler.InvocationStatus.CONTINUE; @@ -1180,9 +1180,9 @@ protected void onInitialLineParsed(HttpHeader httpHeader, } } final GrizzlyResponseStatus responseStatus = - new GrizzlyResponseStatus((HttpResponsePacket) httpHeader, - getURI(context.requestUrl), - provider); + new GrizzlyResponseStatus((HttpResponsePacket) httpHeader, + context.request.getURI(), + provider); context.responseStatus = responseStatus; if (context.statusHandler != null) { return; @@ -1193,6 +1193,10 @@ protected void onInitialLineParsed(HttpHeader httpHeader, final AsyncHandler handler = context.handler; if (handler != null) { context.currentState = handler.onStatusReceived(responseStatus); + if (context.isWSRequest && context.currentState == AsyncHandler.STATE.ABORT) { + httpHeader.setSkipRemainder(true); + context.abort(new HandshakeException("Upgrade failed")); + } } } catch (Exception e) { httpHeader.setSkipRemainder(true); @@ -1221,24 +1225,23 @@ protected void onHttpHeadersParsed(HttpHeader httpHeader, FilterChainContext ctx) { super.onHttpHeadersParsed(httpHeader, ctx); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("RESPONSE: " + httpHeader.toString()); - } + LOGGER.debug("RESPONSE: {}", httpHeader); if (httpHeader.containsHeader(Header.Connection)) { if ("close".equals(httpHeader.getHeader(Header.Connection))) { ConnectionManager.markConnectionAsDoNotCache(ctx.getConnection()); } } - if (httpHeader.isSkipRemainder()) { - return; - } final HttpTransactionContext context = provider.getHttpTransactionContext(ctx.getConnection()); + if (httpHeader.isSkipRemainder() || context.establishingTunnel) { + return; + } + final AsyncHandler handler = context.handler; final List filters = context.provider.clientConfig.getResponseFilters(); final GrizzlyResponseHeaders responseHeaders = new GrizzlyResponseHeaders((HttpResponsePacket) httpHeader, - null, - provider); + null, + provider); if (!filters.isEmpty()) { FilterContext fc = new FilterContext.FilterContextBuilder() .asyncHandler(handler).request(context.request) @@ -1260,16 +1263,16 @@ protected void onHttpHeadersParsed(HttpHeader httpHeader, context.provider.connectionManager; final Connection c = m.obtainConnection(newRequest, - context.future); + context.future); final HttpTransactionContext newContext = context.copy(); context.future = null; provider.setHttpTransactionContext(c, newContext); try { context.provider.execute(c, - newRequest, - newHandler, - context.future); + newRequest, + newHandler, + context.future); } catch (IOException ioe) { newContext.abort(ioe); } @@ -1281,8 +1284,8 @@ protected void onHttpHeadersParsed(HttpHeader httpHeader, } if (context.statusHandler != null && context.invocationStatus == StatusHandler.InvocationStatus.CONTINUE) { final boolean result = context.statusHandler.handleStatus(((HttpResponsePacket) httpHeader), - context, - ctx); + context, + ctx); if (!result) { httpHeader.setSkipRemainder(true); return; @@ -1291,14 +1294,15 @@ protected void onHttpHeadersParsed(HttpHeader httpHeader, if (context.isWSRequest) { try { context.protocolHandler.setConnection(ctx.getConnection()); - DefaultWebSocket ws = new DefaultWebSocket(context.protocolHandler); - context.webSocket = new GrizzlyWebSocketAdapter(ws); + final GrizzlyWebSocketAdapter webSocketAdapter = createWebSocketAdapter(context); + context.webSocket = webSocketAdapter; + SimpleWebSocket ws = webSocketAdapter.gWebSocket; if (context.currentState == AsyncHandler.STATE.UPGRADE) { httpHeader.setChunked(false); ws.onConnect(); - WebSocketEngine.getEngine().setWebSocketHolder(ctx.getConnection(), - context.protocolHandler, - ws); + WebSocketHolder.set(ctx.getConnection(), + context.protocolHandler, + ws); ((WebSocketUpgradeHandler) context.handler).onSuccess(context.webSocket); final int wsTimeout = context.provider.clientConfig.getWebSocketIdleTimeoutInMs(); IdleTimeoutFilter.setCustomTimeout(ctx.getConnection(), @@ -1347,25 +1351,53 @@ protected boolean onHttpPacketParsed(HttpHeader httpHeader, FilterChainContext c result = super.onHttpPacketParsed(httpHeader, ctx); - final HttpTransactionContext context = cleanup(ctx, provider); - - final AsyncHandler handler = context.handler; - if (handler != null) { + final HttpTransactionContext context = provider.getHttpTransactionContext(ctx.getConnection()); + if (context.establishingTunnel + && HttpStatus.OK_200.statusMatches( + ((HttpResponsePacket) httpHeader).getStatus())) { + context.establishingTunnel = false; + final Connection c = ctx.getConnection(); + context.tunnelEstablished(c); try { - context.result(handler.onCompleted()); - } catch (Exception e) { + context.provider.execute(c, + context.request, + context.handler, + context.future); + return result; + } catch (IOException e) { context.abort(e); + return result; } } else { - context.done(null); - } + cleanup(ctx, provider); + final AsyncHandler handler = context.handler; + if (handler != null) { + try { + context.result(handler.onCompleted()); + } catch (Exception e) { + context.abort(e); + } + } else { + context.done(); + } - return result; + return result; + } } // ----------------------------------------------------- Private Methods + private static GrizzlyWebSocketAdapter createWebSocketAdapter(final HttpTransactionContext context) { + SimpleWebSocket ws = new SimpleWebSocket(context.protocolHandler); + AsyncHttpProviderConfig config = context.provider.clientConfig.getAsyncHttpProviderConfig(); + boolean bufferFragments = true; + if (config instanceof GrizzlyAsyncHttpProviderConfig) { + bufferFragments = (Boolean) ((GrizzlyAsyncHttpProviderConfig) config).getProperty(BUFFER_WEBSOCKET_FRAGMENTS); + } + + return new GrizzlyWebSocketAdapter(ws, bufferFragments); + } private static boolean isRedirectAllowed(final HttpTransactionContext ctx) { boolean allowed = ctx.request.isRedirectEnabled(); @@ -1388,8 +1420,8 @@ private static HttpTransactionContext cleanup(final FilterChainContext ctx, if (!context.provider.connectionManager.canReturnConnection(c)) { context.abort(new IOException("Maximum pooled connections exceeded")); } else { - if (!context.provider.connectionManager.returnConnection(context.requestUrl, c)) { - ctx.getConnection().close().markForRecycle(true); + if (!context.provider.connectionManager.returnConnection(context.request, c)) { + ctx.getConnection().close(); } } @@ -1397,14 +1429,6 @@ private static HttpTransactionContext cleanup(final FilterChainContext ctx, } - - private static URI getURI(String url) { - - return AsyncHttpProviderUtils.createUri(url); - - } - - private static boolean redirectCountExceeded(final HttpTransactionContext context) { return (context.redirectCount.get() > context.maxRedirectCount); @@ -1453,6 +1477,13 @@ public boolean handleStatus(final HttpResponsePacket responsePacket, } if (realm == null) { httpTransactionContext.invocationStatus = InvocationStatus.STOP; + if (httpTransactionContext.handler != null) { + try { + httpTransactionContext.handler.onStatusReceived(httpTransactionContext.responseStatus); + } catch (Exception e) { + httpTransactionContext.abort(e); + } + } return true; } @@ -1461,19 +1492,20 @@ public boolean handleStatus(final HttpResponsePacket responsePacket, final Request req = httpTransactionContext.request; realm = new Realm.RealmBuilder().clone(realm) .setScheme(realm.getAuthScheme()) - .setUri(URI.create(httpTransactionContext.requestUrl).getPath()) + .setUri(httpTransactionContext.request.getURI().getPath()) .setMethodName(req.getMethod()) .setUsePreemptiveAuth(true) .parseWWWAuthenticateHeader(auth) .build(); - if (auth.toLowerCase().startsWith("basic")) { + String lowerCaseAuth = auth.toLowerCase(Locale.ENGLISH); + if (lowerCaseAuth.startsWith("basic")) { req.getHeaders().remove(Header.Authorization.toString()); try { req.getHeaders().add(Header.Authorization.toString(), AuthenticatorUtils.computeBasicAuthentication(realm)); } catch (UnsupportedEncodingException ignored) { } - } else if (auth.toLowerCase().startsWith("digest")) { + } else if (lowerCaseAuth.startsWith("digest")) { req.getHeaders().remove(Header.Authorization.toString()); try { req.getHeaders().add(Header.Authorization.toString(), @@ -1540,9 +1572,9 @@ public boolean handleStatus(final HttpResponsePacket responsePacket, URI orig; if (httpTransactionContext.lastRedirectURI == null) { - orig = AsyncHttpProviderUtils.createUri(httpTransactionContext.requestUrl); + orig = httpTransactionContext.request.getURI(); } else { - orig = AsyncHttpProviderUtils.getRedirectUri(AsyncHttpProviderUtils.createUri(httpTransactionContext.requestUrl), + orig = AsyncHttpProviderUtils.getRedirectUri(httpTransactionContext.request.getURI(), httpTransactionContext.lastRedirectURI); } httpTransactionContext.lastRedirectURI = redirectURL; @@ -1644,8 +1676,9 @@ private static Request newRequest(final URI uri, builder.setQueryParameters(null); } for (String cookieStr : response.getHeaders().values(Header.Cookie)) { - Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr); - builder.addOrReplaceCookie(c); + for (Cookie c : CookieDecoder.decode(cookieStr)) { + builder.addOrReplaceCookie(c); + } } return builder.build(); @@ -1664,7 +1697,7 @@ private static final class ClientEncodingFilter implements EncodingFilter { public boolean applyEncoding(HttpHeader httpPacket) { httpPacket.addHeader(Header.AcceptEncoding, "gzip"); - return true; + return false; } @@ -1803,7 +1836,7 @@ public boolean doHandle(final FilterChainContext ctx, String charset = request.getBodyEncoding(); if (charset == null) { - charset = Charsets.DEFAULT_CHARACTER_ENCODING; + charset = Charsets.ASCII_CHARSET.name(); } final byte[] data = new String(request.getByteData(), charset).getBytes(charset); final MemoryManager mm = ctx.getMemoryManager(); @@ -1839,7 +1872,7 @@ public boolean doHandle(final FilterChainContext ctx, String charset = request.getBodyEncoding(); if (charset == null) { - charset = Charsets.DEFAULT_CHARACTER_ENCODING; + charset = Charsets.ASCII_CHARSET.name(); } final byte[] data = request.getStringData().getBytes(charset); final MemoryManager mm = ctx.getMemoryManager(); @@ -1890,8 +1923,7 @@ private final class ParamsBodyHandler implements BodyHandler { public boolean handlesBodyType(final Request request) { - final FluentStringsMap params = request.getParams(); - return (params != null && !params.isEmpty()); + return isNonEmpty(request.getParams()); } @SuppressWarnings({"unchecked"}) @@ -1906,14 +1938,14 @@ public boolean doHandle(final FilterChainContext ctx, StringBuilder sb = null; String charset = request.getBodyEncoding(); if (charset == null) { - charset = Charsets.DEFAULT_CHARACTER_ENCODING; + charset = Charsets.ASCII_CHARSET.name(); } final FluentStringsMap params = request.getParams(); if (!params.isEmpty()) { for (Map.Entry> entry : params.entrySet()) { String name = entry.getKey(); List values = entry.getValue(); - if (values != null && !values.isEmpty()) { + if (isNonEmpty(values)) { if (sb == null) { sb = new StringBuilder(128); } @@ -2037,8 +2069,7 @@ private static final class PartsBodyHandler implements BodyHandler { public boolean handlesBodyType(final Request request) { - final List parts = request.getParts(); - return (parts != null && !parts.isEmpty()); + return isNonEmpty(request.getParts()); } @SuppressWarnings({"unchecked"}) @@ -2050,7 +2081,7 @@ public boolean doHandle(final FilterChainContext ctx, MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity( request.getParts(), - request.getParams()); + request.getHeaders()); requestPacket.setContentLengthLong(mre.getContentLength()); requestPacket.setContentType(mre.getContentType()); final MemoryManager mm = ctx.getMemoryManager(); @@ -2126,15 +2157,13 @@ public boolean doHandle(final FilterChainContext ctx, @Override public void updated(WriteResult result) { final AsyncHandler handler = context.handler; - if (handler != null) { - if (TransferCompletionHandler.class.isAssignableFrom(handler.getClass())) { - final long written = result.getWrittenSize(); - final long total = context.totalBodyWritten.addAndGet(written); - ((TransferCompletionHandler) handler).onContentWriteProgress( - written, - total, - requestPacket.getContentLength()); - } + if (handler instanceof TransferCompletionHandler) { + final long written = result.getWrittenSize(); + final long total = context.totalBodyWritten.addAndGet(written); + ((TransferCompletionHandler) handler).onContentWriteProgress( + written, + total, + requestPacket.getContentLength()); } } }); @@ -2262,13 +2291,12 @@ void doAsyncTrackedConnection(final Request request, final GrizzlyResponseFuture requestFuture, final CompletionHandler connectHandler) throws IOException, ExecutionException, InterruptedException { - final String url = request.getUrl(); - Connection c = pool.poll(AsyncHttpProviderUtils.getBaseUrl(url)); + Connection c = pool.poll(getPoolKey(request, requestFuture.getProxy())); if (c == null) { if (!connectionMonitor.acquire()) { throw new IOException("Max connections exceeded"); } - doAsyncConnect(url, request, requestFuture, connectHandler); + doAsyncConnect(request, requestFuture, connectHandler); } else { provider.touchConnection(c, request); connectHandler.completed(c); @@ -2280,25 +2308,19 @@ Connection obtainConnection(final Request request, final GrizzlyResponseFuture requestFuture) throws IOException, ExecutionException, InterruptedException, TimeoutException { - final Connection c = (obtainConnection0(request.getUrl(), - request, - requestFuture)); + final Connection c = obtainConnection0(request, requestFuture); DO_NOT_CACHE.set(c, Boolean.TRUE); return c; } - void doAsyncConnect(final String url, - final Request request, + void doAsyncConnect(final Request request, final GrizzlyResponseFuture requestFuture, final CompletionHandler connectHandler) throws IOException, ExecutionException, InterruptedException { - final URI uri = AsyncHttpProviderUtils.createUri(url); - ProxyServer proxy = getProxyServer(request); - if (ProxyUtils.avoidProxy(proxy, request)) { - proxy = null; - } + ProxyServer proxy = requestFuture.getProxy(); + final URI uri = request.getURI(); String host = ((proxy != null) ? proxy.getHost() : uri.getHost()); int port = ((proxy != null) ? proxy.getPort() : uri.getPort()); if(request.getLocalAddress()!=null) { @@ -2311,18 +2333,14 @@ void doAsyncConnect(final String url, } - private Connection obtainConnection0(final String url, - final Request request, + private Connection obtainConnection0(final Request request, final GrizzlyResponseFuture requestFuture) throws IOException, ExecutionException, InterruptedException, TimeoutException { - final URI uri = AsyncHttpProviderUtils.createUri(url); - ProxyServer proxy = getProxyServer(request); - if (ProxyUtils.avoidProxy(proxy, request)) { - proxy = null; - } - String host = ((proxy != null) ? proxy.getHost() : uri.getHost()); - int port = ((proxy != null) ? proxy.getPort() : uri.getPort()); + final URI uri = request.getURI(); + final ProxyServer proxy = requestFuture.getProxy(); + String host = (proxy != null) ? proxy.getHost() : uri.getHost(); + int port = (proxy != null) ? proxy.getPort() : uri.getPort(); int cTimeout = provider.clientConfig.getConnectionTimeoutInMs(); FutureImpl future = Futures.createSafeFuture(); CompletionHandler ch = Futures.toCompletionHandler(future, @@ -2338,26 +2356,16 @@ private Connection obtainConnection0(final String url, } } - private ProxyServer getProxyServer(Request request) { - - ProxyServer proxyServer = request.getProxyServer(); - if (proxyServer == null) { - proxyServer = provider.clientConfig.getProxyServer(); - } - return proxyServer; - - } - - boolean returnConnection(final String url, final Connection c) { + boolean returnConnection(final Request request, final Connection c) { + ProxyServer proxyServer = ProxyUtils.getProxyServer(provider.clientConfig, request); final boolean result = (DO_NOT_CACHE.get(c) == null - && pool.offer(AsyncHttpProviderUtils.getBaseUrl(url), c)); + && pool.offer(getPoolKey(request, proxyServer), c)); if (result) { if (provider.resolver != null) { provider.resolver.setTimeoutMillis(c, IdleTimeoutFilter.FOREVER); } } return result; - } @@ -2411,6 +2419,11 @@ public void updated(Connection result) { }; } + private static String getPoolKey(Request request, ProxyServer proxyServer) { + URI uri = proxyServer != null? proxyServer.getURI(): request.getURI(); + return request.getConnectionPoolKeyStrategy().getKey(uri); + } + // ------------------------------------------------------ Nested Classes private static class ConnectionMonitor implements Connection.CloseListener { @@ -2503,6 +2516,11 @@ public NextAction handleWrite(FilterChainContext ctx) throws IOException { } + @Override + public void onFilterChainChanged(FilterChain filterChain) { + // no-op + } + // ----------------------------------------------------- Private Methods @@ -2571,13 +2589,16 @@ public void getBytes(byte[] bytes) { private static final class GrizzlyWebSocketAdapter implements WebSocket { - private final org.glassfish.grizzly.websockets.WebSocket gWebSocket; + final SimpleWebSocket gWebSocket; + final boolean bufferFragments; // -------------------------------------------------------- Constructors - GrizzlyWebSocketAdapter(final org.glassfish.grizzly.websockets.WebSocket gWebSocket) { - this.gWebSocket = gWebSocket; + GrizzlyWebSocketAdapter(final SimpleWebSocket gWebSocket, + final boolean bufferFragments) { + this.gWebSocket = gWebSocket; + this.bufferFragments = bufferFragments; } @@ -2592,7 +2613,7 @@ public WebSocket sendMessage(byte[] message) { @Override public WebSocket stream(byte[] fragment, boolean last) { - if (fragment != null && fragment.length > 0) { + if (isNonEmpty(fragment)) { gWebSocket.stream(last, fragment, 0, fragment.length); } return this; @@ -2600,7 +2621,7 @@ public WebSocket stream(byte[] fragment, boolean last) { @Override public WebSocket stream(byte[] fragment, int offset, int len, boolean last) { - if (fragment != null && fragment.length > 0) { + if (isNonEmpty(fragment)) { gWebSocket.stream(last, fragment, offset, len); } return this; @@ -2658,14 +2679,25 @@ public void close() { private static final class AHCWebSocketListenerAdapter implements org.glassfish.grizzly.websockets.WebSocketListener { private final WebSocketListener ahcListener; - private final WebSocket webSocket; + private final GrizzlyWebSocketAdapter webSocket; + private final StringBuilder stringBuffer; + private final ByteArrayOutputStream byteArrayOutputStream; + // -------------------------------------------------------- Constructors - AHCWebSocketListenerAdapter(final WebSocketListener ahcListener, WebSocket webSocket) { + AHCWebSocketListenerAdapter(final WebSocketListener ahcListener, + final GrizzlyWebSocketAdapter webSocket) { this.ahcListener = ahcListener; this.webSocket = webSocket; + if (webSocket.bufferFragments) { + stringBuffer = new StringBuilder(); + byteArrayOutputStream = new ByteArrayOutputStream(); + } else { + stringBuffer = null; + byteArrayOutputStream = null; + } } @@ -2675,7 +2707,7 @@ private static final class AHCWebSocketListenerAdapter implements org.glassfish. @Override public void onClose(org.glassfish.grizzly.websockets.WebSocket gWebSocket, DataFrame dataFrame) { try { - if (WebSocketCloseCodeReasonListener.class.isAssignableFrom(ahcListener.getClass())) { + if (ahcListener instanceof WebSocketCloseCodeReasonListener) { ClosingFrame cf = ClosingFrame.class.cast(dataFrame); WebSocketCloseCodeReasonListener.class.cast(ahcListener).onClose(webSocket, cf.getCode(), cf.getReason()); } else { @@ -2698,7 +2730,7 @@ public void onConnect(org.glassfish.grizzly.websockets.WebSocket gWebSocket) { @Override public void onMessage(org.glassfish.grizzly.websockets.WebSocket webSocket, String s) { try { - if (WebSocketTextListener.class.isAssignableFrom(ahcListener.getClass())) { + if (ahcListener instanceof WebSocketTextListener) { WebSocketTextListener.class.cast(ahcListener).onMessage(s); } } catch (Throwable e) { @@ -2709,7 +2741,7 @@ public void onMessage(org.glassfish.grizzly.websockets.WebSocket webSocket, Stri @Override public void onMessage(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] bytes) { try { - if (WebSocketByteListener.class.isAssignableFrom(ahcListener.getClass())) { + if (ahcListener instanceof WebSocketByteListener) { WebSocketByteListener.class.cast(ahcListener).onMessage(bytes); } } catch (Throwable e) { @@ -2720,7 +2752,7 @@ public void onMessage(org.glassfish.grizzly.websockets.WebSocket webSocket, byte @Override public void onPing(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] bytes) { try { - if (WebSocketPingListener.class.isAssignableFrom(ahcListener.getClass())) { + if (ahcListener instanceof WebSocketPingListener) { WebSocketPingListener.class.cast(ahcListener).onPing(bytes); } } catch (Throwable e) { @@ -2731,7 +2763,7 @@ public void onPing(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] @Override public void onPong(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] bytes) { try { - if (WebSocketPongListener.class.isAssignableFrom(ahcListener.getClass())) { + if (ahcListener instanceof WebSocketPongListener) { WebSocketPongListener.class.cast(ahcListener).onPong(bytes); } } catch (Throwable e) { @@ -2740,10 +2772,23 @@ public void onPong(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] } @Override - public void onFragment(org.glassfish.grizzly.websockets.WebSocket webSocket, String s, boolean b) { + public void onFragment(org.glassfish.grizzly.websockets.WebSocket webSocket, String s, boolean last) { try { - if (WebSocketTextListener.class.isAssignableFrom(ahcListener.getClass())) { - WebSocketTextListener.class.cast(ahcListener).onFragment(s, b); + if (this.webSocket.bufferFragments) { + synchronized (this.webSocket) { + stringBuffer.append(s); + if (last) { + if (ahcListener instanceof WebSocketTextListener) { + final String message = stringBuffer.toString(); + stringBuffer.setLength(0); + WebSocketTextListener.class.cast(ahcListener).onMessage(message); + } + } + } + } else { + if (ahcListener instanceof WebSocketTextListener) { + WebSocketTextListener.class.cast(ahcListener).onFragment(s, last); + } } } catch (Throwable e) { ahcListener.onError(e); @@ -2751,10 +2796,23 @@ public void onFragment(org.glassfish.grizzly.websockets.WebSocket webSocket, Str } @Override - public void onFragment(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] bytes, boolean b) { + public void onFragment(org.glassfish.grizzly.websockets.WebSocket webSocket, byte[] bytes, boolean last) { try { - if (WebSocketByteListener.class.isAssignableFrom(ahcListener.getClass())) { - WebSocketByteListener.class.cast(ahcListener).onFragment(bytes, b); + if (this.webSocket.bufferFragments) { + synchronized (this.webSocket) { + byteArrayOutputStream.write(bytes); + if (last) { + if (ahcListener instanceof WebSocketByteListener) { + final byte[] bytesLocal = byteArrayOutputStream.toByteArray(); + byteArrayOutputStream.reset(); + WebSocketByteListener.class.cast(ahcListener).onMessage(bytesLocal); + } + } + } + } else { + if (ahcListener instanceof WebSocketByteListener) { + WebSocketByteListener.class.cast(ahcListener).onFragment(bytes, last); + } } } catch (Throwable e) { ahcListener.onError(e); @@ -2783,7 +2841,35 @@ public int hashCode() { return result; } } // END AHCWebSocketListenerAdapter - + + + public static void main(String[] args) { + SecureRandom secureRandom = new SecureRandom(); + SSLContext sslContext = null; + try { + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, secureRandom); + } catch (Exception e) { + e.printStackTrace(); + } + AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder() + .setConnectionTimeoutInMs(5000) + .setSSLContext(sslContext).build(); + AsyncHttpClient client = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + try { + long start = System.currentTimeMillis(); + try { + client.executeRequest(client.prepareGet("http://www.google.com/").build()).get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + System.out.println("COMPLETE: " + (System.currentTimeMillis() - start) + "ms"); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProviderConfig.java b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProviderConfig.java index 70b7425391..066e1a959d 100644 --- a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProviderConfig.java +++ b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProviderConfig.java @@ -14,6 +14,7 @@ package com.ning.http.client.providers.grizzly; import com.ning.http.client.AsyncHttpProviderConfig; +import org.glassfish.grizzly.http.HttpCodecFilter; import org.glassfish.grizzly.nio.transport.TCPNIOTransport; import java.util.HashMap; @@ -49,7 +50,24 @@ public static enum Property { * * @see TransportCustomizer */ - TRANSPORT_CUSTOMIZER(TransportCustomizer.class); + TRANSPORT_CUSTOMIZER(TransportCustomizer.class), + + + /** + * Defines the maximum HTTP packet header size. + */ + MAX_HTTP_PACKET_HEADER_SIZE(Integer.class, HttpCodecFilter.DEFAULT_MAX_HTTP_PACKET_HEADER_SIZE), + + + /** + * By default, Websocket messages that are fragmented will be buffered. Once all + * fragments have been accumulated, the appropriate onMessage() call back will be + * invoked with the complete message. If this functionality is not desired, set + * this property to false. + */ + BUFFER_WEBSOCKET_FRAGMENTS(Boolean.class, true) + + ; final Object defaultValue; diff --git a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyConnectionsPool.java b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyConnectionsPool.java index e3478e5769..8859fb821c 100644 --- a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyConnectionsPool.java +++ b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyConnectionsPool.java @@ -13,14 +13,18 @@ package com.ning.http.client.providers.grizzly; +import static com.ning.http.util.DateUtil.millisTime; + import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.ConnectionsPool; +import org.glassfish.grizzly.CloseListener; +import org.glassfish.grizzly.CloseType; import org.glassfish.grizzly.Connection; import org.glassfish.grizzly.Grizzly; import org.glassfish.grizzly.attributes.Attribute; -import org.glassfish.grizzly.attributes.NullaryFunction; import org.glassfish.grizzly.utils.DataStructures; +import org.glassfish.grizzly.utils.NullaryFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,34 +59,68 @@ public class GrizzlyConnectionsPool implements ConnectionsPool() { + public void onClosed(Connection connection, CloseType closeType) + throws IOException { + if (closeType == CloseType.REMOTELY) { + if (LOG.isInfoEnabled()) { + LOG.info("Remote closed connection ({}). Removing from cache", + connection.toString()); + } + } + GrizzlyConnectionsPool.this.removeAll(connection); + } + }; // ------------------------------------------------------------ Constructors + @SuppressWarnings("UnusedDeclaration") + public GrizzlyConnectionsPool(final boolean cacheSSLConnections, + final int timeout, + final int maxConnectionLifeTimeInMs, + final int maxConnectionsPerHost, + final int maxConnections, + final DelayedExecutor delayedExecutor) { + this.cacheSSLConnections = cacheSSLConnections; + this.timeout = timeout; + this.maxConnectionLifeTimeInMs = maxConnectionLifeTimeInMs; + this.maxConnectionsPerHost = maxConnectionsPerHost; + this.maxConnections = maxConnections; + unlimitedConnections = (maxConnections == -1); + if (delayedExecutor != null) { + this.delayedExecutor = delayedExecutor; + ownsDelayedExecutor = false; + } else { + this.delayedExecutor = + new DelayedExecutor(Executors.newSingleThreadExecutor(), + this); + ownsDelayedExecutor = true; + } + if (!this.delayedExecutor.isStarted) { + this.delayedExecutor.start(); + } + } + + public GrizzlyConnectionsPool(final AsyncHttpClientConfig config) { cacheSSLConnections = config.isSslConnectionPoolEnabled(); timeout = config.getIdleConnectionInPoolTimeoutInMs(); + maxConnectionLifeTimeInMs = config.getMaxConnectionLifeTimeInMs(); maxConnectionsPerHost = config.getMaxConnectionPerHost(); maxConnections = config.getMaxTotalConnections(); unlimitedConnections = (maxConnections == -1); - delayedExecutor = new DelayedExecutor(Executors.newSingleThreadExecutor()); + delayedExecutor = new DelayedExecutor(Executors.newSingleThreadExecutor(), this); delayedExecutor.start(); - listener = new Connection.CloseListener() { - @Override - public void onClosed(Connection connection, Connection.CloseType closeType) throws IOException { - if (closeType == Connection.CloseType.REMOTELY) { - if (LOG.isInfoEnabled()) { - LOG.info("Remote closed connection ({}). Removing from cache", connection.toString()); - } - } - GrizzlyConnectionsPool.this.removeAll(connection); - } - }; - + ownsDelayedExecutor = true; } @@ -94,7 +132,7 @@ public void onClosed(Connection connection, Connection.CloseType closeType) thro */ public boolean offer(String uri, Connection connection) { - if (cacheSSLConnections && isSecure(uri)) { + if (isSecure(uri) && !cacheSSLConnections) { return false; } @@ -105,7 +143,7 @@ public boolean offer(String uri, Connection connection) { new Object[]{uri, connection}); } DelayedExecutor.IdleConnectionQueue newPool = - delayedExecutor.createIdleConnectionQueue(timeout); + delayedExecutor.createIdleConnectionQueue(timeout, maxConnectionLifeTimeInMs); conQueue = connectionsPool.putIfAbsent(uri, newPool); if (conQueue == null) { conQueue = newPool; @@ -119,13 +157,14 @@ public boolean offer(String uri, Connection connection) { final int total = totalCachedConnections.incrementAndGet(); if (LOG.isDebugEnabled()) { LOG.debug("[offer] Pooling connection [{}] for uri [{}]. Current size (for host; before pooling): [{}]. Max size (for host): [{}]. Total number of cached connections: [{}].", - new Object[]{connection, uri, size, maxConnectionsPerHost, total}); + connection, uri, size, maxConnectionsPerHost, total); } return true; } if (LOG.isDebugEnabled()) { LOG.debug("[offer] Unable to pool connection [{}] for uri [{}]. Current size (for host): [{}]. Max size (for host): [{}]. Total number of cached connections: [{}].", - new Object[]{connection, uri, size, maxConnectionsPerHost, totalCachedConnections.get()}); + connection, uri, size, maxConnectionsPerHost, + totalCachedConnections.get()); } return false; @@ -190,6 +229,9 @@ public boolean removeAll(Connection connection) { boolean removed = entry.getValue().remove(connection); isRemoved |= removed; } + if (isRemoved) { + totalCachedConnections.decrementAndGet(); + } return isRemoved; } @@ -219,8 +261,10 @@ public void destroy() { entry.getValue().destroy(); } connectionsPool.clear(); - delayedExecutor.stop(); - delayedExecutor.getThreadPool().shutdownNow(); + if (ownsDelayedExecutor) { + delayedExecutor.stop(); + delayedExecutor.getThreadPool().shutdownNow(); + } } @@ -230,7 +274,7 @@ public void destroy() { private boolean isSecure(String uri) { - return (uri.charAt(0) == 'h' && uri.charAt(4) == 's'); + return (uri.startsWith("https") || uri.startsWith("wss")); } @@ -238,7 +282,7 @@ private boolean isSecure(String uri) { // ---------------------------------------------------------- Nested Classes - private static final class DelayedExecutor { + public static final class DelayedExecutor { public final static long UNSET_TIMEOUT = -1; private final ExecutorService threadPool; @@ -248,25 +292,31 @@ private static final class DelayedExecutor { private final Object sync = new Object(); private volatile boolean isStarted; private final long checkIntervalMs; + private final AtomicInteger totalCachedConnections; // -------------------------------------------------------- Constructors - private DelayedExecutor(final ExecutorService threadPool) { - this(threadPool, 1000, TimeUnit.MILLISECONDS); + public DelayedExecutor(final ExecutorService threadPool, + final GrizzlyConnectionsPool connectionsPool) { + this(threadPool, 1000, TimeUnit.MILLISECONDS, connectionsPool); } - // ----------------------------------------------------- Private Methods - - private DelayedExecutor(final ExecutorService threadPool, + public DelayedExecutor(final ExecutorService threadPool, final long checkInterval, - final TimeUnit timeunit) { + final TimeUnit timeunit, + final GrizzlyConnectionsPool connectionsPool) { this.threadPool = threadPool; this.checkIntervalMs = TimeUnit.MILLISECONDS.convert(checkInterval, timeunit); + totalCachedConnections = connectionsPool.totalCachedConnections; } + + // ----------------------------------------------------- Private Methods + + private void start() { synchronized (sync) { if (!isStarted) { @@ -289,15 +339,15 @@ private ExecutorService getThreadPool() { return threadPool; } - private IdleConnectionQueue createIdleConnectionQueue(final long timeout) { - final IdleConnectionQueue queue = new IdleConnectionQueue(timeout); + private IdleConnectionQueue createIdleConnectionQueue(final long timeout, final long maxConnectionLifeTimeInMs) { + final IdleConnectionQueue queue = new IdleConnectionQueue(timeout, maxConnectionLifeTimeInMs); queues.add(queue); return queue; } @SuppressWarnings({"NumberEquality"}) - private static boolean wasModified(final Long l1, final Long l2) { - return l1 != l2 && (l1 != null ? !l1.equals(l2) : !l2.equals(l1)); + private static boolean wasModified(final long l1, final long l2) { + return l1 != l2; } @@ -310,7 +360,7 @@ private class DelayedRunnable implements Runnable { @Override public void run() { while (isStarted) { - final long currentTimeMs = System.currentTimeMillis(); + final long currentTimeMs = millisTime(); for (final IdleConnectionQueue delayQueue : queues) { if (delayQueue.queue.isEmpty()) continue; @@ -321,7 +371,7 @@ public void run() { final Connection element = it.next(); final Long timeoutMs = resolver.getTimeoutMs(element); - if (timeoutMs == null || timeoutMs == UNSET_TIMEOUT) { + if (timeoutMs == UNSET_TIMEOUT) { it.remove(); if (wasModified(timeoutMs, resolver.getTimeoutMs(element))) { @@ -337,7 +387,8 @@ public void run() { if (LOG.isDebugEnabled()) { LOG.debug("Idle connection ({}) detected. Removing from cache.", element.toString()); } - element.close().markForRecycle(true); + totalCachedConnections.decrementAndGet(); + element.close(); } catch (Exception ignored) { } } @@ -367,12 +418,14 @@ final class IdleConnectionQueue { final TimeoutResolver resolver = new TimeoutResolver(); final long timeout; final AtomicInteger count = new AtomicInteger(0); + final long maxConnectionLifeTimeInMs; // ---------------------------------------------------- Constructors - public IdleConnectionQueue(final long timeout) { + public IdleConnectionQueue(final long timeout, final long maxConnectionLifeTimeInMs) { this.timeout = timeout; + this.maxConnectionLifeTimeInMs = maxConnectionLifeTimeInMs; } @@ -380,9 +433,25 @@ public IdleConnectionQueue(final long timeout) { void offer(final Connection c) { - if (timeout >= 0) { - resolver.setTimeoutMs(c, System.currentTimeMillis() + timeout); + long timeoutMs = UNSET_TIMEOUT; + long currentTime = millisTime(); + if (maxConnectionLifeTimeInMs < 0 && timeout >= 0) { + timeoutMs = currentTime + timeout; + } else if (maxConnectionLifeTimeInMs >= 0) { + long t = resolver.getTimeoutMs(c); + if (t == UNSET_TIMEOUT) { + if (timeout >= 0) { + timeoutMs = currentTime + Math.min(maxConnectionLifeTimeInMs, timeout); + } else { + timeoutMs = currentTime + maxConnectionLifeTimeInMs; + } + } else { + if (timeout >= 0) { + timeoutMs = Math.min(t, currentTime + timeout); + } + } } + resolver.setTimeoutMs(c, timeoutMs); queue.offer(c); count.incrementAndGet(); } @@ -411,7 +480,7 @@ boolean isEmpty() { void destroy() { for (Connection c : queue) { - c.close().markForRecycle(true); + c.close(); } queue.clear(); queues.remove(this); @@ -445,7 +514,7 @@ boolean removeTimeout(final Connection c) { return true; } - Long getTimeoutMs(final Connection c) { + long getTimeoutMs(final Connection c) { return IDLE_ATTR.get(c).timeoutMs; } @@ -458,7 +527,7 @@ void setTimeoutMs(final Connection c, final long timeoutMs) { static final class IdleRecord { - volatile long timeoutMs; + volatile long timeoutMs = UNSET_TIMEOUT; } // END IdleRecord diff --git a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponse.java b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponse.java index 52f3fece36..8df137e885 100644 --- a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponse.java +++ b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponse.java @@ -13,6 +13,8 @@ package com.ning.http.client.providers.grizzly; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.Cookie; import com.ning.http.client.FluentCaseInsensitiveStringsMap; import com.ning.http.client.HttpResponseBodyPart; @@ -33,6 +35,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; +import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; @@ -61,26 +64,24 @@ public class GrizzlyResponse implements Response { public GrizzlyResponse(final HttpResponseStatus status, final HttpResponseHeaders headers, - final Collection bodyParts) { + final List bodyParts) { this.status = status; this.headers = headers; this.bodyParts = bodyParts; - if (bodyParts != null && !bodyParts.isEmpty()) { - HttpResponseBodyPart[] parts = - bodyParts.toArray(new HttpResponseBodyPart[bodyParts.size()]); - if (parts.length == 1) { - responseBody = ((GrizzlyResponseBodyPart) parts[0]).getBodyBuffer(); + if (isNonEmpty(bodyParts)) { + if (bodyParts.size() == 1) { + responseBody = ((GrizzlyResponseBodyPart) bodyParts.get(0)).getBodyBuffer(); } else { - final Buffer firstBuffer = ((GrizzlyResponseBodyPart) parts[0]).getBodyBuffer(); + final Buffer firstBuffer = ((GrizzlyResponseBodyPart) bodyParts.get(0)).getBodyBuffer(); final MemoryManager mm = MemoryManager.DEFAULT_MEMORY_MANAGER; Buffer constructedBodyBuffer = firstBuffer; - for (int i = 1, len = parts.length; i < len; i++) { + for (int i = 1, len = bodyParts.size(); i < len; i++) { constructedBodyBuffer = Buffers.appendBuffers(mm, constructedBodyBuffer, - ((GrizzlyResponseBodyPart) parts[i]).getBodyBuffer()); + ((GrizzlyResponseBodyPart) bodyParts.get(i)).getBodyBuffer()); } responseBody = constructedBodyBuffer; } @@ -162,7 +163,7 @@ public String getResponseBodyExcerpt(int maxLength) throws IOException { */ public String getResponseBody() throws IOException { - return getResponseBody(Charsets.DEFAULT_CHARACTER_ENCODING); + return getResponseBody(null); } @@ -171,9 +172,25 @@ public String getResponseBody() throws IOException { * {@inheritDoc} */ public byte[] getResponseBodyAsBytes() throws IOException { + final byte[] responseBodyBytes = new byte[responseBody.remaining()]; + final int origPos = responseBody.position(); + responseBody.get(responseBodyBytes); + responseBody.position(origPos); + return responseBodyBytes; + } - return getResponseBody().getBytes(Charsets.DEFAULT_CHARACTER_ENCODING); + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException { + return responseBody.toByteBuffer(); + } + /** + * @return the response body as a Grizzly {@link Buffer}. + * + * @since 1.7.11. + */ + @SuppressWarnings("UnusedDeclaration") + private Buffer getResponseBodyAsBuffer() { + return responseBody; } @@ -231,9 +248,16 @@ public FluentCaseInsensitiveStringsMap getHeaders() { * {@inheritDoc} */ public boolean isRedirected() { - - return between(status.getStatusCode(), 300, 399); - + switch (status.getStatusCode()) { + case 301: + case 302: + case 303: + case 307: + case 308: + return true; + default: + return false; + } } @@ -248,16 +272,16 @@ public List getCookies() { if (cookies == null) { List values = headers.getHeaders().get("set-cookie"); - if (values != null && !values.isEmpty()) { + if (isNonEmpty(values)) { CookiesBuilder.ServerCookiesBuilder builder = - new CookiesBuilder.ServerCookiesBuilder(false); + new CookiesBuilder.ServerCookiesBuilder(false, true); for (String header : values) { builder.parse(header); } cookies = convertCookies(builder.build()); } else { - cookies = Collections.unmodifiableList(Collections.emptyList()); + cookies = Collections.emptyList(); } } return cookies; @@ -277,7 +301,7 @@ public boolean hasResponseStatus() { * {@inheritDoc} */ public boolean hasResponseHeaders() { - return (headers != null && !headers.getHeaders().isEmpty()); + return headers != null && !headers.getHeaders().isEmpty(); } @@ -285,7 +309,7 @@ public boolean hasResponseHeaders() { * {@inheritDoc} */ public boolean hasResponseBody() { - return (bodyParts != null && !bodyParts.isEmpty()); + return isNonEmpty(bodyParts); } @@ -328,14 +352,4 @@ private Charset getCharset(final String charset) { return Charsets.lookupCharset(charsetLocal); } - - - private boolean between(final int value, - final int lowerBound, - final int upperBound) { - - return (value >= lowerBound && value <= upperBound); - - } - -} +} \ No newline at end of file diff --git a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponseFuture.java b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponseFuture.java index 239b677206..fb68580678 100644 --- a/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponseFuture.java +++ b/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponseFuture.java @@ -14,14 +14,13 @@ package com.ning.http.client.providers.grizzly; import com.ning.http.client.AsyncHandler; +import com.ning.http.client.ProxyServer; import com.ning.http.client.Request; import com.ning.http.client.listenable.AbstractListenableFuture; import org.glassfish.grizzly.Connection; import org.glassfish.grizzly.impl.FutureImpl; -import java.io.IOException; -import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -38,10 +37,11 @@ public class GrizzlyResponseFuture extends AbstractListenableFuture { private final AtomicBoolean done = new AtomicBoolean(false); + private final AtomicBoolean cancelled = new AtomicBoolean(false); private final AsyncHandler handler; private final GrizzlyAsyncHttpProvider provider; private final Request request; - + private final ProxyServer proxy; private Connection connection; FutureImpl delegate; @@ -52,34 +52,44 @@ public class GrizzlyResponseFuture extends AbstractListenableFuture { GrizzlyResponseFuture(final GrizzlyAsyncHttpProvider provider, final Request request, - final AsyncHandler handler) { + final AsyncHandler handler, + final ProxyServer proxy) { this.provider = provider; this.request = request; this.handler = handler; - + this.proxy = proxy; } // ----------------------------------- Methods from AbstractListenableFuture - public void done(Callable callable) { - - done.compareAndSet(false, true); - super.done(); + public void done() { + if (!done.compareAndSet(false, true) || cancelled.get()) { + return; + } + runListeners(); } public void abort(Throwable t) { + if (done.get() || !cancelled.compareAndSet(false, true)) { + return; + } + delegate.failure(t); if (handler != null) { - handler.onThrowable(t); + try { + handler.onThrowable(t); + } catch (Throwable ignore) { + } + } closeConnection(); - done(); + runListeners(); } @@ -121,8 +131,16 @@ public boolean getAndSetWriteBody(boolean writeBody) { public boolean cancel(boolean mayInterruptIfRunning) { - handler.onThrowable(new CancellationException()); - done(); + if (done.get() || !cancelled.compareAndSet(false, true)) { + return false; + } + if (handler != null) { + try { + handler.onThrowable(new CancellationException()); + } catch (Throwable ignore) { + } + } + runListeners(); return delegate.cancel(mayInterruptIfRunning); } @@ -182,10 +200,13 @@ void setDelegate(final FutureImpl delegate) { private void closeConnection() { - if (connection != null && !connection.isOpen()) { + if (connection != null && connection.isOpen()) { connection.close().markForRecycle(true); } } + public ProxyServer getProxy() { + return proxy; + } } diff --git a/src/main/java/com/ning/http/client/providers/jdk/JDKAsyncHttpProvider.java b/src/main/java/com/ning/http/client/providers/jdk/JDKAsyncHttpProvider.java index 37c8af7748..011884c171 100644 --- a/src/main/java/com/ning/http/client/providers/jdk/JDKAsyncHttpProvider.java +++ b/src/main/java/com/ning/http/client/providers/jdk/JDKAsyncHttpProvider.java @@ -12,6 +12,8 @@ */ package com.ning.http.client.providers.jdk; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.AsyncHandler; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProvider; @@ -41,6 +43,7 @@ import com.ning.http.util.ProxyUtils; import com.ning.http.util.SslUtils; import com.ning.http.util.UTF8UrlEncoder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,7 +71,6 @@ import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -102,7 +104,7 @@ public JDKAsyncHttpProvider(AsyncHttpClientConfig config) { this.config = config; AsyncHttpProviderConfig providerConfig = config.getAsyncHttpProviderConfig(); - if (providerConfig != null && JDKAsyncHttpProviderConfig.class.isAssignableFrom(providerConfig.getClass())) { + if (providerConfig instanceof JDKAsyncHttpProviderConfig) { configure(JDKAsyncHttpProviderConfig.class.cast(providerConfig)); } } @@ -130,11 +132,10 @@ public ListenableFuture execute(Request request, AsyncHandler handler, throw new IOException(String.format("Too many connections %s", config.getMaxTotalConnections())); } - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); + ProxyServer proxyServer = ProxyUtils.getProxyServer(config, request); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); - boolean avoidProxy = ProxyUtils.avoidProxy(proxyServer, request); Proxy proxy = null; - if (!avoidProxy && (proxyServer != null || realm != null)) { + if (proxyServer != null || realm != null) { try { proxy = configureProxyAndAuth(proxyServer, realm); } catch (AuthenticationException e) { @@ -163,11 +164,10 @@ public ListenableFuture execute(Request request, AsyncHandler handler, } private HttpURLConnection createUrlConnection(Request request) throws IOException { - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); + ProxyServer proxyServer = ProxyUtils.getProxyServer(config, request); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); - boolean avoidProxy = ProxyUtils.avoidProxy(proxyServer, request); Proxy proxy = null; - if (!avoidProxy && proxyServer != null || realm != null) { + if (proxyServer != null || realm != null) { try { proxy = configureProxyAndAuth(proxyServer, realm); } catch (AuthenticationException e) { @@ -175,13 +175,8 @@ private HttpURLConnection createUrlConnection(Request request) throws IOExceptio } } - HttpURLConnection urlConnection = null; - if (proxy == null) { - urlConnection = - (HttpURLConnection) AsyncHttpProviderUtils.createUri(request.getUrl()).toURL().openConnection(Proxy.NO_PROXY); - } else { - urlConnection = (HttpURLConnection) AsyncHttpProviderUtils.createUri(request.getUrl()).toURL().openConnection(proxy); - } + HttpURLConnection urlConnection = (HttpURLConnection) + request.getURI().toURL().openConnection(proxy == null ? Proxy.NO_PROXY : proxy); if (request.getUrl().startsWith("https")) { HttpsURLConnection secure = (HttpsURLConnection) urlConnection; @@ -205,7 +200,7 @@ public void close() { isClose.set(true); } - public Response prepareResponse(HttpResponseStatus status, HttpResponseHeaders headers, Collection bodyParts) { + public Response prepareResponse(HttpResponseStatus status, HttpResponseHeaders headers, List bodyParts) { return new JDKResponse(status, headers, bodyParts); } @@ -243,7 +238,7 @@ public T call() throws Exception { configure(uri, urlConnection, request); urlConnection.connect(); - if (TransferCompletionHandler.class.isAssignableFrom(asyncHandler.getClass())) { + if (asyncHandler instanceof TransferCompletionHandler) { throw new IllegalStateException(TransferCompletionHandler.class.getName() + "not supported by this provider"); } @@ -358,14 +353,15 @@ public T call() throws Exception { } } - if (ProgressAsyncHandler.class.isAssignableFrom(asyncHandler.getClass())) { - ProgressAsyncHandler.class.cast(asyncHandler).onHeaderWriteCompleted(); - ProgressAsyncHandler.class.cast(asyncHandler).onContentWriteCompleted(); + if (asyncHandler instanceof ProgressAsyncHandler) { + ProgressAsyncHandler progressAsyncHandler = (ProgressAsyncHandler) asyncHandler; + progressAsyncHandler.onHeaderWriteCompleted(); + progressAsyncHandler.onContentWriteCompleted(); } try { T t = asyncHandler.onCompleted(); future.content(t); - future.done(null); + future.done(); return t; } catch (Throwable t) { RuntimeException ex = new RuntimeException(); @@ -375,7 +371,7 @@ public T call() throws Exception { } catch (Throwable t) { logger.debug(t.getMessage(), t); - if (IOException.class.isAssignableFrom(t.getClass()) && config.getIOExceptionFilters().size() > 0) { + if (t instanceof IOException && !config.getIOExceptionFilters().isEmpty()) { FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(asyncHandler) .request(request).ioException(IOException.class.cast(t)).build(); @@ -385,7 +381,7 @@ public T call() throws Exception { if (config.getMaxTotalConnections() != -1) { maxConnections.decrementAndGet(); } - future.done(null); + future.done(); } if (fc.replayRequest()) { @@ -426,20 +422,18 @@ private FilterContext handleIoException(FilterContext fc) throws FilterException } private Throwable filterException(Throwable t) { - if (UnknownHostException.class.isAssignableFrom(t.getClass())) { + if (t instanceof UnknownHostException) { t = new ConnectException(t.getMessage()); - } - if (SocketTimeoutException.class.isAssignableFrom(t.getClass())) { + } else if (t instanceof SocketTimeoutException) { int responseTimeoutInMs = config.getRequestTimeoutInMs(); if (request.getPerRequestConfig() != null && request.getPerRequestConfig().getRequestTimeoutInMs() != -1) { responseTimeoutInMs = request.getPerRequestConfig().getRequestTimeoutInMs(); } t = new TimeoutException(String.format("No response received after %s", responseTimeoutInMs)); - } - if (SSLHandshakeException.class.isAssignableFrom(t.getClass())) { + } else if (t instanceof SSLHandshakeException) { Throwable t2 = new ConnectException(); t2.initCause(t); t = t2; @@ -494,9 +488,9 @@ private void configure(URI uri, HttpURLConnection urlConnection, Request request } } - String ka = config.getAllowPoolingConnection() ? "keep-alive" : "close"; + String ka = AsyncHttpProviderUtils.keepAliveHeaderValue(config); urlConnection.setRequestProperty("Connection", ka); - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); + ProxyServer proxyServer = ProxyUtils.getProxyServer(config, request); boolean avoidProxy = ProxyUtils.avoidProxy(proxyServer, uri.getHost()); if (!avoidProxy) { urlConnection.setRequestProperty("Proxy-Connection", ka); @@ -518,7 +512,7 @@ private void configure(URI uri, HttpURLConnection urlConnection, Request request AuthenticatorUtils.computeBasicAuthentication(realm)); break; case DIGEST: - if (realm.getNonce() != null && !realm.getNonce().equals("")) { + if (isNonEmpty(realm.getNonce())) { try { urlConnection.setRequestProperty("Authorization", AuthenticatorUtils.computeDigestAuthentication(realm)); @@ -552,7 +546,7 @@ private void configure(URI uri, HttpURLConnection urlConnection, Request request urlConnection.setRequestProperty("User-Agent", AsyncHttpProviderUtils.constructUserAgent(JDKAsyncHttpProvider.class)); } - if (request.getCookies() != null && !request.getCookies().isEmpty()) { + if (isNonEmpty(request.getCookies())) { urlConnection.setRequestProperty("Cookie", AsyncHttpProviderUtils.encodeCookies(request.getCookies())); } @@ -619,7 +613,7 @@ private void configure(URI uri, HttpURLConnection urlConnection, Request request lenght = MAX_BUFFERED_BYTES; } - MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity(request.getParts(), request.getParams()); + MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity(request.getParts(), request.getHeaders()); urlConnection.setRequestProperty("Content-Type", mre.getContentType()); urlConnection.setRequestProperty("Content-Length", String.valueOf(mre.getContentLength())); diff --git a/src/main/java/com/ning/http/client/providers/jdk/JDKDelegateFuture.java b/src/main/java/com/ning/http/client/providers/jdk/JDKDelegateFuture.java index 9553e02152..b1dbcc3a3a 100644 --- a/src/main/java/com/ning/http/client/providers/jdk/JDKDelegateFuture.java +++ b/src/main/java/com/ning/http/client/providers/jdk/JDKDelegateFuture.java @@ -12,11 +12,12 @@ */ package com.ning.http.client.providers.jdk; +import static com.ning.http.util.DateUtil.millisTime; + import com.ning.http.client.AsyncHandler; import com.ning.http.client.ListenableFuture; import java.net.HttpURLConnection; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -30,9 +31,9 @@ public JDKDelegateFuture(AsyncHandler asyncHandler, int responseTimeoutInMs, this.delegateFuture = delegateFuture; } - public void done(Callable callable) { - delegateFuture.done(callable); - super.done(callable); + public void done() { + delegateFuture.done(); + super.done(); } public void abort(Throwable t) { @@ -66,7 +67,7 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution content = innerFuture.get(timeout, unit); } } catch (Throwable t) { - if (!contentProcessed.get() && timeout != -1 && ((System.currentTimeMillis() - touch.get()) <= responseTimeoutInMs)) { + if (!contentProcessed.get() && timeout != -1 && ((millisTime() - touch.get()) <= responseTimeoutInMs)) { return get(timeout, unit); } timedOut.set(true); @@ -77,7 +78,7 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution delegateFuture.abort(new ExecutionException(exception.get())); } delegateFuture.content(content); - delegateFuture.done(null); + delegateFuture.done(); return content; } } diff --git a/src/main/java/com/ning/http/client/providers/jdk/JDKFuture.java b/src/main/java/com/ning/http/client/providers/jdk/JDKFuture.java index 4666459dc9..0ec695ae9f 100644 --- a/src/main/java/com/ning/http/client/providers/jdk/JDKFuture.java +++ b/src/main/java/com/ning/http/client/providers/jdk/JDKFuture.java @@ -12,13 +12,14 @@ */ package com.ning.http.client.providers.jdk; +import static com.ning.http.util.DateUtil.millisTime; + import com.ning.http.client.AsyncHandler; import com.ning.http.client.listenable.AbstractListenableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.HttpURLConnection; -import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -40,7 +41,7 @@ public class JDKFuture extends AbstractListenableFuture { protected final AtomicBoolean timedOut = new AtomicBoolean(false); protected final AtomicBoolean isDone = new AtomicBoolean(false); protected final AtomicReference exception = new AtomicReference(); - protected final AtomicLong touch = new AtomicLong(System.currentTimeMillis()); + protected final AtomicLong touch = new AtomicLong(millisTime()); protected final AtomicBoolean contentProcessed = new AtomicBoolean(false); protected final HttpURLConnection urlConnection; private boolean writeHeaders; @@ -58,9 +59,9 @@ protected void setInnerFuture(Future innerFuture) { this.innerFuture = innerFuture; } - public void done(Callable callable) { + public void done() { isDone.set(true); - super.done(); + runListeners(); } public void abort(Throwable t) { @@ -75,7 +76,7 @@ public void abort(Throwable t) { logger.debug("asyncHandler.onThrowable", te); } } - super.done(); + runListeners(); } public void content(V v) { @@ -90,10 +91,10 @@ public boolean cancel(boolean mayInterruptIfRunning) { logger.debug("asyncHandler.onThrowable", te); } cancelled.set(true); - super.done(); + runListeners(); return innerFuture.cancel(mayInterruptIfRunning); } else { - super.done(); + runListeners(); return false; } } @@ -126,7 +127,7 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution content = innerFuture.get(timeout, unit); } } catch (TimeoutException t) { - if (!contentProcessed.get() && timeout != -1 && ((System.currentTimeMillis() - touch.get()) <= responseTimeoutInMs)) { + if (!contentProcessed.get() && timeout != -1 && ((millisTime() - touch.get()) <= responseTimeoutInMs)) { return get(timeout, unit); } @@ -149,7 +150,7 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution * @return true if response has expired and should be terminated. */ public boolean hasExpired() { - return responseTimeoutInMs != -1 && ((System.currentTimeMillis() - touch.get()) > responseTimeoutInMs); + return responseTimeoutInMs != -1 && ((millisTime() - touch.get()) > responseTimeoutInMs); } /** @@ -157,7 +158,7 @@ public boolean hasExpired() { */ /* @Override */ public void touch() { - touch.set(System.currentTimeMillis()); + touch.set(millisTime()); } /** diff --git a/src/main/java/com/ning/http/client/providers/jdk/JDKResponse.java b/src/main/java/com/ning/http/client/providers/jdk/JDKResponse.java index f1fd4d2ce1..00cae65b0a 100644 --- a/src/main/java/com/ning/http/client/providers/jdk/JDKResponse.java +++ b/src/main/java/com/ning/http/client/providers/jdk/JDKResponse.java @@ -12,10 +12,12 @@ */ package com.ning.http.client.providers.jdk; +import static com.ning.http.util.MiscUtil.isNonEmpty; + +import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder; import com.ning.http.client.Cookie; import com.ning.http.client.FluentCaseInsensitiveStringsMap; import com.ning.http.client.HttpResponseBodyPart; -import com.ning.http.client.HttpResponseBodyPartsInputStream; import com.ning.http.client.HttpResponseHeaders; import com.ning.http.client.HttpResponseStatus; import com.ning.http.client.Response; @@ -26,29 +28,29 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; +import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class JDKResponse implements Response { private final static String DEFAULT_CHARSET = "ISO-8859-1"; - private final static String HEADERS_NOT_COMPUTED = "Response's headers hasn't been computed by your AsyncHandler."; private final URI uri; - private final Collection bodyParts; + private final List bodyParts; private final HttpResponseHeaders headers; private final HttpResponseStatus status; - private final List cookies = new ArrayList(); + private List cookies; private AtomicBoolean contentComputed = new AtomicBoolean(false); private String content; public JDKResponse(HttpResponseStatus status, HttpResponseHeaders headers, - Collection bodyParts) { + List bodyParts) { this.bodyParts = bodyParts; this.headers = headers; @@ -80,33 +82,25 @@ public byte[] getResponseBodyAsBytes() throws IOException { return AsyncHttpProviderUtils.contentToByte(bodyParts); } + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException { + return ByteBuffer.wrap(getResponseBodyAsBytes()); + } + public String getResponseBody(String charset) throws IOException { - String contentType = getContentType(); - if (contentType != null && charset == null) { - charset = AsyncHttpProviderUtils.parseCharset(contentType); - } - if (charset == null) { - charset = DEFAULT_CHARSET; - } - - if (!contentComputed.get()) { - content = AsyncHttpProviderUtils.contentToString(bodyParts, charset); + if (!contentComputed.get()) { + content = AsyncHttpProviderUtils.contentToString(bodyParts, computeCharset(charset)); } return content; } - + /* @Override */ public InputStream getResponseBodyAsStream() throws IOException { if (contentComputed.get()) { return new ByteArrayInputStream(content.getBytes(DEFAULT_CHARSET)); } - if (bodyParts.size() > 0) { - return new HttpResponseBodyPartsInputStream(bodyParts.toArray(new HttpResponseBodyPart[bodyParts.size()])); - } else { - return new ByteArrayInputStream("".getBytes()); - } + return AsyncHttpProviderUtils.contentToInputStream(bodyParts); } /* @Override */ @@ -116,14 +110,7 @@ public String getResponseBodyExcerpt(int maxLength) throws IOException { } public String getResponseBodyExcerpt(int maxLength, String charset) throws IOException { - String contentType = getContentType(); - if (contentType != null && charset == null) { - charset = AsyncHttpProviderUtils.parseCharset(contentType); - } - - if (charset == null) { - charset = DEFAULT_CHARSET; - } + charset = computeCharset(charset); if (!contentComputed.get()) { content = AsyncHttpProviderUtils.contentToString(bodyParts, charset == null ? DEFAULT_CHARSET : charset); @@ -131,6 +118,15 @@ public String getResponseBodyExcerpt(int maxLength, String charset) throws IOExc return content.length() <= maxLength ? content : content.substring(0, maxLength); } + + private String computeCharset(String charset) { + if (charset == null) { + String contentType = getContentType(); + if (contentType != null) + charset = AsyncHttpProviderUtils.parseCharset(contentType); // parseCharset can return null + } + return charset != null? charset: DEFAULT_CHARSET; + } /* @Override */ @@ -141,64 +137,63 @@ public URI getUri() throws MalformedURLException { /* @Override */ public String getContentType() { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders().getFirstValue("Content-Type"); + return getHeader("Content-Type"); } /* @Override */ public String getHeader(String name) { - if (headers == null) { - throw new IllegalStateException(); - } - return headers.getHeaders().getFirstValue(name); + return headers != null? headers.getHeaders().getFirstValue(name): null; } /* @Override */ public List getHeaders(String name) { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders().get(name); + return headers != null? headers.getHeaders().get(name): Collections. emptyList(); } /* @Override */ public FluentCaseInsensitiveStringsMap getHeaders() { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders(); + return headers != null? headers.getHeaders(): new FluentCaseInsensitiveStringsMap(); } /* @Override */ public boolean isRedirected() { - return (status.getStatusCode() >= 300) && (status.getStatusCode() <= 399); + switch (status.getStatusCode()) { + case 301: + case 302: + case 303: + case 307: + case 308: + return true; + default: + return false; + } } /* @Override */ public List getCookies() { if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); + return Collections.emptyList(); } - if (cookies.isEmpty()) { + if (!isNonEmpty(cookies)) { + List localCookies = new ArrayList(); for (Map.Entry> header : headers.getHeaders().entrySet()) { if (header.getKey().equalsIgnoreCase("Set-Cookie")) { // TODO: ask for parsed header List v = header.getValue(); for (String value : v) { - Cookie cookie = AsyncHttpProviderUtils.parseCookie(value); - cookies.add(cookie); + Set cookies = CookieDecoder.decode(value); + localCookies.addAll(cookies); } } } + cookies = Collections.unmodifiableList(localCookies); } - return Collections.unmodifiableList(cookies); + return cookies; } /** @@ -206,7 +201,7 @@ public List getCookies() { */ /* @Override */ public boolean hasResponseStatus() { - return (bodyParts != null ? true : false); + return bodyParts != null; } /** @@ -214,7 +209,7 @@ public boolean hasResponseStatus() { */ /* @Override */ public boolean hasResponseHeaders() { - return (headers != null ? true : false); + return headers != null; } /** @@ -222,6 +217,6 @@ public boolean hasResponseHeaders() { */ /* @Override */ public boolean hasResponseBody() { - return (bodyParts != null && bodyParts.size() > 0 ? true : false); + return isNonEmpty(bodyParts); } } diff --git a/src/main/java/com/ning/http/client/providers/netty/BodyChunkedInput.java b/src/main/java/com/ning/http/client/providers/netty/BodyChunkedInput.java index 9ea1de6609..1cf8282b2c 100644 --- a/src/main/java/com/ning/http/client/providers/netty/BodyChunkedInput.java +++ b/src/main/java/com/ning/http/client/providers/netty/BodyChunkedInput.java @@ -16,68 +16,61 @@ import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.handler.stream.ChunkedInput; -import java.io.IOException; import java.nio.ByteBuffer; /** * Adapts a {@link Body} to Netty's {@link ChunkedInput}. */ -class BodyChunkedInput - implements ChunkedInput { +class BodyChunkedInput implements ChunkedInput { - private final Body body; - - private final int chunkSize = 1024 * 8; + private static final int DEFAULT_CHUNK_SIZE = 8 * 1024; - private ByteBuffer nextChunk; + private final Body body; + private final int contentLength; + private final int chunkSize; - private static final ByteBuffer EOF = ByteBuffer.allocate(0); + private boolean endOfInput; public BodyChunkedInput(Body body) { if (body == null) { throw new IllegalArgumentException("no body specified"); } this.body = body; + contentLength = (int) body.getContentLength(); + if (contentLength <= 0) + chunkSize = DEFAULT_CHUNK_SIZE; + else + chunkSize = Math.min(contentLength, DEFAULT_CHUNK_SIZE); } - private ByteBuffer peekNextChuck() - throws IOException { + public boolean hasNextChunk() throws Exception { + // unused + throw new UnsupportedOperationException(); + } - if (nextChunk == null) { + public Object nextChunk() throws Exception { + if (endOfInput) { + return null; + } else { ByteBuffer buffer = ByteBuffer.allocate(chunkSize); - if (body.read(buffer) < 0) { - nextChunk = EOF; + long r = body.read(buffer); + if (r < 0L) { + endOfInput = true; + return null; } else { + endOfInput = r == contentLength || r < chunkSize && contentLength > 0; buffer.flip(); - nextChunk = buffer; + return ChannelBuffers.wrappedBuffer(buffer); } } - return nextChunk; - } - - public boolean hasNextChunk() - throws Exception { - return !isEndOfInput(); - } - - public Object nextChunk() - throws Exception { - ByteBuffer buffer = peekNextChuck(); - if (buffer == EOF) { - return null; - } - nextChunk = null; - return ChannelBuffers.wrappedBuffer(buffer); } - public boolean isEndOfInput() - throws Exception { - return peekNextChuck() == EOF; + public boolean isEndOfInput() throws Exception { + // called by ChunkedWriteHandler AFTER nextChunk + return endOfInput; } - public void close() - throws Exception { + public void close() throws Exception { body.close(); } - } diff --git a/src/main/java/com/ning/http/client/providers/netty/ChannelBufferUtil.java b/src/main/java/com/ning/http/client/providers/netty/ChannelBufferUtil.java new file mode 100644 index 0000000000..e2707f6dfe --- /dev/null +++ b/src/main/java/com/ning/http/client/providers/netty/ChannelBufferUtil.java @@ -0,0 +1,35 @@ +/* + * Copyright 2010 Ning, Inc. + * + * Ning licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.ning.http.client.providers.netty; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class ChannelBufferUtil { + + public static byte[] channelBuffer2bytes(ChannelBuffer b) { + int readable = b.readableBytes(); + int readerIndex = b.readerIndex(); + if (b.hasArray()) { + byte[] array = b.array(); + if (b.arrayOffset() == 0 && readerIndex == 0 && array.length == readable) { + return array; + } + } + byte[] array = new byte[readable]; + b.getBytes(readerIndex, array); + return array; + } +} diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java b/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java index da017b1679..1722c6e0cf 100644 --- a/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java @@ -21,6 +21,7 @@ import com.ning.http.client.AsyncHttpProvider; import com.ning.http.client.Body; import com.ning.http.client.BodyGenerator; +import com.ning.http.client.ConnectionPoolKeyStrategy; import com.ning.http.client.ConnectionsPool; import com.ning.http.client.Cookie; import com.ning.http.client.FluentCaseInsensitiveStringsMap; @@ -55,6 +56,8 @@ import com.ning.http.util.ProxyUtils; import com.ning.http.util.SslUtils; import com.ning.http.util.UTF8UrlEncoder; +import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder; +import com.ning.org.jboss.netty.handler.codec.http.CookieEncoder; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBufferOutputStream; @@ -75,8 +78,7 @@ import org.jboss.netty.channel.socket.ClientSocketChannelFactory; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory; -import org.jboss.netty.handler.codec.http.CookieEncoder; -import org.jboss.netty.handler.codec.http.DefaultCookie; +import org.jboss.netty.handler.codec.PrematureChannelClosureException; import org.jboss.netty.handler.codec.http.DefaultHttpChunkTrailer; import org.jboss.netty.handler.codec.http.DefaultHttpRequest; import org.jboss.netty.handler.codec.http.HttpChunk; @@ -91,7 +93,9 @@ import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseDecoder; import org.jboss.netty.handler.codec.http.HttpVersion; +import org.jboss.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame; +import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder; import org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameEncoder; import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame; @@ -113,12 +117,12 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; +import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -131,11 +135,24 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.DISABLE_NESTED_REQUEST; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.EXECUTE_ASYNC_CONNECT; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.HTTPS_CLIENT_CODEC_MAX_CHUNK_SIZE; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.HTTPS_CLIENT_CODEC_MAX_HEADER_SIZE; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.HTTPS_CLIENT_CODEC_MAX_INITIAL_LINE_LENGTH; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.HTTP_CLIENT_CODEC_MAX_CHUNK_SIZE; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.HTTP_CLIENT_CODEC_MAX_HEADER_SIZE; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.HTTP_CLIENT_CODEC_MAX_INITIAL_LINE_LENGTH; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.REUSE_ADDRESS; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.SOCKET_CHANNEL_FACTORY; +import static com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig.USE_BLOCKING_IO; import static com.ning.http.util.AsyncHttpProviderUtils.DEFAULT_CHARSET; +import static com.ning.http.util.DateUtil.millisTime; +import static com.ning.http.util.MiscUtil.isNonEmpty; import static org.jboss.netty.channel.Channels.pipeline; public class NettyAsyncHttpProvider extends SimpleChannelUpstreamHandler implements AsyncHttpProvider { - private final static String WEBSOCKET_KEY = "Sec-WebSocket-Key"; private final static String HTTP_HANDLER = "httpHandler"; protected final static String SSL_HANDLER = "sslHandler"; private final static String HTTPS = "https"; @@ -143,6 +160,7 @@ public class NettyAsyncHttpProvider extends SimpleChannelUpstreamHandler impleme private static final String WEBSOCKET = "ws"; private static final String WEBSOCKET_SSL = "wss"; private final static Logger log = LoggerFactory.getLogger(NettyAsyncHttpProvider.class); + private final static Charset UTF8 = Charset.forName("UTF-8"); private final ClientBootstrap plainBootstrap; private final ClientBootstrap secureBootstrap; private final ClientBootstrap webSocketBootstrap; @@ -151,18 +169,24 @@ public class NettyAsyncHttpProvider extends SimpleChannelUpstreamHandler impleme private final AsyncHttpClientConfig config; private final AtomicBoolean isClose = new AtomicBoolean(false); private final ClientSocketChannelFactory socketChannelFactory; - - private final ChannelGroup openChannels = new - CleanupChannelGroup("asyncHttpClient") { - @Override - public boolean remove(Object o) { - boolean removed = super.remove(o); - if (removed && trackConnections) { - freeConnections.release(); - } - return removed; - } - }; + private final boolean allowReleaseSocketChannelFactory; + private int httpClientCodecMaxInitialLineLength = 4096; + private int httpClientCodecMaxHeaderSize = 8192; + private int httpClientCodecMaxChunkSize = 8192; + private int httpsClientCodecMaxInitialLineLength = 4096; + private int httpsClientCodecMaxHeaderSize = 8192; + private int httpsClientCodecMaxChunkSize = 8192; + + private final ChannelGroup openChannels = new CleanupChannelGroup("asyncHttpClient") { + @Override + public boolean remove(Object o) { + boolean removed = super.remove(o); + if (removed && trackConnections) { + freeConnections.release(); + } + return removed; + } + }; private final ConnectionsPool connectionsPool; private Semaphore freeConnections = null; private final NettyAsyncHttpProviderConfig asyncHttpProviderConfig; @@ -171,41 +195,55 @@ public boolean remove(Object o) { private final boolean trackConnections; private final boolean useRawUrl; private final static NTLMEngine ntlmEngine = new NTLMEngine(); - private final static SpnegoEngine spnegoEngine = new SpnegoEngine(); + private static SpnegoEngine spnegoEngine = null; private final Protocol httpProtocol = new HttpProtocol(); private final Protocol webSocketProtocol = new WebSocketProtocol(); + private static boolean isNTLM(List auth) { + return isNonEmpty(auth) && auth.get(0).startsWith("NTLM"); + } + public NettyAsyncHttpProvider(AsyncHttpClientConfig config) { - if (config.getAsyncHttpProviderConfig() != null - && NettyAsyncHttpProviderConfig.class.isAssignableFrom(config.getAsyncHttpProviderConfig().getClass())) { + if (config.getAsyncHttpProviderConfig() instanceof NettyAsyncHttpProviderConfig) { asyncHttpProviderConfig = NettyAsyncHttpProviderConfig.class.cast(config.getAsyncHttpProviderConfig()); } else { asyncHttpProviderConfig = new NettyAsyncHttpProviderConfig(); } - if (asyncHttpProviderConfig.getProperty(NettyAsyncHttpProviderConfig.USE_BLOCKING_IO) != null) { + if (asyncHttpProviderConfig.getProperty(USE_BLOCKING_IO) != null) { socketChannelFactory = new OioClientSocketChannelFactory(config.executorService()); + this.allowReleaseSocketChannelFactory = true; } else { - ExecutorService e; - Object o = asyncHttpProviderConfig.getProperty(NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE); - if (o != null && ExecutorService.class.isAssignableFrom(o.getClass())) { - e = ExecutorService.class.cast(o); + // check if external NioClientSocketChannelFactory is defined + Object oo = asyncHttpProviderConfig.getProperty(SOCKET_CHANNEL_FACTORY); + if (oo instanceof NioClientSocketChannelFactory) { + this.socketChannelFactory = NioClientSocketChannelFactory.class.cast(oo); + + // cannot allow releasing shared channel factory + this.allowReleaseSocketChannelFactory = false; } else { - e = Executors.newCachedThreadPool(); + ExecutorService e; + Object o = asyncHttpProviderConfig.getProperty(BOSS_EXECUTOR_SERVICE); + if (o instanceof ExecutorService) { + e = ExecutorService.class.cast(o); + } else { + e = Executors.newCachedThreadPool(); + } + int numWorkers = config.getIoThreadMultiplier() * Runtime.getRuntime().availableProcessors(); + log.debug("Number of application's worker threads is {}", numWorkers); + socketChannelFactory = new NioClientSocketChannelFactory(e, config.executorService(), numWorkers); + this.allowReleaseSocketChannelFactory = true; } - int numWorkers = config.getIoThreadMultiplier() * Runtime.getRuntime().availableProcessors(); - log.debug("Number of application's worker threads is {}", numWorkers); - socketChannelFactory = new NioClientSocketChannelFactory(e, config.executorService(), numWorkers); } plainBootstrap = new ClientBootstrap(socketChannelFactory); secureBootstrap = new ClientBootstrap(socketChannelFactory); webSocketBootstrap = new ClientBootstrap(socketChannelFactory); secureWebSocketBootstrap = new ClientBootstrap(socketChannelFactory); - configureNetty(); - this.config = config; + configureNetty(); + // This is dangerous as we can't catch a wrong typed ConnectionsPool ConnectionsPool cp = (ConnectionsPool) config.getConnectionsPool(); if (cp == null && config.getAllowPoolingConnection()) { @@ -227,9 +265,9 @@ public NettyAsyncHttpProvider(AsyncHttpClientConfig config) { @Override public String toString() { - return String.format("NettyAsyncHttpProvider:\n\t- maxConnections: %d\n\t- openChannels: %s\n\t- connectionPools: %s", - config.getMaxTotalConnections() - freeConnections.availablePermits(), - openChannels.toString(), + return String.format("NettyAsyncHttpProvider:\n\t- maxConnections: %d\n\t- openChannels: %s\n\t- connectionPools: %s",// + config.getMaxTotalConnections() - freeConnections.availablePermits(),// + openChannels.toString(),// connectionsPool.toString()); } @@ -238,6 +276,8 @@ void configureNetty() { for (Entry entry : asyncHttpProviderConfig.propertiesSet()) { plainBootstrap.setOption(entry.getKey(), entry.getValue()); } + configureHttpClientCodec(); + configureHttpsClientCodec(); } plainBootstrap.setPipelineFactory(new ChannelPipelineFactory() { @@ -246,7 +286,7 @@ void configureNetty() { public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = pipeline(); - pipeline.addLast(HTTP_HANDLER, new HttpClientCodec()); + pipeline.addLast(HTTP_HANDLER, createHttpClientCodec()); if (config.getRequestCompressionLevel() > 0) { pipeline.addLast("deflater", new HttpContentCompressor(config.getRequestCompressionLevel())); @@ -263,10 +303,10 @@ public ChannelPipeline getPipeline() throws Exception { DefaultChannelFuture.setUseDeadLockChecker(false); if (asyncHttpProviderConfig != null) { - Object value = asyncHttpProviderConfig.getProperty(NettyAsyncHttpProviderConfig.EXECUTE_ASYNC_CONNECT); - if (value != null && Boolean.class.isAssignableFrom(value.getClass())) { + Object value = asyncHttpProviderConfig.getProperty(EXECUTE_ASYNC_CONNECT); + if (value instanceof Boolean) { executeConnectAsync = Boolean.class.cast(value); - } else if (asyncHttpProviderConfig.getProperty(NettyAsyncHttpProviderConfig.DISABLE_NESTED_REQUEST) != null) { + } else if (asyncHttpProviderConfig.getProperty(DISABLE_NESTED_REQUEST) != null) { DefaultChannelFuture.setUseDeadLockChecker(true); } } @@ -276,14 +316,26 @@ public ChannelPipeline getPipeline() throws Exception { /* @Override */ public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = pipeline(); - pipeline.addLast("ws-decoder", new HttpResponseDecoder()); - pipeline.addLast("ws-encoder", new HttpRequestEncoder()); + pipeline.addLast("http-decoder", new HttpResponseDecoder()); + pipeline.addLast("http-encoder", new HttpRequestEncoder()); pipeline.addLast("httpProcessor", NettyAsyncHttpProvider.this); return pipeline; } }); } + protected void configureHttpClientCodec() { + httpClientCodecMaxInitialLineLength = asyncHttpProviderConfig.getProperty(HTTP_CLIENT_CODEC_MAX_INITIAL_LINE_LENGTH, Integer.class, httpClientCodecMaxInitialLineLength); + httpClientCodecMaxHeaderSize = asyncHttpProviderConfig.getProperty(HTTP_CLIENT_CODEC_MAX_HEADER_SIZE, Integer.class, httpClientCodecMaxHeaderSize); + httpClientCodecMaxChunkSize = asyncHttpProviderConfig.getProperty(HTTP_CLIENT_CODEC_MAX_CHUNK_SIZE, Integer.class, httpClientCodecMaxChunkSize); + } + + protected void configureHttpsClientCodec() { + httpsClientCodecMaxInitialLineLength = asyncHttpProviderConfig.getProperty(HTTPS_CLIENT_CODEC_MAX_INITIAL_LINE_LENGTH, Integer.class, httpsClientCodecMaxInitialLineLength); + httpsClientCodecMaxHeaderSize = asyncHttpProviderConfig.getProperty(HTTPS_CLIENT_CODEC_MAX_HEADER_SIZE, Integer.class, httpsClientCodecMaxHeaderSize); + httpsClientCodecMaxChunkSize = asyncHttpProviderConfig.getProperty(HTTPS_CLIENT_CODEC_MAX_CHUNK_SIZE, Integer.class, httpsClientCodecMaxChunkSize); + } + void constructSSLPipeline(final NettyConnectListener cl) { secureBootstrap.setPipelineFactory(new ChannelPipelineFactory() { @@ -298,7 +350,7 @@ public ChannelPipeline getPipeline() throws Exception { abort(cl.future(), ex); } - pipeline.addLast(HTTP_HANDLER, new HttpClientCodec()); + pipeline.addLast(HTTP_HANDLER, createHttpsClientCodec()); if (config.isCompressionEnabled()) { pipeline.addLast("inflater", new HttpContentDecompressor()); @@ -321,8 +373,8 @@ public ChannelPipeline getPipeline() throws Exception { abort(cl.future(), ex); } - pipeline.addLast("ws-decoder", new HttpResponseDecoder()); - pipeline.addLast("ws-encoder", new HttpRequestEncoder()); + pipeline.addLast("http-decoder", new HttpResponseDecoder()); + pipeline.addLast("http-encoder", new HttpRequestEncoder()); pipeline.addLast("httpProcessor", NettyAsyncHttpProvider.this); return pipeline; @@ -337,15 +389,15 @@ public ChannelPipeline getPipeline() throws Exception { } } - private Channel lookupInCache(URI uri) { - final Channel channel = connectionsPool.poll(AsyncHttpProviderUtils.getBaseUrl(uri)); + private Channel lookupInCache(URI uri, ConnectionPoolKeyStrategy connectionPoolKeyStrategy) { + final Channel channel = connectionsPool.poll(connectionPoolKeyStrategy.getKey(uri)); if (channel != null) { log.debug("Using cached Channel {}\n for uri {}\n", channel, uri); try { // Always make sure the channel who got cached support the proper protocol. It could - // only occurs when a HttpMethod.CONNECT is used agains a proxy that require upgrading from http to + // only occurs when a HttpMethod.CONNECT is used against a proxy that require upgrading from http to // https. return verifyChannelPipeline(channel, uri.getScheme()); } catch (Exception ex) { @@ -363,6 +415,14 @@ private SSLEngine createSSLEngine() throws IOException, GeneralSecurityException return sslEngine; } + private HttpClientCodec createHttpClientCodec() { + return new HttpClientCodec(httpClientCodecMaxInitialLineLength, httpClientCodecMaxHeaderSize, httpClientCodecMaxChunkSize); + } + + private HttpClientCodec createHttpsClientCodec() { + return new HttpClientCodec(httpsClientCodecMaxInitialLineLength, httpsClientCodecMaxHeaderSize, httpsClientCodecMaxChunkSize); + } + private Channel verifyChannelPipeline(Channel channel, String scheme) throws IOException, GeneralSecurityException { if (channel.getPipeline().get(SSL_HANDLER) != null && HTTP.equalsIgnoreCase(scheme)) { @@ -375,25 +435,25 @@ private Channel verifyChannelPipeline(Channel channel, String scheme) throws IOE return channel; } - protected final void writeRequest(final Channel channel, - final AsyncHttpClientConfig config, - final NettyResponseFuture future, - final HttpRequest nettyRequest) { + protected final void writeRequest(final Channel channel, final AsyncHttpClientConfig config, final NettyResponseFuture future) { + + HttpRequest nettyRequest = future.getNettyRequest(); + boolean ssl = channel.getPipeline().get(SslHandler.class) != null; + try { /** - * If the channel is dead because it was pooled and the remote server decided to close it, - * we just let it go and the closeChannel do it's work. + * If the channel is dead because it was pooled and the remote server decided to close it, we just let it go and the closeChannel do it's work. */ if (!channel.isOpen() || !channel.isConnected()) { return; } Body body = null; - if (!future.getNettyRequest().getMethod().equals(HttpMethod.CONNECT)) { + if (!nettyRequest.getMethod().equals(HttpMethod.CONNECT)) { BodyGenerator bg = future.getRequest().getBodyGenerator(); if (bg != null) { // Netty issue with chunking. - if (InputStreamBodyGenerator.class.isAssignableFrom(bg.getClass())) { + if (bg instanceof InputStreamBodyGenerator) { InputStreamBodyGenerator.class.cast(bg).patchNettyChunkingIssue(true); } @@ -408,22 +468,24 @@ protected final void writeRequest(final Channel channel, } else { nettyRequest.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); } - } else { - body = null; + + } else if (future.getRequest().getParts() != null) { + String contentType = nettyRequest.getHeader(HttpHeaders.Names.CONTENT_TYPE); + String length = nettyRequest.getHeader(HttpHeaders.Names.CONTENT_LENGTH); + body = new MultipartBody(future.getRequest().getParts(), contentType, length); } } - if (TransferCompletionHandler.class.isAssignableFrom(future.getAsyncHandler().getClass())) { + if (future.getAsyncHandler() instanceof TransferCompletionHandler) { FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - for (String s : future.getNettyRequest().getHeaderNames()) { - for (String header : future.getNettyRequest().getHeaders(s)) { + for (String s : nettyRequest.getHeaderNames()) { + for (String header : nettyRequest.getHeaders(s)) { h.add(s, header); } } - TransferCompletionHandler.class.cast(future.getAsyncHandler()).transferAdapter( - new NettyTransferAdapter(h, nettyRequest.getContent(), future.getRequest().getFile())); + TransferCompletionHandler.class.cast(future.getAsyncHandler()).transferAdapter(new NettyTransferAdapter(h, nettyRequest.getContent(), future.getRequest().getFile())); } // Leave it to true. @@ -442,7 +504,7 @@ protected final void writeRequest(final Channel channel, } if (future.getAndSetWriteBody(true)) { - if (!future.getNettyRequest().getMethod().equals(HttpMethod.CONNECT)) { + if (!nettyRequest.getMethod().equals(HttpMethod.CONNECT)) { if (future.getRequest().getFile() != null) { final File file = future.getRequest().getFile(); @@ -453,13 +515,22 @@ protected final void writeRequest(final Channel channel, fileLength = raf.length(); ChannelFuture writeFuture; - if (channel.getPipeline().get(SslHandler.class) != null) { - writeFuture = channel.write(new ChunkedFile(raf, 0, fileLength, 8192)); + if (ssl) { + writeFuture = channel.write(new ChunkedFile(raf, 0, fileLength, MAX_BUFFERED_BYTES)); } else { final FileRegion region = new OptimizedFileRegion(raf, 0, fileLength); writeFuture = channel.write(region); } - writeFuture.addListener(new ProgressListener(false, future.getAsyncHandler(), future)); + writeFuture.addListener(new ProgressListener(false, future.getAsyncHandler(), future) { + public void operationComplete(ChannelFuture cf) { + try { + raf.close(); + } catch (IOException e) { + log.warn("Failed to close request body: {}", e.getMessage(), e); + } + super.operationComplete(cf); + } + }); } catch (IOException ex) { if (raf != null) { try { @@ -469,18 +540,10 @@ protected final void writeRequest(final Channel channel, } throw ex; } - } else if (body != null || future.getRequest().getParts() != null) { - /** - * TODO: AHC-78: SSL + zero copy isn't supported by the MultiPart class and pretty complex to implements. - */ - if (future.getRequest().getParts() != null) { - String boundary = future.getNettyRequest().getHeader("Content-Type"); - String length = future.getNettyRequest().getHeader("Content-Length"); - body = new MultipartBody(future.getRequest().getParts(), boundary, length); - } + } else if (body != null) { ChannelFuture writeFuture; - if (channel.getPipeline().get(SslHandler.class) == null && (body instanceof RandomAccessBody)) { + if (!ssl && body instanceof RandomAccessBody) { BodyFileRegion bodyFileRegion = new BodyFileRegion((RandomAccessBody) body); writeFuture = channel.write(bodyFileRegion); } else { @@ -512,10 +575,11 @@ public void operationComplete(ChannelFuture cf) { try { future.touch(); - int delay = requestTimeout(config, future.getRequest().getPerRequestConfig()); - if (delay != -1 && !future.isDone() && !future.isCancelled()) { + int requestTimeout = requestTimeoutInMs(config, future.getRequest().getPerRequestConfig()); + int schedulePeriod = requestTimeout != -1 ? (config.getIdleConnectionTimeoutInMs() != -1 ? Math.min(requestTimeout, config.getIdleConnectionTimeoutInMs()) : requestTimeout) : config.getIdleConnectionTimeoutInMs(); + if (schedulePeriod != -1 && !future.isDone() && !future.isCancelled()) { ReaperFuture reaperFuture = new ReaperFuture(future); - Future scheduledFuture = config.reaper().scheduleAtFixedRate(reaperFuture, 0, delay, TimeUnit.MILLISECONDS); + Future scheduledFuture = config.reaper().scheduleAtFixedRate(reaperFuture, 0, schedulePeriod, TimeUnit.MILLISECONDS); reaperFuture.setScheduledFuture(scheduledFuture); future.setReaperFuture(reaperFuture); } @@ -525,63 +589,55 @@ public void operationComplete(ChannelFuture cf) { } - private static boolean isProxyServer(AsyncHttpClientConfig config, Request request) { - return request.getProxyServer() != null || config.getProxyServer() != null; - } - - protected final static HttpRequest buildRequest(AsyncHttpClientConfig config, Request request, URI uri, - boolean allowConnect, ChannelBuffer buffer) throws IOException { + protected final static HttpRequest buildRequest(AsyncHttpClientConfig config, Request request, URI uri, boolean allowConnect, ChannelBuffer buffer, ProxyServer proxyServer) throws IOException { String method = request.getMethod(); - if (allowConnect && (isProxyServer(config, request) && isSecure(uri))) { + if (allowConnect && proxyServer != null && isSecure(uri)) { method = HttpMethod.CONNECT.toString(); } - return construct(config, request, new HttpMethod(method), uri, buffer); + return construct(config, request, new HttpMethod(method), uri, buffer, proxyServer); } - @SuppressWarnings("deprecation") - private static HttpRequest construct(AsyncHttpClientConfig config, - Request request, - HttpMethod m, - URI uri, - ChannelBuffer buffer) throws IOException { + private static SpnegoEngine getSpnegoEngine() { + if (spnegoEngine == null) + spnegoEngine = new SpnegoEngine(); + return spnegoEngine; + } - String host = AsyncHttpProviderUtils.getHost(uri); - boolean webSocket = isWebSocket(uri); + private static HttpRequest construct(AsyncHttpClientConfig config, Request request, HttpMethod m, URI uri, ChannelBuffer buffer, ProxyServer proxyServer) throws IOException { + + String host = null; if (request.getVirtualHost() != null) { host = request.getVirtualHost(); + } else { + host = AsyncHttpProviderUtils.getHost(uri); } HttpRequest nettyRequest; if (m.equals(HttpMethod.CONNECT)) { nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_0, m, AsyncHttpProviderUtils.getAuthority(uri)); } else { - StringBuilder path = null; - if (isProxyServer(config, request)) - path = new StringBuilder(uri.toString()); - else { - path = new StringBuilder(uri.getRawPath()); - if (uri.getQuery() != null) { - path.append("?").append(uri.getRawQuery()); - } - } - nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, m, path.toString()); + String path = null; + if (proxyServer != null && !(isSecure(uri) && config.isUseRelativeURIsWithSSLProxies())) + path = uri.toString(); + else if (uri.getRawQuery() != null) + path = uri.getRawPath() + "?" + uri.getRawQuery(); + else + path = uri.getRawPath(); + nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, m, path); } - + boolean webSocket = isWebSocket(uri); if (webSocket) { nettyRequest.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET); nettyRequest.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE); - nettyRequest.addHeader("Origin", "http://" + uri.getHost() + ":" - + (uri.getPort() == -1 ? isSecure(uri.getScheme()) ? 443 : 80 : uri.getPort())); - nettyRequest.addHeader(WEBSOCKET_KEY, WebSocketUtil.getKey()); - nettyRequest.addHeader("Sec-WebSocket-Version", "13"); + nettyRequest.addHeader(HttpHeaders.Names.ORIGIN, "http://" + uri.getHost() + ":" + uri.getPort()); + nettyRequest.addHeader(HttpHeaders.Names.SEC_WEBSOCKET_KEY, WebSocketUtil.getKey()); + nettyRequest.addHeader(HttpHeaders.Names.SEC_WEBSOCKET_VERSION, "13"); } if (host != null) { - if (uri.getPort() == -1) { - nettyRequest.setHeader(HttpHeaders.Names.HOST, host); - } else if (request.getVirtualHost() != null) { + if (request.getVirtualHost() != null || uri.getPort() == -1) { nettyRequest.setHeader(HttpHeaders.Names.HOST, host); } else { nettyRequest.setHeader(HttpHeaders.Names.HOST, host + ":" + uri.getPort()); @@ -591,13 +647,11 @@ private static HttpRequest construct(AsyncHttpClientConfig config, } if (!m.equals(HttpMethod.CONNECT)) { - FluentCaseInsensitiveStringsMap h = request.getHeaders(); - if (h != null) { - for (String name : h.keySet()) { - if (!"host".equalsIgnoreCase(name)) { - for (String value : h.get(name)) { - nettyRequest.addHeader(name, value); - } + for (Entry> header : request.getHeaders()) { + String name = header.getKey(); + if (!HttpHeaders.Names.HOST.equalsIgnoreCase(name)) { + for (String value : header.getValue()) { + nettyRequest.addHeader(name, value); } } } @@ -607,11 +661,10 @@ private static HttpRequest construct(AsyncHttpClientConfig config, } } else { List auth = request.getHeaders().get(HttpHeaders.Names.PROXY_AUTHORIZATION); - if (auth != null && auth.size() > 0 && auth.get(0).startsWith("NTLM")) { + if (isNTLM(auth)) { nettyRequest.addHeader(HttpHeaders.Names.PROXY_AUTHORIZATION, auth.get(0)); } } - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); if (realm != null && realm.getUsePreemptiveAuth()) { @@ -627,68 +680,63 @@ private static HttpRequest construct(AsyncHttpClientConfig config, } switch (realm.getAuthScheme()) { - case BASIC: - nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, - AuthenticatorUtils.computeBasicAuthentication(realm)); - break; - case DIGEST: - if (realm.getNonce() != null && !realm.getNonce().equals("")) { - try { - nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, - AuthenticatorUtils.computeDigestAuthentication(realm)); - } catch (NoSuchAlgorithmException e) { - throw new SecurityException(e); - } - } - break; - case NTLM: - try { - nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, - ntlmEngine.generateType1Msg("NTLM " + domain, authHost)); - } catch (NTLMEngineException e) { - IOException ie = new IOException(); - ie.initCause(e); - throw ie; - } - break; - case KERBEROS: - case SPNEGO: - String challengeHeader = null; - String server = proxyServer == null ? host : proxyServer.getHost(); + case BASIC: + nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, AuthenticatorUtils.computeBasicAuthentication(realm)); + break; + case DIGEST: + if (isNonEmpty(realm.getNonce())) { try { - challengeHeader = spnegoEngine.generateToken(server); - } catch (Throwable e) { - IOException ie = new IOException(); - ie.initCause(e); - throw ie; + nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, AuthenticatorUtils.computeDigestAuthentication(realm)); + } catch (NoSuchAlgorithmException e) { + throw new SecurityException(e); } - nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challengeHeader); - break; - case NONE: - break; - default: - throw new IllegalStateException(String.format("Invalid Authentication %s", realm.toString())); + } + break; + case NTLM: + try { + nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, ntlmEngine.generateType1Msg("NTLM " + domain, authHost)); + } catch (NTLMEngineException e) { + IOException ie = new IOException(); + ie.initCause(e); + throw ie; + } + break; + case KERBEROS: + case SPNEGO: + String challengeHeader = null; + String server = proxyServer == null ? host : proxyServer.getHost(); + try { + challengeHeader = getSpnegoEngine().generateToken(server); + } catch (Throwable e) { + IOException ie = new IOException(); + ie.initCause(e); + throw ie; + } + nettyRequest.setHeader(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challengeHeader); + break; + case NONE: + break; + default: + throw new IllegalStateException(String.format("Invalid Authentication %s", realm.toString())); } } if (!webSocket && !request.getHeaders().containsKey(HttpHeaders.Names.CONNECTION)) { - nettyRequest.setHeader(HttpHeaders.Names.CONNECTION, "keep-alive"); + nettyRequest.setHeader(HttpHeaders.Names.CONNECTION, AsyncHttpProviderUtils.keepAliveHeaderValue(config)); } - boolean avoidProxy = ProxyUtils.avoidProxy(proxyServer, request); - if (!avoidProxy) { + if (proxyServer != null) { if (!request.getHeaders().containsKey("Proxy-Connection")) { - nettyRequest.setHeader("Proxy-Connection", "keep-alive"); + nettyRequest.setHeader("Proxy-Connection", AsyncHttpProviderUtils.keepAliveHeaderValue(config)); } if (proxyServer.getPrincipal() != null) { - if (proxyServer.getNtlmDomain() != null && proxyServer.getNtlmDomain().length() > 0) { + if (isNonEmpty(proxyServer.getNtlmDomain())) { List auth = request.getHeaders().get(HttpHeaders.Names.PROXY_AUTHORIZATION); - if (!(auth != null && auth.size() > 0 && auth.get(0).startsWith("NTLM"))) { + if (!isNTLM(auth)) { try { - String msg = ntlmEngine.generateType1Msg(proxyServer.getNtlmDomain(), - proxyServer.getHost()); + String msg = ntlmEngine.generateType1Msg(proxyServer.getNtlmDomain(), proxyServer.getHost()); nettyRequest.setHeader(HttpHeaders.Names.PROXY_AUTHORIZATION, "NTLM " + msg); } catch (NTLMEngineException e) { IOException ie = new IOException(); @@ -697,40 +745,28 @@ private static HttpRequest construct(AsyncHttpClientConfig config, } } } else { - nettyRequest.setHeader(HttpHeaders.Names.PROXY_AUTHORIZATION, - AuthenticatorUtils.computeBasicAuthentication(proxyServer)); + nettyRequest.setHeader(HttpHeaders.Names.PROXY_AUTHORIZATION, AuthenticatorUtils.computeBasicAuthentication(proxyServer)); } } } // Add default accept headers. - if (request.getHeaders().getFirstValue("Accept") == null) { + if (!request.getHeaders().containsKey(HttpHeaders.Names.ACCEPT)) { nettyRequest.setHeader(HttpHeaders.Names.ACCEPT, "*/*"); } - if (request.getHeaders().getFirstValue("User-Agent") != null) { - nettyRequest.setHeader("User-Agent", request.getHeaders().getFirstValue("User-Agent")); + String userAgentHeader = request.getHeaders().getFirstValue(HttpHeaders.Names.USER_AGENT); + if (userAgentHeader != null) { + nettyRequest.setHeader(HttpHeaders.Names.USER_AGENT, userAgentHeader); } else if (config.getUserAgent() != null) { - nettyRequest.setHeader("User-Agent", config.getUserAgent()); + nettyRequest.setHeader(HttpHeaders.Names.USER_AGENT, config.getUserAgent()); } else { - nettyRequest.setHeader("User-Agent", AsyncHttpProviderUtils.constructUserAgent(NettyAsyncHttpProvider.class)); + nettyRequest.setHeader(HttpHeaders.Names.USER_AGENT, AsyncHttpProviderUtils.constructUserAgent(NettyAsyncHttpProvider.class)); } if (!m.equals(HttpMethod.CONNECT)) { - if (request.getCookies() != null && !request.getCookies().isEmpty()) { - CookieEncoder httpCookieEncoder = new CookieEncoder(false); - Iterator ic = request.getCookies().iterator(); - Cookie c; - org.jboss.netty.handler.codec.http.Cookie cookie; - while (ic.hasNext()) { - c = ic.next(); - cookie = new DefaultCookie(c.getName(), c.getValue()); - cookie.setPath(c.getPath()); - cookie.setMaxAge(c.getMaxAge()); - cookie.setDomain(c.getDomain()); - httpCookieEncoder.addCookie(cookie); - } - nettyRequest.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); + if (isNonEmpty(request.getCookies())) { + nettyRequest.setHeader(HttpHeaders.Names.COOKIE, CookieEncoder.encodeClientSide(request.getCookies(), config.isRfc6265CookieEncoding())); } String reqType = request.getMethod(); @@ -746,15 +782,16 @@ private static HttpRequest construct(AsyncHttpClientConfig config, nettyRequest.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(request.getByteData().length)); nettyRequest.setContent(ChannelBuffers.wrappedBuffer(request.getByteData())); } else if (request.getStringData() != null) { - nettyRequest.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(request.getStringData().getBytes(bodyCharset).length)); - nettyRequest.setContent(ChannelBuffers.wrappedBuffer(request.getStringData().getBytes(bodyCharset))); + byte[] bytes = request.getStringData().getBytes(bodyCharset); + nettyRequest.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(bytes.length)); + nettyRequest.setContent(ChannelBuffers.wrappedBuffer(bytes)); } else if (request.getStreamData() != null) { int[] lengthWrapper = new int[1]; byte[] bytes = AsyncHttpProviderUtils.readFully(request.getStreamData(), lengthWrapper); int length = lengthWrapper[0]; nettyRequest.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(length)); nettyRequest.setContent(ChannelBuffers.wrappedBuffer(bytes, 0, length)); - } else if (request.getParams() != null && !request.getParams().isEmpty()) { + } else if (isNonEmpty(request.getParams())) { StringBuilder sb = new StringBuilder(); for (final Entry> paramEntry : request.getParams()) { final String key = paramEntry.getKey(); @@ -771,38 +808,23 @@ private static HttpRequest construct(AsyncHttpClientConfig config, nettyRequest.setContent(ChannelBuffers.wrappedBuffer(sb.toString().getBytes(bodyCharset))); if (!request.getHeaders().containsKey(HttpHeaders.Names.CONTENT_TYPE)) { - nettyRequest.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/x-www-form-urlencoded"); + nettyRequest.setHeader(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED); } } else if (request.getParts() != null) { - int lenght = computeAndSetContentLength(request, nettyRequest); - - if (lenght == -1) { - lenght = MAX_BUFFERED_BYTES; - } - - MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity(request.getParts(), request.getParams()); + MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity(request.getParts(), request.getHeaders()); nettyRequest.setHeader(HttpHeaders.Names.CONTENT_TYPE, mre.getContentType()); nettyRequest.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(mre.getContentLength())); - /** - * TODO: AHC-78: SSL + zero copy isn't supported by the MultiPart class and pretty complex to implements. - */ - - if (isSecure(uri)) { - ChannelBuffer b = ChannelBuffers.dynamicBuffer(lenght); - mre.writeRequest(new ChannelBufferOutputStream(b)); - nettyRequest.setContent(b); - } } else if (request.getEntityWriter() != null) { - int lenght = computeAndSetContentLength(request, nettyRequest); + int length = (int) request.getContentLength(); - if (lenght == -1) { - lenght = MAX_BUFFERED_BYTES; + if (length == -1) { + length = MAX_BUFFERED_BYTES; } - ChannelBuffer b = ChannelBuffers.dynamicBuffer(lenght); + ChannelBuffer b = ChannelBuffers.dynamicBuffer(length); request.getEntityWriter().writeEntity(new ChannelBufferOutputStream(b)); nettyRequest.setHeader(HttpHeaders.Names.CONTENT_LENGTH, b.writerIndex()); nettyRequest.setContent(b); @@ -834,11 +856,13 @@ public void close() { config.executorService().shutdown(); config.reaper().shutdown(); - socketChannelFactory.releaseExternalResources(); - plainBootstrap.releaseExternalResources(); - secureBootstrap.releaseExternalResources(); - webSocketBootstrap.releaseExternalResources(); - secureWebSocketBootstrap.releaseExternalResources(); + if (this.allowReleaseSocketChannelFactory) { + socketChannelFactory.releaseExternalResources(); + plainBootstrap.releaseExternalResources(); + secureBootstrap.releaseExternalResources(); + webSocketBootstrap.releaseExternalResources(); + secureWebSocketBootstrap.releaseExternalResources(); + } } catch (Throwable t) { log.warn("Unexpected error on close", t); } @@ -846,9 +870,7 @@ public void close() { /* @Override */ - public Response prepareResponse(final HttpResponseStatus status, - final HttpResponseHeaders headers, - final Collection bodyParts) { + public Response prepareResponse(final HttpResponseStatus status, final HttpResponseHeaders headers, final List bodyParts) { return new NettyResponse(status, headers, bodyParts); } @@ -858,16 +880,11 @@ public ListenableFuture execute(Request request, final AsyncHandler as return doConnect(request, asyncHandler, null, true, executeConnectAsync, false); } - private void execute(final Request request, final NettyResponseFuture f, boolean useCache, boolean asyncConnect) throws IOException { - doConnect(request, f.getAsyncHandler(), f, useCache, asyncConnect, false); - } - private void execute(final Request request, final NettyResponseFuture f, boolean useCache, boolean asyncConnect, boolean reclaimCache) throws IOException { doConnect(request, f.getAsyncHandler(), f, useCache, asyncConnect, reclaimCache); } - private ListenableFuture doConnect(final Request request, final AsyncHandler asyncHandler, NettyResponseFuture f, - boolean useCache, boolean asyncConnect, boolean reclaimCache) throws IOException { + private ListenableFuture doConnect(final Request request, final AsyncHandler asyncHandler, NettyResponseFuture f, boolean useCache, boolean asyncConnect, boolean reclaimCache) throws IOException { if (isClose.get()) { throw new IOException("Closed"); @@ -877,38 +894,40 @@ private ListenableFuture doConnect(final Request request, final AsyncHand throw new IOException("WebSocket method must be a GET"); } - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); - String requestUrl; + ProxyServer proxyServer = ProxyUtils.getProxyServer(config, request); + boolean useProxy = proxyServer != null; + + URI uri; if (useRawUrl) { - requestUrl = request.getRawUrl(); + uri = request.getRawURI(); } else { - requestUrl = request.getUrl(); + uri = request.getURI(); } - URI uri = AsyncHttpProviderUtils.createUri(requestUrl); Channel channel = null; if (useCache) { if (f != null && f.reuseChannel() && f.channel() != null) { channel = f.channel(); } else { - channel = lookupInCache(uri); + URI connectionKeyUri = useProxy ? proxyServer.getURI() : uri; + channel = lookupInCache(connectionKeyUri, request.getConnectionPoolKeyStrategy()); } } ChannelBuffer bufferedBytes = null; - if (f != null && f.getRequest().getFile() == null && - !f.getNettyRequest().getMethod().getName().equals(HttpMethod.CONNECT.getName())) { + if (f != null && f.getRequest().getFile() == null && !f.getNettyRequest().getMethod().getName().equals(HttpMethod.CONNECT.getName())) { bufferedBytes = f.getNettyRequest().getContent(); } - boolean useSSl = isSecure(uri) && proxyServer == null; + boolean useSSl = isSecure(uri) && !useProxy; if (channel != null && channel.isOpen() && channel.isConnected()) { - HttpRequest nettyRequest = buildRequest(config, request, uri, f == null ? false : f.isConnectAllowed(), bufferedBytes); + HttpRequest nettyRequest = null; if (f == null) { - f = newFuture(uri, request, asyncHandler, nettyRequest, config, this); + nettyRequest = buildRequest(config, request, uri, false, bufferedBytes, proxyServer); + f = newFuture(uri, request, asyncHandler, nettyRequest, config, this, proxyServer); } else { - nettyRequest = buildRequest(config, request, uri, f.isConnectAllowed(), bufferedBytes); + nettyRequest = buildRequest(config, request, uri, f.isConnectAllowed(), bufferedBytes, proxyServer); f.setNettyRequest(nettyRequest); } f.setState(NettyResponseFuture.STATE.POOLED); @@ -918,7 +937,7 @@ private ListenableFuture doConnect(final Request request, final AsyncHand channel.getPipeline().getContext(NettyAsyncHttpProvider.class).setAttachment(f); try { - writeRequest(channel, config, f, nettyRequest); + writeRequest(channel, config, f); } catch (Exception ex) { log.debug("writeRequest failure", ex); if (useSSl && ex.getMessage() != null && ex.getMessage().contains("SSLEngine")) { @@ -968,7 +987,6 @@ private ListenableFuture doConnect(final Request request, final AsyncHand } NettyConnectListener c = new NettyConnectListener.Builder(config, request, asyncHandler, f, this, bufferedBytes).build(uri); - boolean avoidProxy = ProxyUtils.avoidProxy(proxyServer, uri.getHost()); if (useSSl) { constructSSLPipeline(c); @@ -979,23 +997,23 @@ private ListenableFuture doConnect(final Request request, final AsyncHand bootstrap.setOption("connectTimeoutMillis", config.getConnectionTimeoutInMs()); // Do no enable this with win. - if (System.getProperty("os.name").toLowerCase().indexOf("win") == -1) { - bootstrap.setOption("reuseAddress", asyncHttpProviderConfig.getProperty(NettyAsyncHttpProviderConfig.REUSE_ADDRESS)); + if (!System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win")) { + bootstrap.setOption("reuseAddress", asyncHttpProviderConfig.getProperty(REUSE_ADDRESS)); } try { InetSocketAddress remoteAddress; if (request.getInetAddress() != null) { remoteAddress = new InetSocketAddress(request.getInetAddress(), AsyncHttpProviderUtils.getPort(uri)); - } else if (proxyServer == null || avoidProxy) { + } else if (!useProxy) { remoteAddress = new InetSocketAddress(AsyncHttpProviderUtils.getHost(uri), AsyncHttpProviderUtils.getPort(uri)); } else { remoteAddress = new InetSocketAddress(proxyServer.getHost(), proxyServer.getPort()); } - if(request.getLocalAddress() != null){ + if (request.getLocalAddress() != null) { channelFuture = bootstrap.connect(remoteAddress, new InetSocketAddress(request.getLocalAddress(), 0)); - }else{ + } else { channelFuture = bootstrap.connect(remoteAddress); } @@ -1007,10 +1025,7 @@ private ListenableFuture doConnect(final Request request, final AsyncHand return c.future(); } - boolean directInvokation = true; - if (IN_IO_THREAD.get() && DefaultChannelFuture.isUseDeadLockChecker()) { - directInvokation = false; - } + boolean directInvokation = !(IN_IO_THREAD.get() && DefaultChannelFuture.isUseDeadLockChecker()); if (directInvokation && !asyncConnect && request.getFile() == null) { int timeOut = config.getConnectionTimeoutInMs() > 0 ? config.getConnectionTimeoutInMs() : Integer.MAX_VALUE; @@ -1050,7 +1065,7 @@ private ListenableFuture doConnect(final Request request, final AsyncHand return c.future(); } - protected static int requestTimeout(AsyncHttpClientConfig config, PerRequestConfig perRequestConfig) { + protected static int requestTimeoutInMs(AsyncHttpClientConfig config, PerRequestConfig perRequestConfig) { int result; if (perRequestConfig != null) { int prRequestTimeout = perRequestConfig.getRequestTimeoutInMs(); @@ -1076,7 +1091,6 @@ private void finishChannel(final ChannelHandlerContext ctx) { log.debug("Closing Channel {} ", ctx.getChannel()); - try { ctx.getChannel().close(); } catch (Throwable t) { @@ -1091,7 +1105,7 @@ private void finishChannel(final ChannelHandlerContext ctx) { @Override public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e) throws Exception { - //call super to reset the read timeout + // call super to reset the read timeout super.messageReceived(ctx, e); IN_IO_THREAD.set(Boolean.TRUE); if (ctx.getAttachment() == null) { @@ -1128,18 +1142,13 @@ public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e) thr p.handle(ctx, e); } - private Realm kerberosChallenge(List proxyAuth, - Request request, - ProxyServer proxyServer, - FluentCaseInsensitiveStringsMap headers, - Realm realm, - NettyResponseFuture future) throws NTLMEngineException { + private Realm kerberosChallenge(List proxyAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture future) throws NTLMEngineException { - URI uri = URI.create(request.getUrl()); + URI uri = request.getURI(); String host = request.getVirtualHost() == null ? AsyncHttpProviderUtils.getHost(uri) : request.getVirtualHost(); String server = proxyServer == null ? host : proxyServer.getHost(); try { - String challengeHeader = spnegoEngine.generateToken(server); + String challengeHeader = getSpnegoEngine().generateToken(server); headers.remove(HttpHeaders.Names.AUTHORIZATION); headers.add(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challengeHeader); @@ -1149,12 +1158,9 @@ private Realm kerberosChallenge(List proxyAuth, } else { realmBuilder = new Realm.RealmBuilder(); } - return realmBuilder.setUri(uri.getPath()) - .setMethodName(request.getMethod()) - .setScheme(Realm.AuthScheme.KERBEROS) - .build(); + return realmBuilder.setUri(uri.getRawPath()).setMethodName(request.getMethod()).setScheme(Realm.AuthScheme.KERBEROS).build(); } catch (Throwable throwable) { - if (proxyAuth.contains("NTLM")) { + if (isNTLM(proxyAuth)) { return ntlmChallenge(proxyAuth, request, proxyServer, headers, realm, future); } abort(future, throwable); @@ -1162,12 +1168,24 @@ private Realm kerberosChallenge(List proxyAuth, } } - private Realm ntlmChallenge(List wwwAuth, - Request request, - ProxyServer proxyServer, - FluentCaseInsensitiveStringsMap headers, - Realm realm, - NettyResponseFuture future) throws NTLMEngineException { + private void addType3NTLMAuthorizationHeader( + List auth, + FluentCaseInsensitiveStringsMap headers, + String username, + String password, + String domain, + String workstation) throws NTLMEngineException { + headers.remove(HttpHeaders.Names.AUTHORIZATION); + + if (isNTLM(auth)) { + String serverChallenge = auth.get(0).trim().substring("NTLM ".length()); + String challengeHeader = ntlmEngine.generateType3Msg(username, password, domain, workstation, serverChallenge); + + headers.add(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader); + } + } + + private Realm ntlmChallenge(List wwwAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture future) throws NTLMEngineException { boolean useRealm = (proxyServer == null && realm != null); @@ -1180,23 +1198,12 @@ private Realm ntlmChallenge(List wwwAuth, if (realm != null && !realm.isNtlmMessageType2Received()) { String challengeHeader = ntlmEngine.generateType1Msg(ntlmDomain, ntlmHost); + URI uri = request.getURI(); headers.add(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader); - newRealm = new Realm.RealmBuilder().clone(realm).setScheme(realm.getAuthScheme()) - .setUri(URI.create(request.getUrl()).getPath()) - .setMethodName(request.getMethod()) - .setNtlmMessageType2Received(true) - .build(); + newRealm = new Realm.RealmBuilder().clone(realm).setScheme(realm.getAuthScheme()).setUri(uri.getRawPath()).setMethodName(request.getMethod()).setNtlmMessageType2Received(true).build(); future.getAndSetAuth(false); } else { - headers.remove(HttpHeaders.Names.AUTHORIZATION); - - if (wwwAuth.get(0).startsWith("NTLM ")) { - String serverChallenge = wwwAuth.get(0).trim().substring("NTLM ".length()); - String challengeHeader = ntlmEngine.generateType3Msg(principal, password, - ntlmDomain, ntlmHost, serverChallenge); - - headers.add(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader); - } + addType3NTLMAuthorizationHeader(wwwAuth, headers, principal, password, ntlmDomain, ntlmHost); Realm.RealmBuilder realmBuilder; Realm.AuthScheme authScheme; @@ -1207,52 +1214,39 @@ private Realm ntlmChallenge(List wwwAuth, realmBuilder = new Realm.RealmBuilder(); authScheme = Realm.AuthScheme.NTLM; } - newRealm = realmBuilder.setScheme(authScheme) - .setUri(URI.create(request.getUrl()).getPath()) - .setMethodName(request.getMethod()) - .build(); + newRealm = realmBuilder.setScheme(authScheme).setUri(request.getURI().getPath()).setMethodName(request.getMethod()).build(); } return newRealm; } - private Realm ntlmProxyChallenge(List wwwAuth, - Request request, - ProxyServer proxyServer, - FluentCaseInsensitiveStringsMap headers, - Realm realm, - NettyResponseFuture future) throws NTLMEngineException { + private Realm ntlmProxyChallenge(List wwwAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture future) throws NTLMEngineException { future.getAndSetAuth(false); - headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION); - - if (wwwAuth.get(0).startsWith("NTLM ")) { - String serverChallenge = wwwAuth.get(0).trim().substring("NTLM ".length()); - String challengeHeader = ntlmEngine.generateType3Msg(proxyServer.getPrincipal(), - proxyServer.getPassword(), - proxyServer.getNtlmDomain(), - proxyServer.getHost(), - serverChallenge); - headers.add(HttpHeaders.Names.PROXY_AUTHORIZATION, "NTLM " + challengeHeader); - } + + addType3NTLMAuthorizationHeader(wwwAuth, headers, proxyServer.getPrincipal(), proxyServer.getPassword(), proxyServer.getNtlmDomain(), proxyServer.getHost()); Realm newRealm; + Realm.RealmBuilder realmBuilder; if (realm != null) { realmBuilder = new Realm.RealmBuilder().clone(realm); } else { realmBuilder = new Realm.RealmBuilder(); } - newRealm = realmBuilder//.setScheme(realm.getAuthScheme()) - .setUri(URI.create(request.getUrl()).getPath()) - .setMethodName(request.getMethod()) - .build(); + newRealm = realmBuilder.setUri(request.getURI().getPath()).setMethodName(request.getMethod()).build(); return newRealm; } - private void drainChannel(final ChannelHandlerContext ctx, final NettyResponseFuture future, final boolean keepAlive, final URI uri) { + private String getPoolKey(NettyResponseFuture future) throws MalformedURLException { + URI uri = future.getProxyServer() != null ? future.getProxyServer().getURI() : future.getURI(); + return future.getConnectionPoolKeyStrategy().getKey(uri); + } + + private void drainChannel(final ChannelHandlerContext ctx, final NettyResponseFuture future) { ctx.setAttachment(new AsyncCallable(future) { public Object call() throws Exception { - if (keepAlive && ctx.getChannel().isReadable() && connectionsPool.offer(AsyncHttpProviderUtils.getBaseUrl(uri), ctx.getChannel())) { + + if (future.getKeepAlive() && ctx.getChannel().isReadable() && connectionsPool.offer(getPoolKey(future), ctx.getChannel())) { return null; } @@ -1288,7 +1282,7 @@ private void replayRequest(final NettyResponseFuture future, FilterContext fc future.touch(); log.debug("\n\nReplaying Request {}\n for Future {}\n", newRequest, future); - drainChannel(ctx, future, future.getKeepAlive(), future.getURI()); + drainChannel(ctx, future); nextRequest(newRequest, future); return; } @@ -1333,14 +1327,14 @@ private void upgradeProtocol(ChannelPipeline p, String scheme) throws IOExceptio if (isSecure(scheme)) { if (p.get(SSL_HANDLER) == null) { - p.addFirst(HTTP_HANDLER, new HttpClientCodec()); + p.addFirst(HTTP_HANDLER, createHttpClientCodec()); p.addFirst(SSL_HANDLER, new SslHandler(createSSLEngine())); } else { - p.addAfter(SSL_HANDLER, HTTP_HANDLER, new HttpClientCodec()); + p.addAfter(SSL_HANDLER, HTTP_HANDLER, createHttpClientCodec()); } } else { - p.addFirst(HTTP_HANDLER, new HttpClientCodec()); + p.addFirst(HTTP_HANDLER, createHttpClientCodec()); } } @@ -1370,9 +1364,8 @@ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws NettyResponseFuture future = (NettyResponseFuture) ctx.getAttachment(); future.touch(); - if (config.getIOExceptionFilters().size() > 0) { - FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(future.getAsyncHandler()) - .request(future.getRequest()).ioException(new IOException("Channel Closed")).build(); + if (!config.getIOExceptionFilters().isEmpty()) { + FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(future.getAsyncHandler()).request(future.getRequest()).ioException(new IOException("Channel Closed")).build(); fc = handleIoException(fc, future); if (fc.replayRequest() && !future.cannotBeReplay()) { @@ -1385,8 +1378,8 @@ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws p.onClose(ctx, e); if (future != null && !future.isDone() && !future.isCancelled()) { - if (!remotelyClosed(ctx.getChannel(), future)) { - abort(future, new IOException("Remotely Closed " + ctx.getChannel())); + if (remotelyClosed(ctx.getChannel(), future)) { + abort(future, new IOException("Remotely Closed")); } } else { closeChannel(ctx); @@ -1397,21 +1390,20 @@ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws protected boolean remotelyClosed(Channel channel, NettyResponseFuture future) { if (isClose.get()) { - return false; + return true; } connectionsPool.removeAll(channel); - if (future == null && channel.getPipeline().getContext(NettyAsyncHttpProvider.class).getAttachment() != null - && NettyResponseFuture.class.isAssignableFrom( - channel.getPipeline().getContext(NettyAsyncHttpProvider.class).getAttachment().getClass())) { - future = (NettyResponseFuture) - channel.getPipeline().getContext(NettyAsyncHttpProvider.class).getAttachment(); + if (future == null) { + Object attachment = channel.getPipeline().getContext(NettyAsyncHttpProvider.class).getAttachment(); + if (attachment instanceof NettyResponseFuture) + future = (NettyResponseFuture) attachment; } if (future == null || future.cannotBeReplay()) { log.debug("Unable to recover future {}\n", future); - return false; + return true; } future.setState(NettyResponseFuture.STATE.RECONNECTED); @@ -1420,19 +1412,19 @@ protected boolean remotelyClosed(Channel channel, NettyResponseFuture future) try { nextRequest(future.getRequest(), future); - return true; + return false; } catch (IOException iox) { future.setState(NettyResponseFuture.STATE.CLOSED); future.abort(iox); log.error("Remotely Closed, unable to recover", iox); + return true; } - return false; } private void markAsDone(final NettyResponseFuture future, final ChannelHandlerContext ctx) throws MalformedURLException { // We need to make sure everything is OK before adding the connection back to the pool. try { - future.done(null); + future.done(); } catch (Throwable t) { // Never propagate exception once we know we are done. log.debug(t.getMessage(), t); @@ -1445,10 +1437,9 @@ private void markAsDone(final NettyResponseFuture future, final ChannelHandle private void finishUpdate(final NettyResponseFuture future, final ChannelHandlerContext ctx, boolean lastValidChunk) throws IOException { if (lastValidChunk && future.getKeepAlive()) { - drainChannel(ctx, future, future.getKeepAlive(), future.getURI()); + drainChannel(ctx, future); } else { - if (future.getKeepAlive() && ctx.getChannel().isReadable() && - connectionsPool.offer(AsyncHttpProviderUtils.getBaseUrl(future.getURI()), ctx.getChannel())) { + if (future.getKeepAlive() && ctx.getChannel().isReadable() && connectionsPool.offer(getPoolKey(future), ctx.getChannel())) { markAsDone(future, ctx); return; } @@ -1476,24 +1467,18 @@ private final boolean updateBodyAndInterrupt(final NettyResponseFuture future return state; } - //Simple marker for stopping publishing bytes. + // Simple marker for stopping publishing bytes. final static class DiscardEvent { } @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) - throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { Channel channel = e.getChannel(); Throwable cause = e.getCause(); NettyResponseFuture future = null; - /** Issue 81 - if (e.getCause() != null && e.getCause().getClass().isAssignableFrom(PrematureChannelClosureException.class)) { - return; - } - */ - if (e.getCause() != null && e.getCause().getClass().getSimpleName().equals("PrematureChannelClosureException")) { + if (e.getCause() instanceof PrematureChannelClosureException) { return; } @@ -1503,7 +1488,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) try { - if (cause != null && ClosedChannelException.class.isAssignableFrom(cause.getClass())) { + if (cause instanceof ClosedChannelException) { return; } @@ -1512,11 +1497,10 @@ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) future.attachChannel(null, false); future.touch(); - if (IOException.class.isAssignableFrom(cause.getClass())) { + if (cause instanceof IOException) { - if (config.getIOExceptionFilters().size() > 0) { - FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(future.getAsyncHandler()) - .request(future.getRequest()).ioException(new IOException("Channel Closed")).build(); + if (!config.getIOExceptionFilters().isEmpty()) { + FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(future.getAsyncHandler()).request(future.getRequest()).ioException(new IOException("Channel Closed")).build(); fc = handleIoException(fc, future); if (fc.replayRequest()) { @@ -1564,8 +1548,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) protected static boolean abortOnConnectCloseException(Throwable cause) { try { for (StackTraceElement element : cause.getStackTrace()) { - if (element.getClassName().equals("sun.nio.ch.SocketChannelImpl") - && element.getMethodName().equals("checkConnect")) { + if (element.getClassName().equals("sun.nio.ch.SocketChannelImpl") && element.getMethodName().equals("checkConnect")) { return true; } } @@ -1582,8 +1565,7 @@ protected static boolean abortOnConnectCloseException(Throwable cause) { protected static boolean abortOnDisconnectException(Throwable cause) { try { for (StackTraceElement element : cause.getStackTrace()) { - if (element.getClassName().equals("org.jboss.netty.handler.ssl.SslHandler") - && element.getMethodName().equals("channelDisconnected")) { + if (element.getClassName().equals("org.jboss.netty.handler.ssl.SslHandler") && element.getMethodName().equals("channelDisconnected")) { return true; } } @@ -1600,8 +1582,7 @@ protected static boolean abortOnDisconnectException(Throwable cause) { protected static boolean abortOnReadCloseException(Throwable cause) { for (StackTraceElement element : cause.getStackTrace()) { - if (element.getClassName().equals("sun.nio.ch.SocketDispatcher") - && element.getMethodName().equals("read")) { + if (element.getClassName().equals("sun.nio.ch.SocketDispatcher") && element.getMethodName().equals("read")) { return true; } } @@ -1616,8 +1597,7 @@ protected static boolean abortOnReadCloseException(Throwable cause) { protected static boolean abortOnWriteCloseException(Throwable cause) { for (StackTraceElement element : cause.getStackTrace()) { - if (element.getClassName().equals("sun.nio.ch.SocketDispatcher") - && element.getMethodName().equals("write")) { + if (element.getClassName().equals("sun.nio.ch.SocketDispatcher") && element.getMethodName().equals("write")) { return true; } } @@ -1629,30 +1609,20 @@ protected static boolean abortOnWriteCloseException(Throwable cause) { return false; } - private final static int computeAndSetContentLength(Request request, HttpRequest r) { - int length = (int) request.getContentLength(); - if (length == -1 && r.getHeader(HttpHeaders.Names.CONTENT_LENGTH) != null) { - length = Integer.valueOf(r.getHeader(HttpHeaders.Names.CONTENT_LENGTH)); - } - - if (length >= 0) { - r.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(length)); - } - return length; - } - - public static NettyResponseFuture newFuture(URI uri, - Request request, - AsyncHandler asyncHandler, - HttpRequest nettyRequest, - AsyncHttpClientConfig config, - NettyAsyncHttpProvider provider) { + public static NettyResponseFuture newFuture(URI uri, Request request, AsyncHandler asyncHandler, HttpRequest nettyRequest, AsyncHttpClientConfig config, NettyAsyncHttpProvider provider, ProxyServer proxyServer) { - NettyResponseFuture f = new NettyResponseFuture(uri, request, asyncHandler, nettyRequest, - requestTimeout(config, request.getPerRequestConfig()), config.getIdleConnectionTimeoutInMs(), provider); + NettyResponseFuture f = new NettyResponseFuture(uri,// + request,// + asyncHandler,// + nettyRequest,// + requestTimeoutInMs(config, request.getPerRequestConfig()),// + config.getIdleConnectionTimeoutInMs(),// + provider,// + request.getConnectionPoolKeyStrategy(),// + proxyServer); - if (request.getHeaders().getFirstValue("Expect") != null - && request.getHeaders().getFirstValue("Expect").equalsIgnoreCase("100-Continue")) { + String expectHeader = request.getHeaders().getFirstValue(HttpHeaders.Names.EXPECT); + if (expectHeader != null && expectHeader.equalsIgnoreCase(HttpHeaders.Values.CONTINUE)) { f.getAndSetWriteBody(false); } return f; @@ -1676,7 +1646,7 @@ public void operationComplete(ChannelFuture cf) { Throwable cause = cf.getCause(); if (cause != null && future.getState() != NettyResponseFuture.STATE.NEW) { - if (IllegalStateException.class.isAssignableFrom(cause.getClass())) { + if (cause instanceof IllegalStateException) { log.debug(cause.getMessage(), cause); try { cf.getChannel().close(); @@ -1686,9 +1656,7 @@ public void operationComplete(ChannelFuture cf) { return; } - if (ClosedChannelException.class.isAssignableFrom(cause.getClass()) - || abortOnReadCloseException(cause) - || abortOnWriteCloseException(cause)) { + if (cause instanceof ClosedChannelException || abortOnReadCloseException(cause) || abortOnWriteCloseException(cause)) { if (log.isDebugEnabled()) { log.debug(cf.getCause() == null ? "" : cf.getCause().getMessage(), cf.getCause()); @@ -1708,15 +1676,12 @@ public void operationComplete(ChannelFuture cf) { future.touch(); /** - * We need to make sure we aren't in the middle of an authorization process before publishing events - * as we will re-publish again the same event after the authorization, causing unpredictable behavior. + * We need to make sure we aren't in the middle of an authorization process before publishing events as we will re-publish again the same event after the authorization, causing unpredictable behavior. */ Realm realm = future.getRequest().getRealm() != null ? future.getRequest().getRealm() : NettyAsyncHttpProvider.this.getConfig().getRealm(); - boolean startPublishing = future.isInAuth() - || realm == null - || realm.getUsePreemptiveAuth() == true; + boolean startPublishing = future.isInAuth() || realm == null || realm.getUsePreemptiveAuth() == true; - if (startPublishing && ProgressAsyncHandler.class.isAssignableFrom(asyncHandler.getClass())) { + if (startPublishing && asyncHandler instanceof ProgressAsyncHandler) { if (notifyHeaders) { ProgressAsyncHandler.class.cast(asyncHandler).onHeaderWriteCompleted(); } else { @@ -1727,17 +1692,15 @@ public void operationComplete(ChannelFuture cf) { public void operationProgressed(ChannelFuture cf, long amount, long current, long total) { future.touch(); - if (ProgressAsyncHandler.class.isAssignableFrom(asyncHandler.getClass())) { + if (asyncHandler instanceof ProgressAsyncHandler) { ProgressAsyncHandler.class.cast(asyncHandler).onContentWriteProgress(amount, current, total); } } } /** - * Because some implementation of the ThreadSchedulingService do not clean up cancel task until they try to run - * them, we wrap the task with the future so the when the NettyResponseFuture cancel the reaper future - * this wrapper will release the references to the channel and the nettyResponseFuture immediately. Otherwise, - * the memory referenced this way will only be released after the request timeout period which can be arbitrary long. + * Because some implementation of the ThreadSchedulingService do not clean up cancel task until they try to run them, we wrap the task with the future so the when the NettyResponseFuture cancel the reaper future this wrapper will release the references to the channel and the + * nettyResponseFuture immediately. Otherwise, the memory referenced this way will only be released after the request timeout period which can be arbitrary long. */ private final class ReaperFuture implements Future, Runnable { private Future scheduledFuture; @@ -1787,6 +1750,12 @@ public boolean isDone() { return scheduledFuture.isDone(); } + private void expire(String message) { + log.debug("{} for {}", message, nettyResponseFuture); + abort(nettyResponseFuture, new TimeoutException(message)); + nettyResponseFuture = null; + } + /** * @Override */ @@ -1796,22 +1765,22 @@ public synchronized void run() { return; } - if (nettyResponseFuture != null && nettyResponseFuture.hasExpired() - && !nettyResponseFuture.isDone() && !nettyResponseFuture.isCancelled()) { - log.debug("Request Timeout expired for {}\n", nettyResponseFuture); + boolean futureDone = nettyResponseFuture.isDone(); + boolean futureCanceled = nettyResponseFuture.isCancelled(); - int requestTimeout = config.getRequestTimeoutInMs(); - PerRequestConfig p = nettyResponseFuture.getRequest().getPerRequestConfig(); - if (p != null && p.getRequestTimeoutInMs() != -1) { - requestTimeout = p.getRequestTimeoutInMs(); - } + if (nettyResponseFuture != null && !futureDone && !futureCanceled) { - abort(nettyResponseFuture, new TimeoutException(String.format("No response received after %s", requestTimeout))); + long now = millisTime(); + if (nettyResponseFuture.hasRequestTimedOut(now)) { + long age = now - nettyResponseFuture.getStart(); + expire("Request reached time out of " + nettyResponseFuture.getRequestTimeoutInMs() + " ms after " + age + " ms"); - nettyResponseFuture = null; - } + } else if (nettyResponseFuture.hasConnectionIdleTimedOut(now)) { + long age = now - nettyResponseFuture.getStart(); + expire("Request reached idle time out of " + nettyResponseFuture.getIdleConnectionTimeoutInMs() + " ms after " + age + " ms"); + } - if (nettyResponseFuture == null || nettyResponseFuture.isDone() || nettyResponseFuture.isCancelled()) { + } else if (nettyResponseFuture == null || futureDone || futureCanceled) { cancel(true); } } @@ -1876,9 +1845,7 @@ public long getCount() { public long transferTo(WritableByteChannel target, long position) throws IOException { long count = this.count - position; if (count < 0 || position < 0) { - throw new IllegalArgumentException( - "position out of range: " + position + - " (expected: 0 - " + (this.count - 1) + ")"); + throw new IllegalArgumentException("position out of range: " + position + " (expected: 0 - " + (this.count - 1) + ")"); } if (count == 0) { return 0L; @@ -1965,23 +1932,17 @@ public void destroy() { } private static final boolean validateWebSocketRequest(Request request, AsyncHandler asyncHandler) { - if (request.getMethod() != "GET" || !WebSocketUpgradeHandler.class.isAssignableFrom(asyncHandler.getClass())) { + if (request.getMethod() != "GET" || !(asyncHandler instanceof WebSocketUpgradeHandler)) { return false; } return true; } - private boolean redirect(Request request, - NettyResponseFuture future, - HttpResponse response, - final ChannelHandlerContext ctx) throws Exception { + private boolean redirect(Request request, NettyResponseFuture future, HttpResponse response, final ChannelHandlerContext ctx) throws Exception { int statusCode = response.getStatus().getCode(); boolean redirectEnabled = request.isRedirectOverrideSet() ? request.isRedirectEnabled() : config.isRedirectEnabled(); - if (redirectEnabled && (statusCode == 302 - || statusCode == 301 - || statusCode == 303 - || statusCode == 307)) { + if (redirectEnabled && (statusCode == 302 || statusCode == 301 || statusCode == 303 || statusCode == 307)) { if (future.incrementAndGetCurrentRedirectCount() < config.getMaxRedirects()) { // We must allow 401 handling again. @@ -1990,18 +1951,14 @@ private boolean redirect(Request request, String location = response.getHeader(HttpHeaders.Names.LOCATION); URI uri = AsyncHttpProviderUtils.getRedirectUri(future.getURI(), location); boolean stripQueryString = config.isRemoveQueryParamOnRedirect(); - if (!uri.toString().equalsIgnoreCase(future.getURI().toString())) { - final RequestBuilder nBuilder = stripQueryString ? - new RequestBuilder(future.getRequest()).setQueryParameters(null) - : new RequestBuilder(future.getRequest()); - - if (!(statusCode < 302 || statusCode > 303) - && !(statusCode == 302 - && config.isStrict302Handling())) { + if (!uri.toString().equals(future.getURI().toString())) { + final RequestBuilder nBuilder = stripQueryString ? new RequestBuilder(future.getRequest()).setQueryParameters(null) : new RequestBuilder(future.getRequest()); + + if (!(statusCode < 302 || statusCode > 303) && !(statusCode == 302 && config.isStrict302Handling())) { nBuilder.setMethod("GET"); } - final URI initialConnectionUri = future.getURI(); final boolean initialConnectionKeepAlive = future.getKeepAlive(); + final String initialPoolKey = getPoolKey(future); future.setURI(uri); String newUrl = uri.toString(); if (request.getUrl().startsWith(WEBSOCKET)) { @@ -2010,19 +1967,20 @@ private boolean redirect(Request request, log.debug("Redirecting to {}", newUrl); for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE)) { - Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr); - nBuilder.addOrReplaceCookie(c); + for (Cookie c : CookieDecoder.decode(cookieStr)) { + nBuilder.addOrReplaceCookie(c); + } } for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE2)) { - Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr); - nBuilder.addOrReplaceCookie(c); + for (Cookie c : CookieDecoder.decode(cookieStr)) { + nBuilder.addOrReplaceCookie(c); + } } AsyncCallable ac = new AsyncCallable(future) { public Object call() throws Exception { - if (initialConnectionKeepAlive && ctx.getChannel().isReadable() && - connectionsPool.offer(AsyncHttpProviderUtils.getBaseUrl(initialConnectionUri), ctx.getChannel())) { + if (initialConnectionKeepAlive && ctx.getChannel().isReadable() && connectionsPool.offer(initialPoolKey, ctx.getChannel())) { return null; } finishChannel(ctx); @@ -2061,6 +2019,7 @@ public void handle(final ChannelHandlerContext ctx, final MessageEvent e) throws HttpRequest nettyRequest = future.getNettyRequest(); AsyncHandler handler = future.getAsyncHandler(); Request request = future.getRequest(); + ProxyServer proxyServer = future.getProxyServer(); HttpResponse response = null; try { if (e.getMessage() instanceof HttpResponse) { @@ -2074,19 +2033,14 @@ public void handle(final ChannelHandlerContext ctx, final MessageEvent e) throws int statusCode = response.getStatus().getCode(); String ka = response.getHeader(HttpHeaders.Names.CONNECTION); - future.setKeepAlive(ka == null || ka.toLowerCase().equals("keep-alive")); + future.setKeepAlive(ka == null || ka.equalsIgnoreCase(HttpHeaders.Values.KEEP_ALIVE)); List wwwAuth = getAuthorizationToken(response.getHeaders(), HttpHeaders.Names.WWW_AUTHENTICATE); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); HttpResponseStatus status = new ResponseStatus(future.getURI(), response, NettyAsyncHttpProvider.this); HttpResponseHeaders responseHeaders = new ResponseHeaders(future.getURI(), response, NettyAsyncHttpProvider.this); - FilterContext fc = new FilterContext.FilterContextBuilder() - .asyncHandler(handler) - .request(request) - .responseStatus(status) - .responseHeaders(responseHeaders) - .build(); + FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(handler).request(request).responseStatus(status).responseHeaders(responseHeaders).build(); for (ResponseFilter asyncFilter : config.getResponseFilters()) { try { @@ -2110,49 +2064,40 @@ public void handle(final ChannelHandlerContext ctx, final MessageEvent e) throws } Realm newRealm = null; - ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer(); final FluentCaseInsensitiveStringsMap headers = request.getHeaders(); final RequestBuilder builder = new RequestBuilder(future.getRequest()); - //if (realm != null && !future.getURI().getPath().equalsIgnoreCase(realm.getUri())) { - // builder.setUrl(future.getURI().toString()); - //} + // if (realm != null && !future.getURI().getPath().equalsIgnoreCase(realm.getUri())) { + // builder.setUrl(future.getURI().toString()); + // } - if (statusCode == 401 - && wwwAuth.size() > 0 - && !future.getAndSetAuth(true)) { + if (statusCode == 401 && realm != null && !wwwAuth.isEmpty() && !future.getAndSetAuth(true)) { future.setState(NettyResponseFuture.STATE.NEW); // NTLM - if (!wwwAuth.contains("Kerberos") && (wwwAuth.contains("NTLM") || (wwwAuth.contains("Negotiate")))) { + if (!wwwAuth.contains("Kerberos") && (isNTLM(wwwAuth) || (wwwAuth.contains("Negotiate")))) { newRealm = ntlmChallenge(wwwAuth, request, proxyServer, headers, realm, future); // SPNEGO KERBEROS } else if (wwwAuth.contains("Negotiate")) { newRealm = kerberosChallenge(wwwAuth, request, proxyServer, headers, realm, future); - if (newRealm == null) return; + if (newRealm == null) + return; } else { Realm.RealmBuilder realmBuilder; if (realm != null) { - realmBuilder = new Realm.RealmBuilder().clone(realm).setScheme(realm.getAuthScheme()) - ; + realmBuilder = new Realm.RealmBuilder().clone(realm).setScheme(realm.getAuthScheme()); } else { realmBuilder = new Realm.RealmBuilder(); } - newRealm = realmBuilder - .setUri(URI.create(request.getUrl()).getPath()) - .setMethodName(request.getMethod()) - .setUsePreemptiveAuth(true) - .parseWWWAuthenticateHeader(wwwAuth.get(0)) - .build(); + newRealm = realmBuilder.setUri(request.getURI().getPath()).setMethodName(request.getMethod()).setUsePreemptiveAuth(true).parseWWWAuthenticateHeader(wwwAuth.get(0)).build(); } - final Realm nr = new Realm.RealmBuilder().clone(newRealm) - .setUri(request.getUrl()).build(); + final Realm nr = new Realm.RealmBuilder().clone(newRealm).setUri(request.getUrl()).build(); log.debug("Sending authentication to {}", request.getUrl()); AsyncCallable ac = new AsyncCallable(future) { public Object call() throws Exception { - drainChannel(ctx, future, future.getKeepAlive(), future.getURI()); + drainChannel(ctx, future); nextRequest(builder.setHeaders(headers).setRealm(nr).build(), future); return null; } @@ -2170,25 +2115,24 @@ public Object call() throws Exception { if (statusCode == 100) { future.getAndSetWriteHeaders(false); future.getAndSetWriteBody(true); - writeRequest(ctx.getChannel(), config, future, nettyRequest); + writeRequest(ctx.getChannel(), config, future); return; } List proxyAuth = getAuthorizationToken(response.getHeaders(), HttpHeaders.Names.PROXY_AUTHENTICATE); - if (statusCode == 407 - && proxyAuth.size() > 0 - && !future.getAndSetAuth(true)) { + if (statusCode == 407 && realm != null && !proxyAuth.isEmpty() && !future.getAndSetAuth(true)) { log.debug("Sending proxy authentication to {}", request.getUrl()); future.setState(NettyResponseFuture.STATE.NEW); - if (!proxyAuth.contains("Kerberos") && (proxyAuth.get(0).contains("NTLM") || (proxyAuth.contains("Negotiate")))) { + if (!proxyAuth.contains("Kerberos") && (isNTLM(proxyAuth) || (proxyAuth.contains("Negotiate")))) { newRealm = ntlmProxyChallenge(proxyAuth, request, proxyServer, headers, realm, future); // SPNEGO KERBEROS } else if (proxyAuth.contains("Negotiate")) { newRealm = kerberosChallenge(proxyAuth, request, proxyServer, headers, realm, future); - if (newRealm == null) return; + if (newRealm == null) + return; } else { newRealm = future.getRequest().getRealm(); } @@ -2200,8 +2144,7 @@ public Object call() throws Exception { return; } - if (future.getNettyRequest().getMethod().equals(HttpMethod.CONNECT) - && statusCode == 200) { + if (future.getNettyRequest().getMethod().equals(HttpMethod.CONNECT) && statusCode == 200) { log.debug("Connected to {}:{}", proxyServer.getHost(), proxyServer.getPort()); @@ -2211,7 +2154,7 @@ public Object call() throws Exception { try { log.debug("Connecting to proxy {} for scheme {}", proxyServer, request.getUrl()); - upgradeProtocol(ctx.getChannel().getPipeline(), URI.create(request.getUrl()).getScheme()); + upgradeProtocol(ctx.getChannel().getPipeline(), request.getURI().getScheme()); } catch (Throwable ex) { abort(future, ex); } @@ -2222,12 +2165,13 @@ public Object call() throws Exception { return; } - if (redirect(request, future, response, ctx)) return; + if (redirect(request, future, response, ctx)) + return; if (!future.getAndSetStatusReceived(true) && updateStatusAndInterrupt(handler, status)) { finishUpdate(future, ctx, response.isChunked()); return; - } else if (updateHeadersAndInterrupt(handler, responseHeaders)) { + } else if (!response.getHeaders().isEmpty() && updateHeadersAndInterrupt(handler, responseHeaders)) { finishUpdate(future, ctx, response.isChunked()); return; } else if (!response.isChunked()) { @@ -2241,27 +2185,24 @@ public Object call() throws Exception { if (nettyRequest.getMethod().equals(HttpMethod.HEAD)) { updateBodyAndInterrupt(future, handler, new ResponseBodyPart(future.getURI(), response, NettyAsyncHttpProvider.this, true)); markAsDone(future, ctx); - drainChannel(ctx, future, future.getKeepAlive(), future.getURI()); + drainChannel(ctx, future); } } else if (e.getMessage() instanceof HttpChunk) { HttpChunk chunk = (HttpChunk) e.getMessage(); if (handler != null) { - if (chunk.isLast() || updateBodyAndInterrupt(future, handler, - new ResponseBodyPart(future.getURI(), null, NettyAsyncHttpProvider.this, chunk, chunk.isLast()))) { + if (chunk.isLast() || updateBodyAndInterrupt(future, handler, new ResponseBodyPart(future.getURI(), null, NettyAsyncHttpProvider.this, chunk, chunk.isLast()))) { if (chunk instanceof DefaultHttpChunkTrailer) { - updateHeadersAndInterrupt(handler, new ResponseHeaders(future.getURI(), - future.getHttpResponse(), NettyAsyncHttpProvider.this, (HttpChunkTrailer) chunk)); + updateHeadersAndInterrupt(handler, new ResponseHeaders(future.getURI(), future.getHttpResponse(), NettyAsyncHttpProvider.this, (HttpChunkTrailer) chunk)); } finishUpdate(future, ctx, !chunk.isLast()); } } } } catch (Exception t) { - if (IOException.class.isAssignableFrom(t.getClass()) && config.getIOExceptionFilters().size() > 0) { - FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(future.getAsyncHandler()) - .request(future.getRequest()).ioException(IOException.class.cast(t)).build(); + if (t instanceof IOException && !config.getIOExceptionFilters().isEmpty()) { + FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(future.getAsyncHandler()).request(future.getRequest()).ioException(IOException.class.cast(t)).build(); fc = handleIoException(fc, future); if (fc.replayRequest()) { @@ -2289,6 +2230,22 @@ public void onClose(ChannelHandlerContext ctx, ChannelStateEvent e) { } private final class WebSocketProtocol implements Protocol { + private static final byte OPCODE_CONT = 0x0; + private static final byte OPCODE_TEXT = 0x1; + private static final byte OPCODE_BINARY = 0x2; + private static final byte OPCODE_UNKNOWN = -1; + protected byte pendingOpcode = OPCODE_UNKNOWN; + + // We don't need to synchronize as replacing the "ws-decoder" will process using the same thread. + private void invokeOnSucces(ChannelHandlerContext ctx, WebSocketUpgradeHandler h) { + if (!h.touchSuccess()) { + try { + h.onSuccess(new NettyWebSocket(ctx.getChannel())); + } catch (Exception ex) { + NettyAsyncHttpProvider.this.log.warn("onSuccess unexexpected exception", ex); + } + } + } // @Override public void handle(ChannelHandlerContext ctx, MessageEvent e) throws Exception { @@ -2301,12 +2258,7 @@ public void handle(ChannelHandlerContext ctx, MessageEvent e) throws Exception { HttpResponseStatus s = new ResponseStatus(future.getURI(), response, NettyAsyncHttpProvider.this); HttpResponseHeaders responseHeaders = new ResponseHeaders(future.getURI(), response, NettyAsyncHttpProvider.this); - FilterContext fc = new FilterContext.FilterContextBuilder() - .asyncHandler(h) - .request(request) - .responseStatus(s) - .responseHeaders(responseHeaders) - .build(); + FilterContext fc = new FilterContext.FilterContextBuilder().asyncHandler(h).request(request).responseStatus(s).responseHeaders(responseHeaders).build(); for (ResponseFilter asyncFilter : config.getResponseFilters()) { try { fc = asyncFilter.filter(fc); @@ -2329,10 +2281,10 @@ public void handle(ChannelHandlerContext ctx, MessageEvent e) throws Exception { } future.setHttpResponse(response); - if (redirect(request, future, response, ctx)) return; + if (redirect(request, future, response, ctx)) + return; - final org.jboss.netty.handler.codec.http.HttpResponseStatus status = - new org.jboss.netty.handler.codec.http.HttpResponseStatus(101, "Web Socket Protocol Handshake"); + final org.jboss.netty.handler.codec.http.HttpResponseStatus status = new org.jboss.netty.handler.codec.http.HttpResponseStatus(101, "Web Socket Protocol Handshake"); final boolean validStatus = response.getStatus().equals(status); final boolean validUpgrade = response.getHeader(HttpHeaders.Names.UPGRADE) != null; @@ -2346,31 +2298,35 @@ public void handle(ChannelHandlerContext ctx, MessageEvent e) throws Exception { s = new ResponseStatus(future.getURI(), response, NettyAsyncHttpProvider.this); final boolean statusReceived = h.onStatusReceived(s) == STATE.UPGRADE; - if (!statusReceived) { - h.onClose(new NettyWebSocket(ctx.getChannel()), 1002, "Bad response status " + response.getStatus().getCode()); - future.done(null); + final boolean headerOK = h.onHeadersReceived(responseHeaders) == STATE.CONTINUE; + if (!headerOK || !validStatus || !validUpgrade || !validConnection || !statusReceived) { + abort(future, new IOException("Invalid handshake response")); return; } - if (!validStatus || !validUpgrade || !validConnection) { - throw new IOException("Invalid handshake response"); - } - - String accept = response.getHeader("Sec-WebSocket-Accept"); - String key = WebSocketUtil.getAcceptKey(future.getNettyRequest().getHeader(WEBSOCKET_KEY)); + String accept = response.getHeader(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT); + String key = WebSocketUtil.getAcceptKey(future.getNettyRequest().getHeader(HttpHeaders.Names.SEC_WEBSOCKET_KEY)); if (accept == null || !accept.equals(key)) { throw new IOException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, key)); } - ctx.getPipeline().replace("ws-decoder", "ws-decoder", new WebSocket08FrameDecoder(false, false)); - ctx.getPipeline().replace("ws-encoder", "ws-encoder", new WebSocket08FrameEncoder(true)); - if (h.onHeadersReceived(responseHeaders) == STATE.CONTINUE) { - h.onSuccess(new NettyWebSocket(ctx.getChannel())); - } - future.done(null); + ctx.getPipeline().replace("http-encoder", "ws-encoder", new WebSocket08FrameEncoder(true)); + ctx.getPipeline().get(HttpResponseDecoder.class).replace("ws-decoder", new WebSocket08FrameDecoder(false, false)); + + invokeOnSucces(ctx, h); + future.done(); } else if (e.getMessage() instanceof WebSocketFrame) { + + invokeOnSucces(ctx, h); + final WebSocketFrame frame = (WebSocketFrame) e.getMessage(); + if (frame instanceof TextWebSocketFrame) { + pendingOpcode = OPCODE_TEXT; + } else if (frame instanceof BinaryWebSocketFrame) { + pendingOpcode = OPCODE_BINARY; + } + HttpChunk webSocketChunk = new HttpChunk() { private ChannelBuffer content; @@ -2396,16 +2352,27 @@ public void setContent(ChannelBuffer content) { h.onBodyPartReceived(rp); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); - webSocket.onMessage(rp.getBodyPartBytes()); - webSocket.onTextMessage(frame.getBinaryData().toString("UTF-8")); - if (CloseWebSocketFrame.class.isAssignableFrom(frame.getClass())) { - try { - webSocket.onClose(CloseWebSocketFrame.class.cast(frame).getStatusCode(), CloseWebSocketFrame.class.cast(frame).getReasonText()); - } catch (Throwable t) { - // Swallow any exception that may comes from a Netty version released before 3.4.0 - log.trace("", t); + if (webSocket != null) { + if (pendingOpcode == OPCODE_BINARY) { + webSocket.onBinaryFragment(rp.getBodyPartBytes(), frame.isFinalFragment()); + } else { + webSocket.onTextFragment(frame.getBinaryData().toString(UTF8), frame.isFinalFragment()); } + + if (frame instanceof CloseWebSocketFrame) { + try { + ctx.setAttachment(DiscardEvent.class); + webSocket.onClose(CloseWebSocketFrame.class.cast(frame).getStatusCode(), CloseWebSocketFrame.class.cast(frame).getReasonText()); + } catch (Throwable t) { + // Swallow any exception that may comes from a Netty version released before 3.4.0 + log.trace("", t); + } finally { + h.resetSuccess(); + } + } + } else { + log.debug("UpgradeHandler returned a null NettyWebSocket "); } } } else { @@ -2413,11 +2380,11 @@ public void setContent(ChannelBuffer content) { } } - //@Override + // @Override public void onError(ChannelHandlerContext ctx, ExceptionEvent e) { try { log.warn("onError {}", e); - if (ctx.getAttachment() == null || !NettyResponseFuture.class.isAssignableFrom(ctx.getAttachment().getClass())) { + if (!(ctx.getAttachment() instanceof NettyResponseFuture)) { return; } @@ -2425,17 +2392,19 @@ public void onError(ChannelHandlerContext ctx, ExceptionEvent e) { WebSocketUpgradeHandler h = WebSocketUpgradeHandler.class.cast(nettyResponse.getAsyncHandler()); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); - webSocket.onError(e.getCause()); - webSocket.close(); + if (webSocket != null) { + webSocket.onError(e.getCause()); + webSocket.close(); + } } catch (Throwable t) { log.error("onError", t); } } - //@Override + // @Override public void onClose(ChannelHandlerContext ctx, ChannelStateEvent e) { log.trace("onClose {}", e); - if (ctx.getAttachment() == null || !NettyResponseFuture.class.isAssignableFrom(ctx.getAttachment().getClass())) { + if (!(ctx.getAttachment() instanceof NettyResponseFuture)) { return; } @@ -2443,8 +2412,10 @@ public void onClose(ChannelHandlerContext ctx, ChannelStateEvent e) { NettyResponseFuture nettyResponse = NettyResponseFuture.class.cast(ctx.getAttachment()); WebSocketUpgradeHandler h = WebSocketUpgradeHandler.class.cast(nettyResponse.getAsyncHandler()); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); + h.resetSuccess(); - webSocket.close(); + if (!(ctx.getAttachment() instanceof DiscardEvent)) + webSocket.close(1006, "Connection was closed abnormally (that is, with no close frame being sent)."); } catch (Throwable t) { log.error("onError", t); } @@ -2463,4 +2434,3 @@ private static boolean isSecure(URI uri) { return isSecure(uri.getScheme()); } } - diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProviderConfig.java b/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProviderConfig.java index 7c976149e6..79bd0741b7 100644 --- a/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProviderConfig.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProviderConfig.java @@ -57,6 +57,25 @@ public class NettyAsyncHttpProviderConfig implements AsyncHttpProviderConfig properties = new ConcurrentHashMap(); public NettyAsyncHttpProviderConfig() { @@ -85,6 +104,20 @@ public Object getProperty(String name) { return properties.get(name); } + /** + * Return the value associated with the property's name + * + * @param name + * @return this instance of AsyncHttpProviderConfig + */ + public T getProperty(String name, Class type, T defaultValue) { + Object value = properties.get(name); + if (value != null && type.isAssignableFrom(value.getClass())) { + return type.cast(value); + } + return defaultValue; + } + /** * Remove the value associated with the property's name * @@ -103,4 +136,4 @@ public Object removeProperty(String name) { public Set> propertiesSet() { return properties.entrySet(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java b/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java index 301399c36e..0dbca7a339 100644 --- a/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java @@ -18,8 +18,11 @@ import com.ning.http.client.AsyncHandler; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.ProxyServer; import com.ning.http.client.Request; import com.ning.http.util.AllowAllHostnameVerifier; +import com.ning.http.util.ProxyUtils; + import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; @@ -49,11 +52,10 @@ final class NettyConnectListener implements ChannelFutureListener { private final AtomicBoolean handshakeDone = new AtomicBoolean(false); private NettyConnectListener(AsyncHttpClientConfig config, - NettyResponseFuture future, - HttpRequest nettyRequest) { + NettyResponseFuture future) { this.config = config; this.future = future; - this.nettyRequest = nettyRequest; + this.nettyRequest = future.getNettyRequest(); } public NettyResponseFuture future() { @@ -71,7 +73,7 @@ public final void operationComplete(ChannelFuture f) throws Exception { } HostnameVerifier v = config.getHostnameVerifier(); - if (sslHandler != null && !AllowAllHostnameVerifier.class.isAssignableFrom(v.getClass())) { + if (sslHandler != null && !(v instanceof AllowAllHostnameVerifier)) { // TODO: channel.getRemoteAddress()).getHostName() is very expensive. Should cache the result. if (!v.verify(InetSocketAddress.class.cast(channel.getRemoteAddress()).getHostName(), sslHandler.getEngine().getSession())) { @@ -79,13 +81,13 @@ public final void operationComplete(ChannelFuture f) throws Exception { } } - future.provider().writeRequest(f.getChannel(), config, future, nettyRequest); + future.provider().writeRequest(f.getChannel(), config, future); } else { Throwable cause = f.getCause(); logger.debug("Trying to recover a dead cached channel {} with a retry value of {} ", f.getChannel(), future.canRetry()); if (future.canRetry() && cause != null && (NettyAsyncHttpProvider.abortOnDisconnectException(cause) - || ClosedChannelException.class.isAssignableFrom(cause.getClass()) + || cause instanceof ClosedChannelException || future.getState() != NettyResponseFuture.STATE.NEW)) { logger.debug("Retrying {} ", nettyRequest); @@ -137,14 +139,15 @@ public Builder(AsyncHttpClientConfig config, Request request, AsyncHandler as } public NettyConnectListener build(final URI uri) throws IOException { - HttpRequest nettyRequest = NettyAsyncHttpProvider.buildRequest(config, request, uri, true, buffer); + ProxyServer proxyServer = ProxyUtils.getProxyServer(config, request); + HttpRequest nettyRequest = NettyAsyncHttpProvider.buildRequest(config, request, uri, true, buffer, proxyServer); if (future == null) { - future = NettyAsyncHttpProvider.newFuture(uri, request, asyncHandler, nettyRequest, config, provider); + future = NettyAsyncHttpProvider.newFuture(uri, request, asyncHandler, nettyRequest, config, provider, proxyServer); } else { future.setNettyRequest(nettyRequest); future.setRequest(request); } - return new NettyConnectListener(config, future, nettyRequest); + return new NettyConnectListener(config, future); } } } diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyConnectionsPool.java b/src/main/java/com/ning/http/client/providers/netty/NettyConnectionsPool.java index 18e582774a..08acb0bf02 100644 --- a/src/main/java/com/ning/http/client/providers/netty/NettyConnectionsPool.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyConnectionsPool.java @@ -12,6 +12,8 @@ */ package com.ning.http.client.providers.netty; +import static com.ning.http.util.DateUtil.millisTime; + import com.ning.http.client.ConnectionsPool; import org.jboss.netty.channel.Channel; import org.slf4j.Logger; @@ -34,18 +36,31 @@ public class NettyConnectionsPool implements ConnectionsPool { private final static Logger log = LoggerFactory.getLogger(NettyConnectionsPool.class); private final ConcurrentHashMap> connectionsPool = new ConcurrentHashMap>(); private final ConcurrentHashMap channel2IdleChannel = new ConcurrentHashMap(); + private final ConcurrentHashMap channel2CreationDate = new ConcurrentHashMap(); private final AtomicBoolean isClosed = new AtomicBoolean(false); - private final Timer idleConnectionDetector = new Timer(true); + private final Timer idleConnectionDetector; private final boolean sslConnectionPoolEnabled; private final int maxTotalConnections; private final int maxConnectionPerHost; + private final int maxConnectionLifeTimeInMs; private final long maxIdleTime; public NettyConnectionsPool(NettyAsyncHttpProvider provider) { - this.maxTotalConnections = provider.getConfig().getMaxTotalConnections(); - this.maxConnectionPerHost = provider.getConfig().getMaxConnectionPerHost(); - this.sslConnectionPoolEnabled = provider.getConfig().isSslConnectionPoolEnabled(); - this.maxIdleTime = provider.getConfig().getIdleConnectionInPoolTimeoutInMs(); + this(provider.getConfig().getMaxTotalConnections(),// + provider.getConfig().getMaxConnectionPerHost(),// + provider.getConfig().getIdleConnectionInPoolTimeoutInMs(),// + provider.getConfig().getMaxConnectionLifeTimeInMs(),// + provider.getConfig().isSslConnectionPoolEnabled(),// + new Timer(true)); + } + + public NettyConnectionsPool(int maxTotalConnections, int maxConnectionPerHost, long maxIdleTime, int maxConnectionLifeTimeInMs, boolean sslConnectionPoolEnabled, Timer idleConnectionDetector) { + this.maxTotalConnections = maxTotalConnections; + this.maxConnectionPerHost = maxConnectionPerHost; + this.sslConnectionPoolEnabled = sslConnectionPoolEnabled; + this.maxIdleTime = maxIdleTime; + this.maxConnectionLifeTimeInMs = maxConnectionLifeTimeInMs; + this.idleConnectionDetector = idleConnectionDetector; this.idleConnectionDetector.schedule(new IdleChannelDetector(), maxIdleTime, maxIdleTime); } @@ -57,7 +72,7 @@ private static class IdleChannel { IdleChannel(String uri, Channel channel) { this.uri = uri; this.channel = channel; - this.start = System.currentTimeMillis(); + this.start = millisTime(); } @Override @@ -93,7 +108,7 @@ public void run() { } List channelsInTimeout = new ArrayList(); - long currentTime = System.currentTimeMillis(); + long currentTime = millisTime(); for (IdleChannel idleChannel : channel2IdleChannel.values()) { long age = currentTime - idleChannel.start; @@ -105,18 +120,16 @@ public void run() { channelsInTimeout.add(idleChannel); } } - long endConcurrentLoop = System.currentTimeMillis(); + long endConcurrentLoop = millisTime(); for (IdleChannel idleChannel : channelsInTimeout) { Object attachment = idleChannel.channel.getPipeline().getContext(NettyAsyncHttpProvider.class).getAttachment(); - if (attachment != null) { - if (NettyResponseFuture.class.isAssignableFrom(attachment.getClass())) { - NettyResponseFuture future = (NettyResponseFuture) attachment; - - if (!future.isDone() && !future.isCancelled()) { - log.debug("Future not in appropriate state %s\n", future); - continue; - } + if (attachment instanceof NettyResponseFuture) { + NettyResponseFuture future = (NettyResponseFuture) attachment; + + if (!future.isDone() && !future.isCancelled()) { + log.debug("Future not in appropriate state %s\n", future); + continue; } } @@ -126,8 +139,15 @@ public void run() { } } - log.trace(String.format("%d channel open, %d idle channels closed (times: 1st-loop=%d, 2nd-loop=%d).\n", - connectionsPool.size(), channelsInTimeout.size(), endConcurrentLoop - currentTime, System.currentTimeMillis() - endConcurrentLoop)); + if (log.isTraceEnabled()) { + int openChannels = 0; + for (ConcurrentLinkedQueue hostChannels: connectionsPool.values()) { + openChannels += hostChannels.size(); + } + log.trace(String.format("%d channel open, %d idle channels closed (times: 1st-loop=%d, 2nd-loop=%d).\n", + openChannels, channelsInTimeout.size(), endConcurrentLoop - currentTime, millisTime() - endConcurrentLoop)); + } + } catch (Throwable t) { log.error("uncaught exception!", t); } @@ -144,6 +164,14 @@ public boolean offer(String uri, Channel channel) { return false; } + Long createTime = channel2CreationDate.get(channel); + if (createTime == null) { + channel2CreationDate.putIfAbsent(channel, millisTime()); + } else if (maxConnectionLifeTimeInMs != -1 && (createTime + maxConnectionLifeTimeInMs) < millisTime()) { + log.debug("Channel {} expired", channel); + return false; + } + log.debug("Adding uri: {} for channel {}", uri, channel); channel.getPipeline().getContext(NettyAsyncHttpProvider.class).setAttachment(new NettyAsyncHttpProvider.DiscardEvent()); @@ -185,7 +213,7 @@ public Channel poll(String uri) { if (idleConnectionForHost != null) { boolean poolEmpty = false; while (!poolEmpty && idleChannel == null) { - if (idleConnectionForHost.size() > 0) { + if (!idleConnectionForHost.isEmpty()) { synchronized (idleConnectionForHost) { idleChannel = idleConnectionForHost.poll(); if (idleChannel != null) { @@ -221,6 +249,7 @@ private boolean remove(IdleChannel pooledChannel) { * {@inheritDoc} */ public boolean removeAll(Channel channel) { + channel2CreationDate.remove(channel); return !isClosed.get() && remove(channel2IdleChannel.get(channel)); } @@ -250,11 +279,13 @@ public void destroy() { } connectionsPool.clear(); channel2IdleChannel.clear(); + channel2CreationDate.clear(); } private void close(Channel channel) { try { channel.getPipeline().getContext(NettyAsyncHttpProvider.class).setAttachment(new NettyAsyncHttpProvider.DiscardEvent()); + channel2CreationDate.remove(channel); channel.close(); } catch (Throwable t) { // noop diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyResponse.java b/src/main/java/com/ning/http/client/providers/netty/NettyResponse.java index 5af231d626..787501d3fd 100644 --- a/src/main/java/com/ning/http/client/providers/netty/NettyResponse.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyResponse.java @@ -15,6 +15,9 @@ */ package com.ning.http.client.providers.netty; +import static com.ning.http.util.MiscUtil.isNonEmpty; + +import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder; import com.ning.http.client.Cookie; import com.ning.http.client.FluentCaseInsensitiveStringsMap; import com.ning.http.client.HttpResponseBodyPart; @@ -22,41 +25,41 @@ import com.ning.http.client.HttpResponseStatus; import com.ning.http.client.Response; import com.ning.http.util.AsyncHttpProviderUtils; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBufferInputStream; -import org.jboss.netty.buffer.ChannelBuffers; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBufferInputStream; +import org.jboss.netty.buffer.ChannelBuffers; /** * Wrapper around the {@link com.ning.http.client.Response} API. */ public class NettyResponse implements Response { - private final static String DEFAULT_CHARSET = "ISO-8859-1"; - private final static String HEADERS_NOT_COMPUTED = "Response's headers hasn't been computed by your AsyncHandler."; + private final static Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); - private final URI uri; - private final Collection bodyParts; + private final List bodyParts; private final HttpResponseHeaders headers; private final HttpResponseStatus status; - private final List cookies = new ArrayList(); + private List cookies; public NettyResponse(HttpResponseStatus status, HttpResponseHeaders headers, - Collection bodyParts) { + List bodyParts) { this.status = status; this.headers = headers; this.bodyParts = bodyParts; - uri = status.getUrl(); } /* @Override */ @@ -73,7 +76,11 @@ public String getStatusText() { /* @Override */ public byte[] getResponseBodyAsBytes() throws IOException { - return AsyncHttpProviderUtils.contentToByte(bodyParts); + return ChannelBufferUtil.channelBuffer2bytes(getResponseBodyAsChannelBuffer()); + } + + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException { + return getResponseBodyAsChannelBuffer().toByteBuffer(); } /* @Override */ @@ -82,31 +89,32 @@ public String getResponseBody() throws IOException { } public String getResponseBody(String charset) throws IOException { - String contentType = getContentType(); - if (contentType != null && charset == null) { - charset = AsyncHttpProviderUtils.parseCharset(contentType); - } - - if (charset == null) { - charset = DEFAULT_CHARSET; - } - - return AsyncHttpProviderUtils.contentToString(bodyParts, charset); + return getResponseBodyAsChannelBuffer().toString(computeCharset(charset)); } /* @Override */ public InputStream getResponseBodyAsStream() throws IOException { - ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); - for (HttpResponseBodyPart bp : bodyParts) { - // Ugly. TODO - // (1) We must remove the downcast, - // (2) we need a CompositeByteArrayInputStream to avoid - // copying the bytes. - if (bp.getClass().isAssignableFrom(ResponseBodyPart.class)) { - buf.writeBytes(bp.getBodyPartBytes()); + return new ChannelBufferInputStream(getResponseBodyAsChannelBuffer()); + } + + public ChannelBuffer getResponseBodyAsChannelBuffer() throws IOException { + ChannelBuffer b = null; + switch (bodyParts.size()) { + case 0: + b = ChannelBuffers.EMPTY_BUFFER; + break; + case 1: + b = ResponseBodyPart.class.cast(bodyParts.get(0)).getChannelBuffer(); + break; + default: + ChannelBuffer[] channelBuffers = new ChannelBuffer[bodyParts.size()]; + for (int i = 0; i < bodyParts.size(); i++) { + channelBuffers[i] = ResponseBodyPart.class.cast(bodyParts.get(i)).getChannelBuffer(); } + b = ChannelBuffers.wrappedBuffer(channelBuffers); } - return new ChannelBufferInputStream(buf); + + return b; } /* @Override */ @@ -116,86 +124,85 @@ public String getResponseBodyExcerpt(int maxLength) throws IOException { } public String getResponseBodyExcerpt(int maxLength, String charset) throws IOException { - String contentType = getContentType(); - if (contentType != null && charset == null) { - charset = AsyncHttpProviderUtils.parseCharset(contentType); - } + String response = getResponseBody(charset); + return response.length() <= maxLength ? response : response.substring(0, maxLength); + } + private Charset computeCharset(String charset) { if (charset == null) { - charset = DEFAULT_CHARSET; + String contentType = getContentType(); + if (contentType != null) + charset = AsyncHttpProviderUtils.parseCharset(contentType); // parseCharset can return null } - - String response = AsyncHttpProviderUtils.contentToString(bodyParts, charset); - return response.length() <= maxLength ? response : response.substring(0, maxLength); + return charset != null ? Charset.forName(charset) : DEFAULT_CHARSET; } /* @Override */ public URI getUri() throws MalformedURLException { - return uri; + return status.getUrl(); } /* @Override */ public String getContentType() { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders().getFirstValue("Content-Type"); + return getHeader("Content-Type"); } /* @Override */ public String getHeader(String name) { - if (headers == null) { - throw new IllegalStateException(); - } - return headers.getHeaders().getFirstValue(name); + return headers != null ? headers.getHeaders().getFirstValue(name) : null; } /* @Override */ public List getHeaders(String name) { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders().get(name); + return headers != null ? headers.getHeaders().get(name) : Collections. emptyList(); } /* @Override */ public FluentCaseInsensitiveStringsMap getHeaders() { - if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); - } - return headers.getHeaders(); + return headers != null ? headers.getHeaders() : new FluentCaseInsensitiveStringsMap(); } /* @Override */ public boolean isRedirected() { - return (status.getStatusCode() >= 300) && (status.getStatusCode() <= 399); + switch (status.getStatusCode()) { + case 301: + case 302: + case 303: + case 307: + case 308: + return true; + default: + return false; + } } /* @Override */ public List getCookies() { if (headers == null) { - throw new IllegalStateException(HEADERS_NOT_COMPUTED); + return Collections.emptyList(); } - if (cookies.isEmpty()) { + if (cookies == null) { + List localCookies = new ArrayList(); for (Map.Entry> header : headers.getHeaders().entrySet()) { if (header.getKey().equalsIgnoreCase("Set-Cookie")) { // TODO: ask for parsed header List v = header.getValue(); for (String value : v) { - Cookie cookie = AsyncHttpProviderUtils.parseCookie(value); - cookies.add(cookie); + Set cookies = CookieDecoder.decode(value); + localCookies.addAll(cookies); } } } + cookies = Collections.unmodifiableList(localCookies); } - return Collections.unmodifiableList(cookies); + return cookies; } /** @@ -203,7 +210,7 @@ public List getCookies() { */ /* @Override */ public boolean hasResponseStatus() { - return (status != null ? true : false); + return status != null; } /** @@ -211,7 +218,7 @@ public boolean hasResponseStatus() { */ /* @Override */ public boolean hasResponseHeaders() { - return (headers != null ? true : false); + return headers != null; } /** @@ -219,7 +226,7 @@ public boolean hasResponseHeaders() { */ /* @Override */ public boolean hasResponseBody() { - return (bodyParts != null && bodyParts.size() > 0 ? true : false); + return isNonEmpty(bodyParts); } } diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyResponseFuture.java b/src/main/java/com/ning/http/client/providers/netty/NettyResponseFuture.java index bb58421dcb..171a2bda6c 100755 --- a/src/main/java/com/ning/http/client/providers/netty/NettyResponseFuture.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyResponseFuture.java @@ -15,18 +15,10 @@ */ package com.ning.http.client.providers.netty; -import com.ning.http.client.AsyncHandler; -import com.ning.http.client.Request; -import com.ning.http.client.listenable.AbstractListenableFuture; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static com.ning.http.util.DateUtil.millisTime; import java.net.MalformedURLException; import java.net.URI; -import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -38,9 +30,21 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.ConnectionPoolKeyStrategy; +import com.ning.http.client.ProxyServer; +import com.ning.http.client.Request; +import com.ning.http.client.listenable.AbstractListenableFuture; + /** * A {@link Future} that can be used to track when an asynchronous HTTP request has been fully processed. - * + * * @param */ public final class NettyResponseFuture extends AbstractListenableFuture { @@ -49,17 +53,14 @@ public final class NettyResponseFuture extends AbstractListenableFuture { public final static String MAX_RETRY = "com.ning.http.client.providers.netty.maxRetry"; enum STATE { - NEW, - POOLED, - RECONNECTED, - CLOSED, + NEW, POOLED, RECONNECTED, CLOSED, } private final CountDownLatch latch = new CountDownLatch(1); private final AtomicBoolean isDone = new AtomicBoolean(false); private final AtomicBoolean isCancelled = new AtomicBoolean(false); private AsyncHandler asyncHandler; - private final int responseTimeoutInMs; + private final int requestTimeoutInMs; private final int idleConnectionTimeoutInMs; private Request request; private HttpRequest nettyRequest; @@ -72,8 +73,8 @@ enum STATE { private volatile Future reaperFuture; private final AtomicBoolean inAuth = new AtomicBoolean(false); private final AtomicBoolean statusReceived = new AtomicBoolean(false); - private final AtomicLong touch = new AtomicLong(System.currentTimeMillis()); - private final long start = System.currentTimeMillis(); + private final AtomicLong touch = new AtomicLong(millisTime()); + private final long start = millisTime(); private final NettyAsyncHttpProvider asyncHttpProvider; private final AtomicReference state = new AtomicReference(STATE.NEW); private final AtomicBoolean contentProcessed = new AtomicBoolean(false); @@ -85,22 +86,28 @@ enum STATE { private boolean writeBody; private final AtomicBoolean throwableCalled = new AtomicBoolean(false); private boolean allowConnect = false; - - public NettyResponseFuture(URI uri, - Request request, - AsyncHandler asyncHandler, - HttpRequest nettyRequest, - int responseTimeoutInMs, - int idleConnectionTimeoutInMs, - NettyAsyncHttpProvider asyncHttpProvider) { + private final ConnectionPoolKeyStrategy connectionPoolKeyStrategy; + private final ProxyServer proxyServer; + + public NettyResponseFuture(URI uri,// + Request request,// + AsyncHandler asyncHandler,// + HttpRequest nettyRequest,// + int requestTimeoutInMs,// + int idleConnectionTimeoutInMs,// + NettyAsyncHttpProvider asyncHttpProvider,// + ConnectionPoolKeyStrategy connectionPoolKeyStrategy,// + ProxyServer proxyServer) { this.asyncHandler = asyncHandler; - this.responseTimeoutInMs = responseTimeoutInMs; + this.requestTimeoutInMs = requestTimeoutInMs; this.idleConnectionTimeoutInMs = idleConnectionTimeoutInMs; this.request = request; this.nettyRequest = nettyRequest; this.uri = uri; this.asyncHttpProvider = asyncHttpProvider; + this.connectionPoolKeyStrategy = connectionPoolKeyStrategy; + this.proxyServer = proxyServer; if (System.getProperty(MAX_RETRY) != null) { maxRetry = Integer.valueOf(System.getProperty(MAX_RETRY)); @@ -119,6 +126,14 @@ protected void setURI(URI uri) { this.uri = uri; } + public ConnectionPoolKeyStrategy getConnectionPoolKeyStrategy() { + return connectionPoolKeyStrategy; + } + + public ProxyServer getProxyServer() { + return proxyServer; + } + /** * {@inheritDoc} */ @@ -146,7 +161,8 @@ void setAsyncHandler(AsyncHandler asyncHandler) { public boolean cancel(boolean force) { cancelReaper(); - if (isCancelled.get()) return false; + if (isCancelled.get()) + return false; try { channel.getPipeline().getContext(NettyAsyncHttpProvider.class).setAttachment(new NettyAsyncHttpProvider.DiscardEvent()); @@ -163,19 +179,26 @@ public boolean cancel(boolean force) { } latch.countDown(); isCancelled.set(true); - super.done(); + runListeners(); return true; } /** * Is the Future still valid - * + * * @return true if response has expired and should be terminated. */ public boolean hasExpired() { - long now = System.currentTimeMillis(); - return idleConnectionTimeoutInMs != -1 && ((now - touch.get()) >= idleConnectionTimeoutInMs) - || responseTimeoutInMs != -1 && ((now - start) >= responseTimeoutInMs); + long now = millisTime(); + return hasConnectionIdleTimedOut(now) || hasRequestTimedOut(now); + } + + public boolean hasConnectionIdleTimedOut(long now) { + return idleConnectionTimeoutInMs != -1 && (now - touch.get()) >= idleConnectionTimeoutInMs; + } + + public boolean hasRequestTimedOut(long now) { + return requestTimeoutInMs != -1 && (now - start) >= requestTimeoutInMs; } /** @@ -184,7 +207,7 @@ public boolean hasExpired() { /* @Override */ public V get() throws InterruptedException, ExecutionException { try { - return get(responseTimeoutInMs, TimeUnit.MILLISECONDS); + return get(requestTimeoutInMs, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { cancelReaper(); throw new ExecutionException(e); @@ -193,7 +216,7 @@ public V get() throws InterruptedException, ExecutionException { void cancelReaper() { if (reaperFuture != null) { - reaperFuture.cancel(true); + reaperFuture.cancel(false); } } @@ -212,15 +235,23 @@ public V get(long l, TimeUnit tu) throws InterruptedException, TimeoutException, if (expired) { isCancelled.set(true); - TimeoutException te = new TimeoutException(String.format("No response received after %s", l)); + try { + channel.getPipeline().getContext(NettyAsyncHttpProvider.class).setAttachment(new NettyAsyncHttpProvider.DiscardEvent()); + channel.close(); + } catch (Throwable t) { + // Ignore + } if (!throwableCalled.getAndSet(true)) { try { - asyncHandler.onThrowable(te); - } catch (Throwable t) { - logger.debug("asyncHandler.onThrowable", t); + TimeoutException te = new TimeoutException(String.format("No response received after %s", l)); + try { + asyncHandler.onThrowable(te); + } catch (Throwable t) { + logger.debug("asyncHandler.onThrowable", t); + } + throw new ExecutionException(te); } finally { cancelReaper(); - throw new ExecutionException(te); } } } @@ -249,12 +280,14 @@ V getContent() throws ExecutionException { } catch (Throwable ex) { if (!throwableCalled.getAndSet(true)) { try { - asyncHandler.onThrowable(ex); - } catch (Throwable t) { - logger.debug("asyncHandler.onThrowable", t); + try { + asyncHandler.onThrowable(ex); + } catch (Throwable t) { + logger.debug("asyncHandler.onThrowable", t); + } + throw new RuntimeException(ex); } finally { cancelReaper(); - throw new RuntimeException(ex); } } } @@ -263,7 +296,8 @@ V getContent() throws ExecutionException { return update; } - public final void done(Callable callable) { + public final void done() { + try { cancelReaper(); @@ -272,27 +306,24 @@ public final void done(Callable callable) { } getContent(); isDone.set(true); - if (callable != null) { - try { - callable.call(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } } catch (ExecutionException t) { return; } catch (RuntimeException t) { - exEx.compareAndSet(null, new ExecutionException(t)); + Throwable exception = t.getCause() != null ? t.getCause() : t; + exEx.compareAndSet(null, new ExecutionException(exception)); + } finally { latch.countDown(); } - super.done(); + + runListeners(); } public final void abort(final Throwable t) { cancelReaper(); - if (isDone.get() || isCancelled.get()) return; + if (isDone.get() || isCancelled.get()) + return; exEx.compareAndSet(null, new ExecutionException(t)); if (!throwableCalled.getAndSet(true)) { @@ -305,7 +336,7 @@ public final void abort(final Throwable t) { } } latch.countDown(); - super.done(); + runListeners(); } public void content(V v) { @@ -378,7 +409,7 @@ public boolean getAndSetStatusReceived(boolean sr) { */ /* @Override */ public void touch() { - touch.set(System.currentTimeMillis()); + touch.set(millisTime()); } /** @@ -446,38 +477,45 @@ public void setRequest(Request request) { } /** - * Return true if the {@link Future} cannot be recovered. There is some scenario where a connection can be - * closed by an unexpected IOException, and in some situation we can recover from that exception. - * + * Return true if the {@link Future} cannot be recovered. There is some scenario where a connection can be closed by an unexpected IOException, and in some situation we can recover from that exception. + * * @return true if that {@link Future} cannot be recovered. */ public boolean cannotBeReplay() { - return isDone() - || !canRetry() - || isCancelled() - || (channel() != null && channel().isOpen() && uri.getScheme().compareToIgnoreCase("https") != 0) - || isInAuth(); + return isDone() || !canRetry() || isCancelled() || (channel() != null && channel().isOpen() && uri.getScheme().compareToIgnoreCase("https") != 0) || isInAuth(); + } + + public long getStart() { + return start; + } + + public long getRequestTimeoutInMs() { + return requestTimeoutInMs; + } + + public long getIdleConnectionTimeoutInMs() { + return idleConnectionTimeoutInMs; } @Override public String toString() { - return "NettyResponseFuture{" + - "currentRetry=" + currentRetry + - ",\n\tisDone=" + isDone + - ",\n\tisCancelled=" + isCancelled + - ",\n\tasyncHandler=" + asyncHandler + - ",\n\tresponseTimeoutInMs=" + responseTimeoutInMs + - ",\n\tnettyRequest=" + nettyRequest + - ",\n\tcontent=" + content + - ",\n\turi=" + uri + - ",\n\tkeepAlive=" + keepAlive + - ",\n\thttpResponse=" + httpResponse + - ",\n\texEx=" + exEx + - ",\n\tredirectCount=" + redirectCount + - ",\n\treaperFuture=" + reaperFuture + - ",\n\tinAuth=" + inAuth + - ",\n\tstatusReceived=" + statusReceived + - ",\n\ttouch=" + touch + + return "NettyResponseFuture{" + // + "currentRetry=" + currentRetry + // + ",\n\tisDone=" + isDone + // + ",\n\tisCancelled=" + isCancelled + // + ",\n\tasyncHandler=" + asyncHandler + // + ",\n\trequestTimeoutInMs=" + requestTimeoutInMs + // + ",\n\tnettyRequest=" + nettyRequest + // + ",\n\tcontent=" + content + // + ",\n\turi=" + uri + // + ",\n\tkeepAlive=" + keepAlive + // + ",\n\thttpResponse=" + httpResponse + // + ",\n\texEx=" + exEx + // + ",\n\tredirectCount=" + redirectCount + // + ",\n\treaperFuture=" + reaperFuture + // + ",\n\tinAuth=" + inAuth + // + ",\n\tstatusReceived=" + statusReceived + // + ",\n\ttouch=" + touch + // '}'; } diff --git a/src/main/java/com/ning/http/client/providers/netty/NettyWebSocket.java b/src/main/java/com/ning/http/client/providers/netty/NettyWebSocket.java index 498e15ba12..b9d96249d8 100644 --- a/src/main/java/com/ning/http/client/providers/netty/NettyWebSocket.java +++ b/src/main/java/com/ning/http/client/providers/netty/NettyWebSocket.java @@ -18,13 +18,16 @@ import com.ning.http.client.websocket.WebSocketListener; import com.ning.http.client.websocket.WebSocketTextListener; import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; +import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayOutputStream; import java.util.concurrent.ConcurrentLinkedQueue; import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer; @@ -35,6 +38,10 @@ public class NettyWebSocket implements WebSocket { private final Channel channel; private final ConcurrentLinkedQueue listeners = new ConcurrentLinkedQueue(); + private StringBuilder textBuffer; + private ByteArrayOutputStream byteBuffer; + private int maxBufferSize = 128000000; + public NettyWebSocket(Channel channel) { this.channel = channel; } @@ -90,6 +97,17 @@ public WebSocket removeWebSocketListener(WebSocketListener l) { return this; } + public int getMaxBufferSize() { + return maxBufferSize; + } + + public void setMaxBufferSize(int bufferSize) { + maxBufferSize = bufferSize; + + if(maxBufferSize < 8192) + maxBufferSize = 8192; + } + // @Override public boolean isOpen() { return channel.isOpen(); @@ -97,16 +115,44 @@ public boolean isOpen() { // @Override public void close() { - onClose(); + if (channel.isOpen()) { + onClose(); + listeners.clear(); + channel.write(new CloseWebSocketFrame()).addListener(ChannelFutureListener.CLOSE); + } + } + + // @Override + public void close(int statusCode, String reason) { + onClose(statusCode, reason); listeners.clear(); - channel.close(); } - protected void onMessage(byte[] message) { + protected void onBinaryFragment(byte[] message, boolean last) { for (WebSocketListener l : listeners) { - if (WebSocketByteListener.class.isAssignableFrom(l.getClass())) { + if (l instanceof WebSocketByteListener) { try { - WebSocketByteListener.class.cast(l).onMessage(message); + WebSocketByteListener.class.cast(l).onFragment(message,last); + + if(byteBuffer == null) { + byteBuffer = new ByteArrayOutputStream(); + } + + byteBuffer.write(message); + + if(byteBuffer.size() > maxBufferSize) { + Exception e = new Exception("Exceeded Netty Web Socket maximum buffer size of " + getMaxBufferSize()); + l.onError(e); + this.close(); + return; + } + + + if(last) { + WebSocketByteListener.class.cast(l).onMessage(byteBuffer.toByteArray()); + byteBuffer = null; + textBuffer = null; + } } catch (Exception ex) { l.onError(ex); } @@ -114,11 +160,30 @@ protected void onMessage(byte[] message) { } } - protected void onTextMessage(String message) { + protected void onTextFragment(String message, boolean last) { for (WebSocketListener l : listeners) { - if (WebSocketTextListener.class.isAssignableFrom(l.getClass())) { + if (l instanceof WebSocketTextListener) { try { - WebSocketTextListener.class.cast(l).onMessage(message); + WebSocketTextListener.class.cast(l).onFragment(message,last); + + if(textBuffer == null) { + textBuffer = new StringBuilder(); + } + + textBuffer.append(message); + + if(textBuffer.length() > maxBufferSize) { + Exception e = new Exception("Exceeded Netty Web Socket maximum buffer size of " + getMaxBufferSize()); + l.onError(e); + this.close(); + return; + } + + if(last) { + WebSocketTextListener.class.cast(l).onMessage(textBuffer.toString()); + byteBuffer = null; + textBuffer = null; + } } catch (Exception ex) { l.onError(ex); } @@ -144,7 +209,7 @@ protected void onClose() { protected void onClose(int code, String reason) { for (WebSocketListener l : listeners) { try { - if (WebSocketCloseCodeReasonListener.class.isAssignableFrom(l.getClass())) { + if (l instanceof WebSocketCloseCodeReasonListener) { WebSocketCloseCodeReasonListener.class.cast(l).onClose(this, code, reason); } l.onClose(this); diff --git a/src/main/java/com/ning/http/client/providers/netty/ResponseBodyPart.java b/src/main/java/com/ning/http/client/providers/netty/ResponseBodyPart.java index 8279c76d77..6ed8abc945 100644 --- a/src/main/java/com/ning/http/client/providers/netty/ResponseBodyPart.java +++ b/src/main/java/com/ning/http/client/providers/netty/ResponseBodyPart.java @@ -34,7 +34,7 @@ public class ResponseBodyPart extends HttpResponseBodyPart { private final HttpChunk chunk; private final HttpResponse response; - private final AtomicReference bytes = new AtomicReference(null); + private final AtomicReference bytes = new AtomicReference(null); private final boolean isLast; private boolean closeConnection = false; @@ -63,19 +63,13 @@ public byte[] getBodyPartBytes() { return bytes.get(); } - ChannelBuffer b = chunk != null ? chunk.getContent() : response.getContent(); - int read = b.readableBytes(); - int index = b.readerIndex(); - - byte[] rb = new byte[read]; - b.readBytes(rb); + byte[] rb = ChannelBufferUtil.channelBuffer2bytes(getChannelBuffer()); bytes.set(rb); - b.readerIndex(index); - return bytes.get(); + return rb; } public int writeTo(OutputStream outputStream) throws IOException { - ChannelBuffer b = chunk != null ? chunk.getContent() : response.getContent(); + ChannelBuffer b = getChannelBuffer(); int read = b.readableBytes(); int index = b.readerIndex(); if (read > 0) { @@ -85,6 +79,10 @@ public int writeTo(OutputStream outputStream) throws IOException { return read; } + public ChannelBuffer getChannelBuffer() { + return chunk != null ? chunk.getContent() : response.getContent(); + } + @Override public ByteBuffer getBodyByteBuffer() { return ByteBuffer.wrap(getBodyPartBytes()); diff --git a/src/main/java/com/ning/http/client/providers/netty/ResponseHeaders.java b/src/main/java/com/ning/http/client/providers/netty/ResponseHeaders.java index 0db2b32334..3e552c9da8 100644 --- a/src/main/java/com/ning/http/client/providers/netty/ResponseHeaders.java +++ b/src/main/java/com/ning/http/client/providers/netty/ResponseHeaders.java @@ -22,6 +22,7 @@ import org.jboss.netty.handler.codec.http.HttpResponse; import java.net.URI; +import java.util.Map; /** * A class that represent the HTTP headers. @@ -48,17 +49,13 @@ public ResponseHeaders(URI uri, HttpResponse response, AsyncHttpProvider provide private FluentCaseInsensitiveStringsMap computerHeaders() { FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - for (String s : response.getHeaderNames()) { - for (String header : response.getHeaders(s)) { - h.add(s, header); - } + for (Map.Entry header: response.getHeaders()) { + h.add(header.getKey(), header.getValue()); } - if (trailingHeaders != null && trailingHeaders.getHeaderNames().size() > 0) { - for (final String s : trailingHeaders.getHeaderNames()) { - for (String header : response.getHeaders(s)) { - h.add(s, header); - } + if (trailingHeaders != null) { + for (Map.Entry header: trailingHeaders.getHeaders()) { + h.add(header.getKey(), header.getValue()); } } diff --git a/src/main/java/com/ning/http/client/providers/netty/WebSocketUtil.java b/src/main/java/com/ning/http/client/providers/netty/WebSocketUtil.java index c2a452e66f..5e20a499cf 100644 --- a/src/main/java/com/ning/http/client/providers/netty/WebSocketUtil.java +++ b/src/main/java/com/ning/http/client/providers/netty/WebSocketUtil.java @@ -13,7 +13,6 @@ package com.ning.http.client.providers.netty; import com.ning.http.util.Base64; -import org.jboss.netty.util.CharsetUtil; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; diff --git a/src/main/java/com/ning/http/client/resumable/PropertiesBasedResumableProcessor.java b/src/main/java/com/ning/http/client/resumable/PropertiesBasedResumableProcessor.java index efb6dea06b..d6ed2fc43b 100644 --- a/src/main/java/com/ning/http/client/resumable/PropertiesBasedResumableProcessor.java +++ b/src/main/java/com/ning/http/client/resumable/PropertiesBasedResumableProcessor.java @@ -60,11 +60,11 @@ public void save(Map map) { FileOutputStream os = null; try { - if (!TMP.mkdirs()) { + if (!TMP.exists() && !TMP.mkdirs()) { throw new IllegalStateException("Unable to create directory: " + TMP.getAbsolutePath()); } File f = new File(TMP, storeName); - if (!f.createNewFile()) { + if (!f.exists() && !f.createNewFile()) { throw new IllegalStateException("Unable to create temp file: " + f.getAbsolutePath()); } if (!f.canWrite()) { @@ -90,7 +90,7 @@ public void save(Map map) { } private static String append(Map.Entry e) { - return new StringBuffer(e.getKey()).append("=").append(e.getValue()).append("\n").toString(); + return new StringBuilder(e.getKey()).append("=").append(e.getValue()).append("\n").toString(); } /** diff --git a/src/main/java/com/ning/http/client/resumable/ResumableAsyncHandler.java b/src/main/java/com/ning/http/client/resumable/ResumableAsyncHandler.java index 51d60ccf99..53ebec5cdb 100644 --- a/src/main/java/com/ning/http/client/resumable/ResumableAsyncHandler.java +++ b/src/main/java/com/ning/http/client/resumable/ResumableAsyncHandler.java @@ -177,8 +177,9 @@ public T onCompleted() throws Exception { /* @Override */ public AsyncHandler.STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { responseBuilder.accumulate(headers); - if (headers.getHeaders().getFirstValue("Content-Length") != null) { - contentLength = Integer.valueOf(headers.getHeaders().getFirstValue("Content-Length")); + String contentLengthHeader = headers.getHeaders().getFirstValue("Content-Length"); + if (contentLengthHeader != null) { + contentLength = Integer.valueOf(contentLengthHeader); if (contentLength == null || contentLength == -1) { return AsyncHandler.STATE.ABORT; } diff --git a/src/main/java/com/ning/http/client/resumable/ResumableIOExceptionFilter.java b/src/main/java/com/ning/http/client/resumable/ResumableIOExceptionFilter.java index 7e2bd1d254..9422c0bd8d 100644 --- a/src/main/java/com/ning/http/client/resumable/ResumableIOExceptionFilter.java +++ b/src/main/java/com/ning/http/client/resumable/ResumableIOExceptionFilter.java @@ -23,7 +23,7 @@ */ public class ResumableIOExceptionFilter implements IOExceptionFilter { public FilterContext filter(FilterContext ctx) throws FilterException { - if (ctx.getIOException() != null && ResumableAsyncHandler.class.isAssignableFrom(ctx.getAsyncHandler().getClass())) { + if (ctx.getIOException() != null && ctx.getAsyncHandler() instanceof ResumableAsyncHandler) { Request request = ResumableAsyncHandler.class.cast(ctx.getAsyncHandler()).adjustRequestRange(ctx.getRequest()); diff --git a/src/main/java/com/ning/http/client/webdav/WebDavCompletionHandlerBase.java b/src/main/java/com/ning/http/client/webdav/WebDavCompletionHandlerBase.java index d0a483db6d..d5264c6886 100644 --- a/src/main/java/com/ning/http/client/webdav/WebDavCompletionHandlerBase.java +++ b/src/main/java/com/ning/http/client/webdav/WebDavCompletionHandlerBase.java @@ -32,7 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Collection; +import java.util.List; import java.util.Collections; /** @@ -43,8 +43,8 @@ public abstract class WebDavCompletionHandlerBase implements AsyncHandler { private final Logger logger = LoggerFactory.getLogger(AsyncCompletionHandlerBase.class); - private final Collection bodies = - Collections.synchronizedCollection(new ArrayList()); + private final List bodies = + Collections.synchronizedList(new ArrayList()); private HttpResponseStatus status; private HttpResponseHeaders headers; diff --git a/src/main/java/com/ning/http/client/webdav/WebDavResponse.java b/src/main/java/com/ning/http/client/webdav/WebDavResponse.java index c77cee0320..3a16fc4f00 100644 --- a/src/main/java/com/ning/http/client/webdav/WebDavResponse.java +++ b/src/main/java/com/ning/http/client/webdav/WebDavResponse.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; +import java.nio.ByteBuffer; import java.util.List; /** @@ -49,6 +50,10 @@ public byte[] getResponseBodyAsBytes() throws IOException { return response.getResponseBodyAsBytes(); } + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException { + return ByteBuffer.wrap(getResponseBodyAsBytes()); + } + public InputStream getResponseBodyAsStream() throws IOException { return response.getResponseBodyAsStream(); } diff --git a/src/main/java/com/ning/http/client/websocket/DefaultWebSocketListener.java b/src/main/java/com/ning/http/client/websocket/DefaultWebSocketListener.java index 4db626b0d6..767b89df33 100644 --- a/src/main/java/com/ning/http/client/websocket/DefaultWebSocketListener.java +++ b/src/main/java/com/ning/http/client/websocket/DefaultWebSocketListener.java @@ -1,42 +1,16 @@ /* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * Copyright (c) 2012-2013 Sonatype, Inc. All rights reserved. * - * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. * - * 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 - * https://glassfish.dev.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. + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ + package com.ning.http.client.websocket; /** diff --git a/src/main/java/com/ning/http/client/websocket/WebSocket.java b/src/main/java/com/ning/http/client/websocket/WebSocket.java index 3917a6b4ad..ae7183fe48 100644 --- a/src/main/java/com/ning/http/client/websocket/WebSocket.java +++ b/src/main/java/com/ning/http/client/websocket/WebSocket.java @@ -12,10 +12,12 @@ */ package com.ning.http.client.websocket; +import java.io.Closeable; + /** * A Websocket client */ -public interface WebSocket { +public interface WebSocket extends Closeable { /** * Send a byte message. diff --git a/src/main/java/com/ning/http/client/websocket/WebSocketUpgradeHandler.java b/src/main/java/com/ning/http/client/websocket/WebSocketUpgradeHandler.java index 5483720913..6f7e20f472 100644 --- a/src/main/java/com/ning/http/client/websocket/WebSocketUpgradeHandler.java +++ b/src/main/java/com/ning/http/client/websocket/WebSocketUpgradeHandler.java @@ -32,8 +32,9 @@ public class WebSocketUpgradeHandler implements UpgradeHandler, Async private final long maxByteSize; private final long maxTextSize; private final AtomicBoolean ok = new AtomicBoolean(false); + private final AtomicBoolean onSuccessCalled = new AtomicBoolean(false); - private WebSocketUpgradeHandler(Builder b) { + protected WebSocketUpgradeHandler(Builder b) { l = b.l; protocol = b.protocol; maxByteSize = b.maxByteSize; @@ -44,15 +45,23 @@ private WebSocketUpgradeHandler(Builder b) { * {@inheritDoc} */ @Override - public final void onThrowable(Throwable t) { + public void onThrowable(Throwable t) { onFailure(t); } + public boolean touchSuccess(){ + return onSuccessCalled.getAndSet(true); + } + + public void resetSuccess() { + onSuccessCalled.set(false); + } + /** * {@inheritDoc} */ @Override - public final STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { return STATE.CONTINUE; } @@ -60,7 +69,7 @@ public final STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exce * {@inheritDoc} */ @Override - public final STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { if (responseStatus.getStatusCode() == 101) { return STATE.UPGRADE; } else { @@ -72,7 +81,7 @@ public final STATE onStatusReceived(HttpResponseStatus responseStatus) throws Ex * {@inheritDoc} */ @Override - public final STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { return STATE.CONTINUE; } @@ -80,7 +89,7 @@ public final STATE onHeadersReceived(HttpResponseHeaders headers) throws Excepti * {@inheritDoc} */ @Override - public final WebSocket onCompleted() throws Exception { + public WebSocket onCompleted() throws Exception { if (webSocket == null) { throw new IllegalStateException("WebSocket is null"); } @@ -91,7 +100,7 @@ public final WebSocket onCompleted() throws Exception { * {@inheritDoc} */ @Override - public final void onSuccess(WebSocket webSocket) { + public void onSuccess(WebSocket webSocket) { this.webSocket = webSocket; for (WebSocketListener w : l) { webSocket.addWebSocketListener(w); @@ -104,7 +113,7 @@ public final void onSuccess(WebSocket webSocket) { * {@inheritDoc} */ @Override - public final void onFailure(Throwable t) { + public void onFailure(Throwable t) { for (WebSocketListener w : l) { if (!ok.get() && webSocket != null) { webSocket.addWebSocketListener(w); @@ -113,7 +122,7 @@ public final void onFailure(Throwable t) { } } - public final void onClose(WebSocket webSocket, int status, String reasonPhrase) { + public void onClose(WebSocket webSocket, int status, String reasonPhrase) { // Connect failure if (this.webSocket == null) this.webSocket = webSocket; @@ -122,7 +131,7 @@ public final void onClose(WebSocket webSocket, int status, String reasonPhrase) webSocket.addWebSocketListener(w); } w.onClose(webSocket); - if (WebSocketCloseCodeReasonListener.class.isAssignableFrom(w.getClass())) { + if (w instanceof WebSocketCloseCodeReasonListener) { WebSocketCloseCodeReasonListener.class.cast(w).onClose(webSocket, status, reasonPhrase); } } diff --git a/src/main/java/com/ning/http/multipart/FilePart.java b/src/main/java/com/ning/http/multipart/FilePart.java index 5548085f08..41b7e7025c 100644 --- a/src/main/java/com/ning/http/multipart/FilePart.java +++ b/src/main/java/com/ning/http/multipart/FilePart.java @@ -23,7 +23,7 @@ /** * This class is an adaptation of the Apache HttpClient implementation - * + * * @link http://hc.apache.org/httpclient-3.x/ */ public class FilePart extends PartBase { @@ -51,43 +51,38 @@ public class FilePart extends PartBase { /** * Attachment's file name as a byte array */ - private static final byte[] FILE_NAME_BYTES = - MultipartEncodingUtil.getAsciiBytes(FILE_NAME); + private static final byte[] FILE_NAME_BYTES = MultipartEncodingUtil.getAsciiBytes(FILE_NAME); /** * Source of the file part. */ - private PartSource source; + private final PartSource source; /** * FilePart Constructor. - * - * @param name the name for this part - * @param partSource the source for this part - * @param contentType the content type for this part, if null the - * {@link #DEFAULT_CONTENT_TYPE default} is used - * @param charset the charset encoding for this part, if null the - * {@link #DEFAULT_CHARSET default} is used + * + * @param name the name for this part + * @param partSource the source for this part + * @param contentType the content type for this part, if null the {@link #DEFAULT_CONTENT_TYPE default} is used + * @param charset the charset encoding for this part, if null the {@link #DEFAULT_CHARSET default} is used */ - public FilePart(String name, PartSource partSource, String contentType, String charset) { - - super( - name, - contentType == null ? DEFAULT_CONTENT_TYPE : contentType, - charset == null ? "ISO-8859-1" : charset, - DEFAULT_TRANSFER_ENCODING - ); + public FilePart(String name, PartSource partSource, String contentType, String charset, String contentId) { + super(name, contentType == null ? DEFAULT_CONTENT_TYPE : contentType, charset == null ? "ISO-8859-1" : charset, DEFAULT_TRANSFER_ENCODING, contentId); if (partSource == null) { throw new IllegalArgumentException("Source may not be null"); } this.source = partSource; } + public FilePart(String name, PartSource partSource, String contentType, String charset) { + this(name, partSource, contentType, charset, null); + } + /** * FilePart Constructor. - * - * @param name the name for this part + * + * @param name the name for this part * @param partSource the source for this part */ public FilePart(String name, PartSource partSource) { @@ -96,76 +91,63 @@ public FilePart(String name, PartSource partSource) { /** * FilePart Constructor. - * + * * @param name the name of the file part * @param file the file to post - * @throws java.io.FileNotFoundException if the file is not a normal - * file or if it is not readable. + * @throws java.io.FileNotFoundException if the file is not a normal file or if it is not readable. */ - public FilePart(String name, File file) - throws FileNotFoundException { + public FilePart(String name, File file) throws FileNotFoundException { this(name, new FilePartSource(file), null, null); } /** * FilePart Constructor. - * - * @param name the name of the file part - * @param file the file to post - * @param contentType the content type for this part, if null the - * {@link #DEFAULT_CONTENT_TYPE default} is used - * @param charset the charset encoding for this part, if null the - * {@link #DEFAULT_CHARSET default} is used - * @throws FileNotFoundException if the file is not a normal - * file or if it is not readable. - */ - public FilePart(String name, File file, String contentType, String charset) - throws FileNotFoundException { + * + * @param name the name of the file part + * @param file the file to post + * @param contentType the content type for this part, if null the {@link #DEFAULT_CONTENT_TYPE default} is used + * @param charset the charset encoding for this part, if null the {@link #DEFAULT_CHARSET default} is used + * @throws FileNotFoundException if the file is not a normal file or if it is not readable. + */ + public FilePart(String name, File file, String contentType, String charset) throws FileNotFoundException { this(name, new FilePartSource(file), contentType, charset); } /** * FilePart Constructor. - * - * @param name the name of the file part + * + * @param name the name of the file part * @param fileName the file name - * @param file the file to post - * @throws FileNotFoundException if the file is not a normal - * file or if it is not readable. + * @param file the file to post + * @throws FileNotFoundException if the file is not a normal file or if it is not readable. */ - public FilePart(String name, String fileName, File file) - throws FileNotFoundException { + public FilePart(String name, String fileName, File file) throws FileNotFoundException { this(name, new FilePartSource(fileName, file), null, null); } /** * FilePart Constructor. - * - * @param name the name of the file part - * @param fileName the file name - * @param file the file to post - * @param contentType the content type for this part, if null the - * {@link #DEFAULT_CONTENT_TYPE default} is used - * @param charset the charset encoding for this part, if null the - * {@link #DEFAULT_CHARSET default} is used - * @throws FileNotFoundException if the file is not a normal - * file or if it is not readable. - */ - public FilePart(String name, String fileName, File file, String contentType, String charset) - throws FileNotFoundException { + * + * @param name the name of the file part + * @param fileName the file name + * @param file the file to post + * @param contentType the content type for this part, if null the {@link #DEFAULT_CONTENT_TYPE default} is used + * @param charset the charset encoding for this part, if null the {@link #DEFAULT_CHARSET default} is used + * @throws FileNotFoundException if the file is not a normal file or if it is not readable. + */ + public FilePart(String name, String fileName, File file, String contentType, String charset) throws FileNotFoundException { this(name, new FilePartSource(fileName, file), contentType, charset); } /** * Write the disposition header to the output stream - * + * * @param out The output stream * @throws java.io.IOException If an IO problem occurs */ - protected void sendDispositionHeader(OutputStream out) - throws IOException { - super.sendDispositionHeader(out); + protected void sendDispositionHeader(OutputStream out) throws IOException { String filename = this.source.getFileName(); + super.sendDispositionHeader(out); if (filename != null) { out.write(FILE_NAME_BYTES); out.write(QUOTE_BYTES); @@ -176,7 +158,7 @@ protected void sendDispositionHeader(OutputStream out) /** * Write the data in "source" to the specified stream. - * + * * @param out The output stream. * @throws IOException if an IO problem occurs. */ @@ -202,17 +184,17 @@ protected void sendData(OutputStream out) throws IOException { } } - public void setStalledTime(long ms) { - _stalledTime = ms; - } + public void setStalledTime(long ms) { + _stalledTime = ms; + } - public long getStalledTime() { - return _stalledTime; - } + public long getStalledTime() { + return _stalledTime; + } /** * Returns the source of the file part. - * + * * @return The source. */ protected PartSource getSource() { @@ -221,7 +203,7 @@ protected PartSource getSource() { /** * Return the length of the data. - * + * * @return The length. * @throws IOException if an IO problem occurs */ @@ -229,6 +211,6 @@ protected long lengthOfData() throws IOException { return source.getLength(); } - private long _stalledTime = -1; + private long _stalledTime = -1; } diff --git a/src/main/java/com/ning/http/multipart/FilePartSource.java b/src/main/java/com/ning/http/multipart/FilePartSource.java index 3f652047cc..ddb772ba7c 100644 --- a/src/main/java/com/ning/http/multipart/FilePartSource.java +++ b/src/main/java/com/ning/http/multipart/FilePartSource.java @@ -70,9 +70,7 @@ public FilePartSource(File file) throws FileNotFoundException { public FilePartSource(String fileName, File file) throws FileNotFoundException { this(file); - if (fileName != null) { - this.fileName = fileName; - } + this.fileName = fileName; } /** @@ -96,7 +94,7 @@ public long getLength() { * @see PartSource#getFileName() */ public String getFileName() { - return (fileName == null) ? "noname" : fileName; + return fileName; } /** diff --git a/src/main/java/com/ning/http/multipart/MultipartBody.java b/src/main/java/com/ning/http/multipart/MultipartBody.java index 88ee5da2ea..2fadcc36e3 100644 --- a/src/main/java/com/ning/http/multipart/MultipartBody.java +++ b/src/main/java/com/ning/http/multipart/MultipartBody.java @@ -25,10 +25,10 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.WritableByteChannel; +import java.nio.channels.*; import java.util.ArrayList; import java.util.List; +import java.util.Set; public class MultipartBody implements RandomAccessBody { @@ -48,8 +48,9 @@ public class MultipartBody implements RandomAccessBody { enum FileLocation {NONE, START, MIDDLE, END} - public MultipartBody(List parts, String boundary, String contentLength) { - this.boundary = MultipartEncodingUtil.getAsciiBytes(boundary.substring("multipart/form-data; boundary=".length())); + public MultipartBody(List parts, String contentType, String contentLength) { + this.boundary = MultipartEncodingUtil.getAsciiBytes(contentType.substring(contentType.indexOf("boundary=") + "boundary=".length())); + this.contentLength = Long.parseLong(contentLength); this.parts = parts; @@ -80,7 +81,7 @@ public long read(ByteBuffer buffer) throws IOException { int maxLength = buffer.capacity(); if (startPart == parts.size() && endWritten) { - return overallLength; + return -1; } boolean full = false; @@ -222,7 +223,7 @@ private void initializeFileEnd(FilePart currentPart) private void initializeFileBody(FilePart currentPart) throws IOException { - if (FilePartSource.class.isAssignableFrom(currentPart.getSource().getClass())) { + if (currentPart.getSource() instanceof FilePartSource) { FilePartSource source = (FilePartSource) currentPart.getSource(); @@ -430,6 +431,7 @@ private ByteArrayOutputStream generateFileStart(FilePart filePart) filePart.sendDispositionHeader(overhead); filePart.sendContentTypeHeader(overhead); filePart.sendTransferEncodingHeader(overhead); + filePart.sendContentIdHeader(overhead); filePart.sendEndOfHeader(overhead); return overhead; } @@ -437,10 +439,10 @@ private ByteArrayOutputStream generateFileStart(FilePart filePart) private long handleFilePart(WritableByteChannel target, FilePart filePart) throws IOException { FilePartStallHandler handler = new FilePartStallHandler( filePart.getStalledTime(), filePart); - + handler.start(); - - if (FilePartSource.class.isAssignableFrom(filePart.getSource().getClass())) { + + if (filePart.getSource() instanceof FilePartSource) { int length = 0; length += handleFileHeaders(target, filePart); @@ -464,7 +466,7 @@ private long handleFilePart(WritableByteChannel target, FilePart filePart) throw } try { nWrite = fc.transferTo(fileLength, l, target); - + if (nWrite == 0) { logger.info("Waiting for writing..."); try { @@ -496,7 +498,7 @@ private long handleFilePart(WritableByteChannel target, FilePart filePart) throw } } handler.completed(); - + fc.close(); length += handleFileEnd(target, filePart); @@ -556,7 +558,7 @@ private long handleMultiPart(WritableByteChannel target, Part currentPart) throw return handleStringPart(target, (StringPart) currentPart); } else if (currentPart.getClass().equals(FilePart.class)) { FilePart filePart = (FilePart) currentPart; - + return handleFilePart(target, filePart); } return 0; @@ -569,21 +571,49 @@ private long writeToTarget(WritableByteChannel target, ByteArrayOutputStream byt int maxSpin = 0; synchronized (byteWriter) { ByteBuffer message = ByteBuffer.wrap(byteWriter.toByteArray()); - while ((target.isOpen()) && (written < byteWriter.size())) { - long nWrite = target.write(message); - written += nWrite; - if (nWrite == 0 && maxSpin++ < 10) { - logger.info("Waiting for writing..."); - try { - byteWriter.wait(1000); - } catch (InterruptedException e) { - logger.trace(e.getMessage(), e); + + if (target instanceof SocketChannel) { + final Selector selector = Selector.open(); + try { + final SocketChannel channel = (SocketChannel) target; + channel.register(selector, SelectionKey.OP_WRITE); + + while (written < byteWriter.size()) { + selector.select(1000); + maxSpin++; + final Set selectedKeys = selector.selectedKeys(); + + for (SelectionKey key : selectedKeys) { + if (key.isWritable()) { + written += target.write(message); + maxSpin = 0; + } + } + + if (maxSpin >= 10) { + throw new IOException("Unable to write on channel " + target); + } } - } else { - if (maxSpin >= 10) { - throw new IOException("Unable to write on channel " + target); + } finally { + selector.close(); + } + } else { + while ((target.isOpen()) && (written < byteWriter.size())) { + long nWrite = target.write(message); + written += nWrite; + if (nWrite == 0 && maxSpin++ < 10) { + logger.info("Waiting for writing..."); + try { + byteWriter.wait(1000); + } catch (InterruptedException e) { + logger.trace(e.getMessage(), e); + } + } else { + if (maxSpin >= 10) { + throw new IOException("Unable to write on channel " + target); + } + maxSpin = 0; } - maxSpin = 0; } } } diff --git a/src/main/java/com/ning/http/multipart/MultipartRequestEntity.java b/src/main/java/com/ning/http/multipart/MultipartRequestEntity.java index 0d93447a22..673a58c017 100644 --- a/src/main/java/com/ning/http/multipart/MultipartRequestEntity.java +++ b/src/main/java/com/ning/http/multipart/MultipartRequestEntity.java @@ -15,7 +15,10 @@ */ package com.ning.http.multipart; -import com.ning.http.client.FluentStringsMap; +import static com.ning.http.util.MiscUtil.isNonEmpty; + +import com.ning.http.client.FluentCaseInsensitiveStringsMap; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +28,7 @@ /** * This class is an adaptation of the Apache HttpClient implementation - * + * * @link http://hc.apache.org/httpclient-3.x/ */ public class MultipartRequestEntity implements RequestEntity { @@ -38,15 +41,14 @@ public class MultipartRequestEntity implements RequestEntity { /** * The pool of ASCII chars to be used for generating a multipart boundary. */ - private static byte[] MULTIPART_CHARS = MultipartEncodingUtil.getAsciiBytes( - "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + private static byte[] MULTIPART_CHARS = MultipartEncodingUtil.getAsciiBytes("-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); /** * Generates a random multipart boundary string. - * + * * @return */ - private static byte[] generateMultipartBoundary() { + public static byte[] generateMultipartBoundary() { Random rand = new Random(); byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40 for (int i = 0; i < bytes.length; i++) { @@ -62,45 +64,52 @@ private static byte[] generateMultipartBoundary() { */ protected Part[] parts; - private byte[] multipartBoundary; + private final byte[] multipartBoundary; - private FluentStringsMap methodParams; + private final String contentType; /** * Creates a new multipart entity containing the given parts. - * - * @param parts The parts to include. - * @param methodParams The params of the HttpMethod using this entity. + * + * @param parts The parts to include. */ - public MultipartRequestEntity(Part[] parts, FluentStringsMap methodParams) { + public MultipartRequestEntity(Part[] parts, FluentCaseInsensitiveStringsMap requestHeaders) { if (parts == null) { throw new IllegalArgumentException("parts cannot be null"); } - if (methodParams == null) { - methodParams = new FluentStringsMap(); - } this.parts = parts; - this.methodParams = methodParams; + String contentTypeHeader = requestHeaders.getFirstValue("Content-Type"); + if (isNonEmpty(contentTypeHeader)) { + int boundaryLocation = contentTypeHeader.indexOf("boundary="); + if (boundaryLocation != -1) { + // boundary defined in existing Content-Type + contentType = contentTypeHeader; + multipartBoundary = MultipartEncodingUtil.getAsciiBytes((contentTypeHeader.substring(boundaryLocation + "boundary=".length()).trim())); + } else { + // generate boundary and append it to existing Content-Type + multipartBoundary = generateMultipartBoundary(); + contentType = computeContentType(contentTypeHeader); + } + } else { + multipartBoundary = generateMultipartBoundary(); + contentType = computeContentType(MULTIPART_FORM_CONTENT_TYPE); + } + } + + private String computeContentType(String base) { + StringBuilder buffer = new StringBuilder(base); + if (!base.endsWith(";")) + buffer.append(";"); + return buffer.append(" boundary=").append(MultipartEncodingUtil.getAsciiString(multipartBoundary)).toString(); } /** - * Returns the MIME boundary string that is used to demarcate boundaries of - * this part. The first call to this method will implicitly create a new - * boundary string. To create a boundary string first the - * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise - * a random one is generated. - * + * Returns the MIME boundary string that is used to demarcate boundaries of this part. The first call to this method will implicitly create a new boundary string. To create a boundary string first the HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise a + * random one is generated. + * * @return The boundary string of this entity in ASCII encoding. */ protected byte[] getMultipartBoundary() { - if (multipartBoundary == null) { - String temp = methodParams.get("") == null ? null : methodParams.get("").iterator().next(); - if (temp != null) { - multipartBoundary = MultipartEncodingUtil.getAsciiBytes(temp); - } else { - multipartBoundary = generateMultipartBoundary(); - } - } return multipartBoundary; } @@ -116,33 +125,20 @@ public boolean isRepeatable() { return true; } - /* (non-Javadoc) - * @see org.apache.commons.httpclient.methods.RequestEntity#writeRequest(java.io.OutputStream) - */ public void writeRequest(OutputStream out) throws IOException { - Part.sendParts(out, parts, getMultipartBoundary()); + Part.sendParts(out, parts, multipartBoundary); } - /* (non-Javadoc) - * @see org.apache.commons.httpclient.methods.RequestEntity#getContentLength() - */ public long getContentLength() { try { - return Part.getLengthOfParts(parts, getMultipartBoundary()); + return Part.getLengthOfParts(parts, multipartBoundary); } catch (Exception e) { log.error("An exception occurred while getting the length of the parts", e); return 0; } } - /* (non-Javadoc) - * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType() - */ public String getContentType() { - StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE); - buffer.append("; boundary="); - buffer.append(MultipartEncodingUtil.getAsciiString(getMultipartBoundary())); - return buffer.toString(); + return contentType; } - } diff --git a/src/main/java/com/ning/http/multipart/Part.java b/src/main/java/com/ning/http/multipart/Part.java index f1d5d30807..7fcfdf603c 100644 --- a/src/main/java/com/ning/http/multipart/Part.java +++ b/src/main/java/com/ning/http/multipart/Part.java @@ -21,7 +21,7 @@ /** * This class is an adaptation of the Apache HttpClient implementation - * + * * @link http://hc.apache.org/httpclient-3.x/ */ public abstract class Part implements com.ning.http.client.Part { @@ -32,8 +32,7 @@ public abstract class Part implements com.ning.http.client.Part { protected static final String BOUNDARY = "----------------314159265358979323846"; /** - * The default boundary to be used if etBoundaryBytes(byte[]) has not - * been called. + * The default boundary to be used if etBoundaryBytes(byte[]) has not been called. */ private static final byte[] DEFAULT_BOUNDARY_BYTES = MultipartEncodingUtil.getAsciiBytes(BOUNDARY); @@ -105,12 +104,21 @@ public abstract class Part implements com.ning.http.client.Part { /** * Content type header as a byte array */ - static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = - MultipartEncodingUtil.getAsciiBytes(CONTENT_TRANSFER_ENCODING); + static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = MultipartEncodingUtil.getAsciiBytes(CONTENT_TRANSFER_ENCODING); + + /** + * Content type header + */ + protected static final String CONTENT_ID = "Content-ID: "; + + /** + * Content type header as a byte array + */ + static final byte[] CONTENT_ID_BYTES = MultipartEncodingUtil.getAsciiBytes(CONTENT_ID); /** * Return the boundary string. - * + * * @return the boundary string * @deprecated uses a constant string. Rather use {@link #getPartBoundary} */ @@ -125,36 +133,42 @@ public static String getBoundary() { /** * Return the name of this part. - * + * * @return The name. */ public abstract String getName(); /** * Returns the content type of this part. - * + * * @return the content type, or null to exclude the content type header */ public abstract String getContentType(); /** * Return the character encoding of this part. - * - * @return the character encoding, or null to exclude the character - * encoding header + * + * @return the character encoding, or null to exclude the character encoding header */ public abstract String getCharSet(); /** * Return the transfer encoding of this part. - * + * * @return the transfer encoding, or null to exclude the transfer encoding header */ public abstract String getTransferEncoding(); + /** + * Return the content ID of this part. + * + * @return the content ID, or null to exclude the content ID header + */ + public abstract String getContentId(); + /** * Gets the part boundary to be used. - * + * * @return the part boundary as an array of bytes. * @since 3.0 */ @@ -168,10 +182,8 @@ protected byte[] getPartBoundary() { } /** - * Sets the part boundary. Only meant to be used by - * {@link Part#sendParts(java.io.OutputStream, Part[], byte[])} - * and {@link Part#getLengthOfParts(Part[], byte[])} - * + * Sets the part boundary. Only meant to be used by {@link Part#sendParts(java.io.OutputStream, Part[], byte[])} and {@link Part#getLengthOfParts(Part[], byte[])} + * * @param boundaryBytes An array of ASCII bytes. * @since 3.0 */ @@ -181,9 +193,8 @@ void setPartBoundary(byte[] boundaryBytes) { /** * Tests if this part can be sent more than once. - * - * @return true if {@link #sendData(java.io.OutputStream)} can be successfully called - * more than once. + * + * @return true if {@link #sendData(java.io.OutputStream)} can be successfully called more than once. * @since 3.0 */ public boolean isRepeatable() { @@ -192,32 +203,34 @@ public boolean isRepeatable() { /** * Write the start to the specified output stream - * + * * @param out The output stream * @throws java.io.IOException If an IO problem occurs. */ protected void sendStart(OutputStream out) throws IOException { out.write(EXTRA_BYTES); out.write(getPartBoundary()); - out.write(CRLF_BYTES); } /** * Write the content disposition header to the specified output stream - * + * * @param out The output stream * @throws IOException If an IO problem occurs. */ protected void sendDispositionHeader(OutputStream out) throws IOException { - out.write(CONTENT_DISPOSITION_BYTES); - out.write(QUOTE_BYTES); - out.write(MultipartEncodingUtil.getAsciiBytes(getName())); - out.write(QUOTE_BYTES); + if (getName() != null) { + out.write(CRLF_BYTES); + out.write(CONTENT_DISPOSITION_BYTES); + out.write(QUOTE_BYTES); + out.write(MultipartEncodingUtil.getAsciiBytes(getName())); + out.write(QUOTE_BYTES); + } } /** * Write the content type header to the specified output stream - * + * * @param out The output stream * @throws IOException If an IO problem occurs. */ @@ -236,9 +249,8 @@ protected void sendContentTypeHeader(OutputStream out) throws IOException { } /** - * Write the content transfer encoding header to the specified - * output stream - * + * Write the content transfer encoding header to the specified output stream + * * @param out The output stream * @throws IOException If an IO problem occurs. */ @@ -251,9 +263,24 @@ protected void sendTransferEncodingHeader(OutputStream out) throws IOException { } } + /** + * Write the content ID header to the specified output stream + * + * @param out The output stream + * @throws IOException If an IO problem occurs. + */ + protected void sendContentIdHeader(OutputStream out) throws IOException { + String contentId = getContentId(); + if (contentId != null) { + out.write(CRLF_BYTES); + out.write(CONTENT_ID_BYTES); + out.write(MultipartEncodingUtil.getAsciiBytes(contentId)); + } + } + /** * Write the end of the header to the output stream - * + * * @param out The output stream * @throws IOException If an IO problem occurs. */ @@ -264,7 +291,7 @@ protected void sendEndOfHeader(OutputStream out) throws IOException { /** * Write the data to the specified output stream - * + * * @param out The output stream * @throws IOException If an IO problem occurs. */ @@ -272,7 +299,7 @@ protected void sendEndOfHeader(OutputStream out) throws IOException { /** * Return the length of the main content - * + * * @return long The length. * @throws IOException If an IO problem occurs */ @@ -280,7 +307,7 @@ protected void sendEndOfHeader(OutputStream out) throws IOException { /** * Write the end data to the output stream. - * + * * @param out The output stream * @throws IOException If an IO problem occurs. */ @@ -289,10 +316,8 @@ protected void sendEnd(OutputStream out) throws IOException { } /** - * Write all the data to the output stream. - * If you override this method make sure to override - * #length() as well - * + * Write all the data to the output stream. If you override this method make sure to override #length() as well + * * @param out The output stream * @throws IOException If an IO problem occurs. */ @@ -301,17 +326,15 @@ public void send(OutputStream out) throws IOException { sendDispositionHeader(out); sendContentTypeHeader(out); sendTransferEncodingHeader(out); + sendContentIdHeader(out); sendEndOfHeader(out); sendData(out); sendEnd(out); } - /** - * Return the full length of all the data. - * If you override this method make sure to override - * #send(OutputStream) as well - * + * Return the full length of all the data. If you override this method make sure to override #send(OutputStream) as well + * * @return long The length. * @throws IOException If an IO problem occurs */ @@ -324,6 +347,7 @@ public long length() throws IOException { sendDispositionHeader(overhead); sendContentTypeHeader(overhead); sendTransferEncodingHeader(overhead); + sendContentIdHeader(overhead); sendEndOfHeader(overhead); sendEnd(overhead); return overhead.size() + lengthOfData(); @@ -331,7 +355,7 @@ public long length() throws IOException { /** * Return a string representation of this object. - * + * * @return A string representation of this object. * @see java.lang.Object#toString() */ @@ -341,27 +365,25 @@ public String toString() { /** * Write all parts and the last boundary to the specified output stream. - * - * @param out The stream to write to. + * + * @param out The stream to write to. * @param parts The parts to write. * @throws IOException If an I/O error occurs while writing the parts. */ - public static void sendParts(OutputStream out, final Part[] parts) - throws IOException { + public static void sendParts(OutputStream out, final Part[] parts) throws IOException { sendParts(out, parts, DEFAULT_BOUNDARY_BYTES); } /** * Write all parts and the last boundary to the specified output stream. - * - * @param out The stream to write to. - * @param parts The parts to write. + * + * @param out The stream to write to. + * @param parts The parts to write. * @param partBoundary The ASCII bytes to use as the part boundary. * @throws IOException If an I/O error occurs while writing the parts. * @since 3.0 */ - public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary) - throws IOException { + public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary) throws IOException { if (parts == null) { throw new IllegalArgumentException("Parts may not be null"); @@ -380,8 +402,7 @@ public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary out.write(CRLF_BYTES); } - public static void sendMessageEnd(OutputStream out, byte[] partBoundary) - throws IOException { + public static void sendMessageEnd(OutputStream out, byte[] partBoundary) throws IOException { if (partBoundary == null || partBoundary.length == 0) { throw new IllegalArgumentException("partBoundary may not be empty"); @@ -395,14 +416,13 @@ public static void sendMessageEnd(OutputStream out, byte[] partBoundary) /** * Write all parts and the last boundary to the specified output stream. - * - * @param out The stream to write to. + * + * @param out The stream to write to. * @param part The part to write. * @throws IOException If an I/O error occurs while writing the parts. * @since N/A */ - public static void sendPart(OutputStream out, Part part, byte[] partBoundary) - throws IOException { + public static void sendPart(OutputStream out, Part part, byte[] partBoundary) throws IOException { if (part == null) { throw new IllegalArgumentException("Parts may not be null"); @@ -414,20 +434,19 @@ public static void sendPart(OutputStream out, Part part, byte[] partBoundary) /** * Return the total sum of all parts and that of the last boundary - * + * * @param parts The parts. * @return The total length * @throws IOException If an I/O error occurs while writing the parts. */ - public static long getLengthOfParts(Part[] parts) - throws IOException { + public static long getLengthOfParts(Part[] parts) throws IOException { return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES); } /** * Gets the length of the multipart message including the given parts. - * - * @param parts The parts. + * + * @param parts The parts. * @param partBoundary The ASCII bytes to use as the part boundary. * @return The total length * @throws IOException If an I/O error occurs while writing the parts. diff --git a/src/main/java/com/ning/http/multipart/PartBase.java b/src/main/java/com/ning/http/multipart/PartBase.java index 415bb49186..b37bbad292 100644 --- a/src/main/java/com/ning/http/multipart/PartBase.java +++ b/src/main/java/com/ning/http/multipart/PartBase.java @@ -17,7 +17,7 @@ /** * This class is an adaptation of the Apache HttpClient implementation - * + * * @link http://hc.apache.org/httpclient-3.x/ */ public abstract class PartBase extends Part { @@ -42,15 +42,17 @@ public abstract class PartBase extends Part { */ private String transferEncoding; + private String contentId; + /** * Constructor. - * - * @param name The name of the part - * @param contentType The content type, or null - * @param charSet The character encoding, or null + * + * @param name The name of the part + * @param contentType The content type, or null + * @param charSet The character encoding, or null * @param transferEncoding The transfer encoding, or null */ - public PartBase(String name, String contentType, String charSet, String transferEncoding) { + public PartBase(String name, String contentType, String charSet, String transferEncoding, String contentId) { if (name == null) { throw new IllegalArgumentException("Name must not be null"); @@ -59,11 +61,12 @@ public PartBase(String name, String contentType, String charSet, String transfer this.contentType = contentType; this.charSet = charSet; this.transferEncoding = transferEncoding; + this.contentId = contentId; } /** * Returns the name. - * + * * @return The name. */ public String getName() { @@ -72,7 +75,7 @@ public String getName() { /** * Returns the content type of this part. - * + * * @return String The name. */ public String getContentType() { @@ -81,7 +84,7 @@ public String getContentType() { /** * Return the character encoding of this part. - * + * * @return String The name. */ public String getCharSet() { @@ -90,7 +93,7 @@ public String getCharSet() { /** * Returns the transfer encoding of this part. - * + * * @return String The name. */ public String getTransferEncoding() { @@ -99,9 +102,8 @@ public String getTransferEncoding() { /** * Sets the character encoding. - * - * @param charSet the character encoding, or null to exclude the character - * encoding header + * + * @param charSet the character encoding, or null to exclude the character encoding header */ public void setCharSet(String charSet) { this.charSet = charSet; @@ -109,7 +111,7 @@ public void setCharSet(String charSet) { /** * Sets the content type. - * + * * @param contentType the content type, or null to exclude the content type header */ public void setContentType(String contentType) { @@ -118,7 +120,7 @@ public void setContentType(String contentType) { /** * Sets the part name. - * + * * @param name */ public void setName(String name) { @@ -130,12 +132,18 @@ public void setName(String name) { /** * Sets the transfer encoding. - * - * @param transferEncoding the transfer encoding, or null to exclude the - * transfer encoding header + * + * @param transferEncoding the transfer encoding, or null to exclude the transfer encoding header */ public void setTransferEncoding(String transferEncoding) { this.transferEncoding = transferEncoding; } + public String getContentId() { + return contentId; + } + + public void setContentId(String contentId) { + this.contentId = contentId; + } } diff --git a/src/main/java/com/ning/http/multipart/StringPart.java b/src/main/java/com/ning/http/multipart/StringPart.java index 431362c8bb..6cd36c0ebc 100644 --- a/src/main/java/com/ning/http/multipart/StringPart.java +++ b/src/main/java/com/ning/http/multipart/StringPart.java @@ -20,7 +20,7 @@ /** * This class is an adaptation of the Apache HttpClient implementation - * + * * @link http://hc.apache.org/httpclient-3.x/ */ public class StringPart extends PartBase { @@ -48,24 +48,18 @@ public class StringPart extends PartBase { /** * The String value of this part. */ - private String value; + private final String value; /** * Constructor. - * - * @param name The name of the part - * @param value the string to post - * @param charset the charset to be used to encode the string, if null - * the {@link #DEFAULT_CHARSET default} is used + * + * @param name The name of the part + * @param value the string to post + * @param charset the charset to be used to encode the string, if null the {@link #DEFAULT_CHARSET default} is used */ - public StringPart(String name, String value, String charset) { + public StringPart(String name, String value, String charset, String contentId) { - super( - name, - DEFAULT_CONTENT_TYPE, - charset == null ? DEFAULT_CHARSET : charset, - DEFAULT_TRANSFER_ENCODING - ); + super(name, DEFAULT_CONTENT_TYPE, charset == null ? DEFAULT_CHARSET : charset, DEFAULT_TRANSFER_ENCODING, contentId); if (value == null) { throw new IllegalArgumentException("Value may not be null"); } @@ -76,10 +70,14 @@ public StringPart(String name, String value, String charset) { this.value = value; } + public StringPart(String name, String value, String charset) { + this(name, value, charset, null); + } + /** * Constructor. - * - * @param name The name of the part + * + * @param name The name of the part * @param value the string to post */ public StringPart(String name, String value) { @@ -87,9 +85,8 @@ public StringPart(String name, String value) { } /** - * Gets the content in bytes. Bytes are lazily created to allow the charset to be changed - * after the part is created. - * + * Gets the content in bytes. Bytes are lazily created to allow the charset to be changed after the part is created. + * * @return the content in bytes */ private byte[] getContent() { @@ -101,7 +98,7 @@ private byte[] getContent() { /** * Writes the data to the given OutputStream. - * + * * @param out the OutputStream to write to * @throws java.io.IOException if there is a write error */ @@ -111,7 +108,7 @@ protected void sendData(OutputStream out) throws IOException { /** * Return the length of the data. - * + * * @return The length of the data. * @throws IOException If an IO problem occurs */ @@ -119,7 +116,9 @@ protected long lengthOfData() throws IOException { return getContent().length; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.apache.commons.httpclient.methods.multipart.BasePart#setCharSet(java.lang.String) */ public void setCharSet(String charSet) { diff --git a/src/main/java/com/ning/http/util/AsyncHttpProviderUtils.java b/src/main/java/com/ning/http/util/AsyncHttpProviderUtils.java index 5395998df8..bc910361b6 100644 --- a/src/main/java/com/ning/http/util/AsyncHttpProviderUtils.java +++ b/src/main/java/com/ning/http/util/AsyncHttpProviderUtils.java @@ -12,28 +12,36 @@ */ package com.ning.http.util; +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProvider; import com.ning.http.client.ByteArrayPart; import com.ning.http.client.Cookie; import com.ning.http.client.FilePart; -import com.ning.http.client.FluentStringsMap; +import com.ning.http.client.FluentCaseInsensitiveStringsMap; import com.ning.http.client.HttpResponseBodyPart; +import com.ning.http.client.HttpResponseBodyPartsInputStream; import com.ning.http.client.Part; import com.ning.http.client.StringPart; import com.ning.http.multipart.ByteArrayPartSource; import com.ning.http.multipart.MultipartRequestEntity; import com.ning.http.multipart.PartSource; +import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.List; -import java.util.Locale; +import static com.ning.http.util.MiscUtil.isNonEmpty; /** * {@link com.ning.http.client.AsyncHttpProvider} common utilities. @@ -52,14 +60,13 @@ protected SimpleDateFormat[] initialValue() { return new SimpleDateFormat[] { - new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US), //ASCTIME + new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), // RFC1123 new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), //RFC1036 + new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US), //ASCTIME new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US), new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US), new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US), - new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss Z", Locale.US), - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) // RFC1123 - + new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss Z", Locale.US) }; } }; @@ -134,23 +141,29 @@ public final static SimpleDateFormat[] get() { static final String VERSION = "Version"; - public final static URI createUri(String u) { - URI uri = URI.create(u); + static final byte[] EMPTY_BYTE_ARRAY = "".getBytes(); + + public static final void validateSupportedScheme(URI uri) { final String scheme = uri.getScheme(); if (scheme == null || !scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https") && !scheme.equalsIgnoreCase("ws") && !scheme.equalsIgnoreCase("wss")) { - throw new IllegalArgumentException("The URI scheme, of the URI " + u + throw new IllegalArgumentException("The URI scheme, of the URI " + uri + ", must be equal (ignoring case) to 'http', 'https', 'ws', or 'wss'"); } + } + + public final static URI createUri(String u) { + URI uri = URI.create(u); + validateSupportedScheme(uri); String path = uri.getPath(); if (path == null) { throw new IllegalArgumentException("The URI path, of the URI " + uri + ", must be non-null"); - } else if (path.length() > 0 && path.charAt(0) != '/') { + } else if (isNonEmpty(path) && path.charAt(0) != '/') { throw new IllegalArgumentException("The URI path, of the URI " + uri + ". must start with a '/'"); - } else if (path.length() == 0) { + } else if (!isNonEmpty(path)) { return URI.create(u + "/"); } @@ -181,13 +194,13 @@ public final static String getAuthority(URI uri) { return url; } - public final static String contentToString(Collection bodyParts, String charset) throws UnsupportedEncodingException { + public final static String contentToString(List bodyParts, String charset) throws UnsupportedEncodingException { return new String(contentToByte(bodyParts), charset); } - public final static byte[] contentToByte(Collection bodyParts) throws UnsupportedEncodingException { + public final static byte[] contentToByte(List bodyParts) throws UnsupportedEncodingException { if (bodyParts.size() == 1) { - return bodyParts.iterator().next().getBodyPartBytes(); + return bodyParts.get(0).getBodyPartBytes(); } else { int size = 0; @@ -206,6 +219,10 @@ public final static byte[] contentToByte(Collection bodyPa } } + public final static InputStream contentToInputStream(List bodyParts) throws UnsupportedEncodingException { + return bodyParts.isEmpty()? new ByteArrayInputStream(EMPTY_BYTE_ARRAY) : new HttpResponseBodyPartsInputStream(bodyParts); + } + public final static String getHost(URI uri) { String host = uri.getHost(); if (host == null) { @@ -215,22 +232,51 @@ public final static String getHost(URI uri) { } public final static URI getRedirectUri(URI uri, String location) { - if(location == null) - throw new IllegalArgumentException("URI " + uri + " was redirected to null location"); - URI newUri = uri.resolve(location); - - String scheme = newUri.getScheme(); - - if (scheme == null || !scheme.equalsIgnoreCase("http") - && !scheme.equalsIgnoreCase("https") - && !scheme.equals("ws") - && !scheme.equals("wss")) { - throw new IllegalArgumentException("The URI scheme, of the URI " + newUri - + ", must be equal (ignoring case) to 'ws, 'wss', 'http', or 'https'"); - } + if(location == null) + throw new IllegalArgumentException("URI " + uri + " was redirected to null location"); + + URI locationURI = null; + try { + locationURI = new URI(location); + } catch (URISyntaxException e) { + // rich, we have a badly encoded location, let's try to encode the query params + String[] parts = location.split("\\?"); + if (parts.length != 2) { + throw new IllegalArgumentException("Don't know how to turn this location into a proper URI:" + location, e); + } else { + StringBuilder properUrl = new StringBuilder(location.length()).append(parts[0]).append("?"); + + String[] queryParams = parts[1].split("&"); + for (int i = 0; i < queryParams.length; i++) { + String queryParam = queryParams[i]; + if (i != 0) + properUrl.append("&"); + String[] nameValue = queryParam.split("=", 2); + UTF8UrlEncoder.appendEncoded(properUrl, nameValue[0]); + if (nameValue.length == 2) { + properUrl.append("="); + UTF8UrlEncoder.appendEncoded(properUrl, nameValue[1]); + } + } + + locationURI = URI.create(properUrl.toString()); + } + } + + URI redirectUri = uri.resolve(locationURI); - return newUri; - } + String scheme = redirectUri.getScheme(); + + if (scheme == null || !scheme.equalsIgnoreCase("http") + && !scheme.equalsIgnoreCase("https") + && !scheme.equals("ws") + && !scheme.equals("wss")) { + throw new IllegalArgumentException("The URI scheme, of the URI " + redirectUri + + ", must be equal (ignoring case) to 'ws, 'wss', 'http', or 'https'"); + } + + return redirectUri.normalize(); + } public final static int getPort(URI uri) { int port = uri.getPort(); @@ -243,11 +289,11 @@ public final static int getPort(URI uri) { * This is quite ugly as our internal names are duplicated, but we build on top of HTTP Client implementation. * * @param params - * @param methodParams + * @param requestHeaders * @return a MultipartRequestEntity. * @throws java.io.FileNotFoundException */ - public final static MultipartRequestEntity createMultipartRequestEntity(List params, FluentStringsMap methodParams) throws FileNotFoundException { + public final static MultipartRequestEntity createMultipartRequestEntity(List params, FluentCaseInsensitiveStringsMap requestHeaders) throws FileNotFoundException { com.ning.http.multipart.Part[] parts = new com.ning.http.multipart.Part[params.size()]; int i = 0; @@ -279,7 +325,7 @@ public final static MultipartRequestEntity createMultipartRequestEntity(List httpProvider) { - StringBuffer b = new StringBuffer("AsyncHttpClient/1.0") + StringBuilder b = new StringBuilder("AsyncHttpClient/1.0") .append(" ") .append("(") .append(httpProvider.getSimpleName()) @@ -446,83 +492,47 @@ public static String parseCharset(String contentType) { return null; } + @Deprecated public static Cookie parseCookie(String value) { - String[] fields = value.split(";\\s*"); - String[] cookie = fields[0].split("="); - String cookieName = cookie[0]; - String cookieValue = (cookie.length == 1) ? null : cookie[1]; - - int maxAge = -1; - String path = null; - String domain = null; - boolean secure = false; - - boolean maxAgeSet = false; - boolean expiresSet = false; - - for (int j = 1; j < fields.length; j++) { - if ("secure".equalsIgnoreCase(fields[j])) { - secure = true; - } else if (fields[j].indexOf('=') > 0) { - String[] f = fields[j].split("="); - if (f.length == 1) continue; // Add protection against null field values - - // favor 'max-age' field over 'expires' - if (!maxAgeSet && "max-age".equalsIgnoreCase(f[0])) { - try { - maxAge = Math.max(Integer.valueOf(removeQuote(f[1])), 0); - } catch (NumberFormatException e1) { - // ignore failure to parse -> treat as session cookie - // invalidate a previously parsed expires-field - maxAge = -1; - } - maxAgeSet = true; - } else if (!maxAgeSet && !expiresSet && "expires".equalsIgnoreCase(f[0])) { - try { - maxAge = Math.max(convertExpireField(f[1]), 0); - } catch (Exception e) { - // original behavior, is this correct at all (expires field with max-age semantics)? - try { - maxAge = Math.max(Integer.valueOf(f[1]), 0); - } catch (NumberFormatException e1) { - // ignore failure to parse -> treat as session cookie - } - } - expiresSet = true; - } else if ("domain".equalsIgnoreCase(f[0])) { - domain = f[1]; - } else if ("path".equalsIgnoreCase(f[0])) { - path = f[1]; - } - } - } - - return new Cookie(domain, cookieName, cookieValue, path, maxAge, secure); + return CookieDecoder.decode(value).iterator().next(); } - private static int convertExpireField(String timestring) throws Exception { - Exception exception = null; + public static int convertExpireField(String timestring) { + String trimmedTimeString = removeQuotes(timestring.trim()); + long now = System.currentTimeMillis(); + Date date = null; + for (SimpleDateFormat sdf : simpleDateFormat.get()) { - try { - long expire = sdf.parse(removeQuote(timestring.trim())).getTime(); - return (int) ((expire - System.currentTimeMillis()) / 1000); - } catch (ParseException e) { - exception = e; - } catch (NumberFormatException e) { - exception = e; - } + date = sdf.parse(trimmedTimeString, new ParsePosition(0)); + if (date != null) + break; } - throw exception; + if (date != null) { + long maxAgeMillis = date.getTime() - now; + return (int) (maxAgeMillis / 1000) + (maxAgeMillis % 1000 != 0? 1 : 0); + } else + throw new IllegalArgumentException("Not a valid expire field " + trimmedTimeString); } - private final static String removeQuote(String s) { - if (s.startsWith("\"")) { - s = s.substring(1); - } + public final static String removeQuotes(String s) { + if (MiscUtil.isNonEmpty(s)) { + int start = 0; + int end = s.length(); + boolean changed = false; + + if (s.charAt(0) == '"') { + changed = true; + start++; + } - if (s.endsWith("\"")) { - s = s.substring(0, s.length() - 1); + if (s.charAt(s.length() - 1) == '"') { + changed = true; + end--; + } + + if (changed) + s = s.substring(start, end); } return s; } @@ -536,4 +546,8 @@ public static void checkBodyParts(int statusCode, Collection> 6))); appendSingleByteEncoded(sb, (0x80 | (value & 0x3f))); - } else { + } else if (value < 0x10000) { appendSingleByteEncoded(sb, (0xe0 | (value >> 12))); appendSingleByteEncoded(sb, (0x80 | ((value >> 6) & 0x3f))); appendSingleByteEncoded(sb, (0x80 | (value & 0x3f))); + } else { + appendSingleByteEncoded(sb, (0xf0 | (value >> 18))); + appendSingleByteEncoded(sb, (0x80 | (value >> 12) & 0x3f)); + appendSingleByteEncoded(sb, (0x80 | (value >> 6) & 0x3f)); + appendSingleByteEncoded(sb, (0x80 | (value & 0x3f))); } } diff --git a/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieDecoder.java b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieDecoder.java new file mode 100644 index 0000000000..4e4eecb1b8 --- /dev/null +++ b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieDecoder.java @@ -0,0 +1,321 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.org.jboss.netty.handler.codec.http; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.HashSet; + +import com.ning.org.jboss.netty.util.internal.StringUtil; +import com.ning.http.client.Cookie; +import com.ning.http.util.AsyncHttpProviderUtils; + +/** + * Decodes an HTTP header value into {@link Cookie}s. This decoder can decode the HTTP cookie version 0, 1, and 2. + * + *

+ * {@link HttpRequest} req = ...;
+ * String value = req.getHeader("Cookie");
+ * Set<{@link Cookie}> cookies = new {@link CookieDecoder}().decode(value);
+ * 
+ * + * @see CookieEncoder + * + * @apiviz.stereotype utility + * @apiviz.has org.jboss.netty.handler.codec.http.Cookie oneway - - decodes + */ +public class CookieDecoder { + + private static final char COMMA = ','; + + /** + * Creates a new decoder. + */ + private CookieDecoder() { + } + + /** + * Decodes the specified HTTP header value into {@link Cookie}s. + * + * @return the decoded {@link Cookie}s + */ + public static Set decode(String header) { + List names = new ArrayList(8); + List values = new ArrayList(8); + List rawValues = new ArrayList(8); + extractKeyValuePairs(header, names, values, rawValues); + + if (names.isEmpty()) { + return Collections.emptySet(); + } + + int i; + int version = 0; + + // $Version is the only attribute that can appear before the actual + // cookie name-value pair. + if (names.get(0).equalsIgnoreCase(CookieHeaderNames.VERSION)) { + try { + version = Integer.parseInt(values.get(0)); + } catch (NumberFormatException e) { + // Ignore. + } + i = 1; + } else { + i = 0; + } + + if (names.size() <= i) { + // There's a version attribute, but nothing more. + return Collections.emptySet(); + } + + Set cookies = new HashSet(); + for (; i < names.size(); i++) { + String name = names.get(i); + String value = values.get(i); + String rawValue = rawValues.get(i); + if (value == null) { + value = ""; + } + if (rawValue == null) { + rawValue = ""; + } + + String cookieName = name; + String cookieValue = value; + String cookieRawValue = rawValue; + boolean discard = false; + boolean secure = false; + boolean httpOnly = false; + String comment = null; + String commentURL = null; + String domain = null; + String path = null; + int maxAge = -1; + List ports = Collections.emptyList(); + + for (int j = i + 1; j < names.size(); j++, i++) { + name = names.get(j); + value = values.get(j); + + if (CookieHeaderNames.DISCARD.equalsIgnoreCase(name)) { + discard = true; + } else if (CookieHeaderNames.SECURE.equalsIgnoreCase(name)) { + secure = true; + } else if (CookieHeaderNames.HTTPONLY.equalsIgnoreCase(name)) { + httpOnly = true; + } else if (CookieHeaderNames.COMMENT.equalsIgnoreCase(name)) { + comment = value; + } else if (CookieHeaderNames.COMMENTURL.equalsIgnoreCase(name)) { + commentURL = value; + } else if (CookieHeaderNames.DOMAIN.equalsIgnoreCase(name)) { + domain = value; + } else if (CookieHeaderNames.PATH.equalsIgnoreCase(name)) { + path = value; + } else if (CookieHeaderNames.EXPIRES.equalsIgnoreCase(name)) { + try { + maxAge = AsyncHttpProviderUtils.convertExpireField(value); + } catch (Exception e) { + // original behavior, is this correct at all (expires field with max-age semantics)? + try { + maxAge = Math.max(Integer.valueOf(value), 0); + } catch (NumberFormatException e1) { + // ignore failure to parse -> treat as session cookie + } + } + } else if (CookieHeaderNames.MAX_AGE.equalsIgnoreCase(name)) { + maxAge = Integer.parseInt(value); + } else if (CookieHeaderNames.VERSION.equalsIgnoreCase(name)) { + version = Integer.parseInt(value); + } else if (CookieHeaderNames.PORT.equalsIgnoreCase(name)) { + String[] portList = StringUtil.split(value, COMMA); + ports = new ArrayList(2); + for (String s1 : portList) { + try { + ports.add(Integer.valueOf(s1)); + } catch (NumberFormatException e) { + // Ignore. + } + } + } else { + break; + } + } + + Cookie c = new Cookie(domain, cookieName, cookieValue, cookieRawValue, path, maxAge, secure, version, httpOnly, discard, comment, commentURL, ports); + cookies.add(c); + } + + return cookies; + } + + private static void extractKeyValuePairs(final String header, final List names, final List values, final List rawValues) { + + final int headerLen = header.length(); + loop: for (int i = 0;;) { + + // Skip spaces and separators. + for (;;) { + if (i == headerLen) { + break loop; + } + switch (header.charAt(i)) { + case '\t': + case '\n': + case 0x0b: + case '\f': + case '\r': + case ' ': + case ',': + case ';': + i++; + continue; + } + break; + } + + // Skip '$'. + for (;;) { + if (i == headerLen) { + break loop; + } + if (header.charAt(i) == '$') { + i++; + continue; + } + break; + } + + String name; + String value; + String rawValue; + + if (i == headerLen) { + name = null; + value = null; + rawValue = null; + } else { + int newNameStart = i; + keyValLoop: for (;;) { + switch (header.charAt(i)) { + case ';': + // NAME; (no value till ';') + name = header.substring(newNameStart, i); + value = null; + rawValue = null; + break keyValLoop; + case '=': + // NAME=VALUE + name = header.substring(newNameStart, i); + i++; + if (i == headerLen) { + // NAME= (empty value, i.e. nothing after '=') + value = ""; + rawValue = ""; + break keyValLoop; + } + + int newValueStart = i; + char c = header.charAt(i); + if (c == '"' || c == '\'') { + // NAME="VALUE" or NAME='VALUE' + StringBuilder newValueBuf = new StringBuilder(header.length() - i); + StringBuilder newRawValueBuf = new StringBuilder(header.length() - i); + newRawValueBuf.append(c); + final char q = c; + boolean hadBackslash = false; + i++; + for (;;) { + if (i == headerLen) { + value = newValueBuf.toString(); + rawValue = newRawValueBuf.toString(); + break keyValLoop; + } + if (hadBackslash) { + hadBackslash = false; + c = header.charAt(i++); + newRawValueBuf.append(c); + switch (c) { + case '\\': + case '"': + case '\'': + // Escape last backslash. + newValueBuf.setCharAt(newValueBuf.length() - 1, c); + break; + default: + // Do not escape last backslash. + newValueBuf.append(c); + } + } else { + c = header.charAt(i++); + newRawValueBuf.append(c); + if (c == q) { + value = newValueBuf.toString(); + rawValue = newRawValueBuf.toString(); + break keyValLoop; + } + newValueBuf.append(c); + if (c == '\\') { + hadBackslash = true; + } + } + } + } else { + // NAME=VALUE; + int semiPos = header.indexOf(';', i); + if (semiPos > 0) { + value = rawValue = header.substring(newValueStart, semiPos); + i = semiPos; + } else { + value = rawValue = header.substring(newValueStart); + i = headerLen; + } + } + break keyValLoop; + default: + i++; + } + + if (i == headerLen) { + // NAME (no value till the end of string) + name = header.substring(newNameStart); + value = rawValue = null; + break; + } + } + } + + names.add(name); + values.add(value); + rawValues.add(rawValue); + } + } +} diff --git a/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieEncoder.java b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieEncoder.java new file mode 100644 index 0000000000..a2297f9029 --- /dev/null +++ b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieEncoder.java @@ -0,0 +1,170 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.org.jboss.netty.handler.codec.http; + +import java.util.Collection; + +import com.ning.http.client.Cookie; + +/** + * Encodes {@link Cookie}s into an HTTP header value. This encoder can encode + * the HTTP cookie version 0, 1, and 2. + *

+ * This encoder is stateful. It maintains an internal data structure that + * holds the {@link Cookie}s added by the {@link #addCookie(String, String)} + * method. Once {@link #encode()} is called, all added {@link Cookie}s are + * encoded into an HTTP header value and all {@link Cookie}s in the internal + * data structure are removed so that the encoder can start over. + *

+ * // Client-side example
+ * {@link HttpRequest} req = ...;
+ * {@link CookieEncoder} encoder = new {@link CookieEncoder}(false);
+ * encoder.addCookie("JSESSIONID", "1234");
+ * res.setHeader("Cookie", encoder.encode());
+ *
+ * // Server-side example
+ * {@link HttpResponse} res = ...;
+ * {@link CookieEncoder} encoder = new {@link CookieEncoder}(true);
+ * encoder.addCookie("JSESSIONID", "1234");
+ * res.setHeader("Set-Cookie", encoder.encode());
+ * 
+ * + * @see CookieDecoder + * + * @apiviz.stereotype utility + * @apiviz.has org.jboss.netty.handler.codec.http.Cookie oneway - - encodes + */ +// This fork brings support for RFC6265, that's used if the Cookie has a raw value +public final class CookieEncoder { + + private CookieEncoder() { + } + + public static String encodeClientSide(Collection cookies, boolean useRFC6265Style) { + StringBuilder sb = new StringBuilder(); + + for (Cookie cookie: cookies) { + if (useRFC6265Style) + encodeRFC6265Style(sb, cookie); + else + encodeRFC2965Style(sb, cookie); + } + + if (sb.length() > 0) { + sb.setLength(sb.length() - 2); + } + return sb.toString(); + } + + private static void encodeRFC6265Style(StringBuilder sb, Cookie cookie) { + addUnquoted(sb, cookie.getName(), cookie.getRawValue()); + } + + private static void encodeRFC2965Style(StringBuilder sb, Cookie cookie) { + if (cookie.getVersion() >= 1) { + add(sb, '$' + CookieHeaderNames.VERSION, 1); + } + + add(sb, cookie.getName(), cookie.getValue()); + + if (cookie.getPath() != null) { + add(sb, '$' + CookieHeaderNames.PATH, cookie.getPath()); + } + + if (cookie.getDomain() != null) { + add(sb, '$' + CookieHeaderNames.DOMAIN, cookie.getDomain()); + } + + if (cookie.getVersion() >= 1) { + if (!cookie.getPorts().isEmpty()) { + sb.append('$'); + sb.append(CookieHeaderNames.PORT); + sb.append((char) HttpConstants.EQUALS); + sb.append((char) HttpConstants.DOUBLE_QUOTE); + for (int port: cookie.getPorts()) { + sb.append(port); + sb.append((char) HttpConstants.COMMA); + } + sb.setCharAt(sb.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); + sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); + } + } + } + + private static void add(StringBuilder sb, String name, String val) { + if (val == null) { + addQuoted(sb, name, ""); + return; + } + + for (int i = 0; i < val.length(); i ++) { + char c = val.charAt(i); + switch (c) { + case '\t': case ' ': case '"': case '(': case ')': case ',': + case '/': case ':': case ';': case '<': case '=': case '>': + case '?': case '@': case '[': case '\\': case ']': + case '{': case '}': + addQuoted(sb, name, val); + return; + } + } + + addUnquoted(sb, name, val); + } + + private static void addUnquoted(StringBuilder sb, String name, String val) { + sb.append(name); + sb.append((char) HttpConstants.EQUALS); + sb.append(val); + sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); + } + + private static void addQuoted(StringBuilder sb, String name, String val) { + if (val == null) { + val = ""; + } + + sb.append(name); + sb.append((char) HttpConstants.EQUALS); + sb.append((char) HttpConstants.DOUBLE_QUOTE); + sb.append(val.replace("\\", "\\\\").replace("\"", "\\\"")); + sb.append((char) HttpConstants.DOUBLE_QUOTE); + sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); + } + + private static void add(StringBuilder sb, String name, int val) { + sb.append(name); + sb.append((char) HttpConstants.EQUALS); + sb.append(val); + sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); + } +} diff --git a/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieHeaderNames.java b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieHeaderNames.java new file mode 100644 index 0000000000..5d3e6c9249 --- /dev/null +++ b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/CookieHeaderNames.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.org.jboss.netty.handler.codec.http; + +final class CookieHeaderNames { + static final String PATH = "Path"; + + static final String EXPIRES = "Expires"; + + static final String MAX_AGE = "Max-Age"; + + static final String DOMAIN = "Domain"; + + static final String SECURE = "Secure"; + + static final String HTTPONLY = "HTTPOnly"; + + static final String COMMENT = "Comment"; + + static final String COMMENTURL = "CommentURL"; + + static final String DISCARD = "Discard"; + + static final String PORT = "Port"; + + static final String VERSION = "Version"; + + private CookieHeaderNames() { + // Unused. + } +} + diff --git a/src/main/java/com/ning/org/jboss/netty/handler/codec/http/HttpConstants.java b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/HttpConstants.java new file mode 100644 index 0000000000..1880dc4fed --- /dev/null +++ b/src/main/java/com/ning/org/jboss/netty/handler/codec/http/HttpConstants.java @@ -0,0 +1,60 @@ +package com.ning.org.jboss.netty.handler.codec.http; + +import java.nio.charset.Charset; + +public final class HttpConstants { + + /** + * Horizontal space + */ + public static final byte SP = 32; + + /** + * Horizontal tab + */ + public static final byte HT = 9; + + /** + * Carriage return + */ + public static final byte CR = 13; + + /** + * Equals '=' + */ + public static final byte EQUALS = 61; + + /** + * Line feed character + */ + public static final byte LF = 10; + + /** + * Colon ':' + */ + public static final byte COLON = 58; + + /** + * Semicolon ';' + */ + public static final byte SEMICOLON = 59; + + /** + * Comma ',' + */ + public static final byte COMMA = 44; + + /** + * Double quote '"' + */ + public static final byte DOUBLE_QUOTE = '"'; + + /** + * Default character set (UTF-8) + */ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private HttpConstants() { + // Unused + } +} diff --git a/src/main/java/com/ning/org/jboss/netty/util/internal/StringUtil.java b/src/main/java/com/ning/org/jboss/netty/util/internal/StringUtil.java new file mode 100644 index 0000000000..b7e55976eb --- /dev/null +++ b/src/main/java/com/ning/org/jboss/netty/util/internal/StringUtil.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.ning.org.jboss.netty.util.internal; + +import java.util.ArrayList; +import java.util.List; + +/** + * String utility class. + */ +public final class StringUtil { + + private StringUtil() { + // Unused. + } + + private static final String EMPTY_STRING = ""; + + /** + * Splits the specified {@link String} with the specified delimiter. This operation is a simplified and optimized + * version of {@link String#split(String)}. + */ + public static String[] split(String value, char delim) { + final int end = value.length(); + final List res = new ArrayList(); + + int start = 0; + for (int i = 0; i < end; i ++) { + if (value.charAt(i) == delim) { + if (start == i) { + res.add(EMPTY_STRING); + } else { + res.add(value.substring(start, i)); + } + start = i + 1; + } + } + + if (start == 0) { // If no delimiter was found in the value + res.add(value); + } else { + if (start != end) { + // Add the last element if it's not empty. + res.add(value.substring(start, end)); + } else { + // Truncate trailing empty elements. + for (int i = res.size() - 1; i >= 0; i --) { + if (res.get(i).length() == 0) { + res.remove(i); + } else { + break; + } + } + } + } + + return res.toArray(new String[res.size()]); + } +} + diff --git a/src/site/apt/proxy.apt b/src/site/apt/proxy.apt index d62a0ebb48..5424eefd75 100644 --- a/src/site/apt/proxy.apt +++ b/src/site/apt/proxy.apt @@ -61,3 +61,26 @@ Response r = responseFuture.get(); You can also set the <<>> at the <<>> level. In that case, all request will share the same proxy information. + +Using Java System Properties + + The AsyncHttpClient library supports the standard + {{{http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html#Proxies}Java Proxy System Properties}}. + You can configure this at a global level using the <<>> method on the + <<>>, or by setting the <<>> + system property to true. + +Using JDK ProxySelectors + + The AsyncHttpClient library also supports using the default + {{{http://docs.oracle.com/javase/7/docs/api/java/net/ProxySelector.html}JDK ProxySelector}}. This allows for more + fine grained control over which proxies to use, for example, it can be used in combination with + {{{https://code.google.com/p/proxy-vole/}Proxy Vole}} to use OS configured proxies or to use a proxy.pac file. + + You configure this at a global level using the <<>> method on the + <<>>, or by setting the + <<>> system property to true. + + If you don't change the default JDK <<>>, this setting is very similar to the <<>> + setting, though the <<>> setting does allow more flexibility, such as the ability to use an + HTTPS proxy. diff --git a/src/site/apt/ssl.apt b/src/site/apt/ssl.apt index 9c7e5db94e..aeb3e958a9 100644 --- a/src/site/apt/ssl.apt +++ b/src/site/apt/ssl.apt @@ -35,6 +35,6 @@ SecureRandom secureRandom = new SecureRandom(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, trustManagers, secureRandom); Builder builder = new AsyncHttpClientConfig.Builder(); -builder.setSSLContext(myOwnThreadPool); +builder.setSSLContext(sslContext); AsyncHttpClient client = new AsyncHttpClient(builder.build()); +-----+ diff --git a/src/test/java/com/ning/http/client/async/AsyncProvidersBasicTest.java b/src/test/java/com/ning/http/client/async/AsyncProvidersBasicTest.java index 41421e46ef..b17e263423 100755 --- a/src/test/java/com/ning/http/client/async/AsyncProvidersBasicTest.java +++ b/src/test/java/com/ning/http/client/async/AsyncProvidersBasicTest.java @@ -15,24 +15,12 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncCompletionHandler; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.AsyncHttpClientConfig.Builder; -import com.ning.http.client.AsyncHttpClientConfigBean; -import com.ning.http.client.AsyncHttpProviderConfig; -import com.ning.http.client.Cookie; -import com.ning.http.client.FluentCaseInsensitiveStringsMap; -import com.ning.http.client.MaxRedirectException; -import com.ning.http.client.Part; -import com.ning.http.client.ProxyServer; -import com.ning.http.client.Request; -import com.ning.http.client.RequestBuilder; -import com.ning.http.client.Response; -import com.ning.http.client.StringPart; -import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig; -import org.testng.Assert; -import org.testng.annotations.Test; +import static com.ning.http.util.DateUtil.millisTime; +import static com.ning.http.util.MiscUtil.isNonEmpty; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -56,965 +44,1044 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import org.testng.Assert; +import org.testng.annotations.Test; +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.AsyncHttpClientConfig.Builder; +import com.ning.http.client.AsyncHttpClientConfigBean; +import com.ning.http.client.AsyncHttpProviderConfig; +import com.ning.http.client.Cookie; +import com.ning.http.client.FluentCaseInsensitiveStringsMap; +import com.ning.http.client.MaxRedirectException; +import com.ning.http.client.Part; +import com.ning.http.client.ProxyServer; +import com.ning.http.client.Request; +import com.ning.http.client.RequestBuilder; +import com.ning.http.client.Response; +import com.ning.http.client.StringPart; public abstract class AsyncProvidersBasicTest extends AbstractBasicTest { private static final String UTF_8 = "text/html;charset=UTF-8"; - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncProviderEncodingTest() throws Throwable { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - Request request = new RequestBuilder("GET").setUrl("http://foo.com/foo.html?q=+%20x").build(); - String requestUrl = request.getUrl(); - Assert.assertEquals(requestUrl, "http://foo.com/foo.html?q=%20%20x"); - Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { - @Override - public String onCompleted(Response response) throws Exception { - return response.getUri().toString(); - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Request request = new RequestBuilder("GET").setUrl(getTargetUrl() + "?q=+%20x").build(); + String requestUrl = request.getUrl(); + Assert.assertEquals(requestUrl, getTargetUrl() + "?q=%20%20x"); + Future responseFuture = client.executeRequest(request, new AsyncCompletionHandler() { + @Override + public String onCompleted(Response response) throws Exception { + return response.getUri().toString(); + } - /* @Override */ - public void onThrowable(Throwable t) { - t.printStackTrace(); - Assert.fail("Unexpected exception: " + t.getMessage(), t); - } + /* @Override */ + public void onThrowable(Throwable t) { + t.printStackTrace(); + Assert.fail("Unexpected exception: " + t.getMessage(), t); + } - }); - String url = responseFuture.get(); - Assert.assertEquals(url, "http://foo.com/foo.html?q=%20%20x"); - p.close(); + }); + String url = responseFuture.get(); + Assert.assertEquals(url, getTargetUrl() + "?q=%20%20x"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncProviderEncodingTest2() throws Throwable { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - Request request = new RequestBuilder("GET").setUrl("http://foo.com/foo.html") - .addQueryParameter("q", "a b") - .build(); - - Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { - @Override - public String onCompleted(Response response) throws Exception { - return response.getUri().toString(); - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Request request = new RequestBuilder("GET").setUrl(getTargetUrl() + "").addQueryParameter("q", "a b").build(); - /* @Override */ - public void onThrowable(Throwable t) { - t.printStackTrace(); - Assert.fail("Unexpected exception: " + t.getMessage(), t); - } + Future responseFuture = client.executeRequest(request, new AsyncCompletionHandler() { + @Override + public String onCompleted(Response response) throws Exception { + return response.getUri().toString(); + } + + /* @Override */ + public void onThrowable(Throwable t) { + t.printStackTrace(); + Assert.fail("Unexpected exception: " + t.getMessage(), t); + } - }); - String url = responseFuture.get(); - Assert.assertEquals(url, "http://foo.com/foo.html?q=a%20b"); - p.close(); + }); + String url = responseFuture.get(); + Assert.assertEquals(url, getTargetUrl() + "?q=a%20b"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void emptyRequestURI() throws Throwable { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - Request request = new RequestBuilder("GET").setUrl("http://foo.com/") - .build(); - - Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { - @Override - public String onCompleted(Response response) throws Exception { - return response.getUri().toString(); - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); - /* @Override */ - public void onThrowable(Throwable t) { - t.printStackTrace(); - Assert.fail("Unexpected exception: " + t.getMessage(), t); - } + Future responseFuture = client.executeRequest(request, new AsyncCompletionHandler() { + @Override + public String onCompleted(Response response) throws Exception { + return response.getUri().toString(); + } - }); - String url = responseFuture.get(); - Assert.assertEquals(url, "http://foo.com/"); - p.close(); + /* @Override */ + public void onThrowable(Throwable t) { + t.printStackTrace(); + Assert.fail("Unexpected exception: " + t.getMessage(), t); + } + + }); + String url = responseFuture.get(); + Assert.assertEquals(url, getTargetUrl()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncProviderContentLenghtGETTest() throws Throwable { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch l = new CountDownLatch(1); - URL url = new URL(getTargetUrl()); - final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.connect(); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + URL url = new URL(getTargetUrl()); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.connect(); - Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); - p.executeRequest(request, new AsyncCompletionHandlerAdapter() { + Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); + client.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - int contentLenght = -1; - if (response.getHeader("content-length") != null) { - contentLenght = Integer.valueOf(response.getHeader("content-length")); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + int contentLenght = -1; + if (response.getHeader("content-length") != null) { + contentLenght = Integer.valueOf(response.getHeader("content-length")); + } + int ct = connection.getContentLength(); + assertEquals(contentLenght, ct); + } finally { + l.countDown(); } - int ct = connection.getContentLength(); - assertEquals(contentLenght, ct); - } finally { - l.countDown(); + return response; } - return response; - } - @Override - public void onThrowable(Throwable t) { - try { - Assert.fail("Unexpected exception", t); - } finally { - l.countDown(); + @Override + public void onThrowable(Throwable t) { + try { + Assert.fail("Unexpected exception", t); + } finally { + l.countDown(); + } } - } - - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - - p.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncContentTypeGETTest() throws Throwable { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - - final CountDownLatch l = new CountDownLatch(1); - Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); - p.executeRequest(request, new AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); + client.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getContentType(), UTF_8); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getContentType(), UTF_8); + } finally { + l.countDown(); + } + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - p.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncHeaderGETTest() throws Throwable { - AsyncHttpClient n = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch l = new CountDownLatch(1); - Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); - n.executeRequest(request, new AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).build(); + client.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getContentType(), UTF_8); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getContentType(), UTF_8); + } finally { + l.countDown(); + } + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - n.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncHeaderPOSTTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient n = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Test1", "Test1"); - h.add("Test2", "Test2"); - h.add("Test3", "Test3"); - h.add("Test4", "Test4"); - h.add("Test5", "Test5"); - Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).setHeaders(h).build(); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Test1", "Test1"); + h.add("Test2", "Test2"); + h.add("Test3", "Test3"); + h.add("Test4", "Test4"); + h.add("Test5", "Test5"); + Request request = new RequestBuilder("GET").setUrl(getTargetUrl()).setHeaders(h).build(); - n.executeRequest(request, new AsyncCompletionHandlerAdapter() { + client.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - System.out.println(">>>>> " + response.getStatusText()); - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - assertEquals(response.getHeader("X-Test" + i), "Test" + i); + @Override + public Response onCompleted(Response response) throws Exception { + try { + System.out.println(">>>>> " + response.getStatusText()); + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + assertEquals(response.getHeader("X-Test" + i), "Test" + i); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - n.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncParamPOSTTest() throws Throwable { - AsyncHttpClient n = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); - Map> m = new HashMap>(); - for (int i = 0; i < 5; i++) { - m.put("param_" + i, Arrays.asList("value_" + i)); - } - Request request = new RequestBuilder("POST").setUrl(getTargetUrl()).setHeaders(h).setParameters(m).build(); - n.executeRequest(request, new AsyncCompletionHandlerAdapter() { + Map> m = new HashMap>(); + for (int i = 0; i < 5; i++) { + m.put("param_" + i, Arrays.asList("value_" + i)); + } + Request request = new RequestBuilder("POST").setUrl(getTargetUrl()).setHeaders(h).setParameters(m).build(); + client.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - System.out.println(">>>>> " + response.getHeader("X-param_" + i)); - assertEquals(response.getHeader("X-param_" + i), "value_" + i); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + System.out.println(">>>>> " + response.getHeader("X-param_" + i)); + assertEquals(response.getHeader("X-param_" + i), "value_" + i); + } + + } finally { + l.countDown(); } - - } finally { - l.countDown(); + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - n.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncStatusHEADTest() throws Throwable { - AsyncHttpClient n = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - - final CountDownLatch l = new CountDownLatch(1); - Request request = new RequestBuilder("HEAD").setUrl(getTargetUrl()).build(); - Response response = n.executeRequest(request, new AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + Request request = new RequestBuilder("HEAD").setUrl(getTargetUrl()).build(); + Response response = client.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + } finally { + l.countDown(); + } + return response; } - return response; - } - }).get(); + }).get(); - try { - String s = response.getResponseBody(); - Assert.assertEquals("",s); - } catch (IllegalStateException ex) { - fail(); - } + try { + String s = response.getResponseBody(); + Assert.assertEquals("", s); + } catch (IllegalStateException ex) { + fail(); + } - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - n.close(); - } // TODO: fix test - @Test(groups = {"standalone", "default_provider", "async"}, enabled = false) + @Test(groups = { "standalone", "default_provider", "async" }, enabled = false) public void asyncStatusHEADContentLenghtTest() throws Throwable { - AsyncHttpClient n = getAsyncHttpClient(new AsyncHttpClientConfig.Builder() - .setRequestTimeoutInMs(120 * 1000).build()); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(120 * 1000).build()); + try { + final CountDownLatch l = new CountDownLatch(1); + Request request = new RequestBuilder("HEAD").setUrl(getTargetUrl()).build(); - final CountDownLatch l = new CountDownLatch(1); - Request request = new RequestBuilder("HEAD") - .setUrl(getTargetUrl()) - .build(); + client.executeRequest(request, new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + Assert.fail(); + return response; + } - n.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - Assert.fail(); - return response; - } + @Override + public void onThrowable(Throwable t) { + try { + assertEquals(t.getClass(), IOException.class); + assertEquals(t.getMessage(), "No response received. Connection timed out"); + } finally { + l.countDown(); + } - @Override - public void onThrowable(Throwable t) { - try { - assertEquals(t.getClass(), IOException.class); - assertEquals(t.getMessage(), "No response received. Connection timed out"); - } finally { - l.countDown(); } + }).get(); + if (!l.await(10 * 5 * 1000, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - - if (!l.await(10 * 5 * 1000, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - n.close(); - } - @Test(groups = {"online", "default_provider", "async"}) + @Test(groups = { "online", "default_provider", "async" }) public void asyncNullSchemeTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - + AsyncHttpClient client = getAsyncHttpClient(null); try { - c.prepareGet("www.sun.com").execute(); + client.prepareGet("www.sun.com").execute(); Assert.fail(); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } - c.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoGetTransferEncodingTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch l = new CountDownLatch(1); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); - c.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { + client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getHeader("Transfer-Encoding"), "chunked"); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getHeader("Transfer-Encoding"), "chunked"); + } finally { + l.countDown(); + } + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoGetHeadersTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Test1", "Test1"); - h.add("Test2", "Test2"); - h.add("Test3", "Test3"); - h.add("Test4", "Test4"); - h.add("Test5", "Test5"); - c.prepareGet(getTargetUrl()).setHeaders(h).execute(new AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - assertEquals(response.getHeader("X-Test" + i), "Test" + i); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Test1", "Test1"); + h.add("Test2", "Test2"); + h.add("Test3", "Test3"); + h.add("Test4", "Test4"); + h.add("Test5", "Test5"); + client.prepareGet(getTargetUrl()).setHeaders(h).execute(new AsyncCompletionHandlerAdapter() { + + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + assertEquals(response.getHeader("X-Test" + i), "Test" + i); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - c.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoGetCookieTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Test1", "Test1"); - h.add("Test2", "Test2"); - h.add("Test3", "Test3"); - h.add("Test4", "Test4"); - h.add("Test5", "Test5"); - - final Cookie coo = new Cookie("/", "foo", "value", "/", -1, false); - c.prepareGet(getTargetUrl()).setHeaders(h).addCookie(coo).execute(new AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - List cookies = response.getCookies(); - assertEquals(cookies.size(), 1); - assertEquals(cookies.get(0).toString(), coo.toString()); - } finally { - l.countDown(); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Test1", "Test1"); + h.add("Test2", "Test2"); + h.add("Test3", "Test3"); + h.add("Test4", "Test4"); + h.add("Test5", "Test5"); + + final Cookie coo = new Cookie("/", "foo", "value", "/", -1, false); + client.prepareGet(getTargetUrl()).setHeaders(h).addCookie(coo).execute(new AsyncCompletionHandlerAdapter() { + + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + List cookies = response.getCookies(); + assertEquals(cookies.size(), 1); + assertEquals(cookies.get(0).toString(), coo.toString()); + } finally { + l.countDown(); + } + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostDefaultContentType() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + client.preparePost(getTargetUrl()).addParameter("foo", "bar").execute(new AsyncCompletionHandlerAdapter() { - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - c.preparePost(getTargetUrl()).addParameter("foo", "bar").execute(new AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - FluentCaseInsensitiveStringsMap h = response.getHeaders(); - assertEquals(h.getJoinedValue("X-Content-Type", ", "), "application/x-www-form-urlencoded"); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + FluentCaseInsensitiveStringsMap h = response.getHeaders(); + assertEquals(h.getJoinedValue("X-Content-Type", ", "), "application/x-www-form-urlencoded"); + } finally { + l.countDown(); + } + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostBodyIsoTest() throws Throwable { - - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - Response r = c.preparePost(getTargetUrl()).addHeader("X-ISO", "true").setBody("\u017D\u017D\u017D\u017D\u017D\u017D").execute().get(); - assertEquals(r.getResponseBody().getBytes("ISO-8859-1"),"\u017D\u017D\u017D\u017D\u017D\u017D".getBytes("ISO-8859-1")); - c.close(); - } - - @Test(groups = {"standalone", "default_provider", "async"}) - public void asyncDoPostBytesTest() throws Throwable { - - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response r = client.preparePost(getTargetUrl()).addHeader("X-ISO", "true").setBody("\u017D\u017D\u017D\u017D\u017D\u017D").execute().get(); + assertEquals(r.getResponseBody().getBytes("ISO-8859-1"), "\u017D\u017D\u017D\u017D\u017D\u017D".getBytes("ISO-8859-1")); + } finally { + client.close(); } - sb.deleteCharAt(sb.length() - 1); + } - c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + @Test(groups = { "standalone", "default_provider", "async" }) + public void asyncDoPostBytesTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - System.out.println(">>>>> " + response.getHeader("X-param_" + i)); - assertEquals(response.getHeader("X-param_" + i), "value_" + i); + client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + System.out.println(">>>>> " + response.getHeader("X-param_" + i)); + assertEquals(response.getHeader("X-param_" + i), "value_" + i); + + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; - } - }).get(); + }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostInputStreamTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); + ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes()); - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); - ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes()); - - c.preparePost(getTargetUrl()).setHeaders(h).setBody(is).execute(new AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - System.out.println(">>>>> " + response.getHeader("X-param_" + i)); - assertEquals(response.getHeader("X-param_" + i), "value_" + i); + client.preparePost(getTargetUrl()).setHeaders(h).setBody(is).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + System.out.println(">>>>> " + response.getHeader("X-param_" + i)); + assertEquals(response.getHeader("X-param_" + i), "value_" + i); + + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPutInputStreamTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); + ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes()); - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); - ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes()); - - c.preparePut(getTargetUrl()).setHeaders(h).setBody(is).execute(new AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - System.out.println(">>>>> " + response.getHeader("X-param_" + i)); - assertEquals(response.getHeader("X-param_" + i), "value_" + i); + client.preparePut(getTargetUrl()).setHeaders(h).setBody(is).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + System.out.println(">>>>> " + response.getHeader("X-param_" + i)); + assertEquals(response.getHeader("X-param_" + i), "value_" + i); + + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostEntityWriterTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); + byte[] bytes = sb.toString().getBytes(); + h.add("Content-Length", String.valueOf(bytes.length)); - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); - byte[] bytes = sb.toString().getBytes(); - h.add("Content-Length", String.valueOf(bytes.length)); - - c.preparePost(getTargetUrl()).setHeaders(h).setBody(new Request.EntityWriter() { + client.preparePost(getTargetUrl()).setHeaders(h).setBody(new Request.EntityWriter() { - /* @Override */ - public void writeEntity(OutputStream out) throws IOException { - out.write(sb.toString().getBytes("UTF-8")); - } - }).execute(new AsyncCompletionHandlerAdapter() { + /* @Override */ + public void writeEntity(OutputStream out) throws IOException { + out.write(sb.toString().getBytes("UTF-8")); + } + }).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - System.out.println(">>>>> " + response.getHeader("X-param_" + i)); - assertEquals(response.getHeader("X-param_" + i), "value_" + i); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + System.out.println(">>>>> " + response.getHeader("X-param_" + i)); + assertEquals(response.getHeader("X-param_" + i), "value_" + i); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostMultiPartTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - - Part p = new StringPart("foo", "bar"); + Part p = new StringPart("foo", "bar"); - c.preparePost(getTargetUrl()).addBodyPart(p).execute(new AsyncCompletionHandlerAdapter() { + client.preparePost(getTargetUrl()).addBodyPart(p).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - String xContentType = response.getHeader("X-Content-Type"); - String boundary = xContentType.substring( - (xContentType.indexOf("boundary") + "boundary".length() + 1)); - - String s = response.getResponseBodyExcerpt(boundary.length() + "--".length()).substring("--".length()); - assertEquals(boundary, s); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + String xContentType = response.getHeader("X-Content-Type"); + String boundary = xContentType.substring((xContentType.indexOf("boundary") + "boundary".length() + 1)); + + String s = response.getResponseBodyExcerpt(boundary.length() + "--".length()).substring("--".length()); + assertEquals(boundary, s); + } finally { + l.countDown(); + } + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostBasicGZIPTest() throws Throwable { - AsyncHttpClientConfig cf = new AsyncHttpClientConfig.Builder().setCompressionEnabled(true).build(); - AsyncHttpClient c = getAsyncHttpClient(cf); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); + AsyncHttpClient client = getAsyncHttpClient(cf); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); - c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getHeader("X-Accept-Encoding"), "gzip"); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getHeader("X-Accept-Encoding"), "gzip"); + } finally { + l.countDown(); + } + return response; } - return response; + }).get(); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + client.close(); } - c.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostProxyTest() throws Throwable { - AsyncHttpClientConfig cf = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port2)).build(); - AsyncHttpClient c = getAsyncHttpClient(cf); - - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); - - Response response = c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandler() { - @Override - public Response onCompleted(Response response) throws Exception { - return response; + AsyncHttpClient client = getAsyncHttpClient(cf); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); } + sb.setLength(sb.length() - 1); - @Override - public void onThrowable(Throwable t) { - } - }).get(); + Response response = client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandler() { + @Override + public Response onCompleted(Response response) throws Exception { + return response; + } + @Override + public void onThrowable(Throwable t) { + } + }).get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getHeader("X-Proxy-Connection"), "keep-alive"); - c.close(); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getHeader("X-Proxy-Connection"), "keep-alive"); + } finally { + client.close(); + } } - - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncRequestVirtualServerPOSTTest() throws Throwable { - AsyncHttpClient n = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); + Map> m = new HashMap>(); + for (int i = 0; i < 5; i++) { + m.put("param_" + i, Arrays.asList("value_" + i)); + } + Request request = new RequestBuilder("POST").setUrl(getTargetUrl()).setHeaders(h).setParameters(m).setVirtualHost("localhost:" + port1).build(); - Map> m = new HashMap>(); - for (int i = 0; i < 5; i++) { - m.put("param_" + i, Arrays.asList("value_" + i)); - } - Request request = new RequestBuilder("POST") - .setUrl(getTargetUrl()) - .setHeaders(h) - .setParameters(m) - .setVirtualHost("localhost:" + port1) - .build(); - - Response response = n.executeRequest(request, new AsyncCompletionHandlerAdapter()).get(); - - assertEquals(response.getStatusCode(), 200); - if (response.getHeader("X-Host").startsWith("localhost")) { - assertEquals(response.getHeader("X-Host"), "localhost:" + port1); - } else { - assertEquals(response.getHeader("X-Host"), "127.0.0.1:" + port1); - } - n.close(); + Response response = client.executeRequest(request, new AsyncCompletionHandlerAdapter()).get(); + assertEquals(response.getStatusCode(), 200); + if (response.getHeader("X-Host").startsWith("localhost")) { + assertEquals(response.getHeader("X-Host"), "localhost:" + port1); + } else { + assertEquals(response.getHeader("X-Host"), "127.0.0.1:" + port1); + } + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPutTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); - AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); - - Response response = c.preparePut(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter()).get(); - - assertEquals(response.getStatusCode(), 200); - c.close(); + Response response = client.preparePut(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter()).get(); + assertEquals(response.getStatusCode(), 200); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostLatchBytesTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); - } - sb.deleteCharAt(sb.length() - 1); - - c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - for (int i = 1; i < 5; i++) { - System.out.println(">>>>> " + response.getHeader("X-param_" + i)); - assertEquals(response.getHeader("X-param_" + i), "value_" + i); + client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + for (int i = 1; i < 5; i++) { + System.out.println(">>>>> " + response.getHeader("X-param_" + i)); + assertEquals(response.getHeader("X-param_" + i), "value_" + i); + + } + return response; + } finally { + l.countDown(); } - return response; - } finally { - l.countDown(); } - } - }); + }); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostDelayCancelTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + h.add("LockThread", "true"); + StringBuilder sb = new StringBuilder(); + sb.append("LockThread=true"); - AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - h.add("LockThread", "true"); - StringBuilder sb = new StringBuilder(); - sb.append("LockThread=true"); + Future future = client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public void onThrowable(Throwable t) { + } + }); - Future future = c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter(){ - @Override - public void onThrowable(Throwable t) { - } - }); - future.cancel(true); - Response response = future.get(TIMEOUT, TimeUnit.SECONDS); - Assert.assertNull(response); - c.close(); + // Make sure we are connected before cancelling. I know, Thread.sleep + // sucks! + Thread.sleep(1000); + future.cancel(true); + Response response = future.get(TIMEOUT, TimeUnit.SECONDS); + Assert.assertNull(response); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostDelayBytesTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + h.add("LockThread", "true"); + StringBuilder sb = new StringBuilder(); + sb.append("LockThread=true"); - AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - h.add("LockThread", "true"); - StringBuilder sb = new StringBuilder(); - sb.append("LockThread=true"); + try { + Future future = client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public void onThrowable(Throwable t) { + t.printStackTrace(); + } + }); - try { - Future future = c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public void onThrowable(Throwable t) { - t.printStackTrace(); + future.get(10, TimeUnit.SECONDS); + } catch (ExecutionException ex) { + if (ex.getCause() instanceof TimeoutException) { + Assert.assertTrue(true); } - }); - - future.get(10, TimeUnit.SECONDS); - } catch (ExecutionException ex) { - if (ex.getCause() != null && TimeoutException.class.isAssignableFrom(ex.getCause().getClass())) { + } catch (TimeoutException te) { Assert.assertTrue(true); + } catch (IllegalStateException ex) { + Assert.assertTrue(false); } - } catch (TimeoutException te) { - Assert.assertTrue(true); - } catch (IllegalStateException ex) { - Assert.assertTrue(false); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostNullBytesTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); + + Future future = client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter()); - AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); + Response response = future.get(); + Assert.assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + } finally { + client.close(); } - sb.deleteCharAt(sb.length() - 1); + } - Future future = c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter()); + @Test(groups = { "standalone", "default_provider", "async" }) + public void asyncDoPostListenerBytesTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + sb.append("param_"); + sb.append(i); + sb.append("=value_"); + sb.append(i); + sb.append("&"); + } + sb.setLength(sb.length() - 1); - Response response = future.get(); - Assert.assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - c.close(); + final CountDownLatch l = new CountDownLatch(1); - } + client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + } finally { + l.countDown(); + } + return response; + } + }); - @Test(groups = {"standalone", "default_provider", "async"}) - public void asyncDoPostListenerBytesTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 5; i++) { - sb.append("param_"); - sb.append(i); - sb.append("=value_"); - sb.append(i); - sb.append("&"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Latch time out"); + } + } finally { + client.close(); } - sb.deleteCharAt(sb.length() - 1); - - final CountDownLatch l = new CountDownLatch(1); + } - c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { + @Test(groups = { "standalone", "default_provider", "async" }) + public void asyncConnectInvalidFuture() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + int dummyPort = findFreePort(); + final AtomicInteger count = new AtomicInteger(); + for (int i = 0; i < 20; i++) { try { - assertEquals(response.getStatusCode(), 200); - } finally { - l.countDown(); + Response response = client.preparePost(String.format("http://127.0.0.1:%d/", dummyPort)).execute(new AsyncCompletionHandlerAdapter() { + /* @Override */ + public void onThrowable(Throwable t) { + count.incrementAndGet(); + } + }).get(); + assertNull(response, "Should have thrown ExecutionException"); + } catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + if (!(cause instanceof ConnectException)) { + fail("Should have been caused by ConnectException, not by " + cause.getClass().getName()); + } } - return response; } - }); - - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Latch time out"); + assertEquals(count.get(), 20); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) - public void asyncConnectInvalidFuture() throws Throwable { - - int dummyPort = findFreePort(); - AsyncHttpClient c = getAsyncHttpClient(null); - final AtomicInteger count = new AtomicInteger(); - for (int i = 0; i < 20; i++) { + @Test(groups = { "standalone", "default_provider", "async" }) + public void asyncConnectInvalidPortFuture() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + int dummyPort = findFreePort(); try { - Response response = c.preparePost(String.format("http://127.0.0.1:%d/", dummyPort)).execute(new AsyncCompletionHandlerAdapter() { + Response response = client.preparePost(String.format("http://127.0.0.1:%d/", dummyPort)).execute(new AsyncCompletionHandlerAdapter() { /* @Override */ public void onThrowable(Throwable t) { - count.incrementAndGet(); + t.printStackTrace(); } }).get(); assertNull(response, "Should have thrown ExecutionException"); @@ -1024,600 +1091,628 @@ public void onThrowable(Throwable t) { fail("Should have been caused by ConnectException, not by " + cause.getClass().getName()); } } + } finally { + client.close(); } - assertEquals(count.get(), 20); - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) - public void asyncConnectInvalidPortFuture() throws Throwable { - - int dummyPort = findFreePort(); - AsyncHttpClient c = getAsyncHttpClient(null); + @Test(groups = { "standalone", "default_provider", "async" }) + public void asyncConnectInvalidPort() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); try { - Response response = c.preparePost(String.format("http://127.0.0.1:%d/", dummyPort)).execute(new AsyncCompletionHandlerAdapter() { - /* @Override */ - public void onThrowable(Throwable t) { - t.printStackTrace(); - } - }).get(); - assertNull(response, "Should have thrown ExecutionException"); - } catch (ExecutionException ex) { - Throwable cause = ex.getCause(); - if (!(cause instanceof ConnectException)) { - fail("Should have been caused by ConnectException, not by " + cause.getClass().getName()); + // pick a random unused local port + int port = findFreePort(); + + try { + Response response = client.preparePost(String.format("http://127.0.0.1:%d/", port)).execute(new AsyncCompletionHandlerAdapter() { + /* @Override */ + public void onThrowable(Throwable t) { + t.printStackTrace(); + } + }).get(); + assertNull(response, "No ExecutionException was thrown"); + } catch (ExecutionException ex) { + assertEquals(ex.getCause().getClass(), ConnectException.class); } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) - public void asyncConnectInvalidPort() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - - // pick a random unused local port - int port = findFreePort(); - + @Test(groups = { "standalone", "default_provider", "async" }) + public void asyncConnectInvalidHandlerPort() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); try { - Response response = c.preparePost(String.format("http://127.0.0.1:%d/", port)).execute(new AsyncCompletionHandlerAdapter() { + final CountDownLatch l = new CountDownLatch(1); + int port = findFreePort(); + + client.prepareGet(String.format("http://127.0.0.1:%d/", port)).execute(new AsyncCompletionHandlerAdapter() { /* @Override */ public void onThrowable(Throwable t) { - t.printStackTrace(); + try { + assertEquals(t.getClass(), ConnectException.class); + } finally { + l.countDown(); + } } - }).get(); - assertNull(response, "No ExecutionException was thrown"); - } catch (ExecutionException ex) { - assertEquals(ex.getCause().getClass(), ConnectException.class); - } - c.close(); - } - - @Test(groups = {"standalone", "default_provider", "async"}) - public void asyncConnectInvalidHandlerPort() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - int port = findFreePort(); + }); - c.prepareGet(String.format("http://127.0.0.1:%d/", port)).execute(new AsyncCompletionHandlerAdapter() { - /* @Override */ - public void onThrowable(Throwable t) { - try { - assertEquals(t.getClass(), ConnectException.class); - } finally { - l.countDown(); - } + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); } - }); - - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + } finally { + client.close(); } - c.close(); } - @Test(groups = {"online", "default_provider", "async"}) + @Test(groups = { "online", "default_provider", "async" }) public void asyncConnectInvalidHandlerHost() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - - c.prepareGet("http://null.apache.org:9999/").execute(new AsyncCompletionHandlerAdapter() { - /* @Override */ - public void onThrowable(Throwable t) { - if (t != null) { - if (t.getClass().equals(ConnectException.class)) { - l.countDown(); - } else if (t.getClass().equals(UnresolvedAddressException.class)) { - l.countDown(); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + + client.prepareGet("http://null.apache.org:9999/").execute(new AsyncCompletionHandlerAdapter() { + /* @Override */ + public void onThrowable(Throwable t) { + if (t != null) { + if (t.getClass().equals(ConnectException.class)) { + l.countDown(); + } else if (t.getClass().equals(UnresolvedAddressException.class)) { + l.countDown(); + } } } - } - }); + }); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - c.close(); } - - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncConnectInvalidFuturePort() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - - final AtomicBoolean called = new AtomicBoolean(false); - final AtomicBoolean rightCause = new AtomicBoolean(false); - // pick a random unused local port - int port = findFreePort(); - + AsyncHttpClient client = getAsyncHttpClient(null); try { - Response response = c.prepareGet(String.format("http://127.0.0.1:%d/", port)).execute(new AsyncCompletionHandlerAdapter() { - @Override - public void onThrowable(Throwable t) { - called.set(true); - if (t instanceof ConnectException) { - rightCause.set(true); + final AtomicBoolean called = new AtomicBoolean(false); + final AtomicBoolean rightCause = new AtomicBoolean(false); + // pick a random unused local port + int port = findFreePort(); + + try { + Response response = client.prepareGet(String.format("http://127.0.0.1:%d/", port)).execute(new AsyncCompletionHandlerAdapter() { + @Override + public void onThrowable(Throwable t) { + called.set(true); + if (t instanceof ConnectException) { + rightCause.set(true); + } } - } - }).get(); - assertNull(response, "No ExecutionException was thrown"); - } catch (ExecutionException ex) { - assertEquals(ex.getCause().getClass(), ConnectException.class); + }).get(); + assertNull(response, "No ExecutionException was thrown"); + } catch (ExecutionException ex) { + assertEquals(ex.getCause().getClass(), ConnectException.class); + } + assertTrue(called.get(), "onThrowable should get called."); + assertTrue(rightCause.get(), "onThrowable should get called with ConnectionException"); + } finally { + client.close(); } - assertTrue(called.get(), "onThrowable should get called."); - assertTrue(rightCause.get(), "onThrowable should get called with ConnectionException"); - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncContentLenghtGETTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - Response response = c.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response response = client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public void onThrowable(Throwable t) { - Assert.fail("Unexpected exception", t); - } - }).get(); + @Override + public void onThrowable(Throwable t) { + Assert.fail("Unexpected exception", t); + } + }).get(); - Assert.assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - c.close(); + Assert.assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncResponseBodyTooLarge() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - Response response = c.preparePost(getTargetUrl()).setBody("0123456789").execute(new AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response response = client.preparePost(getTargetUrl()).setBody("0123456789").execute(new AsyncCompletionHandlerAdapter() { - @Override - public void onThrowable(Throwable t) { - Assert.fail("Unexpected exception", t); - } - }).get(); + @Override + public void onThrowable(Throwable t) { + Assert.fail("Unexpected exception", t); + } + }).get(); - Assert.assertNotNull(response.getResponseBodyExcerpt(Integer.MAX_VALUE)); - c.close(); + Assert.assertNotNull(response.getResponseBodyExcerpt(Integer.MAX_VALUE)); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncResponseEmptyBody() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - Response response = c.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response response = client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public void onThrowable(Throwable t) { - Assert.fail("Unexpected exception", t); - } - }).get(); + @Override + public void onThrowable(Throwable t) { + Assert.fail("Unexpected exception", t); + } + }).get(); - assertEquals(response.getResponseBody(),""); - c.close(); + assertEquals(response.getResponseBody(), ""); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider", "asyncAPI"}) + @Test(groups = { "standalone", "default_provider", "asyncAPI" }) public void asyncAPIContentLenghtGETTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(1); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(1); + client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { - client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + try { + assertEquals(response.getStatusCode(), 200); + } finally { + l.countDown(); + } + return response; + } - @Override - public Response onCompleted(Response response) throws Exception { - try { - assertEquals(response.getStatusCode(), 200); - } finally { - l.countDown(); + @Override + public void onThrowable(Throwable t) { } - return response; - } + }); - @Override - public void onThrowable(Throwable t) { + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); } - }); - - - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider", "asyncAPI"}) + @Test(groups = { "standalone", "default_provider", "asyncAPI" }) public void asyncAPIHandlerExceptionTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(1); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(1); - - client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - throw new IllegalStateException("FOO"); - } + client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + throw new IllegalStateException("FOO"); + } - @Override - public void onThrowable(Throwable t) { - try { - if (t.getMessage() != null) { - assertEquals(t.getMessage(), "FOO"); + @Override + public void onThrowable(Throwable t) { + try { + if (t.getMessage() != null) { + assertEquals(t.getMessage(), "FOO"); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); } - } - }); - + }); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - client.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoGetDelayHandlerTest() throws Throwable { - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("LockThread", "true"); AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(5 * 1000).build()); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("LockThread", "true"); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(1); + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(1); - client.prepareGet(getTargetUrl()).setHeaders(h).execute(new AsyncCompletionHandlerAdapter() { + client.prepareGet(getTargetUrl()).setHeaders(h).execute(new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - Assert.fail("Must not receive a response"); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + Assert.fail("Must not receive a response"); + } finally { + l.countDown(); + } + return response; } - return response; - } - @Override - public void onThrowable(Throwable t) { - try { - if (t instanceof TimeoutException) { - Assert.assertTrue(true); - } else { - Assert.fail("Unexpected exception", t); + @Override + public void onThrowable(Throwable t) { + try { + if (t instanceof TimeoutException) { + Assert.assertTrue(true); + } else { + Assert.fail("Unexpected exception", t); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); } - } - }); + }); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - client.close(); - } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoGetQueryStringTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(1); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(1); - - AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { + AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - try { - Assert.assertTrue(response.getHeader("X-pathInfo") != null); - Assert.assertTrue(response.getHeader("X-queryString") != null); - } finally { - l.countDown(); + @Override + public Response onCompleted(Response response) throws Exception { + try { + Assert.assertTrue(response.getHeader("X-pathInfo") != null); + Assert.assertTrue(response.getHeader("X-queryString") != null); + } finally { + l.countDown(); + } + return response; } - return response; - } - }; + }; - Request req = new RequestBuilder("GET") - .setUrl(getTargetUrl() + "?foo=bar").build(); + Request req = new RequestBuilder("GET").setUrl(getTargetUrl() + "?foo=bar").build(); - client.executeRequest(req, handler).get(); + client.executeRequest(req, handler).get(); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoGetKeepAliveHandlerTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(2); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(2); + AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { - AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { + String remoteAddr = null; - String remoteAddr = null; + @Override + public Response onCompleted(Response response) throws Exception { + assertEquals(response.getStatusCode(), 200); + if (remoteAddr == null) { + remoteAddr = response.getHeader("X-KEEP-ALIVE"); + l.countDown(); + } else { + assertEquals(response.getHeader("X-KEEP-ALIVE"), remoteAddr); + l.countDown(); + } - @Override - public Response onCompleted(Response response) throws Exception { - assertEquals(response.getStatusCode(), 200); - if (remoteAddr == null) { - remoteAddr = response.getHeader("X-KEEP-ALIVE"); - l.countDown(); - } else { - assertEquals(response.getHeader("X-KEEP-ALIVE"), remoteAddr); - l.countDown(); + return response; } + }; - return response; - } - }; - - client.prepareGet(getTargetUrl()).execute(handler).get(); - client.prepareGet(getTargetUrl()).execute(handler); + client.prepareGet(getTargetUrl()).execute(handler).get(); + client.prepareGet(getTargetUrl()).execute(handler); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"online", "default_provider", "async"}) + @Test(groups = { "online", "default_provider", "async" }) public void asyncDoGetMaxRedirectTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(new Builder().setMaximumNumberOfRedirects(0).setFollowRedirects(true).build()); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(1); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(1); - - AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { + AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - Assert.fail("Should not be here"); - return response; - } + @Override + public Response onCompleted(Response response) throws Exception { + Assert.fail("Should not be here"); + return response; + } - @Override - public void onThrowable(Throwable t) { - t.printStackTrace(); - try { - assertEquals(t.getClass(), MaxRedirectException.class); - } finally { - l.countDown(); + @Override + public void onThrowable(Throwable t) { + t.printStackTrace(); + try { + assertEquals(t.getClass(), MaxRedirectException.class); + } finally { + l.countDown(); + } } - } - }; + }; - client.prepareGet("http://google.com/").execute(handler); + client.prepareGet("http://google.com/").execute(handler); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"online", "default_provider", "async"}) + @Test(groups = { "online", "default_provider", "async" }) public void asyncDoGetNestedTest() throws Throwable { final AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(2); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(2); - - final AsyncCompletionHandlerAdapter handler = new AsyncCompletionHandlerAdapter() { + final AsyncCompletionHandlerAdapter handler = new AsyncCompletionHandlerAdapter() { - private final static int MAX_NESTED = 2; + private final static int MAX_NESTED = 2; - private AtomicInteger nestedCount = new AtomicInteger(0); + private AtomicInteger nestedCount = new AtomicInteger(0); - @Override - public Response onCompleted(Response response) throws Exception { - try { - if (nestedCount.getAndIncrement() < MAX_NESTED) { - System.out.println("Executing a nested request: " + nestedCount); - client.prepareGet("http://google.com/").execute(this); + @Override + public Response onCompleted(Response response) throws Exception { + try { + if (nestedCount.getAndIncrement() < MAX_NESTED) { + System.out.println("Executing a nested request: " + nestedCount); + client.prepareGet("http://google.com/").execute(this); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); + return response; } - return response; - } - - @Override - public void onThrowable(Throwable t) { - t.printStackTrace(); - } - }; + @Override + public void onThrowable(Throwable t) { + t.printStackTrace(); + } + }; - client.prepareGet("http://www.google.com/").execute(handler); + client.prepareGet("http://www.google.com/").execute(handler); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"online", "default_provider", "async"}) + @Test(groups = { "online", "default_provider", "async" }) public void asyncDoGetStreamAndBodyTest() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); - Response r = client.prepareGet("http://www.google.com/").execute().get(); - - r.getResponseBody(); - r.getResponseBodyAsStream(); + AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); + try { + Response r = client.prepareGet("http://www.google.com/").execute().get(); - client.close(); + r.getResponseBody(); + r.getResponseBodyAsStream(); + } finally { + client.close(); + } } - @Test(groups = {"online", "default_provider", "async"}) + @Test(groups = { "online", "default_provider", "async" }) public void asyncUrlWithoutPathTest() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); - Response r = client.prepareGet("http://www.google.com/").execute().get(); - - r.getResponseBody(); - r.getResponseBodyAsStream(); + AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); + try { + Response r = client.prepareGet("http://www.google.com/").execute().get(); - client.close(); + r.getResponseBody(); + r.getResponseBodyAsStream(); + } finally { + client.close(); + } } - @Test(groups = {"default_provider", "async"}) + @Test(groups = { "default_provider", "async" }) public void optionsTest() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); - Response r = client.prepareOptions(getTargetUrl()).execute().get(); - - assertEquals(r.getStatusCode(), 200); - assertEquals(r.getHeader("Allow"), "GET,HEAD,POST,OPTIONS,TRACE"); + AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); + try { + Response r = client.prepareOptions(getTargetUrl()).execute().get(); - client.close(); + assertEquals(r.getStatusCode(), 200); + assertEquals(r.getHeader("Allow"), "GET,HEAD,POST,OPTIONS,TRACE"); + } finally { + client.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testAwsS3() throws Exception { - final AsyncHttpClient c = getAsyncHttpClient(new Builder().build()); - Response response = c.prepareGet("http://test.s3.amazonaws.com/").execute().get(); - if (response.getResponseBody() == null || response.getResponseBody().equals("")) { - fail("No response Body"); - } else { - assertEquals(response.getStatusCode(), 403); + AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); + try { + Response response = client.prepareGet("http://test.s3.amazonaws.com/").execute().get(); + if (!isNonEmpty(response.getResponseBody())) { + fail("No response Body"); + } else { + assertEquals(response.getStatusCode(), 403); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testAsyncHttpProviderConfig() throws Exception { - final AsyncHttpClient c = getAsyncHttpClient(new Builder().setAsyncHttpClientProviderConfig(getProviderConfig()).build()); - Response response = c.prepareGet("http://test.s3.amazonaws.com/").execute().get(); - if (response.getResponseBody() == null || response.getResponseBody().equals("")) { - fail("No response Body"); - } else { - assertEquals(response.getStatusCode(), 403); + AsyncHttpClient client = getAsyncHttpClient(new Builder().setAsyncHttpClientProviderConfig(getProviderConfig()).build()); + try { + Response response = client.prepareGet("http://test.s3.amazonaws.com/").execute().get(); + if (!isNonEmpty(response.getResponseBody())) { + fail("No response Body"); + } else { + assertEquals(response.getStatusCode(), 403); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void idleRequestTimeoutTest() throws Exception { - AsyncHttpClient c = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder().setIdleConnectionInPoolTimeoutInMs(5000).setRequestTimeoutInMs(10000).build()); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - h.add("LockThread", "true"); - - long t1 = System.currentTimeMillis(); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setIdleConnectionInPoolTimeoutInMs(5000).setRequestTimeoutInMs(10000).build()); try { - c.prepareGet(getTargetUrl()).setHeaders(h).setUrl(getTargetUrl()).execute(new AsyncHandlerAdapter() { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + h.add("LockThread", "true"); - /* @Override */ - public void onThrowable(Throwable t) { -// t.printStackTrace(); - } + long t1 = millisTime(); + try { + client.prepareGet(getTargetUrl()).setHeaders(h).setUrl(getTargetUrl()).execute(new AsyncHandlerAdapter() { - }).get(); - Assert.fail(); - } catch (Throwable ex) { - final long elapsedTime = System.currentTimeMillis() - t1; - System.out.println("EXPIRED: " + (elapsedTime)); - Assert.assertNotNull(ex.getCause()); - Assert.assertTrue(elapsedTime >= 10000 && elapsedTime <= 25000); + /* @Override */ + public void onThrowable(Throwable t) { + // t.printStackTrace(); + } + + }).get(); + Assert.fail(); + } catch (Throwable ex) { + final long elapsedTime = millisTime() - t1; + System.out.println("EXPIRED: " + (elapsedTime)); + Assert.assertNotNull(ex.getCause()); + Assert.assertTrue(elapsedTime >= 10000 && elapsedTime <= 25000); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider", "async"}) + @Test(groups = { "standalone", "default_provider", "async" }) public void asyncDoPostCancelTest() throws Throwable { - - AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - h.add("LockThread", "true"); - StringBuilder sb = new StringBuilder(); - sb.append("LockThread=true"); - - final AtomicReference ex = new AtomicReference(); - ex.set(null); + AsyncHttpClient client = getAsyncHttpClient(null); try { - Future future = c.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + h.add("LockThread", "true"); + StringBuilder sb = new StringBuilder(); + sb.append("LockThread=true"); + + final AtomicReference ex = new AtomicReference(); + ex.set(null); + try { + Future future = client.preparePost(getTargetUrl()).setHeaders(h).setBody(sb.toString()).execute(new AsyncCompletionHandlerAdapter() { - @Override - public void onThrowable(Throwable t) { - if (t instanceof CancellationException) { - ex.set((CancellationException)t); + @Override + public void onThrowable(Throwable t) { + if (t instanceof CancellationException) { + ex.set((CancellationException) t); + } + t.printStackTrace(); } - t.printStackTrace(); - } - - }); - future.cancel(true); - } catch (IllegalStateException ise) { - fail(); - } - Assert.assertNotNull(ex.get()); - c.close(); - } + }); - @Test(groups = {"standalone", "default_provider"}, expectedExceptions = IllegalArgumentException.class) - public void getShouldNotAllowBody() throws IllegalArgumentException, IOException { - AsyncHttpClient c = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder builder = c.prepareGet(getTargetUrl()); - builder.setBody("Boo!"); - builder.execute(); + Thread.sleep(1000); + future.cancel(true); + } catch (IllegalStateException ise) { + fail(); + } + Assert.assertNotNull(ex.get()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}, expectedExceptions = IllegalArgumentException.class) + @Test(groups = { "standalone", "default_provider" }, expectedExceptions = IllegalArgumentException.class) public void headShouldNotAllowBody() throws IllegalArgumentException, IOException { - AsyncHttpClient c = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder builder = c.prepareHead(getTargetUrl()); - builder.setBody("Boo!"); - builder.execute(); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.prepareHead(getTargetUrl()); + builder.setBody("Boo!"); + builder.execute(); + } finally { + client.close(); + } } protected String getBrokenTargetUrl() { return String.format("http:127.0.0.1:%d/foo/test", port1); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }, expectedExceptions = { IllegalArgumentException.class }) public void invalidUri() throws Exception { - AsyncHttpClient c = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder builder = c.prepareGet(getBrokenTargetUrl()); - Response r = c.executeRequest(builder.build()).get(); - assertEquals(200, r.getStatusCode()); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + client.prepareGet(getBrokenTargetUrl()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncHttpClientConfigBeanTest() throws Exception { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfigBean().setUserAgent("test")); - AsyncHttpClient.BoundRequestBuilder builder = c.prepareGet(getTargetUrl()); - Response r = c.executeRequest(builder.build()).get(); - assertEquals(200, r.getStatusCode()); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfigBean().setUserAgent("test")); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.prepareGet(getTargetUrl()); + Response r = client.executeRequest(builder.build()).get(); + assertEquals(200, r.getStatusCode()); + } finally { + client.close(); + } } - @Test(groups = {"default_provider", "async"}) + @Test(groups = { "default_provider", "async" }) public void bodyAsByteTest() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); - Response r = client.prepareGet(getTargetUrl()).execute().get(); - - assertEquals(r.getStatusCode(), 200); - assertEquals(r.getResponseBodyAsBytes(), new byte[]{}); + AsyncHttpClient client = getAsyncHttpClient(new Builder().build()); + try { + Response r = client.prepareGet(getTargetUrl()).execute().get(); - client.close(); + assertEquals(r.getStatusCode(), 200); + assertEquals(r.getResponseBodyAsBytes(), new byte[] {}); + } finally { + client.close(); + } } - @Test(groups = {"default_provider", "async"}) + @Test(groups = { "default_provider", "async" }) public void mirrorByteTest() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - Response r = client.preparePost(getTargetUrl()).setBody("MIRROR").execute().get(); - - assertEquals(r.getStatusCode(), 200); - assertEquals(new String(r.getResponseBodyAsBytes(), "UTF-8"), "MIRROR"); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response r = client.preparePost(getTargetUrl()).setBody("MIRROR").execute().get(); - client.close(); + assertEquals(r.getStatusCode(), 200); + assertEquals(new String(r.getResponseBodyAsBytes(), "UTF-8"), "MIRROR"); + } finally { + client.close(); + } } - protected abstract AsyncHttpProviderConfig getProviderConfig(); + protected abstract AsyncHttpProviderConfig getProviderConfig(); } diff --git a/src/test/java/com/ning/http/client/async/AsyncStreamHandlerTest.java b/src/test/java/com/ning/http/client/async/AsyncStreamHandlerTest.java index 8e77bdd99d..ad78d7b664 100644 --- a/src/test/java/com/ning/http/client/async/AsyncStreamHandlerTest.java +++ b/src/test/java/com/ning/http/client/async/AsyncStreamHandlerTest.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; @@ -40,511 +41,543 @@ public abstract class AsyncStreamHandlerTest extends AbstractBasicTest { private final static String RESPONSE = "param_1_"; private final static String UTF8 = "text/html;charset=utf-8"; - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncStreamGETTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(null); - - c.prepareGet(getTargetUrl()).execute(new AsyncHandlerAdapter() { - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - try { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), UTF8); - return STATE.ABORT; - } finally { - l.countDown(); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + + client.prepareGet(getTargetUrl()).execute(new AsyncHandlerAdapter() { + + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + try { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), UTF8); + return STATE.ABORT; + } finally { + l.countDown(); + } } - } - @Override - public void onThrowable(Throwable t) { - try { - Assert.fail("", t); - } finally { - l.countDown(); + @Override + public void onThrowable(Throwable t) { + try { + Assert.fail("", t); + } finally { + l.countDown(); + } } - } - }); + }); - if (!l.await(5, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(5, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncStreamPOSTTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - Map> m = new HashMap>(); - m.put("param_1", Arrays.asList("value_1")); - - AsyncHttpClient c = getAsyncHttpClient(null); - - c.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { - private StringBuilder builder = new StringBuilder(); - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), UTF8); - return STATE.CONTINUE; - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); + Map> m = new HashMap>(); + m.put("param_1", Arrays.asList("value_1")); - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.append(new String(content.getBodyPartBytes())); - return STATE.CONTINUE; - } + client.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { + private StringBuilder builder = new StringBuilder(); - @Override - public String onCompleted() throws Exception { - try { - String r = builder.toString().trim(); - Assert.assertEquals(r, RESPONSE); - return r; - } finally { - l.countDown(); + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), UTF8); + return STATE.CONTINUE; } - } - }); - if (!l.await(10, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.append(new String(content.getBodyPartBytes())); + return STATE.CONTINUE; + } + + @Override + public String onCompleted() throws Exception { + try { + String r = builder.toString().trim(); + Assert.assertEquals(r, RESPONSE); + return r; + } finally { + l.countDown(); + } + } + }); + + if (!l.await(10, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncStreamInterruptTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); - Map> m = new HashMap>(); - m.put("param_1", Arrays.asList("value_1")); + Map> m = new HashMap>(); + m.put("param_1", Arrays.asList("value_1")); - final AtomicBoolean a = new AtomicBoolean(true); - AsyncHttpClient c = getAsyncHttpClient(null); + final AtomicBoolean a = new AtomicBoolean(true); - c.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { + client.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), UTF8); - return STATE.ABORT; - } + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), UTF8); + return STATE.ABORT; + } - @Override - public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Exception { - a.set(false); - Assert.fail("Interrupted not working"); - return STATE.ABORT; - } + @Override + public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Exception { + a.set(false); + Assert.fail("Interrupted not working"); + return STATE.ABORT; + } - @Override - public void onThrowable(Throwable t) { - try { - Assert.fail("", t); - } finally { - l.countDown(); + @Override + public void onThrowable(Throwable t) { + try { + Assert.fail("", t); + } finally { + l.countDown(); + } } - } - }); + }); - l.await(5, TimeUnit.SECONDS); - Assert.assertTrue(a.get()); - c.close(); + l.await(5, TimeUnit.SECONDS); + Assert.assertTrue(a.get()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncStreamFutureTest() throws Throwable { - Map> m = new HashMap>(); - m.put("param_1", Arrays.asList("value_1")); - AsyncHttpClient c = getAsyncHttpClient(null); - - Future f = c.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { - private StringBuilder builder = new StringBuilder(); - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), UTF8); - return STATE.CONTINUE; - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Map> m = new HashMap>(); + m.put("param_1", Arrays.asList("value_1")); - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.append(new String(content.getBodyPartBytes())); - return STATE.CONTINUE; - } + Future f = client.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { + private StringBuilder builder = new StringBuilder(); - @Override - public String onCompleted() throws Exception { - String r = builder.toString().trim(); - Assert.assertEquals(r, RESPONSE); - return r; - } + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), UTF8); + return STATE.CONTINUE; + } - @Override - public void onThrowable(Throwable t) { - Assert.fail("", t); - } - }); + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.append(new String(content.getBodyPartBytes())); + return STATE.CONTINUE; + } - try { - String r = f.get(5, TimeUnit.SECONDS); - Assert.assertNotNull(r); - Assert.assertEquals(r.trim(), RESPONSE); - } catch (TimeoutException ex) { - Assert.fail(); + @Override + public String onCompleted() throws Exception { + String r = builder.toString().trim(); + Assert.assertEquals(r, RESPONSE); + return r; + } + + @Override + public void onThrowable(Throwable t) { + Assert.fail("", t); + } + }); + + try { + String r = f.get(5, TimeUnit.SECONDS); + Assert.assertNotNull(r); + Assert.assertEquals(r.trim(), RESPONSE); + } catch (TimeoutException ex) { + Assert.fail(); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncStreamThrowableRefusedTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(null); - - c.prepareGet(getTargetUrl()).execute(new AsyncHandlerAdapter() { - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - throw new RuntimeException("FOO"); - } + client.prepareGet(getTargetUrl()).execute(new AsyncHandlerAdapter() { - @Override + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + throw new RuntimeException("FOO"); + } - public void onThrowable(Throwable t) { - try { - if (t.getMessage() != null) { - Assert.assertEquals(t.getMessage(), "FOO"); + @Override + public void onThrowable(Throwable t) { + try { + if (t.getMessage() != null) { + Assert.assertEquals(t.getMessage(), "FOO"); + } + } finally { + l.countDown(); } - } finally { - l.countDown(); } - } - }); - + }); - if (!l.await(10, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + if (!l.await(10, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncStreamReusePOSTTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); - - Map> m = new HashMap>(); - m.put("param_1", Arrays.asList("value_1")); - AsyncHttpClient c = getAsyncHttpClient(null); - - c.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { - private StringBuilder builder = new StringBuilder(); - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), UTF8); - return STATE.CONTINUE; - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.append(new String(content.getBodyPartBytes())); - return STATE.CONTINUE; - } + Map> m = new HashMap>(); + m.put("param_1", Arrays.asList("value_1")); - @Override - public String onCompleted() throws Exception { - try { - String r = builder.toString().trim(); - Assert.assertEquals(r, RESPONSE); - return r; - } finally { - l.countDown(); + client.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { + private StringBuilder builder = new StringBuilder(); + + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), UTF8); + return STATE.CONTINUE; } - } - }); + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.append(new String(content.getBodyPartBytes())); + return STATE.CONTINUE; + } - if (!l.await(20, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); - } + @Override + public String onCompleted() throws Exception { + try { + String r = builder.toString().trim(); + Assert.assertEquals(r, RESPONSE); + return r; + } finally { + l.countDown(); + } - // Let do the same again - c.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { - private StringBuilder builder = new StringBuilder(); + } + }); - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), UTF8); - return STATE.CONTINUE; + if (!l.await(20, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.append(new String(content.getBodyPartBytes())); - return STATE.CONTINUE; - } + // Let do the same again + client.preparePost(getTargetUrl()).setParameters(m).execute(new AsyncHandlerAdapter() { + private StringBuilder builder = new StringBuilder(); - @Override - public String onCompleted() throws Exception { - try { - String r = builder.toString().trim(); - Assert.assertEquals(r, RESPONSE); - return r; - } finally { - l.countDown(); + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), UTF8); + return STATE.CONTINUE; + } + + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.append(new String(content.getBodyPartBytes())); + return STATE.CONTINUE; } - } - }); - if (!l.await(20, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + @Override + public String onCompleted() throws Exception { + try { + String r = builder.toString().trim(); + Assert.assertEquals(r, RESPONSE); + return r; + } finally { + l.countDown(); + } + } + }); + + if (!l.await(20, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void asyncStream301WithBody() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(null); - c.prepareGet("http://google.com/").execute(new AsyncHandlerAdapter() { - private StringBuilder builder = new StringBuilder(); - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(), "text/html; charset=utf-8"); - return STATE.CONTINUE; - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + client.prepareGet("http://google.com/").execute(new AsyncHandlerAdapter() { + private StringBuilder builder = new StringBuilder(); - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.append(new String(content.getBodyPartBytes())); - return STATE.CONTINUE; - } + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getJoinedValue("content-type", ", ").toLowerCase(Locale.ENGLISH), "text/html; charset=utf-8"); + return STATE.CONTINUE; + } - @Override - public String onCompleted() throws Exception { - String r = builder.toString(); - Assert.assertTrue(r.contains("301 Moved")); - l.countDown(); - return r; - } - }); + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.append(new String(content.getBodyPartBytes())); + return STATE.CONTINUE; + } + + @Override + public String onCompleted() throws Exception { + String r = builder.toString(); + Assert.assertTrue(r.contains("301 Moved")); + l.countDown(); + return r; + } + }); - if (!l.await(20, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(20, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void asyncStream301RedirectWithBody() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); - c.prepareGet("http://google.com/").execute(new AsyncHandlerAdapter() { - private StringBuilder builder = new StringBuilder(); - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getFirstValue( "server" ), "gws"); - // This assertion below is not an invariant, since implicitly contains locale-dependant settings - // and fails when run in country having own localized Google site and it's locale relies on something - // other than ISO-8859-1. - // In Hungary for example, http://google.com/ redirects to http://www.google.hu/, a localized - // Google site, that uses ISO-8892-2 encoding (default for HU). Similar is true for other - // non-ISO-8859-1 using countries that have "localized" google, like google.hr, google.rs, google.cz, google.sk etc. - // - // Assert.assertEquals(h.getJoinedValue("content-type", ", "), "text/html; charset=ISO-8859-1"); - return STATE.CONTINUE; - } + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); + try { + final CountDownLatch l = new CountDownLatch(1); + client.prepareGet("http://google.com/").execute(new AsyncHandlerAdapter() { + private StringBuilder builder = new StringBuilder(); - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.append(new String(content.getBodyPartBytes())); - return STATE.CONTINUE; - } + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + Assert.assertEquals(h.getFirstValue("server"), "gws"); + // This assertion below is not an invariant, since implicitly contains locale-dependant settings + // and fails when run in country having own localized Google site and it's locale relies on something + // other than ISO-8859-1. + // In Hungary for example, http://google.com/ redirects to http://www.google.hu/, a localized + // Google site, that uses ISO-8892-2 encoding (default for HU). Similar is true for other + // non-ISO-8859-1 using countries that have "localized" google, like google.hr, google.rs, google.cz, google.sk etc. + // + // Assert.assertEquals(h.getJoinedValue("content-type", ", "), "text/html; charset=ISO-8859-1"); + return STATE.CONTINUE; + } - @Override - public String onCompleted() throws Exception { - String r = builder.toString(); - Assert.assertTrue(!r.contains("301 Moved")); - l.countDown(); + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.append(new String(content.getBodyPartBytes())); + return STATE.CONTINUE; + } - return r; - } - }); + @Override + public String onCompleted() throws Exception { + String r = builder.toString(); + Assert.assertTrue(!r.contains("301 Moved")); + l.countDown(); - if (!l.await(20, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + return r; + } + }); + + if (!l.await(20, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}, timeOut = 3000, description = "Test behavior of 'read only status line' scenario.") + @Test(groups = { "standalone", "default_provider" }, timeOut = 3000, description = "Test behavior of 'read only status line' scenario.") public void asyncStreamJustStatusLine() throws Throwable { - final int STATUS = 0; - final int COMPLETED = 1; - final int OTHER = 2; - final boolean[] whatCalled = new boolean[]{false, false, false}; - final CountDownLatch latch = new CountDownLatch(1); AsyncHttpClient client = getAsyncHttpClient(null); - Future statusCode = client.prepareGet(getTargetUrl()).execute(new AsyncHandler() { - private int status = -1; + try { + final int STATUS = 0; + final int COMPLETED = 1; + final int OTHER = 2; + final boolean[] whatCalled = new boolean[] { false, false, false }; + final CountDownLatch latch = new CountDownLatch(1); + Future statusCode = client.prepareGet(getTargetUrl()).execute(new AsyncHandler() { + private int status = -1; + + /* @Override */ + public void onThrowable(Throwable t) { + whatCalled[OTHER] = true; + latch.countDown(); + } - /* @Override */ - public void onThrowable(Throwable t) { - whatCalled[OTHER] = true; - latch.countDown(); - } + /* @Override */ + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + whatCalled[OTHER] = true; + latch.countDown(); + return STATE.ABORT; + } - /* @Override */ - public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - whatCalled[OTHER] = true; - latch.countDown(); - return STATE.ABORT; - } + /* @Override */ + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + whatCalled[STATUS] = true; + System.out.println(responseStatus); + status = responseStatus.getStatusCode(); + latch.countDown(); + return STATE.ABORT; + } - /* @Override */ - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - whatCalled[STATUS] = true; - System.out.println(responseStatus); - status = responseStatus.getStatusCode(); - latch.countDown(); - return STATE.ABORT; - } + /* @Override */ + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + whatCalled[OTHER] = true; + latch.countDown(); + return STATE.ABORT; + } - /* @Override */ - public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { - whatCalled[OTHER] = true; - latch.countDown(); - return STATE.ABORT; - } + /* @Override */ + public Integer onCompleted() throws Exception { + whatCalled[COMPLETED] = true; + latch.countDown(); + return status; + } + }); - /* @Override */ - public Integer onCompleted() throws Exception { - whatCalled[COMPLETED] = true; - latch.countDown(); - return status; + if (!latch.await(2, TimeUnit.SECONDS)) { + Assert.fail("Timeout"); + return; } - }); - - if (!latch.await(2, TimeUnit.SECONDS)) { - Assert.fail("Timeout"); - return; - } - Integer status = statusCode.get(TIMEOUT, TimeUnit.SECONDS); - Assert.assertEquals((int) status, 200, "Expected status code failed."); + Integer status = statusCode.get(TIMEOUT, TimeUnit.SECONDS); + Assert.assertEquals((int) status, 200, "Expected status code failed."); - if (!whatCalled[STATUS]) { - Assert.fail("onStatusReceived not called."); - } - if (!whatCalled[COMPLETED]) { - Assert.fail("onCompleted not called."); - } - if (whatCalled[OTHER]) { - Assert.fail("Other method of AsyncHandler got called."); + if (!whatCalled[STATUS]) { + Assert.fail("onStatusReceived not called."); + } + if (!whatCalled[COMPLETED]) { + Assert.fail("onCompleted not called."); + } + if (whatCalled[OTHER]) { + Assert.fail("Other method of AsyncHandler got called."); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void asyncOptionsTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(null); - c.prepareOptions("http://www.apache.org/").execute(new AsyncHandlerAdapter() { - - @Override - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - FluentCaseInsensitiveStringsMap h = content.getHeaders(); - Assert.assertNotNull(h); - Assert.assertEquals(h.getJoinedValue("Allow", ", "), "GET,HEAD,POST,OPTIONS,TRACE"); - return STATE.ABORT; - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final CountDownLatch l = new CountDownLatch(1); + client.prepareOptions("http://www.apache.org/").execute(new AsyncHandlerAdapter() { - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - return STATE.CONTINUE; - } + @Override + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + FluentCaseInsensitiveStringsMap h = content.getHeaders(); + Assert.assertNotNull(h); + String result = h.getJoinedValue("Allow", ", "); + String[] resultParts = result.split(","); + String[] expected = "OPTIONS,GET,HEAD,POST,TRACE".split(","); + Arrays.sort(resultParts); + Arrays.sort(expected); + Assert.assertEquals(expected, resultParts); + return STATE.ABORT; + } - @Override - public String onCompleted() throws Exception { - try { - return "OK"; - } finally { - l.countDown(); + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + return STATE.CONTINUE; } - } - }); - if (!l.await(20, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + @Override + public String onCompleted() throws Exception { + try { + return "OK"; + } finally { + l.countDown(); + } + } + }); + + if (!l.await(20, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void closeConnectionTest() throws Throwable { - final CountDownLatch l = new CountDownLatch(1); - AsyncHttpClient c = getAsyncHttpClient(null); - - Response r = c.prepareGet(getTargetUrl()).execute(new AsyncHandler() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response r = client.prepareGet(getTargetUrl()).execute(new AsyncHandler() { - private Response.ResponseBuilder builder = new Response.ResponseBuilder(); + private Response.ResponseBuilder builder = new Response.ResponseBuilder(); - public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { - builder.accumulate(content); - return STATE.CONTINUE; - } + public STATE onHeadersReceived(HttpResponseHeaders content) throws Exception { + builder.accumulate(content); + return STATE.CONTINUE; + } - public void onThrowable(Throwable t) { - } + public void onThrowable(Throwable t) { + } - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - builder.accumulate(content); + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + builder.accumulate(content); - if (content.isLast()) { - content.markUnderlyingConnectionAsClosed(); + if (content.isLast()) { + content.markUnderlyingConnectionAsClosed(); + } + return STATE.CONTINUE; } - return STATE.CONTINUE; - } - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - builder.accumulate(responseStatus); + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + builder.accumulate(responseStatus); - return STATE.CONTINUE; - } + return STATE.CONTINUE; + } - public Response onCompleted() throws Exception { - return builder.build(); - } - }).get(); + public Response onCompleted() throws Exception { + return builder.build(); + } + }).get(); - Assert.assertNotNull(r); - Assert.assertEquals(r.getStatusCode(), 200); - c.close(); + Assert.assertNotNull(r); + Assert.assertEquals(r.getStatusCode(), 200); + } finally { + client.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/AsyncStreamLifecycleTest.java b/src/test/java/com/ning/http/client/async/AsyncStreamLifecycleTest.java index faeaee7cc2..d12e647e58 100644 --- a/src/test/java/com/ning/http/client/async/AsyncStreamLifecycleTest.java +++ b/src/test/java/com/ning/http/client/async/AsyncStreamLifecycleTest.java @@ -47,7 +47,7 @@ /** * Tests default asynchronous life cycle. - * + * * @author Hubert Iwaniuk */ public abstract class AsyncStreamLifecycleTest extends AbstractBasicTest { @@ -63,8 +63,7 @@ public void tearDownGlobal() throws Exception { @Override public AbstractHandler configureHandler() throws Exception { return new AbstractHandler() { - public void handle(String s, Request request, HttpServletRequest req, final HttpServletResponse resp) - throws IOException, ServletException { + public void handle(String s, Request request, HttpServletRequest req, final HttpServletResponse resp) throws IOException, ServletException { resp.setContentType("text/plain;charset=utf-8"); resp.setStatus(200); final Continuation continuation = ContinuationSupport.getContinuation(req); @@ -100,62 +99,64 @@ public void run() { }; } - //TODO Netty only. + // TODO Netty only. - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testStream() throws IOException { - AsyncHttpClient ahc = getAsyncHttpClient(null); - final AtomicBoolean err = new AtomicBoolean(false); - final LinkedBlockingQueue queue = new LinkedBlockingQueue(); - final AtomicBoolean status = new AtomicBoolean(false); - final AtomicInteger headers = new AtomicInteger(0); - final CountDownLatch latch = new CountDownLatch(1); - ahc.executeRequest(ahc.prepareGet(getTargetUrl()).build(), new AsyncHandler() { - public void onThrowable(Throwable t) { - fail("Got throwable.", t); - err.set(true); - } + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final AtomicBoolean err = new AtomicBoolean(false); + final LinkedBlockingQueue queue = new LinkedBlockingQueue(); + final AtomicBoolean status = new AtomicBoolean(false); + final AtomicInteger headers = new AtomicInteger(0); + final CountDownLatch latch = new CountDownLatch(1); + client.executeRequest(client.prepareGet(getTargetUrl()).build(), new AsyncHandler() { + public void onThrowable(Throwable t) { + fail("Got throwable.", t); + err.set(true); + } - public STATE onBodyPartReceived(HttpResponseBodyPart e) throws Exception { - String s = new String(e.getBodyPartBytes()); - log.info("got part: {}", s); - if (s.equals("")) { - //noinspection ThrowableInstanceNeverThrown - log.warn("Sampling stacktrace.", - new Throwable("trace that, we should not get called for empty body.")); + public STATE onBodyPartReceived(HttpResponseBodyPart e) throws Exception { + String s = new String(e.getBodyPartBytes()); + log.info("got part: {}", s); + if (s.isEmpty()) { + // noinspection ThrowableInstanceNeverThrown + log.warn("Sampling stacktrace.", new Throwable("trace that, we should not get called for empty body.")); + } + queue.put(s); + return STATE.CONTINUE; } - queue.put(s); - return STATE.CONTINUE; - } - public STATE onStatusReceived(HttpResponseStatus e) throws Exception { - status.set(true); - return STATE.CONTINUE; - } + public STATE onStatusReceived(HttpResponseStatus e) throws Exception { + status.set(true); + return STATE.CONTINUE; + } - public STATE onHeadersReceived(HttpResponseHeaders e) throws Exception { - if (headers.incrementAndGet() == 2) { - throw new Exception("Analyze this."); + public STATE onHeadersReceived(HttpResponseHeaders e) throws Exception { + if (headers.incrementAndGet() == 2) { + throw new Exception("Analyze this."); + } + return STATE.CONTINUE; } - return STATE.CONTINUE; - } - public Object onCompleted() throws Exception { - latch.countDown(); - return null; + public Object onCompleted() throws Exception { + latch.countDown(); + return null; + } + }); + try { + assertTrue(latch.await(1, TimeUnit.SECONDS), "Latch failed."); + } catch (InterruptedException e) { + fail("Interrupted.", e); } - }); - try { - assertTrue(latch.await(1, TimeUnit.SECONDS), "Latch failed."); - } catch (InterruptedException e) { - fail("Interrupted.", e); + assertFalse(err.get()); + assertEquals(queue.size(), 2); + assertTrue(queue.contains("part1")); + assertTrue(queue.contains("part2")); + assertTrue(status.get()); + assertEquals(headers.get(), 1); + } finally { + client.close(); } - assertFalse(err.get()); - assertEquals(queue.size(), 2); - assertTrue(queue.contains("part1")); - assertTrue(queue.contains("part2")); - assertTrue(status.get()); - assertEquals(headers.get(), 1); - ahc.close(); } } diff --git a/src/test/java/com/ning/http/client/async/AuthTimeoutTest.java b/src/test/java/com/ning/http/client/async/AuthTimeoutTest.java index 37b83eff80..b5a4c99618 100644 --- a/src/test/java/com/ning/http/client/async/AuthTimeoutTest.java +++ b/src/test/java/com/ning/http/client/async/AuthTimeoutTest.java @@ -39,7 +39,6 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -50,8 +49,7 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; -public abstract class AuthTimeoutTest - extends AbstractBasicTest { +public abstract class AuthTimeoutTest extends AbstractBasicTest { private final static String user = "user"; @@ -59,8 +57,7 @@ public abstract class AuthTimeoutTest protected AsyncHttpClient client; - public void setUpServer(String auth) - throws Exception { + public void setUpServer(String auth) throws Exception { server = new Server(); Logger root = Logger.getRootLogger(); root.setLevel(Level.DEBUG); @@ -79,7 +76,7 @@ public void setUpServer(String auth) Constraint constraint = new Constraint(); constraint.setName(auth); - constraint.setRoles(new String[]{user, admin}); + constraint.setRoles(new String[] { user, admin }); constraint.setAuthenticate(true); ConstraintMapping mapping = new ConstraintMapping(); @@ -106,10 +103,8 @@ public void setUpServer(String auth) log.info("Local HTTP server started successfully"); } - private class SimpleHandler - extends AbstractHandler { - public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { + private class SimpleHandler extends AbstractHandler { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // NOTE: handler sends less bytes than are given in Content-Length, which should lead to timeout @@ -129,150 +124,138 @@ public void handle(String s, Request r, HttpServletRequest request, HttpServletR } } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void basicAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void basicAuthTimeoutTest() throws Exception { setUpServer(Constraint.__BASIC_AUTH); - - Future f = execute(false); try { - f.get(); - fail("expected timeout"); - } - catch (Exception e) { - inspectException(e); + Future f = execute(false); + try { + f.get(); + fail("expected timeout"); + } catch (Exception e) { + inspectException(e); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void basicPreemptiveAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void basicPreemptiveAuthTimeoutTest() throws Exception { setUpServer(Constraint.__BASIC_AUTH); - - Future f = execute(true); try { - f.get(); - fail("expected timeout"); - } - catch (Exception e) { - inspectException(e); + Future f = execute(true); + try { + f.get(); + fail("expected timeout"); + } catch (Exception e) { + inspectException(e); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void digestAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void digestAuthTimeoutTest() throws Exception { setUpServer(Constraint.__DIGEST_AUTH); - - Future f = execute(false); try { - f.get(); - fail("expected timeout"); - } - catch (Exception e) { - inspectException(e); + Future f = execute(false); + try { + f.get(); + fail("expected timeout"); + } catch (Exception e) { + inspectException(e); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void digestPreemptiveAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void digestPreemptiveAuthTimeoutTest() throws Exception { setUpServer(Constraint.__DIGEST_AUTH); - Future f = execute(true); try { + Future f = execute(true); f.get(); fail("expected timeout"); - } - catch (Exception e) { + } catch (Exception e) { inspectException(e); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void basicFutureAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void basicFutureAuthTimeoutTest() throws Exception { setUpServer(Constraint.__BASIC_AUTH); - - Future f = execute(false); try { + Future f = execute(false); f.get(1, TimeUnit.SECONDS); fail("expected timeout"); - } - catch (Exception e) { + } catch (Exception e) { inspectException(e); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void basicFuturePreemptiveAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void basicFuturePreemptiveAuthTimeoutTest() throws Exception { setUpServer(Constraint.__BASIC_AUTH); - - Future f = execute(true); try { + Future f = execute(true); f.get(1, TimeUnit.SECONDS); fail("expected timeout"); - } - catch (Exception e) { + } catch (Exception e) { inspectException(e); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void digestFutureAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void digestFutureAuthTimeoutTest() throws Exception { setUpServer(Constraint.__DIGEST_AUTH); - - Future f = execute(false); try { + Future f = execute(false); f.get(1, TimeUnit.SECONDS); fail("expected timeout"); - } - catch (Exception e) { + } catch (Exception e) { inspectException(e); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}, enabled = false) - public void digestFuturePreemptiveAuthTimeoutTest() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = false) + public void digestFuturePreemptiveAuthTimeoutTest() throws Exception { setUpServer(Constraint.__DIGEST_AUTH); - Future f = execute(true); try { + Future f = execute(true); f.get(1, TimeUnit.SECONDS); fail("expected timeout"); - } - catch (Exception e) { + } catch (Exception e) { inspectException(e); + } finally { + client.close(); } - client.close(); } protected void inspectException(Throwable t) { assertNotNull(t.getCause()); assertEquals(t.getCause().getClass(), IOException.class); - if (!t.getCause().getMessage().startsWith("Remotely Closed")){ + if (!t.getCause().getMessage().startsWith("Remotely Closed")) { fail(); - }; + } + ; } - protected Future execute(boolean preemptive) - throws IOException { - client = - getAsyncHttpClient( - new AsyncHttpClientConfig.Builder().setIdleConnectionInPoolTimeoutInMs(2000).setConnectionTimeoutInMs(20000).setRequestTimeoutInMs(2000).build()); - AsyncHttpClient.BoundRequestBuilder r = - client.prepareGet(getTargetUrl()).setRealm(realm(preemptive)).setHeader("X-Content", - "Test"); + protected Future execute(boolean preemptive) throws IOException { + client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setIdleConnectionInPoolTimeoutInMs(2000).setConnectionTimeoutInMs(20000).setRequestTimeoutInMs(2000).build()); + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()).setRealm(realm(preemptive)).setHeader("X-Content", "Test"); Future f = r.execute(); return f; } @@ -287,8 +270,7 @@ protected String getTargetUrl() { } @Override - public AbstractHandler configureHandler() - throws Exception { + public AbstractHandler configureHandler() throws Exception { return new SimpleHandler(); } } diff --git a/src/test/java/com/ning/http/client/async/BasicAuthTest.java b/src/test/java/com/ning/http/client/async/BasicAuthTest.java index bbdefbd3e7..9a2d0a2d29 100644 --- a/src/test/java/com/ning/http/client/async/BasicAuthTest.java +++ b/src/test/java/com/ning/http/client/async/BasicAuthTest.java @@ -23,9 +23,8 @@ import com.ning.http.client.HttpResponseStatus; import com.ning.http.client.Realm; import com.ning.http.client.Response; -import com.ning.http.client.SimpleAsyncHttpClient; -import com.ning.http.client.consumers.AppendableBodyConsumer; import com.ning.http.client.generators.InputStreamBodyGenerator; + import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; @@ -48,13 +47,13 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -62,7 +61,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -74,15 +72,14 @@ public abstract class BasicAuthTest extends AbstractBasicTest { protected final static String admin = "admin"; private Server server2; - + @BeforeClass(alwaysRun = true) @Override public void setUpGlobal() throws Exception { server = new Server(); Logger root = Logger.getRootLogger(); root.setLevel(Level.DEBUG); - root.addAppender(new ConsoleAppender( - new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); + root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); port1 = findFreePort(); Connector listener = new SelectChannelConnector(); @@ -97,7 +94,7 @@ public void setUpGlobal() throws Exception { Constraint constraint = new Constraint(); constraint.setName(Constraint.__BASIC_AUTH); - constraint.setRoles(new String[]{user, admin}); + constraint.setRoles(new String[] { user, admin }); constraint.setAuthenticate(true); ConstraintMapping mapping = new ConstraintMapping(); @@ -136,8 +133,7 @@ private String getFileContent(final File file) { } return sb.toString(); } - throw new IllegalArgumentException("File does not exist or cannot be read: " - + file.getCanonicalPath()); + throw new IllegalArgumentException("File does not exist or cannot be read: " + file.getCanonicalPath()); } catch (IOException ioe) { throw new IllegalStateException(ioe); } finally { @@ -166,7 +162,7 @@ private void setUpSecondServer() throws Exception { Constraint constraint = new Constraint(); constraint.setName(Constraint.__DIGEST_AUTH); - constraint.setRoles(new String[]{user, admin}); + constraint.setRoles(new String[] { user, admin }); constraint.setAuthenticate(true); ConstraintMapping mapping = new ConstraintMapping(); @@ -180,8 +176,7 @@ private void setUpSecondServer() throws Exception { ConstraintSecurityHandler security = new ConstraintSecurityHandler() { @Override - public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3) - throws IOException, ServletException { + public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3) throws IOException, ServletException { System.err.println("request in security handler"); System.err.println("Authorization: " + arg2.getHeader("Authorization")); System.err.println("RequestUri: " + arg2.getRequestURI()); @@ -208,12 +203,7 @@ private void stopSecondServer() throws Exception { private class RedirectHandler extends AbstractHandler { - private AtomicBoolean redirectOnce = new AtomicBoolean(false); - - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { System.err.println("redirecthandler"); System.err.println("request: " + request.getRequestURI()); @@ -240,10 +230,7 @@ public void handle(String s, } private class SimpleHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (request.getHeader("X-401") != null) { response.setStatus(401); @@ -256,7 +243,6 @@ public void handle(String s, response.addHeader("X-Content-Length", String.valueOf(request.getContentLength())); response.setStatus(200); - int size = 10 * 1024; if (request.getContentLength() > 0) { size = request.getContentLength(); @@ -273,28 +259,30 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()) - .setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void redirectAndBasicAuthTest() throws Exception, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = null; try { setUpSecondServer(); client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).setMaximumNumberOfRedirects(10).build()); AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl2()) - // .setHeader( "X-302", "/bla" ) + // .setHeader( "X-302", "/bla" ) .setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); Future f = r.execute(); @@ -304,7 +292,8 @@ public void redirectAndBasicAuthTest() throws Exception, ExecutionException, Tim assertNotNull(resp.getHeader("X-Auth")); } finally { - if (client != null) client.close(); + if (client != null) + client.close(); stopSecondServer(); } } @@ -318,152 +307,165 @@ protected String getTargetUrl2() { return "http://127.0.0.1/" + port2 + "/uff"; } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basic401Test() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()) - .setHeader("X-401", "401").setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - - Future f = r.execute(new AsyncHandler() { + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()).setHeader("X-401", "401").setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - private HttpResponseStatus status; + Future f = r.execute(new AsyncHandler() { + private HttpResponseStatus status; - public void onThrowable(Throwable t) { + public void onThrowable(Throwable t) { - } + } - public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - return STATE.CONTINUE; - } + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + return STATE.CONTINUE; + } - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - this.status = responseStatus; + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + this.status = responseStatus; - if (status.getStatusCode() != 200) { - return STATE.ABORT; + if (status.getStatusCode() != 200) { + return STATE.ABORT; + } + return STATE.CONTINUE; } - return STATE.CONTINUE; - } - public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { - return STATE.CONTINUE; - } + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + return STATE.CONTINUE; + } - public Integer onCompleted() throws Exception { - return status.getStatusCode(); - } - }); - Integer statusCode = f.get(10, TimeUnit.SECONDS); - assertNotNull(statusCode); - assertEquals(statusCode.intValue(), 401); - client.close(); + public Integer onCompleted() throws Exception { + return status.getStatusCode(); + } + }); + Integer statusCode = f.get(10, TimeUnit.SECONDS); + assertNotNull(statusCode); + assertEquals(statusCode.intValue(), 401); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthTestPreemtiveTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()) - .setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).setUsePreemptiveAuth(true).build()); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).setUsePreemptiveAuth(true).build()); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthNegativeTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()) - .setRealm((new Realm.RealmBuilder()).setPrincipal("fake").setPassword(admin).build()); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), 401); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()).setRealm((new Realm.RealmBuilder()).setPrincipal("fake").setPassword(admin).build()); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), 401); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthInputStreamTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - ByteArrayInputStream is = new ByteArrayInputStream("test".getBytes()); - AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()) - .setBody(is).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - - Future f = r.execute(); - Response resp = f.get(30, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), "test"); - client.close(); + try { + ByteArrayInputStream is = new ByteArrayInputStream("test".getBytes()); + AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(is).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); + + Future f = r.execute(); + Response resp = f.get(30, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), "test"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthFileTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - final String fileContent = getFileContent(file); - - AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()) - .setBody(file).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), fileContent); - client.close(); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + final String fileContent = getFileContent(file); + + AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(file).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), fileContent); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthAsyncConfigTest() throws Throwable { - AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder() - .setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()).build()); - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - final String fileContent = getFileContent(file); - - AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(file); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), fileContent); - client.close(); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()).build()); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + final String fileContent = getFileContent(file); + + AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(file); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), fileContent); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicAuthFileNoKeepAliveTest() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(false).build()); - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - final String fileContent = getFileContent(file); - - AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()) - .setBody(file).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), fileContent); - client.close(); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + final String fileContent = getFileContent(file); + + AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(file).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), fileContent); + } finally { + client.close(); + } } @Override @@ -471,40 +473,41 @@ public AbstractHandler configureHandler() throws Exception { return new SimpleHandler(); } - @Test(groups = {"standalone", "default_provider"}) - public void StringBufferBodyConsumerTest() throws Throwable { - - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder() - .setRealmPrincipal(user) - .setRealmPassword(admin) - .setUrl(getTargetUrl()) - .setHeader("Content-Type", "text/html").build(); - - StringBuilder s = new StringBuilder(); - Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new AppendableBodyConsumer(s)); - - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(s.toString(), MY_MESSAGE); - assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK); - assertNotNull(response.getHeader("X-Auth")); + @Test(groups = { "standalone", "default_provider" }) + public void stringBuilderBodyConsumerTest() throws Throwable { + AsyncHttpClient client = getAsyncHttpClient(null); + + try { + AsyncHttpClient.BoundRequestBuilder r = client.preparePost(getTargetUrl()) + .setHeader("Content-Type", "text/html") + .setBody(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes()))) + .setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); + Future f = r.execute(); - client.close(); + System.out.println("waiting for response"); + Response response = f.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getResponseBody(), MY_MESSAGE); + assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK); + assertNotNull(response.getHeader("X-Auth")); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void noneAuthTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()) - .setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); - - Future f = r.execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertNotNull(resp.getHeader("X-Auth")); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet(getTargetUrl()).setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).build()); + + Future f = r.execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertNotNull(resp.getHeader("X-Auth")); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } } } - diff --git a/src/test/java/com/ning/http/client/async/BasicHttpsTest.java b/src/test/java/com/ning/http/client/async/BasicHttpsTest.java index bcd508e2d6..2d2c180fd2 100644 --- a/src/test/java/com/ning/http/client/async/BasicHttpsTest.java +++ b/src/test/java/com/ning/http/client/async/BasicHttpsTest.java @@ -65,10 +65,7 @@ public abstract class BasicHttpsTest extends AbstractBasicTest { public static class EchoHandler extends AbstractHandler { /* @Override */ - public void handle(String pathInContext, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws ServletException, IOException { + public void handle(String pathInContext, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException { httpResponse.setContentType("text/html; charset=utf-8"); Enumeration e = httpRequest.getHeaderNames(); @@ -122,10 +119,16 @@ public void handle(String pathInContext, size = httpRequest.getContentLength(); } byte[] bytes = new byte[size]; + int pos = 0; if (bytes.length > 0) { - //noinspection ResultOfMethodCallIgnored - int read = httpRequest.getInputStream().read(bytes); - httpResponse.getOutputStream().write(bytes, 0, read); + // noinspection ResultOfMethodCallIgnored + int read = 0; + while (read != -1) { + read = httpRequest.getInputStream().read(bytes, pos, bytes.length - pos); + pos += read; + } + + httpResponse.getOutputStream().write(bytes); } httpResponse.setStatus(200); @@ -161,8 +164,7 @@ protected int findFreePort() throws IOException { socket = new ServerSocket(0); return socket.getLocalPort(); - } - finally { + } finally { if (socket != null) { socket.close(); } @@ -203,110 +205,95 @@ public void setUpGlobal() throws Exception { log.info("Local HTTP server started successfully"); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void zeroCopyPostTest() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new Builder().setSSLContext(createSSLContext()).build()); - - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - - Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); - Response resp = f.get(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), "This is a simple test file"); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); + Response resp = f.get(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), "This is a simple test file"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void multipleSSLRequestsTest() throws Throwable { final AsyncHttpClient c = getAsyncHttpClient(new Builder().setSSLContext(createSSLContext()).build()); + try { + String body = "hello there"; - String body = "hello there"; - - // once - Response response = c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); + // once + Response response = c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getResponseBody(), body); + assertEquals(response.getResponseBody(), body); - // twice - response = c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); + // twice + response = c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getResponseBody(), body); - c.close(); + assertEquals(response.getResponseBody(), body); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void multipleSSLWithoutCacheTest() throws Throwable { final AsyncHttpClient c = getAsyncHttpClient(new Builder().setSSLContext(createSSLContext()).setAllowSslConnectionPool(false).build()); + try { + String body = "hello there"; + c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute(); - String body = "hello there"; - c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute(); - - c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute(); + c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute(); - Response response = c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(); + Response response = c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(); - assertEquals(response.getResponseBody(), body); - c.close(); + assertEquals(response.getResponseBody(), body); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void reconnectsAfterFailedCertificationPath() throws Throwable { final AsyncHttpClient c = getAsyncHttpClient(new Builder().setSSLContext(createSSLContext()).build()); - - final String body = "hello there"; - - TRUST_SERVER_CERT.set(false); try { - // first request fails because server certificate is rejected + final String body = "hello there"; + + TRUST_SERVER_CERT.set(false); try { - c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); - } - catch (final ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof ConnectException) { - assertNotNull(cause.getCause()); - assertTrue(cause.getCause() instanceof SSLHandshakeException); - } else { - assertTrue(cause instanceof SSLHandshakeException); + // first request fails because server certificate is rejected + try { + c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); + } catch (final ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + assertNotNull(cause.getCause()); + assertTrue(cause.getCause() instanceof SSLHandshakeException); + } else { + assertTrue(cause instanceof SSLHandshakeException); + } } - } - TRUST_SERVER_CERT.set(true); + TRUST_SERVER_CERT.set(true); - // second request should succeed - final Response response = c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); + // second request should succeed + final Response response = c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getResponseBody(), body); - } - finally { - TRUST_SERVER_CERT.set(true); + assertEquals(response.getResponseBody(), body); + } finally { + TRUST_SERVER_CERT.set(true); + } + } finally { + c.close(); } - c.close(); } private static SSLContext createSSLContext() { @@ -323,15 +310,14 @@ private static SSLContext createSSLContext() { // Initialize the SSLContext to work with our key managers. KeyManager[] keyManagers = kmf.getKeyManagers(); - TrustManager[] trustManagers = new TrustManager[]{DUMMY_TRUST_MANAGER}; + TrustManager[] trustManagers = new TrustManager[] { DUMMY_TRUST_MANAGER }; SecureRandom secureRandom = new SecureRandom(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, trustManagers, secureRandom); return sslContext; - } - catch (Exception e) { + } catch (Exception e) { throw new Error("Failed to initialize the server-side SSLContext", e); } } @@ -342,17 +328,14 @@ public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - public void checkClientTrusted( - X509Certificate[] chain, String authType) throws CertificateException { + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } - public void checkServerTrusted( - X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!TRUST_SERVER_CERT.get()) { throw new CertificateException("Server certificate not trusted."); } } }; - } diff --git a/src/test/java/com/ning/http/client/async/BodyChunkTest.java b/src/test/java/com/ning/http/client/async/BodyChunkTest.java index 8db2bfb649..c86605f704 100644 --- a/src/test/java/com/ning/http/client/async/BodyChunkTest.java +++ b/src/test/java/com/ning/http/client/async/BodyChunkTest.java @@ -31,9 +31,8 @@ public abstract class BodyChunkTest extends AbstractBasicTest { private final static String MY_MESSAGE = "my message"; - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void negativeContentTypeTest() throws Throwable { - AsyncHttpClientConfig.Builder confbuilder = new AsyncHttpClientConfig.Builder(); confbuilder = confbuilder.setConnectionTimeoutInMs(100); confbuilder = confbuilder.setMaximumConnectionsTotal(50); @@ -41,23 +40,19 @@ public void negativeContentTypeTest() throws Throwable { // Create client AsyncHttpClient client = getAsyncHttpClient(confbuilder.build()); + try { + RequestBuilder requestBuilder = new RequestBuilder("POST").setUrl(getTargetUrl()).setHeader("Content-Type", "message/rfc822"); - RequestBuilder requestBuilder = new RequestBuilder("POST") - .setUrl(getTargetUrl()) - .setHeader("Content-Type", "message/rfc822"); - - requestBuilder.setBody(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes()))); + requestBuilder.setBody(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes()))); - Future future = client.executeRequest(requestBuilder.build()); + Future future = client.executeRequest(requestBuilder.build()); - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getResponseBody(), MY_MESSAGE); - - client.close(); + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getResponseBody(), MY_MESSAGE); + } finally { + client.close(); + } } - } - - diff --git a/src/test/java/com/ning/http/client/async/BodyDeferringAsyncHandlerTest.java b/src/test/java/com/ning/http/client/async/BodyDeferringAsyncHandlerTest.java index 48e1836a65..7960b0bfb2 100644 --- a/src/test/java/com/ning/http/client/async/BodyDeferringAsyncHandlerTest.java +++ b/src/test/java/com/ning/http/client/async/BodyDeferringAsyncHandlerTest.java @@ -44,9 +44,7 @@ public abstract class BodyDeferringAsyncHandlerTest extends AbstractBasicTest { public static class SlowAndBigHandler extends AbstractHandler { - public void handle(String pathInContext, Request request, - HttpServletRequest httpRequest, HttpServletResponse httpResponse) - throws IOException, ServletException { + public void handle(String pathInContext, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { // 512MB large download // 512 * 1024 * 1024 = 536870912 @@ -56,8 +54,7 @@ public void handle(String pathInContext, Request request, httpResponse.flushBuffer(); - final boolean wantFailure = httpRequest - .getHeader("X-FAIL-TRANSFER") != null; + final boolean wantFailure = httpRequest.getHeader("X-FAIL-TRANSFER") != null; final boolean wantSlow = httpRequest.getHeader("X-SLOW") != null; OutputStream os = httpResponse.getOutputStream(); @@ -104,8 +101,7 @@ public int getByteCount() { } // simple stream copy just to "consume". It closes streams. - public static void copy(InputStream in, OutputStream out) - throws IOException { + public static void copy(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { @@ -122,163 +118,148 @@ public AbstractHandler configureHandler() throws Exception { public AsyncHttpClientConfig getAsyncHttpClientConfig() { // for this test brevity's sake, we are limiting to 1 retries - return new AsyncHttpClientConfig.Builder().setMaxRequestRetry(0) - .setRequestTimeoutInMs(10000).build(); + return new AsyncHttpClientConfig.Builder().setMaxRequestRetry(0).setRequestTimeoutInMs(10000).build(); } @Test(groups = { "standalone", "default_provider" }) - public void deferredSimple() throws IOException, ExecutionException, - TimeoutException, InterruptedException { + public void deferredSimple() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(getAsyncHttpClientConfig()); - AsyncHttpClient.BoundRequestBuilder r = client - .prepareGet("http://127.0.0.1/" + port1 + "/deferredSimple"); - - CountingOutputStream cos = new CountingOutputStream(); - BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(cos); - Future f = r.execute(bdah); - Response resp = bdah.getResponse(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals( - true, - resp.getHeader("content-length").equals( - String.valueOf(HALF_GIG))); - // we got headers only, it's probably not all yet here (we have BIG file - // downloading) - assertEquals(true, HALF_GIG >= cos.getByteCount()); - - // now be polite and wait for body arrival too (otherwise we would be - // dropping the "line" on server) - f.get(); - // it all should be here now - assertEquals(true, HALF_GIG == cos.getByteCount()); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/deferredSimple"); + + CountingOutputStream cos = new CountingOutputStream(); + BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(cos); + Future f = r.execute(bdah); + Response resp = bdah.getResponse(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(true, resp.getHeader("content-length").equals(String.valueOf(HALF_GIG))); + // we got headers only, it's probably not all yet here (we have BIG file + // downloading) + assertEquals(true, HALF_GIG >= cos.getByteCount()); + + // now be polite and wait for body arrival too (otherwise we would be + // dropping the "line" on server) + f.get(); + // it all should be here now + assertEquals(true, HALF_GIG == cos.getByteCount()); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }, enabled = false) - public void deferredSimpleWithFailure() throws IOException, - ExecutionException, TimeoutException, InterruptedException { + public void deferredSimpleWithFailure() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(getAsyncHttpClientConfig()); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet( - "http://127.0.0.1/" + port1 + "/deferredSimpleWithFailure") - .addHeader("X-FAIL-TRANSFER", Boolean.TRUE.toString()); - - CountingOutputStream cos = new CountingOutputStream(); - BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(cos); - Future f = r.execute(bdah); - Response resp = bdah.getResponse(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals( - true, - resp.getHeader("content-length").equals( - String.valueOf(HALF_GIG))); - // we got headers only, it's probably not all yet here (we have BIG file - // downloading) - assertEquals(true, HALF_GIG >= cos.getByteCount()); - - // now be polite and wait for body arrival too (otherwise we would be - // dropping the "line" on server) try { - f.get(); - Assert.fail("get() should fail with IOException!"); - } catch (Exception e) { - // good + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/deferredSimpleWithFailure").addHeader("X-FAIL-TRANSFER", Boolean.TRUE.toString()); + + CountingOutputStream cos = new CountingOutputStream(); + BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(cos); + Future f = r.execute(bdah); + Response resp = bdah.getResponse(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(true, resp.getHeader("content-length").equals(String.valueOf(HALF_GIG))); + // we got headers only, it's probably not all yet here (we have BIG file + // downloading) + assertEquals(true, HALF_GIG >= cos.getByteCount()); + + // now be polite and wait for body arrival too (otherwise we would be + // dropping the "line" on server) + try { + f.get(); + Assert.fail("get() should fail with IOException!"); + } catch (Exception e) { + // good + } + // it's incomplete, there was an error + assertEquals(false, HALF_GIG == cos.getByteCount()); + } finally { + client.close(); } - // it's incomplete, there was an error - assertEquals(false, HALF_GIG == cos.getByteCount()); - client.close(); } @Test(groups = { "standalone", "default_provider" }) - public void deferredInputStreamTrick() throws IOException, - ExecutionException, TimeoutException, InterruptedException { + public void deferredInputStreamTrick() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(getAsyncHttpClientConfig()); - AsyncHttpClient.BoundRequestBuilder r = client - .prepareGet("http://127.0.0.1/" + port1 - + "/deferredInputStreamTrick"); - - PipedOutputStream pos = new PipedOutputStream(); - PipedInputStream pis = new PipedInputStream(pos); - BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(pos); - - Future f = r.execute(bdah); - - BodyDeferringInputStream is = new BodyDeferringInputStream(f, bdah, pis); - - Response resp = is.getAsapResponse(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals( - true, - resp.getHeader("content-length").equals( - String.valueOf(HALF_GIG))); - // "consume" the body, but our code needs input stream - CountingOutputStream cos = new CountingOutputStream(); - copy(is, cos); - - // now we don't need to be polite, since consuming and closing - // BodyDeferringInputStream does all. - // it all should be here now - assertEquals(true, HALF_GIG == cos.getByteCount()); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/deferredInputStreamTrick"); + + PipedOutputStream pos = new PipedOutputStream(); + PipedInputStream pis = new PipedInputStream(pos); + BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(pos); + + Future f = r.execute(bdah); + + BodyDeferringInputStream is = new BodyDeferringInputStream(f, bdah, pis); + + Response resp = is.getAsapResponse(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(true, resp.getHeader("content-length").equals(String.valueOf(HALF_GIG))); + // "consume" the body, but our code needs input stream + CountingOutputStream cos = new CountingOutputStream(); + copy(is, cos); + + // now we don't need to be polite, since consuming and closing + // BodyDeferringInputStream does all. + // it all should be here now + assertEquals(true, HALF_GIG == cos.getByteCount()); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) - public void deferredInputStreamTrickWithFailure() throws IOException, - ExecutionException, TimeoutException, InterruptedException { + public void deferredInputStreamTrickWithFailure() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(getAsyncHttpClientConfig()); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet( - "http://127.0.0.1/" + port1 - + "/deferredInputStreamTrickWithFailure").addHeader( - "X-FAIL-TRANSFER", Boolean.TRUE.toString()); - - PipedOutputStream pos = new PipedOutputStream(); - PipedInputStream pis = new PipedInputStream(pos); - BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(pos); - - Future f = r.execute(bdah); - - BodyDeferringInputStream is = new BodyDeferringInputStream(f, bdah, pis); - - Response resp = is.getAsapResponse(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals( - true, - resp.getHeader("content-length").equals( - String.valueOf(HALF_GIG))); - // "consume" the body, but our code needs input stream - CountingOutputStream cos = new CountingOutputStream(); try { - copy(is, cos); - Assert.fail("InputStream consumption should fail with IOException!"); - } catch (IOException e) { - // good! + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/deferredInputStreamTrickWithFailure").addHeader("X-FAIL-TRANSFER", Boolean.TRUE.toString()); + + PipedOutputStream pos = new PipedOutputStream(); + PipedInputStream pis = new PipedInputStream(pos); + BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(pos); + + Future f = r.execute(bdah); + + BodyDeferringInputStream is = new BodyDeferringInputStream(f, bdah, pis); + + Response resp = is.getAsapResponse(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(true, resp.getHeader("content-length").equals(String.valueOf(HALF_GIG))); + // "consume" the body, but our code needs input stream + CountingOutputStream cos = new CountingOutputStream(); + try { + copy(is, cos); + Assert.fail("InputStream consumption should fail with IOException!"); + } catch (IOException e) { + // good! + } + } finally { + client.close(); } - client.close(); } @Test(groups = { "standalone", "default_provider" }) - public void testConnectionRefused() throws IOException, ExecutionException, - TimeoutException, InterruptedException { - int newPortWithoutAnyoneListening = findFreePort(); + public void testConnectionRefused() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(getAsyncHttpClientConfig()); - AsyncHttpClient.BoundRequestBuilder r = client - .prepareGet("http://127.0.0.1/" + newPortWithoutAnyoneListening - + "/testConnectionRefused"); - - CountingOutputStream cos = new CountingOutputStream(); - BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(cos); - r.execute(bdah); try { - bdah.getResponse(); - Assert.fail("IOException should be thrown here!"); - } catch (IOException e) { - // good + int newPortWithoutAnyoneListening = findFreePort(); + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + newPortWithoutAnyoneListening + "/testConnectionRefused"); + + CountingOutputStream cos = new CountingOutputStream(); + BodyDeferringAsyncHandler bdah = new BodyDeferringAsyncHandler(cos); + r.execute(bdah); + try { + bdah.getResponse(); + Assert.fail("IOException should be thrown here!"); + } catch (IOException e) { + // good + } + } finally { + client.close(); } - - client.close(); } - } diff --git a/src/test/java/com/ning/http/client/async/ByteBufferCapacityTest.java b/src/test/java/com/ning/http/client/async/ByteBufferCapacityTest.java index ea8e7134fb..b32caf72ce 100644 --- a/src/test/java/com/ning/http/client/async/ByteBufferCapacityTest.java +++ b/src/test/java/com/ning/http/client/async/ByteBufferCapacityTest.java @@ -12,15 +12,10 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.HttpResponseBodyPart; -import com.ning.http.client.Response; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -28,21 +23,25 @@ import java.io.OutputStream; import java.util.Enumeration; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import static org.testng.Assert.*; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.annotations.Test; + +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.HttpResponseBodyPart; +import com.ning.http.client.Response; public abstract class ByteBufferCapacityTest extends AbstractBasicTest { - private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" - + UUID.randomUUID().toString().substring(0, 8)); + private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" + UUID.randomUUID().toString().substring(0, 8)); private class BasicHandler extends AbstractHandler { - public void handle(String s, - org.eclipse.jetty.server.Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, org.eclipse.jetty.server.Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { Enumeration e = httpRequest.getHeaderNames(); String param; @@ -76,44 +75,43 @@ public AbstractHandler configureHandler() throws Exception { return new BasicHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicByteBufferTest() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(null); - final AtomicBoolean completed = new AtomicBoolean(false); - - byte[] bytes = "RatherLargeFileRatherLargeFileRatherLargeFileRatherLargeFile".getBytes("UTF-16"); - long repeats = (1024 * 100 * 10 / bytes.length) + 1; - File largeFile = createTempFile(bytes, (int) repeats); - final AtomicInteger byteReceived = new AtomicInteger(); - try { - Response response = c.preparePut(getTargetUrl()).setBody(largeFile) - .execute(new AsyncCompletionHandlerAdapter() { - /* @Override */ - public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Exception { - byteReceived.addAndGet(content.getBodyByteBuffer().capacity()); - return super.onBodyPartReceived(content); - } - - }).get(); - - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - assertEquals(byteReceived.get(), largeFile.length()); - assertEquals(response.getResponseBody().length(), largeFile.length()); - - } catch (IOException ex) { - fail("Should have timed out"); + byte[] bytes = "RatherLargeFileRatherLargeFileRatherLargeFileRatherLargeFile".getBytes("UTF-16"); + long repeats = (1024 * 100 * 10 / bytes.length) + 1; + File largeFile = createTempFile(bytes, (int) repeats); + final AtomicInteger byteReceived = new AtomicInteger(); + + try { + Response response = c.preparePut(getTargetUrl()).setBody(largeFile).execute(new AsyncCompletionHandlerAdapter() { + /* @Override */ + public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Exception { + byteReceived.addAndGet(content.getBodyByteBuffer().capacity()); + return super.onBodyPartReceived(content); + } + + }).get(); + + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertEquals(byteReceived.get(), largeFile.length()); + assertEquals(response.getResponseBody().length(), largeFile.length()); + + } catch (IOException ex) { + fail("Should have timed out"); + } + } finally { + c.close(); } - c.close(); } public String getTargetUrl() { return String.format("http://127.0.0.1:%d/foo/test", port1); } - public static File createTempFile(byte[] pattern, int repeat) - throws IOException { + public static File createTempFile(byte[] pattern, int repeat) throws IOException { TMP.mkdirs(); TMP.deleteOnExit(); File tmpFile = File.createTempFile("tmpfile-", ".data", TMP); @@ -122,8 +120,7 @@ public static File createTempFile(byte[] pattern, int repeat) return tmpFile; } - public static void write(byte[] pattern, int repeat, File file) - throws IOException { + public static void write(byte[] pattern, int repeat, File file) throws IOException { file.deleteOnExit(); file.getParentFile().mkdirs(); FileOutputStream out = null; @@ -132,8 +129,7 @@ public static void write(byte[] pattern, int repeat, File file) for (int i = 0; i < repeat; i++) { out.write(pattern); } - } - finally { + } finally { if (out != null) { out.close(); } diff --git a/src/test/java/com/ning/http/client/async/ChunkingTest.java b/src/test/java/com/ning/http/client/async/ChunkingTest.java index 1860da24d1..9d4288e9ca 100644 --- a/src/test/java/com/ning/http/client/async/ChunkingTest.java +++ b/src/test/java/com/ning/http/client/async/ChunkingTest.java @@ -35,7 +35,7 @@ /** * Test that the url fetcher is able to communicate via a proxy - * + * * @author dominict */ abstract public class ChunkingTest extends AbstractBasicTest { @@ -58,8 +58,7 @@ abstract public class ChunkingTest extends AbstractBasicTest { baos.write(buf, 0, len); } LARGE_IMAGE_BYTES = baos.toByteArray(); - } - catch (Throwable e) { + } catch (Throwable e) { LARGE_IMAGE_BYTES = new byte[265495]; Random x = new Random(); x.nextBytes(LARGE_IMAGE_BYTES); @@ -67,20 +66,17 @@ abstract public class ChunkingTest extends AbstractBasicTest { } /** - * Tests that the custom chunked stream result in success and - * content returned that is unchunked + * Tests that the custom chunked stream result in success and content returned that is unchunked */ @Test() public void testCustomChunking() throws Throwable { doTest(true); } - private void doTest(boolean customChunkedInputStream) throws Exception { AsyncHttpClient c = null; try { - AsyncHttpClientConfig.Builder bc = - new AsyncHttpClientConfig.Builder(); + AsyncHttpClientConfig.Builder bc = new AsyncHttpClientConfig.Builder(); bc.setAllowPoolingConnection(true); bc.setMaximumConnectionsPerHost(1); @@ -89,7 +85,6 @@ private void doTest(boolean customChunkedInputStream) throws Exception { bc.setRequestTimeoutInMs(1000); bc.setFollowRedirects(true); - c = getAsyncHttpClient(bc.build()); RequestBuilder builder = new RequestBuilder("POST"); @@ -120,14 +115,13 @@ private void doTest(boolean customChunkedInputStream) throws Exception { } else { assertEquals(LARGE_IMAGE_BYTES, readInputStreamToBytes(res.getResponseBodyAsStream())); } - } - catch (Exception e) { + } catch (Exception e) { fail("Exception Thrown:" + e.getMessage()); } - } - finally { - if (c != null) c.close(); + } finally { + if (c != null) + c.close(); } } @@ -143,11 +137,9 @@ private byte[] readInputStreamToBytes(InputStream stream) { } buffer.flush(); data = buffer.toByteArray(); - } - catch (Exception e) { + } catch (Exception e) { - } - finally { + } finally { try { stream.close(); } catch (Exception e2) { diff --git a/src/test/java/com/ning/http/client/async/ComplexClientTest.java b/src/test/java/com/ning/http/client/async/ComplexClientTest.java index 6b7a30ffa5..b52e9431b9 100644 --- a/src/test/java/com/ning/http/client/async/ComplexClientTest.java +++ b/src/test/java/com/ning/http/client/async/ComplexClientTest.java @@ -25,44 +25,38 @@ public abstract class ComplexClientTest extends AbstractBasicTest { - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void multipleRequestsTest() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(null); + try { + String body = "hello there"; - String body = "hello there"; + // once + Response response = c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - // once - Response response = c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); + assertEquals(response.getResponseBody(), body); - assertEquals(response.getResponseBody(), body); + // twice + response = c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - // twice - response = c.preparePost(getTargetUrl()) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); - - assertEquals(response.getResponseBody(), body); - c.close(); + assertEquals(response.getResponseBody(), body); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void urlWithoutSlashTest() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(null); + try { + String body = "hello there"; - String body = "hello there"; - - // once - Response response = c.preparePost(String.format("http://127.0.0.1:%d/foo/test", port1)) - .setBody(body) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); + // once + Response response = c.preparePost(String.format("http://127.0.0.1:%d/foo/test", port1)).setBody(body).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getResponseBody(), body); - c.close(); + assertEquals(response.getResponseBody(), body); + } finally { + c.close(); + } } - } diff --git a/src/test/java/com/ning/http/client/async/ConnectionPoolTest.java b/src/test/java/com/ning/http/client/async/ConnectionPoolTest.java index 8f02356c90..1a82889ec6 100644 --- a/src/test/java/com/ning/http/client/async/ConnectionPoolTest.java +++ b/src/test/java/com/ning/http/client/async/ConnectionPoolTest.java @@ -43,104 +43,97 @@ public abstract class ConnectionPoolTest extends AbstractBasicTest { protected final Logger log = LoggerFactory.getLogger(AbstractBasicTest.class); - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testMaxTotalConnections() { - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setAllowPoolingConnection(true) - .setMaximumConnectionsTotal(1) - .build() - ); - - String url = getTargetUrl(); - int i; - Exception exception = null; - for (i = 0; i < 3; i++) { - try { - log.info("{} requesting url [{}]...", i, url); - Response response = client.prepareGet(url).execute().get(); - log.info("{} response [{}].", i, response); - } catch (Exception ex) { - exception = ex; + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true).setMaximumConnectionsTotal(1).build()); + try { + String url = getTargetUrl(); + int i; + Exception exception = null; + for (i = 0; i < 3; i++) { + try { + log.info("{} requesting url [{}]...", i, url); + Response response = client.prepareGet(url).execute().get(); + log.info("{} response [{}].", i, response); + } catch (Exception ex) { + exception = ex; + } } + assertNull(exception); + } finally { + client.close(); } - assertNull(exception); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testMaxTotalConnectionsException() { - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setAllowPoolingConnection(true) - .setMaximumConnectionsTotal(1) - .build() - ); - - String url = getTargetUrl(); - int i; - Exception exception = null; - for (i = 0; i < 20; i++) { - try { - log.info("{} requesting url [{}]...", i, url); - - if (i < 5) { - client.prepareGet(url).execute().get(); - } else { - client.prepareGet(url).execute(); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true).setMaximumConnectionsTotal(1).build()); + try { + String url = getTargetUrl(); + int i; + Exception exception = null; + for (i = 0; i < 20; i++) { + try { + log.info("{} requesting url [{}]...", i, url); + + if (i < 5) { + client.prepareGet(url).execute().get(); + } else { + client.prepareGet(url).execute(); + } + } catch (Exception ex) { + exception = ex; + break; } - } catch (Exception ex) { - exception = ex; - break; } + assertNotNull(exception); + assertNotNull(exception.getMessage()); + assertEquals(exception.getMessage(), "Too many connections 1"); + } finally { + client.close(); } - assertNotNull(exception); - assertNotNull(exception.getMessage()); - assertEquals(exception.getMessage(),"Too many connections 1"); } - @Test(groups = {"standalone", "default_provider", "async"}, enabled = true, invocationCount = 10, alwaysRun = true) + @Test(groups = { "standalone", "default_provider", "async" }, enabled = true, invocationCount = 10, alwaysRun = true) public void asyncDoGetKeepAliveHandlerTest_channelClosedDoesNotFail() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); + try { + // Use a l in case the assert fail + final CountDownLatch l = new CountDownLatch(2); - // Use a l in case the assert fail - final CountDownLatch l = new CountDownLatch(2); - - final Map remoteAddresses = new - ConcurrentHashMap(); + final Map remoteAddresses = new ConcurrentHashMap(); - AsyncCompletionHandler handler = new - AsyncCompletionHandlerAdapter() { + AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws - Exception { - System.out.println("ON COMPLETED INVOKED " + - response.getHeader("X-KEEP-ALIVE")); - try { - assertEquals(response.getStatusCode(), 200); - remoteAddresses.put(response.getHeader("X-KEEP-ALIVE"), true); - } finally { - l.countDown(); - } - return response; + @Override + public Response onCompleted(Response response) throws Exception { + System.out.println("ON COMPLETED INVOKED " + response.getHeader("X-KEEP-ALIVE")); + try { + assertEquals(response.getStatusCode(), 200); + remoteAddresses.put(response.getHeader("X-KEEP-ALIVE"), true); + } finally { + l.countDown(); } - }; + return response; + } + }; - client.prepareGet(getTargetUrl()).execute(handler).get(); - server.stop(); - server.start(); - client.prepareGet(getTargetUrl()).execute(handler); + client.prepareGet(getTargetUrl()).execute(handler).get(); + server.stop(); + server.start(); + client.prepareGet(getTargetUrl()).execute(handler); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timed out"); + } - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timed out"); + assertEquals(remoteAddresses.size(), 2); + } finally { + client.close(); } - - assertEquals(remoteAddresses.size(), 2); - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testInvalidConnectionsPool() { ConnectionsPool cp = new ConnectionsPool() { @@ -166,25 +159,23 @@ public void destroy() { } }; - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionsPool(cp) - .build() - ); - - Exception exception = null; + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionsPool(cp).build()); try { - client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; + Exception exception = null; + try { + client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNotNull(exception); + assertEquals(exception.getMessage(), "Too many connections -1"); + } finally { + client.close(); } - assertNotNull(exception); - assertEquals(exception.getMessage(), "Too many connections -1"); - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testValidConnectionsPool() { ConnectionsPool cp = new ConnectionsPool() { @@ -210,153 +201,150 @@ public void destroy() { } }; - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionsPool(cp) - .build() - ); - - Exception exception = null; + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionsPool(cp).build()); try { - client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; + Exception exception = null; + try { + client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNull(exception); + } finally { + client.close(); } - assertNull(exception); - client.close(); } - - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void multipleMaxConnectionOpenTest() throws Throwable { - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true) - .setConnectionTimeoutInMs(5000).setMaximumConnectionsTotal(1).build(); + AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true).setConnectionTimeoutInMs(5000).setMaximumConnectionsTotal(1).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + String body = "hello there"; - String body = "hello there"; - - // once - Response response = c.preparePost(getTargetUrl()) - .setBody(body) - .execute().get(TIMEOUT, TimeUnit.SECONDS); + // once + Response response = c.preparePost(getTargetUrl()).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getResponseBody(), body); + assertEquals(response.getResponseBody(), body); - // twice - Exception exception = null; - try { - c.preparePost(String.format("http://127.0.0.1:%d/foo/test", port2)).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); - fail("Should throw exception. Too many connections issued."); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; + // twice + Exception exception = null; + try { + c.preparePost(String.format("http://127.0.0.1:%d/foo/test", port2)).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); + fail("Should throw exception. Too many connections issued."); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNotNull(exception); + assertEquals(exception.getMessage(), "Too many connections 1"); + } finally { + c.close(); } - assertNotNull(exception); - assertEquals(exception.getMessage(), "Too many connections 1"); - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void multipleMaxConnectionOpenTestWithQuery() throws Throwable { - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true) - .setConnectionTimeoutInMs(5000).setMaximumConnectionsTotal(1).build(); + AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true).setConnectionTimeoutInMs(5000).setMaximumConnectionsTotal(1).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + String body = "hello there"; - String body = "hello there"; - - // once - Response response = c.preparePost(getTargetUrl() + "?foo=bar") - .setBody(body) - .execute().get(TIMEOUT, TimeUnit.SECONDS); + // once + Response response = c.preparePost(getTargetUrl() + "?foo=bar").setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getResponseBody(), "foo_" + body); + assertEquals(response.getResponseBody(), "foo_" + body); - // twice - Exception exception = null; - try { - response = c.preparePost(getTargetUrl()).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; + // twice + Exception exception = null; + try { + response = c.preparePost(getTargetUrl()).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNull(exception); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + } finally { + c.close(); } - assertNull(exception); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - c.close(); } /** - * This test just make sure the hack used to catch disconnected channel under win7 doesn't throw any exception. - * The onComplete method must be only called once. - * - * @throws Throwable if something wrong happens. + * This test just make sure the hack used to catch disconnected channel under win7 doesn't throw any exception. The onComplete method must be only called once. + * + * @throws Throwable + * if something wrong happens. */ - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void win7DisconnectTest() throws Throwable { final AtomicInteger count = new AtomicInteger(0); - AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - AsyncCompletionHandler handler = new - AsyncCompletionHandlerAdapter() { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + AsyncCompletionHandler handler = new AsyncCompletionHandlerAdapter() { + + @Override + public Response onCompleted(Response response) throws Exception { - @Override - public Response onCompleted(Response response) throws - Exception { - - count.incrementAndGet(); - StackTraceElement e = new StackTraceElement("sun.nio.ch.SocketDispatcher", "read0", null, -1); - IOException t = new IOException(); - t.setStackTrace(new StackTraceElement[]{e}); - throw t; - } - }; + count.incrementAndGet(); + StackTraceElement e = new StackTraceElement("sun.nio.ch.SocketDispatcher", "read0", null, -1); + IOException t = new IOException(); + t.setStackTrace(new StackTraceElement[] { e }); + throw t; + } + }; - try { - client.prepareGet(getTargetUrl()).execute(handler).get(); - fail("Must have received an exception"); - } catch (ExecutionException ex) { - assertNotNull(ex); - assertNotNull(ex.getCause()); - assertEquals(ex.getCause().getCause().getClass(), IOException.class); - assertEquals(count.get(), 1); + try { + client.prepareGet(getTargetUrl()).execute(handler).get(); + fail("Must have received an exception"); + } catch (ExecutionException ex) { + assertNotNull(ex); + assertNotNull(ex.getCause()); + assertEquals(ex.getCause().getClass(), IOException.class); + assertEquals(count.get(), 1); + } + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void asyncHandlerOnThrowableTest() throws Throwable { - AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final AtomicInteger count = new AtomicInteger(); - final String THIS_IS_NOT_FOR_YOU = "This is not for you"; - final CountDownLatch latch = new CountDownLatch(16); - for (int i = 0; i < 16; i++) { - client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerBase() { - @Override - public Response onCompleted(Response response) throws Exception { - throw new Exception(THIS_IS_NOT_FOR_YOU); - } - }); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + final AtomicInteger count = new AtomicInteger(); + final String THIS_IS_NOT_FOR_YOU = "This is not for you"; + final CountDownLatch latch = new CountDownLatch(16); + for (int i = 0; i < 16; i++) { + client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerBase() { + @Override + public Response onCompleted(Response response) throws Exception { + throw new Exception(THIS_IS_NOT_FOR_YOU); + } + }); - client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerBase() { - /* @Override */ - public void onThrowable(Throwable t) { - if (t.getMessage() != null && t.getMessage().equalsIgnoreCase(THIS_IS_NOT_FOR_YOU)) { - count.incrementAndGet(); + client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandlerBase() { + /* @Override */ + public void onThrowable(Throwable t) { + if (t.getMessage() != null && t.getMessage().equalsIgnoreCase(THIS_IS_NOT_FOR_YOU)) { + count.incrementAndGet(); + } } - } - @Override - public Response onCompleted(Response response) throws Exception { - latch.countDown(); - return response; - } - }); + @Override + public Response onCompleted(Response response) throws Exception { + latch.countDown(); + return response; + } + }); + } + latch.await(TIMEOUT, TimeUnit.SECONDS); + assertEquals(count.get(), 0); + } finally { + client.close(); } - latch.await(TIMEOUT, TimeUnit.SECONDS); - assertEquals(count.get(), 0); - client.close(); } - } - diff --git a/src/test/java/com/ning/http/client/async/DigestAuthTest.java b/src/test/java/com/ning/http/client/async/DigestAuthTest.java index 293139e107..66d1ce3086 100644 --- a/src/test/java/com/ning/http/client/async/DigestAuthTest.java +++ b/src/test/java/com/ning/http/client/async/DigestAuthTest.java @@ -38,7 +38,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -61,8 +60,7 @@ public void setUpGlobal() throws Exception { server = new Server(); Logger root = Logger.getRootLogger(); root.setLevel(Level.DEBUG); - root.addAppender(new ConsoleAppender( - new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); + root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); port1 = findFreePort(); Connector listener = new SelectChannelConnector(); @@ -77,7 +75,7 @@ public void setUpGlobal() throws Exception { Constraint constraint = new Constraint(); constraint.setName(Constraint.__BASIC_AUTH); - constraint.setRoles(new String[]{user, admin}); + constraint.setRoles(new String[] { user, admin }); constraint.setAuthenticate(true); ConstraintMapping mapping = new ConstraintMapping(); @@ -104,10 +102,7 @@ public void setUpGlobal() throws Exception { } private class SimpleHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.addHeader("X-Auth", request.getHeader("Authorization")); response.setStatus(200); @@ -116,50 +111,51 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void digestAuthTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/") - .setRealm((new Realm.RealmBuilder()).setPrincipal(user) - .setPassword(admin) - .setRealmName("MyRealm") - .setScheme(Realm.AuthScheme.DIGEST).build()); - - Future f = r.execute(); - Response resp = f.get(60, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertNotNull(resp.getHeader("X-Auth")); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/").setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).setRealmName("MyRealm").setScheme(Realm.AuthScheme.DIGEST).build()); + + Future f = r.execute(); + Response resp = f.get(60, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertNotNull(resp.getHeader("X-Auth")); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void digestAuthTestWithoutScheme() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/") - .setRealm((new Realm.RealmBuilder()).setPrincipal(user) - .setPassword(admin) - .setRealmName("MyRealm").build()); - - Future f = r.execute(); - Response resp = f.get(60, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertNotNull(resp.getHeader("X-Auth")); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/").setRealm((new Realm.RealmBuilder()).setPrincipal(user).setPassword(admin).setRealmName("MyRealm").build()); + + Future f = r.execute(); + Response resp = f.get(60, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertNotNull(resp.getHeader("X-Auth")); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void digestAuthNegativeTest() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/") - .setRealm((new Realm.RealmBuilder()).setPrincipal("fake").setPassword(admin).setScheme(Realm.AuthScheme.DIGEST).build()); - - Future f = r.execute(); - Response resp = f.get(20, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), 401); - client.close(); + try { + AsyncHttpClient.BoundRequestBuilder r = client.prepareGet("http://127.0.0.1/" + port1 + "/").setRealm((new Realm.RealmBuilder()).setPrincipal("fake").setPassword(admin).setScheme(Realm.AuthScheme.DIGEST).build()); + + Future f = r.execute(); + Response resp = f.get(20, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), 401); + } finally { + client.close(); + } } @Override diff --git a/src/test/java/com/ning/http/client/async/EmptyBodyTest.java b/src/test/java/com/ning/http/client/async/EmptyBodyTest.java index 873e1bba88..ea0bbab5fa 100644 --- a/src/test/java/com/ning/http/client/async/EmptyBodyTest.java +++ b/src/test/java/com/ning/http/client/async/EmptyBodyTest.java @@ -15,19 +15,12 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncHandler; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.HttpResponseBodyPart; -import com.ning.http.client.HttpResponseHeaders; -import com.ning.http.client.HttpResponseStatus; -import com.ning.http.client.Response; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.CountDownLatch; @@ -36,31 +29,34 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertNotNull; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -import static org.testng.Assert.fail; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.annotations.Test; + +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.HttpResponseBodyPart; +import com.ning.http.client.HttpResponseHeaders; +import com.ning.http.client.HttpResponseStatus; +import com.ning.http.client.Response; /** * Tests case where response doesn't have body. - * + * * @author Hubert Iwaniuk */ public abstract class EmptyBodyTest extends AbstractBasicTest { private class NoBodyResponseHandler extends AbstractHandler { - public void handle( - String s, - Request request, - HttpServletRequest req, - HttpServletResponse resp) - throws IOException, ServletException { + public void handle(String s, Request request, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { if (!req.getMethod().equalsIgnoreCase("PUT")) { resp.setStatus(HttpServletResponse.SC_OK); } else { - resp.setStatus(204); + resp.setStatus(204); } request.setHandled(true); } @@ -71,72 +67,77 @@ public AbstractHandler configureHandler() throws Exception { return new NoBodyResponseHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testEmptyBody() throws IOException { AsyncHttpClient ahc = getAsyncHttpClient(null); - final AtomicBoolean err = new AtomicBoolean(false); - final LinkedBlockingQueue queue = new LinkedBlockingQueue(); - final AtomicBoolean status = new AtomicBoolean(false); - final AtomicInteger headers = new AtomicInteger(0); - final CountDownLatch latch = new CountDownLatch(1); - ahc.executeRequest(ahc.prepareGet(getTargetUrl()).build(), new AsyncHandler() { - public void onThrowable(Throwable t) { - fail("Got throwable.", t); - err.set(true); - } + try { + final AtomicBoolean err = new AtomicBoolean(false); + final LinkedBlockingQueue queue = new LinkedBlockingQueue(); + final AtomicBoolean status = new AtomicBoolean(false); + final AtomicInteger headers = new AtomicInteger(0); + final CountDownLatch latch = new CountDownLatch(1); + ahc.executeRequest(ahc.prepareGet(getTargetUrl()).build(), new AsyncHandler() { + public void onThrowable(Throwable t) { + fail("Got throwable.", t); + err.set(true); + } - public STATE onBodyPartReceived(HttpResponseBodyPart e) throws Exception { - String s = new String(e.getBodyPartBytes()); - log.info("got part: {}", s); - if (s.equals("")) { - //noinspection ThrowableInstanceNeverThrown - log.warn("Sampling stacktrace.", - new Throwable("trace that, we should not get called for empty body.")); + public STATE onBodyPartReceived(HttpResponseBodyPart e) throws Exception { + String s = new String(e.getBodyPartBytes()); + log.info("got part: {}", s); + if (s.isEmpty()) { + // noinspection ThrowableInstanceNeverThrown + log.warn("Sampling stacktrace.", new Throwable("trace that, we should not get called for empty body.")); + } + queue.put(s); + return STATE.CONTINUE; } - queue.put(s); - return STATE.CONTINUE; - } - public STATE onStatusReceived(HttpResponseStatus e) throws Exception { - status.set(true); - return AsyncHandler.STATE.CONTINUE; - } + public STATE onStatusReceived(HttpResponseStatus e) throws Exception { + status.set(true); + return AsyncHandler.STATE.CONTINUE; + } - public STATE onHeadersReceived(HttpResponseHeaders e) throws Exception { - if (headers.incrementAndGet() == 2) { - throw new Exception("Analyze this."); + public STATE onHeadersReceived(HttpResponseHeaders e) throws Exception { + if (headers.incrementAndGet() == 2) { + throw new Exception("Analyze this."); + } + return STATE.CONTINUE; } - return STATE.CONTINUE; - } - public Object onCompleted() throws Exception { - latch.countDown(); - return null; + public Object onCompleted() throws Exception { + latch.countDown(); + return null; + } + }); + try { + assertTrue(latch.await(1, TimeUnit.SECONDS), "Latch failed."); + } catch (InterruptedException e) { + fail("Interrupted.", e); } - }); - try { - assertTrue(latch.await(1, TimeUnit.SECONDS), "Latch failed."); - } catch (InterruptedException e) { - fail("Interrupted.", e); + assertFalse(err.get()); + assertEquals(queue.size(), 0); + assertTrue(status.get()); + assertEquals(headers.get(), 1); + } finally { + ahc.close(); } - assertFalse(err.get()); - assertEquals(queue.size(), 0); - assertTrue(status.get()); - assertEquals(headers.get(), 1); - ahc.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testPutEmptyBody() throws Throwable { AsyncHttpClient ahc = getAsyncHttpClient(null); - Response response = ahc.preparePut(getTargetUrl()).setBody("String").execute().get(); + try { + Response response = ahc.preparePut(getTargetUrl()).setBody("String").execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 204); - assertEquals(response.getResponseBody(), ""); - assertTrue(InputStream.class.isAssignableFrom(response.getResponseBodyAsStream().getClass())); - assertEquals(response.getResponseBodyAsStream().read(), -1); + assertNotNull(response); + assertEquals(response.getStatusCode(), 204); + assertEquals(response.getResponseBody(), ""); + assertTrue(response.getResponseBodyAsStream() instanceof InputStream); + assertEquals(response.getResponseBodyAsStream().read(), -1); - ahc.close(); + } finally { + ahc.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/ErrorResponseTest.java b/src/test/java/com/ning/http/client/async/ErrorResponseTest.java index 0613c27ef7..c3b527736a 100644 --- a/src/test/java/com/ning/http/client/async/ErrorResponseTest.java +++ b/src/test/java/com/ning/http/client/async/ErrorResponseTest.java @@ -16,34 +16,35 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.Response; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.annotations.Test; + +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.Response; /** * Tests to reproduce issues with handling of error responses - * + * * @author Tatu Saloranta */ public abstract class ErrorResponseTest extends AbstractBasicTest { final static String BAD_REQUEST_STR = "Very Bad Request! No cookies."; private static class ErrorHandler extends AbstractHandler { - public void handle(String s, Request r, - HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { Thread.sleep(210L); } catch (InterruptedException e) { @@ -61,18 +62,18 @@ public AbstractHandler configureHandler() throws Exception { return new ErrorHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testQueryParameters() throws Exception { AsyncHttpClient client = getAsyncHttpClient(null); - Future f = client - .prepareGet("http://127.0.0.1/" + port1 + "/foo") - .addHeader("Accepts", "*/*") - .execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), 400); - String respStr = resp.getResponseBody(); - assertEquals(BAD_REQUEST_STR, respStr); - client.close(); + try { + Future f = client.prepareGet("http://127.0.0.1/" + port1 + "/foo").addHeader("Accepts", "*/*").execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), 400); + String respStr = resp.getResponseBody(); + assertEquals(BAD_REQUEST_STR, respStr); + } finally { + client.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/Expect100ContinueTest.java b/src/test/java/com/ning/http/client/async/Expect100ContinueTest.java index 8ebb82d34c..d0fd1f32d9 100644 --- a/src/test/java/com/ning/http/client/async/Expect100ContinueTest.java +++ b/src/test/java/com/ning/http/client/async/Expect100ContinueTest.java @@ -15,22 +15,24 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.Response; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.Future; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.annotations.Test; + +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.Response; /** * Test the Expect: 100-Continue. @@ -38,10 +40,7 @@ public abstract class Expect100ContinueTest extends AbstractBasicTest { private class ZeroCopyHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { int size = 10 * 1024; if (httpRequest.getContentLength() > 0) { @@ -58,26 +57,26 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void Expect100Continue() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); + try { + ClassLoader cl = getClass().getClassLoader(); + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); - ClassLoader cl = getClass().getClassLoader(); - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - - Future f = client.preparePut("http://127.0.0.1/" + port1 + "/").setHeader("Expect", "100-continue").setBody(file).execute(); - Response resp = f.get(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), "This is a simple test file"); - client.close(); - + Future f = client.preparePut("http://127.0.0.1/" + port1 + "/").setHeader("Expect", "100-continue").setBody(file).execute(); + Response resp = f.get(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), "This is a simple test file"); + } finally { + client.close(); + } } @Override public AbstractHandler configureHandler() throws Exception { return new ZeroCopyHandler(); } - } diff --git a/src/test/java/com/ning/http/client/async/FilePartLargeFileTest.java b/src/test/java/com/ning/http/client/async/FilePartLargeFileTest.java index 87dd3aa377..49af5aa4c6 100644 --- a/src/test/java/com/ning/http/client/async/FilePartLargeFileTest.java +++ b/src/test/java/com/ning/http/client/async/FilePartLargeFileTest.java @@ -12,66 +12,69 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.FilePart; -import com.ning.http.client.Response; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.Test; +import static org.testng.FileAssert.fail; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.UUID; -import static org.testng.FileAssert.fail; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -public abstract class FilePartLargeFileTest - extends AbstractBasicTest { +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.FilePart; +import com.ning.http.client.Response; + +public abstract class FilePartLargeFileTest extends AbstractBasicTest { private File largeFile; - @Test(groups = {"standalone", "default_provider"}, enabled = true) - public void testPutImageFile() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = true) + public void testPutImageFile() throws Exception { largeFile = getTestFile(); AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(100 * 6000).build(); AsyncHttpClient client = getAsyncHttpClient(config); - BoundRequestBuilder rb = client.preparePut(getTargetUrl()); - - rb.addBodyPart(new FilePart("test", largeFile, "application/octet-stream" , "UTF-8")); + try { + BoundRequestBuilder rb = client.preparePut(getTargetUrl()); - Response response = rb.execute().get(); - Assert.assertEquals(200, response.getStatusCode()); + rb.addBodyPart(new FilePart("test", largeFile, "application/octet-stream", "UTF-8")); - client.close(); + Response response = rb.execute().get(); + Assert.assertEquals(200, response.getStatusCode()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}, enabled = true) - public void testPutLargeTextFile() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = true) + public void testPutLargeTextFile() throws Exception { byte[] bytes = "RatherLargeFileRatherLargeFileRatherLargeFileRatherLargeFile".getBytes("UTF-16"); long repeats = (1024 * 1024 / bytes.length) + 1; largeFile = createTempFile(bytes, (int) repeats); - AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient client = getAsyncHttpClient(config); - BoundRequestBuilder rb = client.preparePut(getTargetUrl()); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + BoundRequestBuilder rb = client.preparePut(getTargetUrl()); - rb.addBodyPart(new FilePart("test", largeFile, "application/octet-stream" , "UTF-8")); + rb.addBodyPart(new FilePart("test", largeFile, "application/octet-stream", "UTF-8")); - Response response = rb.execute().get(); - Assert.assertEquals(200, response.getStatusCode()); - client.close(); + Response response = rb.execute().get(); + Assert.assertEquals(200, response.getStatusCode()); + } finally { + client.close(); + } } private static File getTestFile() { @@ -96,12 +99,10 @@ public void after() { } @Override - public AbstractHandler configureHandler() - throws Exception { + public AbstractHandler configureHandler() throws Exception { return new AbstractHandler() { - public void handle(String arg0, Request arg1, HttpServletRequest req, HttpServletResponse resp) - throws IOException, ServletException { + public void handle(String arg0, Request arg1, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { ServletInputStream in = req.getInputStream(); byte[] b = new byte[8192]; @@ -125,11 +126,9 @@ public void handle(String arg0, Request arg1, HttpServletRequest req, HttpServle }; } - private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" - + UUID.randomUUID().toString().substring(0, 8)); + private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" + UUID.randomUUID().toString().substring(0, 8)); - public static File createTempFile(byte[] pattern, int repeat) - throws IOException { + public static File createTempFile(byte[] pattern, int repeat) throws IOException { TMP.mkdirs(); TMP.deleteOnExit(); File tmpFile = File.createTempFile("tmpfile-", ".data", TMP); @@ -139,8 +138,7 @@ public static File createTempFile(byte[] pattern, int repeat) return tmpFile; } - public static void write(byte[] pattern, int repeat, File file) - throws IOException { + public static void write(byte[] pattern, int repeat, File file) throws IOException { file.deleteOnExit(); file.getParentFile().mkdirs(); FileOutputStream out = null; @@ -149,8 +147,7 @@ public static void write(byte[] pattern, int repeat, File file) for (int i = 0; i < repeat; i++) { out.write(pattern); } - } - finally { + } finally { if (out != null) { out.close(); } diff --git a/src/test/java/com/ning/http/client/async/FilterTest.java b/src/test/java/com/ning/http/client/async/FilterTest.java index 6932087dac..99b21033f2 100644 --- a/src/test/java/com/ning/http/client/async/FilterTest.java +++ b/src/test/java/com/ning/http/client/async/FilterTest.java @@ -42,10 +42,7 @@ public abstract class FilterTest extends AbstractBasicTest { private class BasicHandler extends AbstractHandler { - public void handle(String s, - org.eclipse.jetty.server.Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, org.eclipse.jetty.server.Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { Enumeration e = httpRequest.getHeaderNames(); String param; @@ -65,63 +62,65 @@ public AbstractHandler configureHandler() throws Exception { return new BasicHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicTest() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); b.addRequestFilter(new ThrottleRequestFilter(100)); AsyncHttpClient c = getAsyncHttpClient(b.build()); - - Response response = c.preparePost(getTargetUrl()) - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - c.close(); + try { + Response response = c.preparePost(getTargetUrl()).execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void loadThrottleTest() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); b.addRequestFilter(new ThrottleRequestFilter(10)); AsyncHttpClient c = getAsyncHttpClient(b.build()); + try { + List> futures = new ArrayList>(); + for (int i = 0; i < 200; i++) { + futures.add(c.preparePost(getTargetUrl()).execute()); + } - List> futures = new ArrayList>(); - for (int i = 0; i < 200; i++) { - futures.add(c.preparePost(getTargetUrl()).execute()); - } - - for (Future f : futures) { - Response r = f.get(); - assertNotNull(f.get()); - assertEquals(r.getStatusCode(), 200); + for (Future f : futures) { + Response r = f.get(); + assertNotNull(f.get()); + assertEquals(r.getStatusCode(), 200); + } + } finally { + c.close(); } - - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void maxConnectionsText() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); b.addRequestFilter(new ThrottleRequestFilter(0, 1000)); AsyncHttpClient c = getAsyncHttpClient(b.build()); try { - Response response = c.preparePost(getTargetUrl()) - .execute().get(); + c.preparePost(getTargetUrl()).execute().get(); fail("Should have timed out"); } catch (IOException ex) { assertNotNull(ex); assertEquals(ex.getCause().getClass(), FilterException.class); + } finally { + c.close(); } - c.close(); } public String getTargetUrl() { return String.format("http://127.0.0.1:%d/foo/test", port1); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicResponseFilterTest() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); b.addResponseFilter(new ResponseFilter() { @@ -134,18 +133,18 @@ public FilterContext filter(FilterContext ctx) throws FilterException { AsyncHttpClient c = getAsyncHttpClient(b.build()); try { - Response response = c.preparePost(getTargetUrl()) - .execute().get(); + Response response = c.preparePost(getTargetUrl()).execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void replayResponseFilterTest() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); final AtomicBoolean replay = new AtomicBoolean(true); @@ -165,19 +164,19 @@ public FilterContext filter(FilterContext ctx) throws FilterException { AsyncHttpClient c = getAsyncHttpClient(b.build()); try { - Response response = c.preparePost(getTargetUrl()) - .execute().get(); + Response response = c.preparePost(getTargetUrl()).execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); assertEquals(response.getHeader("X-Replay"), "true"); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void replayStatusCodeResponseFilterTest() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); final AtomicBoolean replay = new AtomicBoolean(true); @@ -197,19 +196,19 @@ public FilterContext filter(FilterContext ctx) throws FilterException { AsyncHttpClient c = getAsyncHttpClient(b.build()); try { - Response response = c.preparePost(getTargetUrl()) - .execute().get(); + Response response = c.preparePost(getTargetUrl()).execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); assertEquals(response.getHeader("X-Replay"), "true"); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void replayHeaderResponseFilterTest() throws Throwable { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); final AtomicBoolean replay = new AtomicBoolean(true); @@ -218,16 +217,10 @@ public void replayHeaderResponseFilterTest() throws Throwable { public FilterContext filter(FilterContext ctx) throws FilterException { - if (ctx.getResponseHeaders() != null - && ctx.getResponseHeaders().getHeaders().getFirstValue("Ping").equals("Pong") - && replay.getAndSet(false)) { + if (ctx.getResponseHeaders() != null && ctx.getResponseHeaders().getHeaders().getFirstValue("Ping").equals("Pong") && replay.getAndSet(false)) { Request request = new RequestBuilder(ctx.getRequest()).addHeader("Ping", "Pong").build(); - return new FilterContext.FilterContextBuilder() - .asyncHandler(ctx.getAsyncHandler()) - .request(request) - .replayRequest(true) - .build(); + return new FilterContext.FilterContextBuilder().asyncHandler(ctx.getAsyncHandler()).request(request).replayRequest(true).build(); } return ctx; } @@ -236,15 +229,15 @@ public FilterContext filter(FilterContext ctx) throws FilterException { AsyncHttpClient c = getAsyncHttpClient(b.build()); try { - Response response = c.preparePost(getTargetUrl()).addHeader("Ping", "Pong") - .execute().get(); + Response response = c.preparePost(getTargetUrl()).addHeader("Ping", "Pong").execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); assertEquals(response.getHeader("Ping"), "Pong"); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } } diff --git a/src/test/java/com/ning/http/client/async/FluentStringsMapTest.java b/src/test/java/com/ning/http/client/async/FluentStringsMapTest.java index d6c6795985..0ca6100ed2 100644 --- a/src/test/java/com/ning/http/client/async/FluentStringsMapTest.java +++ b/src/test/java/com/ning/http/client/async/FluentStringsMapTest.java @@ -119,8 +119,8 @@ public void nullValueTest() { map.add("foo", (String) null); - assertEquals(map.getFirstValue("foo"), ""); - assertEquals(map.getJoinedValue("foo", ", "), ""); + assertEquals(map.getFirstValue("foo"), null); + assertEquals(map.getJoinedValue("foo", ", "), null); assertEquals(map.get("foo").size(), 1); } diff --git a/src/test/java/com/ning/http/client/async/FollowingThreadTest.java b/src/test/java/com/ning/http/client/async/FollowingThreadTest.java index b8de801eb9..82d6c6d95e 100644 --- a/src/test/java/com/ning/http/client/async/FollowingThreadTest.java +++ b/src/test/java/com/ning/http/client/async/FollowingThreadTest.java @@ -30,7 +30,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeoutException; - /** * Simple stress test for exercising the follow redirect. */ @@ -38,59 +37,62 @@ public abstract class FollowingThreadTest extends AbstractBasicTest { private final static int COUNT = 10; - @Test(timeOut = 30 * 1000, groups = {"online", "default_provider", "scalability"}) + @Test(timeOut = 30 * 1000, groups = { "online", "default_provider", "scalability" }) public void testFollowRedirect() throws IOException, ExecutionException, TimeoutException, InterruptedException { final CountDownLatch countDown = new CountDownLatch(COUNT); ExecutorService pool = Executors.newCachedThreadPool(); - for (int i = 0; i < COUNT; i++) { - pool.submit(new Runnable() { - - private int status; - - public void run() { - final CountDownLatch l = new CountDownLatch(1); - final AsyncHttpClient ahc = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); - try { - ahc.prepareGet("http://www.google.com/").execute(new AsyncHandler() { - - public void onThrowable(Throwable t) { - t.printStackTrace(); - } - - public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - System.out.println(new String(bodyPart.getBodyPartBytes())); - return STATE.CONTINUE; - } - - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - status = responseStatus.getStatusCode(); - System.out.println(responseStatus.getStatusText()); - return STATE.CONTINUE; - } - - public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { - return STATE.CONTINUE; - } - - public Integer onCompleted() throws Exception { - l.countDown(); - return status; - } - }); - - l.await(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - ahc.close(); - countDown.countDown(); + try { + for (int i = 0; i < COUNT; i++) { + pool.submit(new Runnable() { + + private int status; + + public void run() { + final CountDownLatch l = new CountDownLatch(1); + final AsyncHttpClient ahc = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); + try { + ahc.prepareGet("http://www.google.com/").execute(new AsyncHandler() { + + public void onThrowable(Throwable t) { + t.printStackTrace(); + } + + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + System.out.println(new String(bodyPart.getBodyPartBytes())); + return STATE.CONTINUE; + } + + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + status = responseStatus.getStatusCode(); + System.out.println(responseStatus.getStatusText()); + return STATE.CONTINUE; + } + + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + return STATE.CONTINUE; + } + + public Integer onCompleted() throws Exception { + l.countDown(); + return status; + } + }); + + l.await(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + ahc.close(); + countDown.countDown(); + } } - } - }); + }); + } + countDown.await(); + } finally { + pool.shutdown(); } - countDown.await(); } } \ No newline at end of file diff --git a/src/test/java/com/ning/http/client/async/Head302Test.java b/src/test/java/com/ning/http/client/async/Head302Test.java index c84f827dce..b3603688e8 100644 --- a/src/test/java/com/ning/http/client/async/Head302Test.java +++ b/src/test/java/com/ning/http/client/async/Head302Test.java @@ -15,19 +15,6 @@ */ package com.ning.http.client.async; -import com.ning.http.client.AsyncCompletionHandlerBase; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.Request; -import com.ning.http.client.RequestBuilder; -import com.ning.http.client.Response; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.omg.CORBA.TIMEOUT; -import org.testng.Assert; -import org.testng.annotations.Test; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CountDownLatch; @@ -35,9 +22,23 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.ning.http.client.AsyncCompletionHandlerBase; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.Request; +import com.ning.http.client.RequestBuilder; +import com.ning.http.client.Response; + /** * Tests HEAD request that gets 302 response. - * + * * @author Hubert Iwaniuk */ public abstract class Head302Test extends AbstractBasicTest { @@ -45,10 +46,7 @@ public abstract class Head302Test extends AbstractBasicTest { * Handler that does Found (302) in response to HEAD method. */ private class Head302handler extends AbstractHandler { - public void handle(String s, - org.eclipse.jetty.server.Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, org.eclipse.jetty.server.Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("HEAD".equalsIgnoreCase(request.getMethod())) { if (request.getPathInfo().endsWith("_moved")) { response.setStatus(HttpServletResponse.SC_OK); @@ -62,24 +60,27 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testHEAD302() throws IOException, BrokenBarrierException, InterruptedException, ExecutionException, TimeoutException { AsyncHttpClient client = getAsyncHttpClient(null); - final CountDownLatch l = new CountDownLatch(1); - Request request = new RequestBuilder("HEAD").setUrl("http://127.0.0.1/" + port1 + "/Test").build(); + try { + final CountDownLatch l = new CountDownLatch(1); + Request request = new RequestBuilder("HEAD").setUrl("http://127.0.0.1/" + port1 + "/Test").build(); - client.executeRequest(request, new AsyncCompletionHandlerBase() { - @Override - public Response onCompleted(Response response) throws Exception { - l.countDown(); - return super.onCompleted(response); - } - }).get(3, TimeUnit.SECONDS); + client.executeRequest(request, new AsyncCompletionHandlerBase() { + @Override + public Response onCompleted(Response response) throws Exception { + l.countDown(); + return super.onCompleted(response); + } + }).get(3, TimeUnit.SECONDS); - if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + if (!l.await(TIMEOUT, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); + } + } finally { + client.close(); } - client.close(); } @Override diff --git a/src/test/java/com/ning/http/client/async/HostnameVerifierTest.java b/src/test/java/com/ning/http/client/async/HostnameVerifierTest.java index e2bff473b1..6ee99cb0d8 100644 --- a/src/test/java/com/ning/http/client/async/HostnameVerifierTest.java +++ b/src/test/java/com/ning/http/client/async/HostnameVerifierTest.java @@ -54,10 +54,7 @@ public abstract class HostnameVerifierTest extends AbstractBasicTest { public static class EchoHandler extends AbstractHandler { /* @Override */ - public void handle(String pathInContext, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws ServletException, IOException { + public void handle(String pathInContext, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException { httpResponse.setContentType("text/html; charset=utf-8"); Enumeration e = httpRequest.getHeaderNames(); @@ -111,10 +108,16 @@ public void handle(String pathInContext, size = httpRequest.getContentLength(); } byte[] bytes = new byte[size]; + int pos = 0; if (bytes.length > 0) { - //noinspection ResultOfMethodCallIgnored - int read = httpRequest.getInputStream().read(bytes); - httpResponse.getOutputStream().write(bytes, 0, read); + // noinspection ResultOfMethodCallIgnored + int read = 0; + while (read != -1) { + read = httpRequest.getInputStream().read(bytes, pos, bytes.length - pos); + pos += read; + } + + httpResponse.getOutputStream().write(bytes); } httpResponse.setStatus(200); @@ -191,71 +194,83 @@ public void setUpGlobal() throws Exception { log.info("Local HTTP server started successfully"); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void positiveHostnameVerifierTest() throws Throwable { final AsyncHttpClient client = getAsyncHttpClient(new Builder().setHostnameVerifier(new PositiveHostVerifier()).setSSLContext(createSSLContext()).build()); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - - Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); - Response resp = f.get(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), "This is a simple test file"); + Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); + Response resp = f.get(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), "This is a simple test file"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void negativeHostnameVerifierTest() throws Throwable { final AsyncHttpClient client = getAsyncHttpClient(new Builder().setHostnameVerifier(new NegativeHostVerifier()).setSSLContext(createSSLContext()).build()); - - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - try { - Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); - } catch (ConnectException ex) { - assertEquals(ConnectException.class, ex.getClass()); + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + try { + client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); + } catch (ConnectException ex) { + assertEquals(ConnectException.class, ex.getClass()); + } + } finally { + client.close(); } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void remoteIDHostnameVerifierTest() throws Throwable { final AsyncHttpClient client = getAsyncHttpClient(new Builder().setHostnameVerifier(new CheckHost("bouette")).setSSLContext(createSSLContext()).build()); - - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - try { - Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); - } catch (ConnectException ex) { - assertEquals(ConnectException.class, ex.getClass()); + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + try { + client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); + } catch (ConnectException ex) { + assertEquals(ConnectException.class, ex.getClass()); + } + } finally { + client.close(); } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void remotePosHostnameVerifierTest() throws Throwable { final AsyncHttpClient client = getAsyncHttpClient(new Builder().setHostnameVerifier(new CheckHost("localhost")).setSSLContext(createSSLContext()).build()); - - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - try { - Future f = client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); - } catch (ConnectException ex) { - assertEquals(ConnectException.class, ex.getClass()); + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + try { + client.preparePost(getTargetUrl()).setBody(file).setHeader("Content-Type", "text/html").execute(); + } catch (ConnectException ex) { + assertEquals(ConnectException.class, ex.getClass()); + } + } finally { + client.close(); } } @@ -305,7 +320,7 @@ private static SSLContext createSSLContext() { // Initialize the SSLContext to work with our key managers. KeyManager[] keyManagers = kmf.getKeyManagers(); - TrustManager[] trustManagers = new TrustManager[]{DUMMY_TRUST_MANAGER}; + TrustManager[] trustManagers = new TrustManager[] { DUMMY_TRUST_MANAGER }; SecureRandom secureRandom = new SecureRandom(); SSLContext sslContext = SSLContext.getInstance("TLS"); @@ -323,17 +338,14 @@ public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - public void checkClientTrusted( - X509Certificate[] chain, String authType) throws CertificateException { + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } - public void checkServerTrusted( - X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!TRUST_SERVER_CERT.get()) { throw new CertificateException("Server certificate not trusted."); } } }; - } diff --git a/src/test/java/com/ning/http/client/async/HttpToHttpsRedirectTest.java b/src/test/java/com/ning/http/client/async/HttpToHttpsRedirectTest.java index 7cd04b99bc..b12b08ad84 100644 --- a/src/test/java/com/ning/http/client/async/HttpToHttpsRedirectTest.java +++ b/src/test/java/com/ning/http/client/async/HttpToHttpsRedirectTest.java @@ -45,11 +45,7 @@ public abstract class HttpToHttpsRedirectTest extends AbstractBasicTest { private class Relative302Handler extends AbstractHandler { - - public void handle(String s, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { String param; httpResponse.setContentType("text/html; charset=utf-8"); @@ -120,33 +116,14 @@ public void setUpGlobal() throws Exception { log.info("Local HTTP server started successfully"); } - private String getBaseUrl(URI uri) { - String url = uri.toString(); - int port = uri.getPort(); - if (port == -1) { - port = getPort(uri); - url = url.substring(0, url.length() - 1) + ":" + port; - } - return url.substring(0, url.lastIndexOf(":") + String.valueOf(port).length() + 1); - } - - private static int getPort(URI uri) { - int port = uri.getPort(); - if (port == -1) - port = uri.getScheme().equals("http") ? 80 : 443; - return port; - } - - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void httpToHttpsRedirect() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setMaximumNumberOfRedirects(5).setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", getTargetUrl2()) - .execute().get(); + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", getTargetUrl2()).execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); assertEquals(response.getHeader("X-httpToHttps"), "PASS"); @@ -157,43 +134,41 @@ public String getTargetUrl2() { return String.format("https://127.0.0.1:%d/foo/test", port2); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void httpToHttpsProperConfig() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setMaximumNumberOfRedirects(5).setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); - - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", getTargetUrl2() + "/test2") - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getHeader("X-httpToHttps"), "PASS"); - - // Test if the internal channel is downgraded to clean http. - response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", getTargetUrl2() + "/foo2") - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getHeader("X-httpToHttps"), "PASS"); - c.close(); + try { + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", getTargetUrl2() + "/test2").execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getHeader("X-httpToHttps"), "PASS"); + + // Test if the internal channel is downgraded to clean http. + response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", getTargetUrl2() + "/foo2").execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getHeader("X-httpToHttps"), "PASS"); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void relativeLocationUrl() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setMaximumNumberOfRedirects(5).setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); - - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", "/foo/test") - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 302); - assertEquals(response.getUri().toString(), getTargetUrl()); - c.close(); + try { + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", "/foo/test").execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 302); + assertEquals(response.getUri().toString(), getTargetUrl()); + } finally { + c.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/InputStreamTest.java b/src/test/java/com/ning/http/client/async/InputStreamTest.java index 70ceb4b48e..f6d8c395ae 100644 --- a/src/test/java/com/ning/http/client/async/InputStreamTest.java +++ b/src/test/java/com/ning/http/client/async/InputStreamTest.java @@ -36,10 +36,7 @@ public abstract class InputStreamTest extends AbstractBasicTest { private class InputStreamHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("POST".equalsIgnoreCase(request.getMethod())) { byte[] b = new byte[3]; request.getInputStream().read(b, 0, 3); @@ -54,43 +51,46 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testInvalidInputStream() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient c = getAsyncHttpClient(null); - FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); - h.add("Content-Type", "application/x-www-form-urlencoded"); + try { + FluentCaseInsensitiveStringsMap h = new FluentCaseInsensitiveStringsMap(); + h.add("Content-Type", "application/x-www-form-urlencoded"); - InputStream is = new InputStream() { + InputStream is = new InputStream() { - public int readAllowed; + public int readAllowed; - @Override - public int available() { - return 1; // Fake - } - - @Override - public int read() throws IOException { - int fakeCount = readAllowed++; - if (fakeCount == 0) { - return (int) 'a'; - } else if (fakeCount == 1) { - return (int) 'b'; - } else if (fakeCount == 2) { - return (int) 'c'; - } else { - return -1; + @Override + public int available() { + return 1; // Fake } - } - }; + @Override + public int read() throws IOException { + int fakeCount = readAllowed++; + if (fakeCount == 0) { + return (int) 'a'; + } else if (fakeCount == 1) { + return (int) 'b'; + } else if (fakeCount == 2) { + return (int) 'c'; + } else { + return -1; + } - Response resp = c.preparePost(getTargetUrl()).setHeaders(h).setBody(is).execute().get(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("X-Param"), "abc"); - c.close(); + } + }; + + Response resp = c.preparePost(getTargetUrl()).setHeaders(h).setBody(is).execute().get(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("X-Param"), "abc"); + } finally { + c.close(); + } } @Override diff --git a/src/test/java/com/ning/http/client/async/ListenableFutureTest.java b/src/test/java/com/ning/http/client/async/ListenableFutureTest.java index 6de42566a9..f80168bc50 100644 --- a/src/test/java/com/ning/http/client/async/ListenableFutureTest.java +++ b/src/test/java/com/ning/http/client/async/ListenableFutureTest.java @@ -27,28 +27,31 @@ public abstract class ListenableFutureTest extends AbstractBasicTest { - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testListenableFuture() throws Throwable { final AtomicInteger statusCode = new AtomicInteger(500); AsyncHttpClient ahc = getAsyncHttpClient(null); - final CountDownLatch latch = new CountDownLatch(1); - final ListenableFuture future = ahc.prepareGet(getTargetUrl()).execute(); - future.addListener(new Runnable(){ + try { + final CountDownLatch latch = new CountDownLatch(1); + final ListenableFuture future = ahc.prepareGet(getTargetUrl()).execute(); + future.addListener(new Runnable() { - public void run() { - try { - statusCode.set(future.get().getStatusCode()); - latch.countDown(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.printStackTrace(); + public void run() { + try { + statusCode.set(future.get().getStatusCode()); + latch.countDown(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } } - } - }, Executors.newFixedThreadPool(1)); + }, Executors.newFixedThreadPool(1)); - latch.await(10, TimeUnit.SECONDS); - assertEquals(statusCode.get(), 200); - ahc.close(); + latch.await(10, TimeUnit.SECONDS); + assertEquals(statusCode.get(), 200); + } finally { + ahc.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/MaxConnectionsInThreads.java b/src/test/java/com/ning/http/client/async/MaxConnectionsInThreads.java index 01484c610d..405ef9b76d 100644 --- a/src/test/java/com/ning/http/client/async/MaxConnectionsInThreads.java +++ b/src/test/java/com/ning/http/client/async/MaxConnectionsInThreads.java @@ -43,96 +43,79 @@ abstract public class MaxConnectionsInThreads extends AbstractBasicTest { private static URI servletEndpointUri; - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testMaxConnectionsWithinThreads() { - String[] urls = new String[]{ - servletEndpointUri.toString(), - servletEndpointUri.toString()}; - - - final AsyncHttpClient client = - getAsyncHttpClient(new AsyncHttpClientConfig.Builder() - .setConnectionTimeoutInMs(1000) - .setRequestTimeoutInMs(5000) - .setAllowPoolingConnection(true) - .setMaximumConnectionsTotal(1) - .setMaximumConnectionsPerHost(1) - .build()); - - - final Boolean[] caughtError = new Boolean[]{Boolean.FALSE}; - List ts = new ArrayList(); - for (int i = 0; i < urls.length; i++) { - final String url = urls[i]; - Thread t = new Thread() { - public void run() { - try { - client.prepareGet(url).execute(); - } catch (IOException e) { - // assert that 2nd request fails, because maxTotalConnections=1 - // System.out.println(i); - caughtError[0] = true; - System.err.println("============"); - e.printStackTrace(); - System.err.println("============"); + String[] urls = new String[] { servletEndpointUri.toString(), servletEndpointUri.toString() }; + final AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionTimeoutInMs(1000).setRequestTimeoutInMs(5000).setAllowPoolingConnection(true).setMaximumConnectionsTotal(1).setMaximumConnectionsPerHost(1).build()); + + try { + final Boolean[] caughtError = new Boolean[] { Boolean.FALSE }; + List ts = new ArrayList(); + for (int i = 0; i < urls.length; i++) { + final String url = urls[i]; + Thread t = new Thread() { + public void run() { + try { + client.prepareGet(url).execute(); + } catch (IOException e) { + // assert that 2nd request fails, because maxTotalConnections=1 + // System.out.println(i); + caughtError[0] = true; + System.err.println("============"); + e.printStackTrace(); + System.err.println("============"); + + } } + }; + t.start(); + ts.add(t); + } + + for (Thread t : ts) { + try { + t.join(); + } catch (InterruptedException e) { + e.printStackTrace(); } - }; - t.start(); - ts.add(t); - } + } - for (Thread t : ts) { + // Let the threads finish try { - t.join(); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Thread.sleep(4500); + } catch (InterruptedException e1) { + e1.printStackTrace(); } - } - - // Let the threads finish - try { - Thread.sleep(4500); - } catch (InterruptedException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - assertTrue("Max Connections should have been reached", caughtError[0]); - - - boolean errorInNotThread = false; - for (int i = 0; i < urls.length; i++) { - final String url = urls[i]; + assertTrue("Max Connections should have been reached", caughtError[0]); + + boolean errorInNotThread = false; + for (int i = 0; i < urls.length; i++) { + final String url = urls[i]; + try { + client.prepareGet(url).execute(); + // client.prepareGet(url).execute(); + } catch (IOException e) { + // assert that 2nd request fails, because maxTotalConnections=1 + // System.out.println(i); + errorInNotThread = true; + System.err.println("============"); + e.printStackTrace(); + System.err.println("============"); + } + } + // Let the request finish try { - client.prepareGet(url).execute(); - // client.prepareGet(url).execute(); - } catch (IOException e) { - // assert that 2nd request fails, because maxTotalConnections=1 - // System.out.println(i); - errorInNotThread = true; - System.err.println("============"); - e.printStackTrace(); - System.err.println("============"); + Thread.sleep(2500); + } catch (InterruptedException e1) { + e1.printStackTrace(); } + assertTrue("Max Connections should have been reached", errorInNotThread); + } finally { + client.close(); } - // Let the request finish - try { - Thread.sleep(2500); - } catch (InterruptedException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - assertTrue("Max Connections should have been reached", errorInNotThread); - - - client.close(); - - } @Override @@ -149,7 +132,6 @@ public void setUpGlobal() throws Exception { server.addConnector(listener); - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); @@ -185,8 +167,7 @@ public void service(HttpServletRequest req, HttpServletResponse res) throws Serv try { sleepTime = Integer.parseInt(req.getParameter("timeout")); - } - catch (NumberFormatException e) { + } catch (NumberFormatException e) { sleepTime = DEFAULT_TIMEOUT; } @@ -200,8 +181,7 @@ public void service(HttpServletRequest req, HttpServletResponse res) throws Serv System.out.println("Servlet is awake for"); System.out.println("======================================="); System.out.flush(); - } - catch (Exception e) { + } catch (Exception e) { } @@ -209,10 +189,12 @@ public void service(HttpServletRequest req, HttpServletResponse res) throws Serv byte[] retVal = "1".getBytes(); OutputStream os = res.getOutputStream(); - - res.setContentLength(retVal.length); - os.write(retVal); - os.close(); + try { + res.setContentLength(retVal.length); + os.write(retVal); + } finally { + os.close(); + } } } } diff --git a/src/test/java/com/ning/http/client/async/MaxTotalConnectionTest.java b/src/test/java/com/ning/http/client/async/MaxTotalConnectionTest.java index ad47d1ceac..307d38937b 100644 --- a/src/test/java/com/ning/http/client/async/MaxTotalConnectionTest.java +++ b/src/test/java/com/ning/http/client/async/MaxTotalConnectionTest.java @@ -1,18 +1,18 @@ /* -* Copyright 2010 Ning, Inc. -* -* Ning licenses this file to you under the Apache License, version 2.0 -* (the "License"); you may not use this file except in compliance with the -* License. You may obtain a copy of the License at: -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ + * Copyright 2010 Ning, Inc. + * + * Ning licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ package com.ning.http.client.async; import com.ning.http.client.AsyncHttpClient; @@ -32,21 +32,11 @@ public abstract class MaxTotalConnectionTest extends AbstractBasicTest { protected final Logger log = LoggerFactory.getLogger(AbstractBasicTest.class); - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testMaxTotalConnectionsExceedingException() { - String[] urls = new String[]{ - "http://google.com/", - "http://github.com/"}; + String[] urls = new String[] { "http://google.com/", "http://github.com/" }; - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionTimeoutInMs(1000) - .setRequestTimeoutInMs(5000) - .setAllowPoolingConnection(false) - .setMaximumConnectionsTotal(1) - .setMaximumConnectionsPerHost(1) - .build() - ); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionTimeoutInMs(1000).setRequestTimeoutInMs(5000).setAllowPoolingConnection(false).setMaximumConnectionsTotal(1).setMaximumConnectionsPerHost(1).build()); boolean caughtError = false; for (int i = 0; i < urls.length; i++) { @@ -64,92 +54,72 @@ public void testMaxTotalConnectionsExceedingException() { @Test public void testMaxTotalConnections() { - String[] urls = new String[]{ - "http://google.com/", - "http://lenta.ru/"}; - - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionTimeoutInMs(1000) - .setRequestTimeoutInMs(5000) - .setAllowPoolingConnection(false) - .setMaximumConnectionsTotal(2) - .setMaximumConnectionsPerHost(1) - .build() - ); - - for (String url : urls) { - try { - client.prepareGet(url).execute(); - } catch (IOException e) { - Assert.fail("Smth wrong with connections handling!"); + String[] urls = new String[] { "http://google.com/", "http://lenta.ru/" }; + + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionTimeoutInMs(1000).setRequestTimeoutInMs(5000).setAllowPoolingConnection(false).setMaximumConnectionsTotal(2).setMaximumConnectionsPerHost(1).build()); + try { + for (String url : urls) { + try { + client.prepareGet(url).execute(); + } catch (IOException e) { + Assert.fail("Smth wrong with connections handling!"); + } } + } finally { + client.close(); } - client.close(); } - /** - * JFA: Disable this test for 1.2.0 release as it can easily fail because a request may complete - * before the second one is made, hence failing. The issue occurs frequently on Linux. + * JFA: Disable this test for 1.2.0 release as it can easily fail because a request may complete before the second one is made, hence failing. The issue occurs frequently on Linux. */ @Test(enabled = false) public void testMaxTotalConnectionsCorrectExceptionHandling() { - String[] urls = new String[]{ - "http://google.com/", - "http://github.com/"}; - - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionTimeoutInMs(1000) - .setRequestTimeoutInMs(5000) - .setAllowPoolingConnection(false) - .setMaximumConnectionsTotal(1) - .setMaximumConnectionsPerHost(1) - .build() - ); - - List futures = new ArrayList(); - boolean caughtError = false; - for (int i = 0; i < urls.length; i++) { - try { - Future future = client.prepareGet(urls[i]).execute(); - if (future != null) { - futures.add(future); + String[] urls = new String[] { "http://google.com/", "http://github.com/" }; + + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionTimeoutInMs(1000).setRequestTimeoutInMs(5000).setAllowPoolingConnection(false).setMaximumConnectionsTotal(1).setMaximumConnectionsPerHost(1).build()); + try { + List futures = new ArrayList(); + boolean caughtError = false; + for (int i = 0; i < urls.length; i++) { + try { + Future future = client.prepareGet(urls[i]).execute(); + if (future != null) { + futures.add(future); + } + } catch (IOException e) { + // assert that 2nd request fails, because maxTotalConnections=1 + Assert.assertEquals(i, 1); + caughtError = true; } - } catch (IOException e) { - // assert that 2nd request fails, because maxTotalConnections=1 - Assert.assertEquals(i, 1); - caughtError = true; } - } - Assert.assertTrue(caughtError); - - // get results of executed requests - for (Future future : futures) { - try { - Object res = future.get(); - } catch (InterruptedException e) { - log.error("Error!", e); - } catch (ExecutionException e) { - log.error("Error!", e); + Assert.assertTrue(caughtError); + + // get results of executed requests + for (Future future : futures) { + try { + Object res = future.get(); + } catch (InterruptedException e) { + log.error("Error!", e); + } catch (ExecutionException e) { + log.error("Error!", e); + } } - } - // try to execute once again, expecting that 1 connection is released - caughtError = false; - for (int i = 0; i < urls.length; i++) { - try { - client.prepareGet(urls[i]).execute(); - } catch (IOException e) { - // assert that 2nd request fails, because maxTotalConnections=1 - Assert.assertEquals(i, 1); - caughtError = true; + // try to execute once again, expecting that 1 connection is released + caughtError = false; + for (int i = 0; i < urls.length; i++) { + try { + client.prepareGet(urls[i]).execute(); + } catch (IOException e) { + // assert that 2nd request fails, because maxTotalConnections=1 + Assert.assertEquals(i, 1); + caughtError = true; + } } + Assert.assertTrue(caughtError); + } finally { + client.close(); } - Assert.assertTrue(caughtError); - client.close(); } } - - diff --git a/src/test/java/com/ning/http/client/async/MultipartUploadTest.java b/src/test/java/com/ning/http/client/async/MultipartUploadTest.java index 580af9ba03..991983e8ca 100644 --- a/src/test/java/com/ning/http/client/async/MultipartUploadTest.java +++ b/src/test/java/com/ning/http/client/async/MultipartUploadTest.java @@ -70,7 +70,7 @@ public abstract class MultipartUploadTest extends AbstractBasicTest { private String BASE_URL; private String servletEndpointRedirectUrl; - public static byte GZIPTEXT[] = new byte[]{31, -117, 8, 8, 11, 43, 79, 75, 0, 3, 104, 101, 108, 108, 111, 46, 116, 120, 116, 0, -53, 72, -51, -55, -55, -25, 2, 0, 32, 48, 58, 54, 6, 0, 0, 0}; + public static byte GZIPTEXT[] = new byte[] { 31, -117, 8, 8, 11, 43, 79, 75, 0, 3, 104, 101, 108, 108, 111, 46, 116, 120, 116, 0, -53, 72, -51, -55, -55, -25, 2, 0, 32, 48, 58, 54, 6, 0, 0, 0 }; @BeforeClass public void setUp() throws Exception { @@ -130,7 +130,7 @@ private File getClasspathFile(String file) throws FileNotFoundException { /** * Tests that the streaming of a file works. */ - @Test (enabled = true) + @Test(enabled = true) public void testSendingSmallFilesAndByteArray() { String expectedContents = "filecontent: hello"; String expectedContents2 = "gzipcontent: hello"; @@ -155,7 +155,6 @@ public void testSendingSmallFilesAndByteArray() { fail("unable to find " + testResource2); } - File testResource3File = null; try { testResource3File = getClasspathFile(testResource3); @@ -179,7 +178,6 @@ public void testSendingSmallFilesAndByteArray() { gzipped.add(true); gzipped.add(false); - boolean tmpFileCreated = false; File tmpFile = null; FileOutputStream os = null; @@ -193,7 +191,6 @@ public void testSendingSmallFilesAndByteArray() { expected.add(expectedContents); gzipped.add(false); - } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); @@ -210,13 +207,10 @@ public void testSendingSmallFilesAndByteArray() { fail("Unable to test ByteArrayMultiPart, as unable to write to filesystem the tmp test content"); } - - AsyncHttpClientConfig.Builder bc = - new AsyncHttpClientConfig.Builder(); + AsyncHttpClientConfig.Builder bc = new AsyncHttpClientConfig.Builder(); bc.setFollowRedirects(true); - AsyncHttpClient c = new AsyncHttpClient(bc.build()); try { @@ -232,8 +226,7 @@ public void testSendingSmallFilesAndByteArray() { builder.addBodyPart(new StringPart("Height", "shrimplike", AsyncHttpProviderUtils.DEFAULT_CHARSET)); builder.addBodyPart(new StringPart("Hair", "ridiculous", AsyncHttpProviderUtils.DEFAULT_CHARSET)); - builder.addBodyPart(new ByteArrayPart("file4", "bytearray.txt", expectedContents.getBytes("UTF-8") ,"text/plain", "UTF-8")); - + builder.addBodyPart(new ByteArrayPart("file4", "bytearray.txt", expectedContents.getBytes("UTF-8"), "text/plain", "UTF-8")); com.ning.http.client.Request r = builder.build(); @@ -243,26 +236,24 @@ public void testSendingSmallFilesAndByteArray() { testSentFile(expected, testFiles, res, gzipped); - c.close(); } catch (Exception e) { e.printStackTrace(); fail("Download Exception"); } finally { + c.close(); FileUtils.deleteQuietly(tmpFile); } } - /** * Test that the files were sent, based on the response from the servlet - * + * * @param expectedContents * @param sourceFiles * @param r * @param deflate */ - private void testSentFile(List expectedContents, List sourceFiles, - Response r, List deflate) { + private void testSentFile(List expectedContents, List sourceFiles, Response r, List deflate) { String content = null; try { content = r.getResponseBody(); @@ -283,11 +274,10 @@ private void testSentFile(List expectedContents, List sourceFiles, String[] responseFiles = tmpFiles.split(","); assertNotNull(responseFiles); - assertEquals( sourceFiles.size(), responseFiles.length); - + assertEquals(sourceFiles.size(), responseFiles.length); System.out.println(Arrays.toString(responseFiles)); - //assertTrue("File should exist: " + tmpFile.getAbsolutePath(),tmpFile.exists()); + // assertTrue("File should exist: " + tmpFile.getAbsolutePath(),tmpFile.exists()); int i = 0; for (File sourceFile : sourceFiles) { @@ -315,7 +305,6 @@ private void testSentFile(List expectedContents, List sourceFiles, IOUtils.closeQuietly(instream); } - tmp = new File(responseFiles[i].trim()); System.out.println("=============================="); System.out.println(tmp.getAbsolutePath()); @@ -323,7 +312,6 @@ private void testSentFile(List expectedContents, List sourceFiles, System.out.flush(); assertTrue(tmp.exists()); - instream = new FileInputStream(tmp); ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); byte[] buf = new byte[8092]; @@ -359,7 +347,8 @@ private void testSentFile(List expectedContents, List sourceFiles, e.printStackTrace(); fail("Download Exception"); } finally { - if (tmp != null) FileUtils.deleteQuietly(tmp); + if (tmp != null) + FileUtils.deleteQuietly(tmp); IOUtils.closeQuietly(instream); i++; } @@ -368,7 +357,7 @@ private void testSentFile(List expectedContents, List sourceFiles, /** * Takes the content that is being passed to it, and streams to a file on disk - * + * * @author dominict */ public static class MockMultipartUploadServlet extends HttpServlet { @@ -379,7 +368,6 @@ public static class MockMultipartUploadServlet extends HttpServlet { private int filesProcessed = 0; private int stringsProcessed = 0; - public MockMultipartUploadServlet() { } @@ -411,8 +399,7 @@ public int getStringsProcessed() { } @Override - public void service(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Check that we have a file upload request boolean isMultipart = ServletFileUpload.isMultipartContent(request); if (isMultipart) { @@ -430,12 +417,10 @@ public void service(HttpServletRequest request, HttpServletResponse response) stream = item.openStream(); if (item.isFormField()) { - System.out.println("Form field " + name + " with value " - + Streams.asString(stream) + " detected."); + System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected."); incrementStringsProcessed(); } else { - System.out.println("File field " + name + " with file name " - + item.getName() + " detected."); + System.out.println("File field " + name + " with file name " + item.getName() + " detected."); // Process the input stream OutputStream os = null; try { @@ -480,5 +465,4 @@ public void service(HttpServletRequest request, HttpServletResponse response) } - } diff --git a/src/test/java/com/ning/http/client/async/MultipleHeaderTest.java b/src/test/java/com/ning/http/client/async/MultipleHeaderTest.java index 6bf0b416b9..1e13f6ecc5 100644 --- a/src/test/java/com/ning/http/client/async/MultipleHeaderTest.java +++ b/src/test/java/com/ning/http/client/async/MultipleHeaderTest.java @@ -43,116 +43,118 @@ /** * @author Hubert Iwaniuk */ -public abstract class MultipleHeaderTest extends AbstractBasicTest{ +public abstract class MultipleHeaderTest extends AbstractBasicTest { private ExecutorService executorService; private ServerSocket serverSocket; private Future voidFuture; - @Test(groups = {"standalone", "default_provider"}) - public void testMultipleOtherHeaders() - throws IOException, ExecutionException, TimeoutException, InterruptedException { - final String[] xffHeaders = new String[]{null, null}; + @Test(groups = { "standalone", "default_provider" }) + public void testMultipleOtherHeaders() throws IOException, ExecutionException, TimeoutException, InterruptedException { + final String[] xffHeaders = new String[] { null, null }; AsyncHttpClient ahc = getAsyncHttpClient(null); - Request req = new RequestBuilder("GET").setUrl("http://localhost/" + port1 + "/MultiOther").build(); - final CountDownLatch latch = new CountDownLatch(1); - ahc.executeRequest(req, new AsyncHandler() { - public void onThrowable(Throwable t) { - t.printStackTrace(System.out); - } + try { + Request req = new RequestBuilder("GET").setUrl("http://localhost/" + port1 + "/MultiOther").build(); + final CountDownLatch latch = new CountDownLatch(1); + ahc.executeRequest(req, new AsyncHandler() { + public void onThrowable(Throwable t) { + t.printStackTrace(System.out); + } - public STATE onBodyPartReceived(HttpResponseBodyPart objectHttpResponseBodyPart) throws Exception { - return STATE.CONTINUE; - } + public STATE onBodyPartReceived(HttpResponseBodyPart objectHttpResponseBodyPart) throws Exception { + return STATE.CONTINUE; + } - public STATE onStatusReceived(HttpResponseStatus objectHttpResponseStatus) throws Exception { - return STATE.CONTINUE; - } + public STATE onStatusReceived(HttpResponseStatus objectHttpResponseStatus) throws Exception { + return STATE.CONTINUE; + } - public STATE onHeadersReceived(HttpResponseHeaders response) throws Exception { - int i = 0; - for (String header : response.getHeaders().get("X-Forwarded-For")) { - xffHeaders[i++] = header; + public STATE onHeadersReceived(HttpResponseHeaders response) throws Exception { + int i = 0; + for (String header : response.getHeaders().get("X-Forwarded-For")) { + xffHeaders[i++] = header; + } + latch.countDown(); + return STATE.CONTINUE; } - latch.countDown(); - return STATE.CONTINUE; - } - public Void onCompleted() throws Exception { - return null; - } - }).get(3, TimeUnit.SECONDS); + public Void onCompleted() throws Exception { + return null; + } + }).get(3, TimeUnit.SECONDS); - if (!latch.await(2, TimeUnit.SECONDS)) { - Assert.fail("Time out"); - } - Assert.assertNotNull(xffHeaders[0]); - Assert.assertNotNull(xffHeaders[1]); - try { - Assert.assertEquals(xffHeaders[0], "abc"); - Assert.assertEquals(xffHeaders[1], "def"); - } catch (AssertionError ex) { - Assert.assertEquals(xffHeaders[1], "abc"); - Assert.assertEquals(xffHeaders[0], "def"); + if (!latch.await(2, TimeUnit.SECONDS)) { + Assert.fail("Time out"); + } + Assert.assertNotNull(xffHeaders[0]); + Assert.assertNotNull(xffHeaders[1]); + try { + Assert.assertEquals(xffHeaders[0], "abc"); + Assert.assertEquals(xffHeaders[1], "def"); + } catch (AssertionError ex) { + Assert.assertEquals(xffHeaders[1], "abc"); + Assert.assertEquals(xffHeaders[0], "def"); + } + } finally { + ahc.close(); } - ahc.close(); } - - @Test(groups = {"standalone", "default_provider"}) - public void testMultipleEntityHeaders() - throws IOException, ExecutionException, TimeoutException, InterruptedException { - final String[] clHeaders = new String[]{null, null}; + @Test(groups = { "standalone", "default_provider" }) + public void testMultipleEntityHeaders() throws IOException, ExecutionException, TimeoutException, InterruptedException { + final String[] clHeaders = new String[] { null, null }; AsyncHttpClient ahc = getAsyncHttpClient(null); - Request req = new RequestBuilder("GET").setUrl("http://localhost/" + port1 + "/MultiEnt").build(); - final CountDownLatch latch = new CountDownLatch(1); - ahc.executeRequest(req, new AsyncHandler() { - public void onThrowable(Throwable t) { - t.printStackTrace(System.out); - } + try { + Request req = new RequestBuilder("GET").setUrl("http://localhost/" + port1 + "/MultiEnt").build(); + final CountDownLatch latch = new CountDownLatch(1); + ahc.executeRequest(req, new AsyncHandler() { + public void onThrowable(Throwable t) { + t.printStackTrace(System.out); + } - public STATE onBodyPartReceived(HttpResponseBodyPart objectHttpResponseBodyPart) throws Exception { - return STATE.CONTINUE; - } + public STATE onBodyPartReceived(HttpResponseBodyPart objectHttpResponseBodyPart) throws Exception { + return STATE.CONTINUE; + } - public STATE onStatusReceived(HttpResponseStatus objectHttpResponseStatus) throws Exception { - return STATE.CONTINUE; - } + public STATE onStatusReceived(HttpResponseStatus objectHttpResponseStatus) throws Exception { + return STATE.CONTINUE; + } - public STATE onHeadersReceived(HttpResponseHeaders response) throws Exception { - try { - int i = 0; - for (String header : response.getHeaders().get("Content-Length")) { - clHeaders[i++] = header; + public STATE onHeadersReceived(HttpResponseHeaders response) throws Exception { + try { + int i = 0; + for (String header : response.getHeaders().get("Content-Length")) { + clHeaders[i++] = header; + } + } finally { + latch.countDown(); } - } finally { - latch.countDown(); + return STATE.CONTINUE; } - return STATE.CONTINUE; - } - public Void onCompleted() throws Exception { - return null; - } - }).get(3, TimeUnit.SECONDS); - - if (!latch.await(2, TimeUnit.SECONDS)) { - Assert.fail("Time out"); - } - Assert.assertNotNull(clHeaders[0]); - Assert.assertNotNull(clHeaders[1]); + public Void onCompleted() throws Exception { + return null; + } + }).get(3, TimeUnit.SECONDS); - // We can predict the order - try { - Assert.assertEquals(clHeaders[0], "2"); - Assert.assertEquals(clHeaders[1], "1"); - } catch (Throwable ex) { - Assert.assertEquals(clHeaders[0], "1"); - Assert.assertEquals(clHeaders[1], "2"); + if (!latch.await(2, TimeUnit.SECONDS)) { + Assert.fail("Time out"); + } + Assert.assertNotNull(clHeaders[0]); + Assert.assertNotNull(clHeaders[1]); + + // We can predict the order + try { + Assert.assertEquals(clHeaders[0], "2"); + Assert.assertEquals(clHeaders[1], "1"); + } catch (Throwable ex) { + Assert.assertEquals(clHeaders[0], "1"); + Assert.assertEquals(clHeaders[1], "2"); + } + } finally { + ahc.close(); } - ahc.close(); - } @BeforeClass(alwaysRun = true) @@ -174,23 +176,12 @@ public Void call() throws Exception { socket.shutdownInput(); if (req.endsWith("MultiEnt")) { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); - outputStreamWriter.append("HTTP/1.0 200 OK\n" + - "Connection: close\n" + - "Content-Type: text/plain; charset=iso-8859-1\n" + - "Content-Length: 2\n" + - "Content-Length: 1\n" + - "\n0\n"); + outputStreamWriter.append("HTTP/1.0 200 OK\n" + "Connection: close\n" + "Content-Type: text/plain; charset=iso-8859-1\n" + "Content-Length: 2\n" + "Content-Length: 1\n" + "\n0\n"); outputStreamWriter.flush(); socket.shutdownOutput(); } else if (req.endsWith("MultiOther")) { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); - outputStreamWriter.append("HTTP/1.0 200 OK\n" + - "Connection: close\n" + - "Content-Type: text/plain; charset=iso-8859-1\n" + - "Content-Length: 1\n" + - "X-Forwarded-For: abc\n" + - "X-Forwarded-For: def\n" + - "\n0\n"); + outputStreamWriter.append("HTTP/1.0 200 OK\n" + "Connection: close\n" + "Content-Type: text/plain; charset=iso-8859-1\n" + "Content-Length: 1\n" + "X-Forwarded-For: abc\n" + "X-Forwarded-For: def\n" + "\n0\n"); outputStreamWriter.flush(); socket.shutdownOutput(); } diff --git a/src/test/java/com/ning/http/client/async/NoNullResponseTest.java b/src/test/java/com/ning/http/client/async/NoNullResponseTest.java index cc9f0890e3..21edc3529a 100644 --- a/src/test/java/com/ning/http/client/async/NoNullResponseTest.java +++ b/src/test/java/com/ning/http/client/async/NoNullResponseTest.java @@ -33,50 +33,45 @@ public abstract class NoNullResponseTest extends AbstractBasicTest { private static final String VERISIGN_HTTPS_URL = "https://www.verisign.com/"; - @Test(invocationCount = 4, groups = {"online", "default_provider"}) + @Test(invocationCount = 4, groups = { "online", "default_provider" }) public void multipleSslRequestsWithDelayAndKeepAlive() throws Throwable { final AsyncHttpClient client = create(); - final BoundRequestBuilder builder = client.prepareGet(VERISIGN_HTTPS_URL); - final Response response1 = builder.execute().get(); - Thread.sleep(5000); - final Response response2 = builder.execute().get(); - if (response2 != null) { - System.out.println("Success (2nd response was not null)."); - } else { - System.out.println("Failed (2nd response was null)."); + try { + final BoundRequestBuilder builder = client.prepareGet(VERISIGN_HTTPS_URL); + final Response response1 = builder.execute().get(); + Thread.sleep(4000); + final Response response2 = builder.execute().get(); + if (response2 != null) { + System.out.println("Success (2nd response was not null)."); + } else { + System.out.println("Failed (2nd response was null)."); + } + Assert.assertNotNull(response1); + Assert.assertNotNull(response2); + } finally { + client.close(); } - Assert.assertNotNull(response1); - Assert.assertNotNull(response2); - client.close(); } private AsyncHttpClient create() throws GeneralSecurityException { - final AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder() - .setCompressionEnabled(true) - .setFollowRedirects(true) - .setSSLContext(getSSLContext()) - .setAllowPoolingConnection(true) - .setConnectionTimeoutInMs(10000) - .setIdleConnectionInPoolTimeoutInMs(60000) - .setRequestTimeoutInMs(10000) - .setMaximumConnectionsPerHost(-1) - .setMaximumConnectionsTotal(-1); + final AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder().setCompressionEnabled(true).setFollowRedirects(true).setSSLContext(getSSLContext()).setAllowPoolingConnection(true).setConnectionTimeoutInMs(10000) + .setIdleConnectionInPoolTimeoutInMs(60000).setRequestTimeoutInMs(10000).setMaximumConnectionsPerHost(-1).setMaximumConnectionsTotal(-1); return getAsyncHttpClient(configBuilder.build()); } private SSLContext getSSLContext() throws GeneralSecurityException { final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, new TrustManager[]{new MockTrustManager()}, null); + sslContext.init(null, new TrustManager[] { new MockTrustManager() }, null); return sslContext; } private static class MockTrustManager implements X509TrustManager { public X509Certificate[] getAcceptedIssuers() { - throw new UnsupportedOperationException(); + return null; } public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { - throw new UnsupportedOperationException(); + // Do nothing. } public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { diff --git a/src/test/java/com/ning/http/client/async/NonAsciiContentLengthTest.java b/src/test/java/com/ning/http/client/async/NonAsciiContentLengthTest.java index 80acc7942f..b8401491d5 100644 --- a/src/test/java/com/ning/http/client/async/NonAsciiContentLengthTest.java +++ b/src/test/java/com/ning/http/client/async/NonAsciiContentLengthTest.java @@ -14,7 +14,6 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; -import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.Response; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Request; @@ -36,61 +35,62 @@ public abstract class NonAsciiContentLengthTest extends AbstractBasicTest { + public void setUpServer() throws Exception { + server = new Server(); + port1 = findFreePort(); + Connector listener = new SelectChannelConnector(); - public void setUpServer() throws Exception { - server = new Server(); - port1 = findFreePort(); - Connector listener = new SelectChannelConnector(); + listener.setHost("127.0.0.1"); + listener.setPort(port1); + server.addConnector(listener); + server.setHandler(new AbstractHandler() { - listener.setHost("127.0.0.1"); - listener.setPort(port1); - server.addConnector(listener); - server.setHandler(new AbstractHandler() { + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + int MAX_BODY_SIZE = 1024; // Can only handle bodies of up to 1024 bytes. + byte[] b = new byte[MAX_BODY_SIZE]; + int offset = 0; + int numBytesRead; + ServletInputStream is = request.getInputStream(); + try { + while ((numBytesRead = is.read(b, offset, MAX_BODY_SIZE - offset)) != -1) { + offset += numBytesRead; + } + } finally { + is.close(); + } + assertEquals(request.getContentLength(), offset); + response.setStatus(200); + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentLength(request.getContentLength()); + ServletOutputStream os = response.getOutputStream(); + try { + os.write(b, 0, offset); + } finally { + os.close(); + } + } + }); + server.start(); + } - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - int MAX_BODY_SIZE = 1024; //Can only handle bodies of up to 1024 bytes. - byte[] b = new byte[MAX_BODY_SIZE]; - int offset = 0; - int numBytesRead; - ServletInputStream is = request.getInputStream(); - try { - while ((numBytesRead = is.read(b, offset, MAX_BODY_SIZE - offset)) != -1) { - offset += numBytesRead; - } - } finally { - is.close(); - } - assertEquals(request.getContentLength(), offset); - response.setStatus(200); - response.setCharacterEncoding(request.getCharacterEncoding()); - response.setContentLength(request.getContentLength()); - ServletOutputStream os = response.getOutputStream(); - try { - os.write(b, 0, offset); - } finally { - os.close(); - } - } - }); - server.start(); - } + @Test(groups = { "standalone", "default_provider" }) + public void testNonAsciiContentLength() throws Exception { + setUpServer(); + execute("test"); + execute("\u4E00"); // Unicode CJK ideograph for one + } - @Test(groups = { "standalone", "default_provider" }) - public void testNonAsciiContentLength() throws Exception { - setUpServer(); - execute("test"); - execute("\u4E00"); // Unicode CJK ideograph for one - } - - protected void execute(String body) throws IOException, InterruptedException, ExecutionException { - AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(body).setBodyEncoding("UTF-8"); - Future f = r.execute(); - Response resp = f.get(); - assertEquals(resp.getStatusCode(), 200); - assertEquals(body, resp.getResponseBody("UTF-8")); - client.close(); - } + protected void execute(String body) throws IOException, InterruptedException, ExecutionException { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + BoundRequestBuilder r = client.preparePost(getTargetUrl()).setBody(body).setBodyEncoding("UTF-8"); + Future f = r.execute(); + Response resp = f.get(); + assertEquals(resp.getStatusCode(), 200); + assertEquals(body, resp.getResponseBody("UTF-8")); + } finally { + client.close(); + } + } } diff --git a/src/test/java/com/ning/http/client/async/ParamEncodingTest.java b/src/test/java/com/ning/http/client/async/ParamEncodingTest.java index 02af3160ee..fc84431aaf 100644 --- a/src/test/java/com/ning/http/client/async/ParamEncodingTest.java +++ b/src/test/java/com/ning/http/client/async/ParamEncodingTest.java @@ -15,6 +15,8 @@ */ package com.ning.http.client.async; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.Response; import org.eclipse.jetty.server.Request; @@ -36,13 +38,10 @@ public abstract class ParamEncodingTest extends AbstractBasicTest { private class ParamEncoding extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("POST".equalsIgnoreCase(request.getMethod())) { String p = request.getParameter("test"); - if (p != null && !p.equals("")) { + if (isNonEmpty(p)) { response.setStatus(HttpServletResponse.SC_OK); response.addHeader("X-Param", p); } else { @@ -56,20 +55,20 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testParameters() throws IOException, ExecutionException, TimeoutException, InterruptedException { String value = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKQLMNOPQRSTUVWXYZ1234567809`~!@#$%^&*()_+-=,.<>/?;:'\"[]{}\\| "; AsyncHttpClient client = getAsyncHttpClient(null); - Future f = client - .preparePost("http://127.0.0.1/" + port1) - .addParameter("test", value) - .execute(); - Response resp = f.get(10, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("X-Param"), value.trim()); - client.close(); + try { + Future f = client.preparePost("http://127.0.0.1/" + port1).addParameter("test", value).execute(); + Response resp = f.get(10, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("X-Param"), value.trim()); + } finally { + client.close(); + } } @Override diff --git a/src/test/java/com/ning/http/client/async/PerRequestRelative302Test.java b/src/test/java/com/ning/http/client/async/PerRequestRelative302Test.java index 756a258339..a476d9adca 100644 --- a/src/test/java/com/ning/http/client/async/PerRequestRelative302Test.java +++ b/src/test/java/com/ning/http/client/async/PerRequestRelative302Test.java @@ -45,11 +45,7 @@ public abstract class PerRequestRelative302Test extends AbstractBasicTest { private class Relative302Handler extends AbstractHandler { - - public void handle(String s, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { String param; httpResponse.setContentType("text/html; charset=utf-8"); @@ -89,46 +85,41 @@ public void setUpGlobal() throws Exception { log.info("Local HTTP server started successfully"); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void redirected302Test() throws Throwable { isSet.getAndSet(false); - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient c = getAsyncHttpClient(cg); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + + // once + Response response = c.prepareGet(getTargetUrl()).setFollowRedirects(true).setHeader("X-redirect", "http://www.microsoft.com/").execute().get(); + + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + + String anyMicrosoftPage = "http://www.microsoft.com[^:]*:80"; + String baseUrl = getBaseUrl(response.getUri()); - // once - Response response = c.prepareGet(getTargetUrl()) - .setFollowRedirects(true) - .setHeader("X-redirect", "http://www.microsoft.com/") - .execute().get(); - - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - - String anyMicrosoftPage = "http://www.microsoft.com[^:]*:80"; - String baseUrl = getBaseUrl(response.getUri()); - - c.close(); - - assertTrue(baseUrl.matches(anyMicrosoftPage), "response does not show redirection to " + anyMicrosoftPage); + assertTrue(baseUrl.matches(anyMicrosoftPage), "response does not show redirection to " + anyMicrosoftPage); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void notRedirected302Test() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + // once + Response response = c.prepareGet(getTargetUrl()).setFollowRedirects(false).setHeader("X-redirect", "http://www.microsoft.com/").execute().get(); - - // once - Response response = c.prepareGet(getTargetUrl()) - .setFollowRedirects(false) - .setHeader("X-redirect", "http://www.microsoft.com/") - .execute().get(); - - assertNotNull(response); - assertEquals(response.getStatusCode(), 302); - - c.close(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 302); + } finally { + c.close(); + } } private String getBaseUrl(URI uri) { @@ -148,41 +139,36 @@ private static int getPort(URI uri) { return port; } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void redirected302InvalidTest() throws Throwable { isSet.getAndSet(false); - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient c = getAsyncHttpClient(cg); + AsyncHttpClient c = getAsyncHttpClient(null); // If the test hit a proxy, no ConnectException will be thrown and instead of 404 will be returned. try { - Response response = c.preparePost(getTargetUrl()) - .setFollowRedirects(true) - .setHeader("X-redirect", String.format("http://127.0.0.1:%d/", port2)) - .execute().get(); + Response response = c.preparePost(getTargetUrl()).setFollowRedirects(true).setHeader("X-redirect", String.format("http://127.0.0.1:%d/", port2)).execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 404); } catch (ExecutionException ex) { assertEquals(ex.getCause().getClass(), ConnectException.class); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void relativeLocationUrl() throws Throwable { isSet.getAndSet(false); - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient c = getAsyncHttpClient(cg); - - Response response = c.preparePost(getTargetUrl()) - .setFollowRedirects(true) - .setHeader("X-redirect", "/foo/test") - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 302); - assertEquals(response.getUri().toString(), getTargetUrl()); - c.close(); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + Response response = c.preparePost(getTargetUrl()).setFollowRedirects(true).setHeader("X-redirect", "/foo/test").execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 302); + assertEquals(response.getUri().toString(), getTargetUrl()); + } finally { + c.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/PerRequestTimeoutTest.java b/src/test/java/com/ning/http/client/async/PerRequestTimeoutTest.java index e50bee78de..599775a71b 100644 --- a/src/test/java/com/ning/http/client/async/PerRequestTimeoutTest.java +++ b/src/test/java/com/ning/http/client/async/PerRequestTimeoutTest.java @@ -1,20 +1,22 @@ /* -* Copyright 2010 Ning, Inc. -* -* Ning licenses this file to you under the Apache License, version 2.0 -* (the "License"); you may not use this file except in compliance with the -* License. You may obtain a copy of the License at: -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ + * Copyright 2010 Ning, Inc. + * + * Ning licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ package com.ning.http.client.async; +import static com.ning.http.util.DateUtil.millisTime; + import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; @@ -44,15 +46,13 @@ /** * Per request timeout configuration test. - * + * * @author Hubert Iwaniuk */ public abstract class PerRequestTimeoutTest extends AbstractBasicTest { private static final String MSG = "Enough is enough."; - protected String getExpectedTimeoutMessage() { - return "No response received after 100"; - } + protected abstract void checkTimeoutMessage(String message); @Override public AbstractHandler configureHandler() throws Exception { @@ -95,14 +95,13 @@ public void run() { } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testRequestTimeout() throws IOException { AsyncHttpClient client = getAsyncHttpClient(null); PerRequestConfig requestConfig = new PerRequestConfig(); requestConfig.setRequestTimeoutInMs(100); - Future responseFuture = - client.prepareGet(getTargetUrl()).setPerRequestConfig(requestConfig).execute(); try { + Future responseFuture = client.prepareGet(getTargetUrl()).setPerRequestConfig(requestConfig).execute(); Response response = responseFuture.get(2000, TimeUnit.MILLISECONDS); assertNull(response); client.close(); @@ -110,21 +109,21 @@ public void testRequestTimeout() throws IOException { fail("Interrupted.", e); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof TimeoutException); - assertEquals(e.getCause().getMessage(), getExpectedTimeoutMessage()); + checkTimeoutMessage(e.getCause().getMessage()); } catch (TimeoutException e) { fail("Timeout.", e); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testGlobalDefaultPerRequestInfiniteTimeout() throws IOException { AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(100).build()); PerRequestConfig requestConfig = new PerRequestConfig(); requestConfig.setRequestTimeoutInMs(-1); - Future responseFuture = - client.prepareGet(getTargetUrl()).setPerRequestConfig(requestConfig).execute(); try { + Future responseFuture = client.prepareGet(getTargetUrl()).setPerRequestConfig(requestConfig).execute(); Response response = responseFuture.get(); assertNotNull(response); client.close(); @@ -132,16 +131,17 @@ public void testGlobalDefaultPerRequestInfiniteTimeout() throws IOException { fail("Interrupted.", e); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof TimeoutException); - assertEquals(e.getCause().getMessage(), getExpectedTimeoutMessage()); + checkTimeoutMessage(e.getCause().getMessage()); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testGlobalRequestTimeout() throws IOException { AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(100).build()); - Future responseFuture = client.prepareGet(getTargetUrl()).execute(); try { + Future responseFuture = client.prepareGet(getTargetUrl()).execute(); Response response = responseFuture.get(2000, TimeUnit.MILLISECONDS); assertNull(response); client.close(); @@ -149,47 +149,48 @@ public void testGlobalRequestTimeout() throws IOException { fail("Interrupted.", e); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof TimeoutException); - assertEquals(e.getCause().getMessage(), getExpectedTimeoutMessage()); + checkTimeoutMessage(e.getCause().getMessage()); } catch (TimeoutException e) { fail("Timeout.", e); + } finally { + client.close(); } - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testGlobalIdleTimeout() throws IOException { - final long times[] = new long[]{-1, -1}; + final long times[] = new long[] { -1, -1 }; AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setIdleConnectionInPoolTimeoutInMs(2000).build()); - Future responseFuture = client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandler() { - @Override - public Response onCompleted(Response response) throws Exception { - return response; - } + try { + Future responseFuture = client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandler() { + @Override + public Response onCompleted(Response response) throws Exception { + return response; + } - @Override - public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { - times[0] = System.currentTimeMillis(); - return super.onBodyPartReceived(content); - } + @Override + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + times[0] = millisTime(); + return super.onBodyPartReceived(content); + } - @Override - public void onThrowable(Throwable t) { - times[1] = System.currentTimeMillis(); - super.onThrowable(t); - } - }); - try { + @Override + public void onThrowable(Throwable t) { + times[1] = millisTime(); + super.onThrowable(t); + } + }); Response response = responseFuture.get(); assertNotNull(response); assertEquals(response.getResponseBody(), MSG + MSG); } catch (InterruptedException e) { fail("Interrupted.", e); } catch (ExecutionException e) { - log.info(String.format("\n@%dms Last body part received\n@%dms Connection killed\n %dms difference.", - times[0], times[1], (times[1] - times[0]))); + log.info(String.format("\n@%dms Last body part received\n@%dms Connection killed\n %dms difference.", times[0], times[1], (times[1] - times[0]))); fail("Timeouted on idle.", e); + } finally { + client.close(); } - client.close(); } } diff --git a/src/test/java/com/ning/http/client/async/PostRedirectGetTest.java b/src/test/java/com/ning/http/client/async/PostRedirectGetTest.java index 658673384b..e581b9f8ab 100644 --- a/src/test/java/com/ning/http/client/async/PostRedirectGetTest.java +++ b/src/test/java/com/ning/http/client/async/PostRedirectGetTest.java @@ -35,10 +35,8 @@ public abstract class PostRedirectGetTest extends AbstractBasicTest { - // ------------------------------------------------------ Test Configuration - @Override public AbstractHandler configureHandler() throws Exception { return new PostRedirectGetHandler(); @@ -46,123 +44,111 @@ public AbstractHandler configureHandler() throws Exception { // ------------------------------------------------------------ Test Methods - @Test(groups = {"standalone", "post_redirect_get"}) + @Test(groups = { "standalone", "post_redirect_get" }) public void postRedirectGet302Test() throws Exception { doTestPositive(302); } - @Test(groups = {"standalone", "post_redirect_get"}) + @Test(groups = { "standalone", "post_redirect_get" }) public void postRedirectGet302StrictTest() throws Exception { doTestNegative(302, true); } - @Test(groups = {"standalone", "post_redirect_get"}) + @Test(groups = { "standalone", "post_redirect_get" }) public void postRedirectGet303Test() throws Exception { doTestPositive(303); } - @Test(groups = {"standalone", "post_redirect_get"}) + @Test(groups = { "standalone", "post_redirect_get" }) public void postRedirectGet301Test() throws Exception { doTestNegative(301, false); } - @Test(groups = {"standalone", "post_redirect_get"}) + @Test(groups = { "standalone", "post_redirect_get" }) public void postRedirectGet307Test() throws Exception { doTestNegative(307, false); } - // --------------------------------------------------------- Private Methods - private void doTestNegative(final int status, boolean strict) throws Exception { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true). - setStrict302Handling(strict). - addResponseFilter(new ResponseFilter() { - @Override - public FilterContext filter(FilterContext ctx) throws FilterException { - // pass on the x-expect-get and remove the x-redirect - // headers if found in the response - ctx.getResponseHeaders().getHeaders().get("x-expect-post"); - ctx.getRequest().getHeaders().add("x-expect-post", "true"); - ctx.getRequest().getHeaders().remove("x-redirect"); - return ctx; - } - }).build()); - Request request = new RequestBuilder("POST").setUrl(getTargetUrl()) - .addParameter("q", "a b") - .addHeader("x-redirect", +status + "@" + "http://localhost/" + port1 + "/foo/bar/baz") - .addHeader("x-negative", "true") - .build(); - Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { - + AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).setStrict302Handling(strict).addResponseFilter(new ResponseFilter() { @Override - public Integer onCompleted(Response response) throws Exception { - return response.getStatusCode(); + public FilterContext filter(FilterContext ctx) throws FilterException { + // pass on the x-expect-get and remove the x-redirect + // headers if found in the response + ctx.getResponseHeaders().getHeaders().get("x-expect-post"); + ctx.getRequest().getHeaders().add("x-expect-post", "true"); + ctx.getRequest().getHeaders().remove("x-redirect"); + return ctx; } + }).build()); + try { + Request request = new RequestBuilder("POST").setUrl(getTargetUrl()).addParameter("q", "a b").addHeader("x-redirect", +status + "@" + "http://localhost/" + port1 + "/foo/bar/baz").addHeader("x-negative", "true").build(); + Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { + + @Override + public Integer onCompleted(Response response) throws Exception { + return response.getStatusCode(); + } - /* @Override */ - public void onThrowable(Throwable t) { - t.printStackTrace(); - Assert.fail("Unexpected exception: " + t.getMessage(), t); - } + /* @Override */ + public void onThrowable(Throwable t) { + t.printStackTrace(); + Assert.fail("Unexpected exception: " + t.getMessage(), t); + } - }); - int statusCode = responseFuture.get(); - Assert.assertEquals(statusCode, 200); - p.close(); + }); + int statusCode = responseFuture.get(); + Assert.assertEquals(statusCode, 200); + } finally { + p.close(); + } } - private void doTestPositive(final int status) throws Exception { - AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true). - addResponseFilter(new ResponseFilter() { - @Override - public FilterContext filter(FilterContext ctx) throws FilterException { - // pass on the x-expect-get and remove the x-redirect - // headers if found in the response - ctx.getResponseHeaders().getHeaders().get("x-expect-get"); - ctx.getRequest().getHeaders().add("x-expect-get", "true"); - ctx.getRequest().getHeaders().remove("x-redirect"); - return ctx; - } - }).build()); - Request request = new RequestBuilder("POST").setUrl(getTargetUrl()) - .addParameter("q", "a b") - .addHeader("x-redirect", +status + "@" + "http://localhost/" + port1 + "/foo/bar/baz") - .build(); - Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { - + AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).addResponseFilter(new ResponseFilter() { @Override - public Integer onCompleted(Response response) throws Exception { - return response.getStatusCode(); + public FilterContext filter(FilterContext ctx) throws FilterException { + // pass on the x-expect-get and remove the x-redirect + // headers if found in the response + ctx.getResponseHeaders().getHeaders().get("x-expect-get"); + ctx.getRequest().getHeaders().add("x-expect-get", "true"); + ctx.getRequest().getHeaders().remove("x-redirect"); + return ctx; } + }).build()); + try { + Request request = new RequestBuilder("POST").setUrl(getTargetUrl()).addParameter("q", "a b").addHeader("x-redirect", +status + "@" + "http://localhost/" + port1 + "/foo/bar/baz").build(); + Future responseFuture = p.executeRequest(request, new AsyncCompletionHandler() { + + @Override + public Integer onCompleted(Response response) throws Exception { + return response.getStatusCode(); + } - /* @Override */ - public void onThrowable(Throwable t) { - t.printStackTrace(); - Assert.fail("Unexpected exception: " + t.getMessage(), t); - } + /* @Override */ + public void onThrowable(Throwable t) { + t.printStackTrace(); + Assert.fail("Unexpected exception: " + t.getMessage(), t); + } - }); - int statusCode = responseFuture.get(); - Assert.assertEquals(statusCode, 200); - p.close(); + }); + int statusCode = responseFuture.get(); + Assert.assertEquals(statusCode, 200); + } finally { + p.close(); + } } - // ---------------------------------------------------------- Nested Classes - public static class PostRedirectGetHandler extends AbstractHandler { final AtomicInteger counter = new AtomicInteger(); /* @Override */ - public void handle(String pathInContext, - org.eclipse.jetty.server.Request request, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String pathInContext, org.eclipse.jetty.server.Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { final boolean expectGet = (httpRequest.getHeader("x-expect-get") != null); final boolean expectPost = (httpRequest.getHeader("x-expect-post") != null); @@ -212,7 +198,6 @@ public void handle(String pathInContext, return; } - httpResponse.sendError(500); httpResponse.getOutputStream().flush(); httpResponse.getOutputStream().close(); diff --git a/src/test/java/com/ning/http/client/async/PostWithQSTest.java b/src/test/java/com/ning/http/client/async/PostWithQSTest.java index d99498bd18..af49ee6728 100644 --- a/src/test/java/com/ning/http/client/async/PostWithQSTest.java +++ b/src/test/java/com/ning/http/client/async/PostWithQSTest.java @@ -15,6 +15,8 @@ */ package com.ning.http.client.async; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.AsyncCompletionHandlerBase; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.HttpResponseStatus; @@ -39,7 +41,7 @@ /** * Tests POST request with Query String. - * + * * @author Hubert Iwaniuk */ public abstract class PostWithQSTest extends AbstractBasicTest { @@ -48,13 +50,10 @@ public abstract class PostWithQSTest extends AbstractBasicTest { * POST with QS server part. */ private class PostWithQSHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("POST".equalsIgnoreCase(request.getMethod())) { String qs = request.getQueryString(); - if (qs != null && !qs.equals("") && request.getContentLength() == 3) { + if (isNonEmpty(qs) && request.getContentLength() == 3) { ServletInputStream is = request.getInputStream(); response.setStatus(HttpServletResponse.SC_OK); byte buf[] = new byte[is.available()]; @@ -72,54 +71,86 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void postWithQS() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=b").setBody("abc".getBytes()).execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - client.close(); + try { + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=b").setBody("abc".getBytes()).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void postWithNulParamQS() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=").setBody("abc".getBytes()).execute(new AsyncCompletionHandlerBase() { + try { + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=").setBody("abc".getBytes()).execute(new AsyncCompletionHandlerBase() { - /* @Override */ - public STATE onStatusReceived(final HttpResponseStatus status) throws Exception { - if (!status.getUrl().toURL().toString().equals("http://127.0.0.1/" + port1 + "/?a")) { - throw new IOException(status.getUrl().toURL().toString()); + /* @Override */ + public STATE onStatusReceived(final HttpResponseStatus status) throws Exception { + if (!status.getUrl().toURL().toString().equals("http://127.0.0.1/" + port1 + "/?a=")) { + throw new IOException(status.getUrl().toURL().toString()); + } + return super.onStatusReceived(status); } - return super.onStatusReceived(status); - } - }); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - client.close(); + }); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void postWithNulParamsQS() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=b&c&d=e").setBody("abc".getBytes()).execute(new AsyncCompletionHandlerBase() { + try { + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=b&c&d=e").setBody("abc".getBytes()).execute(new AsyncCompletionHandlerBase() { - /* @Override */ - public STATE onStatusReceived(final HttpResponseStatus status) throws Exception { - if (!status.getUrl().toURL().toString().equals("http://127.0.0.1/" + port1 + "/?a=b&c&d=e")) { - throw new IOException("failed to parse the query properly"); + /* @Override */ + public STATE onStatusReceived(final HttpResponseStatus status) throws Exception { + if (!status.getUrl().toURL().toString().equals("http://127.0.0.1/" + port1 + "/?a=b&c&d=e")) { + throw new IOException("failed to parse the query properly"); + } + return super.onStatusReceived(status); } - return super.onStatusReceived(status); - } - }); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - client.close(); + }); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } + } + + @Test(groups = { "standalone", "default_provider" }) + public void postWithEmptyParamsQS() throws IOException, ExecutionException, TimeoutException, InterruptedException { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/?a=b&c=&d=e").setBody("abc".getBytes()).execute(new AsyncCompletionHandlerBase() { + + /* @Override */ + public STATE onStatusReceived(final HttpResponseStatus status) throws Exception { + if (!status.getUrl().toURL().toString().equals("http://127.0.0.1/" + port1 + "/?a=b&c=&d=e")) { + throw new IOException("failed to parse the query properly"); + } + return super.onStatusReceived(status); + } + + }); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + } finally { + client.close(); + } } @Override diff --git a/src/test/java/com/ning/http/client/async/ProviderUtil.java b/src/test/java/com/ning/http/client/async/ProviderUtil.java index 70f79dc94c..17cdbe33d1 100644 --- a/src/test/java/com/ning/http/client/async/ProviderUtil.java +++ b/src/test/java/com/ning/http/client/async/ProviderUtil.java @@ -1,28 +1,28 @@ /* -* Copyright 2010 Ning, Inc. -* -* Ning licenses this file to you under the Apache License, version 2.0 -* (the "License"); you may not use this file except in compliance with the -* License. You may obtain a copy of the License at: -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ + * Copyright 2010 Ning, Inc. + * + * Ning licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ package com.ning.http.client.async; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.providers.apache.ApacheAsyncHttpProvider; +import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; import com.ning.http.client.providers.jdk.JDKAsyncHttpProvider; public class ProviderUtil { - public static AsyncHttpClient nettyProvider(AsyncHttpClientConfig config) { if (config == null) { return new AsyncHttpClient(); @@ -31,6 +31,13 @@ public static AsyncHttpClient nettyProvider(AsyncHttpClientConfig config) { } } + public static AsyncHttpClient grizzlyProvider(AsyncHttpClientConfig config) { + if (config == null) { + config = new AsyncHttpClientConfig.Builder().build(); + } + return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + } + public static AsyncHttpClient apacheProvider(AsyncHttpClientConfig config) { if (config == null) { return new AsyncHttpClient(new ApacheAsyncHttpProvider(new AsyncHttpClientConfig.Builder().build())); diff --git a/src/test/java/com/ning/http/client/async/ProxyTest.java b/src/test/java/com/ning/http/client/async/ProxyTest.java index 941897ce0c..e827ec41fe 100644 --- a/src/test/java/com/ning/http/client/async/ProxyTest.java +++ b/src/test/java/com/ning/http/client/async/ProxyTest.java @@ -16,16 +16,17 @@ package com.ning.http.client.async; import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.ProxyServer; import com.ning.http.client.Response; -import com.ning.http.client.ProxyServer.Protocol; -import com.ning.http.util.ProxyUtils; import java.io.IOException; -import java.net.ConnectException; +import java.net.*; +import java.util.Arrays; +import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -42,15 +43,12 @@ /** * Proxy usage tests. - * + * * @author Hubert Iwaniuk */ public abstract class ProxyTest extends AbstractBasicTest { private class ProxyHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("GET".equalsIgnoreCase(request.getMethod())) { response.addHeader("target", r.getUri().getPath()); response.setStatus(HttpServletResponse.SC_OK); @@ -66,73 +64,83 @@ public AbstractHandler configureHandler() throws Exception { return new ProxyHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testRequestLevelProxy() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - String target = "http://127.0.0.1:1234/"; - Future f = client - .prepareGet(target) - .setProxyServer(new ProxyServer("127.0.0.1", port1)) - .execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("target"), "/"); - client.close(); + try { + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).setProxyServer(new ProxyServer("127.0.0.1", port1)).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testGlobalProxy() throws IOException, ExecutionException, TimeoutException, InterruptedException { - AsyncHttpClientConfig cfg - = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port1)).build(); + AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port1)).build(); AsyncHttpClient client = getAsyncHttpClient(cfg); - String target = "http://127.0.0.1:1234/"; - Future f = client - .prepareGet(target) - .execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("target"), "/"); - client.close(); + try { + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testBothProxies() throws IOException, ExecutionException, TimeoutException, InterruptedException { - AsyncHttpClientConfig cfg - = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port1 - 1)).build(); + AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port1 - 1)).build(); AsyncHttpClient client = getAsyncHttpClient(cfg); - String target = "http://127.0.0.1:1234/"; - Future f = client - .prepareGet(target) - .setProxyServer(new ProxyServer("127.0.0.1", port1)) - .execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("target"), "/"); - client.close(); + try { + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).setProxyServer(new ProxyServer("127.0.0.1", port1)).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); + } finally { + client.close(); + } } - - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testNonProxyHosts() throws IOException, ExecutionException, TimeoutException, InterruptedException { - AsyncHttpClientConfig cfg - = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port1 - 1)).build(); + AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().setProxyServer(new ProxyServer("127.0.0.1", port1 - 1)).build(); AsyncHttpClient client = getAsyncHttpClient(cfg); try { String target = "http://127.0.0.1:1234/"; - client.prepareGet(target) - .setProxyServer(new ProxyServer("127.0.0.1", port1).addNonProxyHost("127.0.0.1")) - .execute().get(); + client.prepareGet(target).setProxyServer(new ProxyServer("127.0.0.1", port1).addNonProxyHost("127.0.0.1")).execute().get(); assertFalse(true); } catch (Throwable e) { assertNotNull(e.getCause()); assertEquals(e.getCause().getClass(), ConnectException.class); + } finally { + client.close(); } + } - client.close(); + @Test(groups = { "standalone", "default_provider" }) + public void testNonProxyHostIssue202() throws IOException, ExecutionException, TimeoutException, InterruptedException { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + String target = "http://127.0.0.1/" + port1 + "/"; + Future f = client.prepareGet(target).setProxyServer(new ProxyServer("127.0.0.1", port1 - 1).addNonProxyHost("127.0.0.1")).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) @@ -150,29 +158,31 @@ public void testProxyProperties() throws IOException, ExecutionException, Timeou AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().setUseProxyProperties(true).build(); AsyncHttpClient client = getAsyncHttpClient(cfg); + try { + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); - String target = "http://127.0.0.1:1234/"; - Future f = client.prepareGet(target).execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("target"), "/"); + target = "http://localhost:1234/"; + f = client.prepareGet(target).execute(); + try { + resp = f.get(3, TimeUnit.SECONDS); + fail("should not be able to connect"); + } catch (ExecutionException e) { + // ok, no proxy used + } - target = "http://localhost:1234/"; - f = client.prepareGet(target).execute(); - try { - resp = f.get(3, TimeUnit.SECONDS); - fail("should not be able to connect"); - } catch (ExecutionException e) { - // ok, no proxy used + } finally { + client.close(); } - - client.close(); } finally { System.setProperties(originalProps); } } - + @Test(groups = { "standalone", "default_provider" }) public void testIgnoreProxyPropertiesByDefault() throws IOException, ExecutionException, TimeoutException, InterruptedException { Properties originalProps = System.getProperties(); @@ -186,24 +196,25 @@ public void testIgnoreProxyPropertiesByDefault() throws IOException, ExecutionEx System.setProperty("http.proxyPort", String.valueOf(port1)); System.setProperty("http.nonProxyHosts", "localhost"); - AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient client = getAsyncHttpClient(cfg); - - String target = "http://127.0.0.1:1234/"; - Future f = client.prepareGet(target).execute(); + AsyncHttpClient client = getAsyncHttpClient(null); try { - f.get(3, TimeUnit.SECONDS); - fail("should not be able to connect"); - } catch (ExecutionException e) { - // ok, no proxy used - } + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).execute(); + try { + f.get(3, TimeUnit.SECONDS); + fail("should not be able to connect"); + } catch (ExecutionException e) { + // ok, no proxy used + } - client.close(); + } finally { + client.close(); + } } finally { System.setProperties(originalProps); } } - + @Test(groups = { "standalone", "default_provider" }) public void testProxyActivationProperty() throws IOException, ExecutionException, TimeoutException, InterruptedException { Properties originalProps = System.getProperties(); @@ -218,29 +229,105 @@ public void testProxyActivationProperty() throws IOException, ExecutionException System.setProperty("http.nonProxyHosts", "localhost"); System.setProperty("com.ning.http.client.AsyncHttpClientConfig.useProxyProperties", "true"); - AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient client = getAsyncHttpClient(cfg); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); - String target = "http://127.0.0.1:1234/"; - Future f = client.prepareGet(target).execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("target"), "/"); + target = "http://localhost:1234/"; + f = client.prepareGet(target).execute(); + try { + resp = f.get(3, TimeUnit.SECONDS); + fail("should not be able to connect"); + } catch (ExecutionException e) { + // ok, no proxy used + } - target = "http://localhost:1234/"; - f = client.prepareGet(target).execute(); + } finally { + client.close(); + } + } finally { + System.setProperties(originalProps); + } + } + + @Test(groups = { "standalone", "default_provider" }) + public void testWildcardNonProxyHosts() throws IOException, ExecutionException, TimeoutException, InterruptedException { + Properties originalProps = System.getProperties(); + try { + Properties props = new Properties(); + props.putAll(originalProps); + + System.setProperties(props); + + System.setProperty("http.proxyHost", "127.0.0.1"); + System.setProperty("http.proxyPort", String.valueOf(port1)); + System.setProperty("http.nonProxyHosts", "127.*"); + + AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().setUseProxyProperties(true).build(); + AsyncHttpClient client = getAsyncHttpClient(cfg); try { - resp = f.get(3, TimeUnit.SECONDS); - fail("should not be able to connect"); - } catch (ExecutionException e) { - // ok, no proxy used + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).execute(); + try { + f.get(3, TimeUnit.SECONDS); + fail("should not be able to connect"); + } catch (ExecutionException e) { + // ok, no proxy used + } + } finally { + client.close(); } - - client.close(); } finally { System.setProperties(originalProps); } } - + + @Test(groups = { "standalone", "default_provider" }) + public void testUseProxySelector() throws IOException, ExecutionException, TimeoutException, InterruptedException { + ProxySelector originalProxySelector = ProxySelector.getDefault(); + try { + ProxySelector.setDefault(new ProxySelector() { + public List select(URI uri) { + if (uri.getHost().equals("127.0.0.1")) { + return Arrays.asList(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", port1))); + } else { + return Arrays.asList(Proxy.NO_PROXY); + } + } + + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + } + }); + + AsyncHttpClientConfig cfg = new AsyncHttpClientConfig.Builder().setUseProxySelector(true).build(); + AsyncHttpClient client = getAsyncHttpClient(cfg); + try { + String target = "http://127.0.0.1:1234/"; + Future f = client.prepareGet(target).execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("target"), "/"); + + target = "http://localhost:1234/"; + f = client.prepareGet(target).execute(); + try { + f.get(3, TimeUnit.SECONDS); + fail("should not be able to connect"); + } catch (ExecutionException e) { + // ok, no proxy used + } + } finally { + client.close(); + } + } finally { + ProxySelector.setDefault(originalProxySelector); + } + } + } diff --git a/src/test/java/com/ning/http/client/async/ProxyyTunnellingTest.java b/src/test/java/com/ning/http/client/async/ProxyyTunnellingTest.java index f1dd8a631b..da2a0756f3 100644 --- a/src/test/java/com/ning/http/client/async/ProxyyTunnellingTest.java +++ b/src/test/java/com/ning/http/client/async/ProxyyTunnellingTest.java @@ -28,11 +28,13 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.testng.Assert.assertEquals; @@ -86,7 +88,7 @@ public void setUpGlobal() throws Exception { log.info("Local HTTP server started successfully"); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testRequestProxy() throws IOException, InterruptedException, ExecutionException, TimeoutException { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); b.setFollowRedirects(true); @@ -95,28 +97,30 @@ public void testRequestProxy() throws IOException, InterruptedException, Executi AsyncHttpClientConfig config = b.build(); AsyncHttpClient asyncHttpClient = getAsyncHttpClient(config); - - RequestBuilder rb = new RequestBuilder("GET").setProxyServer(ps).setUrl(getTargetUrl2()); - Future responseFuture = asyncHttpClient.executeRequest(rb.build(), new AsyncCompletionHandlerBase() { - - public void onThrowable(Throwable t) { - t.printStackTrace(); - log.debug(t.getMessage(), t); - } - - @Override - public Response onCompleted(Response response) throws Exception { - return response; - } - }); - Response r = responseFuture.get(); - assertEquals(r.getStatusCode(), 200); - assertEquals(r.getHeader("server"), "Jetty(8.1.1.v20120215)"); - - asyncHttpClient.close(); + try { + RequestBuilder rb = new RequestBuilder("GET").setProxyServer(ps).setUrl(getTargetUrl2()); + Future responseFuture = asyncHttpClient.executeRequest(rb.build(), new AsyncCompletionHandlerBase() { + + public void onThrowable(Throwable t) { + t.printStackTrace(); + log.debug(t.getMessage(), t); + } + + @Override + public Response onCompleted(Response response) throws Exception { + return response; + } + }); + Response r = responseFuture.get(); + assertEquals(r.getStatusCode(), 200); + assertEquals(r.getHeader("X-Proxy-Connection"), "keep-alive"); + + } finally { + asyncHttpClient.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testConfigProxy() throws IOException, InterruptedException, ExecutionException, TimeoutException { AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder(); b.setFollowRedirects(true); @@ -126,45 +130,53 @@ public void testConfigProxy() throws IOException, InterruptedException, Executio AsyncHttpClientConfig config = b.build(); AsyncHttpClient asyncHttpClient = getAsyncHttpClient(config); - - RequestBuilder rb = new RequestBuilder("GET").setUrl(getTargetUrl2()); - Future responseFuture = asyncHttpClient.executeRequest(rb.build(), new AsyncCompletionHandlerBase() { - - public void onThrowable(Throwable t) { - t.printStackTrace(); - log.debug(t.getMessage(), t); - } - - @Override - public Response onCompleted(Response response) throws Exception { - return response; - } - }); - Response r = responseFuture.get(); - assertEquals(r.getStatusCode(), 200); - assertEquals(r.getHeader("server"), "Jetty(8.1.1.v20120215)"); - - asyncHttpClient.close(); + try { + RequestBuilder rb = new RequestBuilder("GET").setUrl(getTargetUrl2()); + Future responseFuture = asyncHttpClient.executeRequest(rb.build(), new AsyncCompletionHandlerBase() { + + public void onThrowable(Throwable t) { + t.printStackTrace(); + log.debug(t.getMessage(), t); + } + + @Override + public Response onCompleted(Response response) throws Exception { + return response; + } + }); + Response r = responseFuture.get(); + assertEquals(r.getStatusCode(), 200); + assertEquals(r.getHeader("X-Proxy-Connection"), "keep-alive"); + } finally { + asyncHttpClient.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testSimpleAHCConfigProxy() throws IOException, InterruptedException, ExecutionException, TimeoutException { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder() - .setProxyProtocol(ProxyServer.Protocol.HTTPS) - .setProxyHost("127.0.0.1") - .setProxyPort(port1) - .setFollowRedirects(true) - .setUrl(getTargetUrl2()) - .setHeader("Content-Type", "text/html").build(); - - StringBuffer s = new StringBuffer(); - Response r = client.get().get(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProxyProtocol(ProxyServer.Protocol.HTTPS).setProxyHost("127.0.0.1").setProxyPort(port1).setFollowRedirects(true).setUrl(getTargetUrl2()).setHeader("Content-Type", "text/html").build(); + try { + Response r = client.get().get(); - assertEquals(r.getStatusCode(), 200); - assertEquals(r.getHeader("server"), "Jetty(8.1.1.v20120215)"); + assertEquals(r.getStatusCode(), 200); + assertEquals(r.getHeader("X-Proxy-Connection"), "keep-alive"); + } finally { + client.close(); + } + } - client.close(); + @Test(groups = { "standalone", "default_provider" }) + public void testNonProxyHostsSsl() throws IOException, ExecutionException, TimeoutException, InterruptedException { + AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response resp = client.prepareGet(getTargetUrl2()).setProxyServer(new ProxyServer("127.0.0.1", port1 - 1).addNonProxyHost("127.0.0.1")).execute().get(3, TimeUnit.SECONDS); + + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("X-pathInfo"), "/foo/test"); + } finally { + client.close(); + } } } - diff --git a/src/test/java/com/ning/http/client/async/PutLargeFileTest.java b/src/test/java/com/ning/http/client/async/PutLargeFileTest.java index 27cc6b61f6..a5d8d73651 100644 --- a/src/test/java/com/ning/http/client/async/PutLargeFileTest.java +++ b/src/test/java/com/ning/http/client/async/PutLargeFileTest.java @@ -34,46 +34,47 @@ /** * @author Benjamin Hanzelmann */ -public abstract class PutLargeFileTest - extends AbstractBasicTest { +public abstract class PutLargeFileTest extends AbstractBasicTest { private File largeFile; - @Test(groups = {"standalone", "default_provider"}, enabled = true) - public void testPutLargeFile() - throws Exception { + @Test(groups = { "standalone", "default_provider" }, enabled = true) + public void testPutLargeFile() throws Exception { byte[] bytes = "RatherLargeFileRatherLargeFileRatherLargeFileRatherLargeFile".getBytes("UTF-16"); long repeats = (1024 * 1024 * 100 / bytes.length) + 1; largeFile = createTempFile(bytes, (int) repeats); int timeout = (int) (largeFile.length() / 1000); AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().setConnectionTimeoutInMs(timeout).build(); AsyncHttpClient client = getAsyncHttpClient(config); - BoundRequestBuilder rb = client.preparePut(getTargetUrl()); + try { + BoundRequestBuilder rb = client.preparePut(getTargetUrl()); - rb.setBody(largeFile); + rb.setBody(largeFile); - Response response = rb.execute().get(); - Assert.assertEquals(200, response.getStatusCode()); - client.close(); + Response response = rb.execute().get(); + Assert.assertEquals(200, response.getStatusCode()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) - public void testPutSmallFile() - throws Exception { + @Test(groups = { "standalone", "default_provider" }) + public void testPutSmallFile() throws Exception { byte[] bytes = "RatherLargeFileRatherLargeFileRatherLargeFileRatherLargeFile".getBytes("UTF-16"); long repeats = (1024 / bytes.length) + 1; - int timeout = (5000); largeFile = createTempFile(bytes, (int) repeats); - AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build(); - AsyncHttpClient client = getAsyncHttpClient(config); - BoundRequestBuilder rb = client.preparePut(getTargetUrl()); + AsyncHttpClient client = getAsyncHttpClient(null); + try { + BoundRequestBuilder rb = client.preparePut(getTargetUrl()); - rb.setBody(largeFile); + rb.setBody(largeFile); - Response response = rb.execute().get(); - Assert.assertEquals(200, response.getStatusCode()); - client.close(); + Response response = rb.execute().get(); + Assert.assertEquals(200, response.getStatusCode()); + } finally { + client.close(); + } } @AfterMethod @@ -82,12 +83,10 @@ public void after() { } @Override - public AbstractHandler configureHandler() - throws Exception { + public AbstractHandler configureHandler() throws Exception { return new AbstractHandler() { - public void handle(String arg0, Request arg1, HttpServletRequest req, HttpServletResponse resp) - throws IOException, ServletException { + public void handle(String arg0, Request arg1, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { ServletInputStream in = req.getInputStream(); byte[] b = new byte[8092]; @@ -110,11 +109,9 @@ public void handle(String arg0, Request arg1, HttpServletRequest req, HttpServle }; } - private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" - + UUID.randomUUID().toString().substring(0, 8)); + private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" + UUID.randomUUID().toString().substring(0, 8)); - public static File createTempFile(byte[] pattern, int repeat) - throws IOException { + public static File createTempFile(byte[] pattern, int repeat) throws IOException { TMP.mkdirs(); TMP.deleteOnExit(); File tmpFile = File.createTempFile("tmpfile-", ".data", TMP); @@ -124,8 +121,7 @@ public static File createTempFile(byte[] pattern, int repeat) return tmpFile; } - public static void write(byte[] pattern, int repeat, File file) - throws IOException { + public static void write(byte[] pattern, int repeat, File file) throws IOException { file.deleteOnExit(); file.getParentFile().mkdirs(); FileOutputStream out = null; @@ -134,12 +130,10 @@ public static void write(byte[] pattern, int repeat, File file) for (int i = 0; i < repeat; i++) { out.write(pattern); } - } - finally { + } finally { if (out != null) { out.close(); } } } - } diff --git a/src/test/java/com/ning/http/client/async/QueryParametersTest.java b/src/test/java/com/ning/http/client/async/QueryParametersTest.java index 028a9543d4..27d796a07a 100644 --- a/src/test/java/com/ning/http/client/async/QueryParametersTest.java +++ b/src/test/java/com/ning/http/client/async/QueryParametersTest.java @@ -15,6 +15,8 @@ */ package com.ning.http.client.async; +import static com.ning.http.util.MiscUtil.isNonEmpty; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.Response; import org.eclipse.jetty.server.Request; @@ -38,18 +40,15 @@ /** * Testing query parameters support. - * + * * @author Hubert Iwaniuk */ public abstract class QueryParametersTest extends AbstractBasicTest { private class QueryStringHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("GET".equalsIgnoreCase(request.getMethod())) { String qs = request.getQueryString(); - if (qs != null && !qs.equals("")) { + if (isNonEmpty(qs)) { for (String qnv : qs.split("&")) { String nv[] = qnv.split("="); response.addHeader(nv[0], nv[1]); @@ -70,61 +69,63 @@ public AbstractHandler configureHandler() throws Exception { return new QueryStringHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testQueryParameters() throws IOException, ExecutionException, TimeoutException, InterruptedException { AsyncHttpClient client = getAsyncHttpClient(null); - Future f = client - .prepareGet("http://127.0.0.1/" + port1) - .addQueryParameter("a", "1") - .addQueryParameter("b", "2") - .execute(); - Response resp = f.get(3, TimeUnit.SECONDS); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getHeader("a"), "1"); - assertEquals(resp.getHeader("b"), "2"); - client.close(); + try { + Future f = client.prepareGet("http://127.0.0.1/" + port1).addQueryParameter("a", "1").addQueryParameter("b", "2").execute(); + Response resp = f.get(3, TimeUnit.SECONDS); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getHeader("a"), "1"); + assertEquals(resp.getHeader("b"), "2"); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testUrlRequestParametersEncoding() throws IOException, ExecutionException, InterruptedException { String URL = getTargetUrl() + "?q="; String REQUEST_PARAM = "github github \ngithub"; AsyncHttpClient client = getAsyncHttpClient(null); - String requestUrl2 = URL + URLEncoder.encode(REQUEST_PARAM, "UTF-8"); - LoggerFactory.getLogger(QueryParametersTest.class).info("Executing request [{}] ...", requestUrl2); - Response response = client.prepareGet(requestUrl2).execute().get(); - String s = URLDecoder.decode(response.getHeader("q"), "UTF-8"); - assertEquals(s, REQUEST_PARAM); - client.close(); + try { + String requestUrl2 = URL + URLEncoder.encode(REQUEST_PARAM, "UTF-8"); + LoggerFactory.getLogger(QueryParametersTest.class).info("Executing request [{}] ...", requestUrl2); + Response response = client.prepareGet(requestUrl2).execute().get(); + String s = URLDecoder.decode(response.getHeader("q"), "UTF-8"); + assertEquals(s, REQUEST_PARAM); + } finally { + client.close(); + } } - - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void urlWithColonTest_Netty() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(null); - String query = "test:colon:"; - Response response = c.prepareGet(String.format("http://127.0.0.1:%d/foo/test/colon?q=%s", port1, query)) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); + try { + String query = "test:colon:"; + Response response = c.prepareGet(String.format("http://127.0.0.1:%d/foo/test/colon?q=%s", port1, query)).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - assertEquals(response.getHeader("q"), URLEncoder.encode(query, "UTF-8")); - c.close(); + assertEquals(response.getHeader("q"), URLEncoder.encode(query, "UTF-8")); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void urlWithColonTest_JDK() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(null); + try { + String query = "test:colon:"; + Response response = c.prepareGet(String.format("http://127.0.0.1:%d/foo/test/colon?q=%s", port1, query)).setHeader("Content-Type", "text/html").execute().get(TIMEOUT, TimeUnit.SECONDS); - String query = "test:colon:"; - Response response = c.prepareGet(String.format("http://127.0.0.1:%d/foo/test/colon?q=%s", port1, query)) - .setHeader("Content-Type", "text/html") - .execute().get(TIMEOUT, TimeUnit.SECONDS); - - assertEquals(response.getHeader("q"), URLEncoder.encode(query, "UTF-8")); - c.close(); + assertEquals(response.getHeader("q"), URLEncoder.encode(query, "UTF-8")); + } finally { + c.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/RC10KTest.java b/src/test/java/com/ning/http/client/async/RC10KTest.java index 51a1e09602..53ae5d4000 100644 --- a/src/test/java/com/ning/http/client/async/RC10KTest.java +++ b/src/test/java/com/ning/http/client/async/RC10KTest.java @@ -46,7 +46,7 @@ /** * Reverse C10K Problem test. - * + * * @author Hubert Iwaniuk */ public abstract class RC10KTest extends AbstractBasicTest { @@ -102,20 +102,22 @@ public void handle(String s, Request r, HttpServletRequest req, HttpServletRespo @Test(timeOut = 10 * 60 * 1000, groups = "scalability") public void rc10kProblem() throws IOException, ExecutionException, TimeoutException, InterruptedException { - AsyncHttpClient ahc = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder().setMaximumConnectionsPerHost(C10K).setAllowPoolingConnection(true).build()); - List> resps = new ArrayList>(C10K); - int i = 0; - while (i < C10K) { - resps.add(ahc.prepareGet(String.format("http://127.0.0.1:%d/%d", ports[i % SRV_COUNT], i)).execute(new MyAsyncHandler(i++))); - } - i = 0; - for (Future fResp : resps) { - Integer resp = fResp.get(); - assertNotNull(resp); - assertEquals(resp.intValue(), i++); + AsyncHttpClient ahc = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setMaximumConnectionsPerHost(C10K).setAllowPoolingConnection(true).build()); + try { + List> resps = new ArrayList>(C10K); + int i = 0; + while (i < C10K) { + resps.add(ahc.prepareGet(String.format("http://127.0.0.1:%d/%d", ports[i % SRV_COUNT], i)).execute(new MyAsyncHandler(i++))); + } + i = 0; + for (Future fResp : resps) { + Integer resp = fResp.get(); + assertNotNull(resp); + assertEquals(resp.intValue(), i++); + } + } finally { + ahc.close(); } - ahc.close(); } private class MyAsyncHandler implements AsyncHandler { @@ -132,7 +134,7 @@ public void onThrowable(Throwable t) { public STATE onBodyPartReceived(HttpResponseBodyPart event) throws Exception { String s = new String(event.getBodyPartBytes()); - result.compareAndSet(-1, new Integer(s.trim().equals("") ? "-1" : s)); + result.compareAndSet(-1, new Integer(s.trim().isEmpty() ? "-1" : s)); return STATE.CONTINUE; } diff --git a/src/test/java/com/ning/http/client/async/RedirectConnectionUsageTest.java b/src/test/java/com/ning/http/client/async/RedirectConnectionUsageTest.java index 6cc687e49f..e8b6387164 100644 --- a/src/test/java/com/ning/http/client/async/RedirectConnectionUsageTest.java +++ b/src/test/java/com/ning/http/client/async/RedirectConnectionUsageTest.java @@ -21,7 +21,6 @@ import com.ning.http.client.ListenableFuture; import com.ning.http.client.RequestBuilder; import com.ning.http.client.Response; -import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.nio.SelectChannelConnector; @@ -144,8 +143,6 @@ public void testGetRedirectFinalUrl() { // can hang here if (c != null) c.close(); } - - } protected abstract AsyncHttpProviderConfig getProviderConfig(); @@ -182,6 +179,4 @@ public void service(HttpServletRequest req, HttpServletResponse res) throws Serv os.close(); } } - - } diff --git a/src/test/java/com/ning/http/client/async/Relative302Test.java b/src/test/java/com/ning/http/client/async/Relative302Test.java index e8a72640b4..0d189048da 100644 --- a/src/test/java/com/ning/http/client/async/Relative302Test.java +++ b/src/test/java/com/ning/http/client/async/Relative302Test.java @@ -45,11 +45,7 @@ public abstract class Relative302Test extends AbstractBasicTest { private class Relative302Handler extends AbstractHandler { - - public void handle(String s, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { String param; httpResponse.setContentType("text/html; charset=utf-8"); @@ -89,26 +85,24 @@ public void setUpGlobal() throws Exception { log.info("Local HTTP server started successfully"); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void redirected302Test() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + // once + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", "http://www.google.com/").execute().get(); - // once - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", "http://www.google.com/") - .execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - - String anyGoogleSubdomain = "http://www.google.[a-z]{1,}:80"; - String baseUrl = getBaseUrl( response.getUri() ); - - assertTrue(baseUrl.matches( anyGoogleSubdomain ), "response does not show redirection to " + anyGoogleSubdomain); + String baseUrl = getBaseUrl(response.getUri()); - c.close(); + assertTrue(baseUrl.startsWith("http://www.google./"), "response does not show redirection to a google subdomain, got " + baseUrl); + } finally { + c.close(); + } } private String getBaseUrl(URI uri) { @@ -128,7 +122,7 @@ private static int getPort(URI uri) { return port; } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void redirected302InvalidTest() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build(); @@ -136,59 +130,56 @@ public void redirected302InvalidTest() throws Throwable { // If the test hit a proxy, no ConnectException will be thrown and instead of 404 will be returned. try { - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", String.format("http://127.0.0.1:%d/", port2)) - .execute().get(); + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", String.format("http://127.0.0.1:%d/", port2)).execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 404); } catch (ExecutionException ex) { assertEquals(ex.getCause().getClass(), ConnectException.class); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void absolutePathRedirectTest() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + String redirectTarget = "/bar/test"; + String destinationUrl = new URI(getTargetUrl()).resolve(redirectTarget).toString(); + + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", redirectTarget).execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getUri().toString(), destinationUrl); - String redirectTarget = "/bar/test"; - String destinationUrl = new URI(getTargetUrl()).resolve(redirectTarget).toString(); - - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", redirectTarget) - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getUri().toString(), destinationUrl); - - log.debug("{} was redirected to {}", redirectTarget, destinationUrl); - - c.close(); + log.debug("{} was redirected to {}", redirectTarget, destinationUrl); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void relativePathRedirectTest() throws Throwable { isSet.getAndSet(false); AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + String redirectTarget = "bar/test1"; + String destinationUrl = new URI(getTargetUrl()).resolve(redirectTarget).toString(); - String redirectTarget = "bar/test1"; - String destinationUrl = new URI(getTargetUrl()).resolve(redirectTarget).toString(); - - Response response = c.prepareGet(getTargetUrl()) - .setHeader("X-redirect", redirectTarget) - .execute().get(); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getUri().toString(), destinationUrl); - - log.debug("{} was redirected to {}", redirectTarget, destinationUrl); - - c.close(); + Response response = c.prepareGet(getTargetUrl()).setHeader("X-redirect", redirectTarget).execute().get(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getUri().toString(), destinationUrl); + + log.debug("{} was redirected to {}", redirectTarget, destinationUrl); + } finally { + c.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/RemoteSiteTest.java b/src/test/java/com/ning/http/client/async/RemoteSiteTest.java index 17f7cff736..43f4a498b4 100644 --- a/src/test/java/com/ning/http/client/async/RemoteSiteTest.java +++ b/src/test/java/com/ning/http/client/async/RemoteSiteTest.java @@ -41,103 +41,112 @@ * Unit tests for remote site. *

* see http://github.com/MSch/ning-async-http-client-bug/tree/master - * + * * @author Martin Schurrer */ -public abstract class RemoteSiteTest extends AbstractBasicTest{ +public abstract class RemoteSiteTest extends AbstractBasicTest { public static final String URL = "http://google.com/?q="; - public static final String REQUEST_PARAM = "github github \n" + - "github"; - - @Test(groups = {"online", "default_provider"}) + public static final String REQUEST_PARAM = "github github \n" + "github"; + + @Test(groups = { "online", "default_provider" }) public void testGoogleCom() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).build()); - // Works - Response response = c.prepareGet("http://www.google.com/").execute().get(10,TimeUnit.SECONDS); - assertNotNull(response); + try { + Response response = c.prepareGet("http://www.google.com/").execute().get(10, TimeUnit.SECONDS); + assertNotNull(response); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testMailGoogleCom() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).build()); - - Response response = c.prepareGet("http://mail.google.com/").execute().get(10,TimeUnit.SECONDS); - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); + try { + Response response = c.prepareGet("http://mail.google.com/").execute().get(10, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testMicrosoftCom() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).build()); - - // Works - Response response = c.prepareGet("http://microsoft.com/").execute().get(10,TimeUnit.SECONDS); - assertNotNull(response); - assertEquals(response.getStatusCode(), 301); + try { + Response response = c.prepareGet("http://microsoft.com/").execute().get(10, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(response.getStatusCode(), 301); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testWwwMicrosoftCom() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).build()); - - Response response = c.prepareGet("http://www.microsoft.com/").execute().get(10,TimeUnit.SECONDS); - assertNotNull(response); - assertEquals(response.getStatusCode(), 302); + try { + Response response = c.prepareGet("http://www.microsoft.com/").execute().get(10, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(response.getStatusCode(), 302); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testUpdateMicrosoftCom() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).build()); - - Response response = c.prepareGet("http://update.microsoft.com/").execute().get(10,TimeUnit.SECONDS); - assertNotNull(response); - assertEquals(response.getStatusCode(), 302); + try { + Response response = c.prepareGet("http://update.microsoft.com/").execute().get(10, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(response.getStatusCode(), 302); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testGoogleComWithTimeout() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).build()); - - // Works - Response response = c.prepareGet("http://google.com/").execute().get(10,TimeUnit.SECONDS); - assertNotNull(response); - assertEquals(response.getStatusCode(), 301); + try { + Response response = c.prepareGet("http://google.com/").execute().get(10, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(response.getStatusCode(), 301); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void asyncStatusHEADContentLenghtTest() throws Throwable { - AsyncHttpClient p = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); - - final CountDownLatch l = new CountDownLatch(1); - Request request = new RequestBuilder("HEAD") - .setUrl("http://www.google.com/") - .build(); - - p.executeRequest(request, new AsyncCompletionHandlerAdapter() { - @Override - public Response onCompleted(Response response) throws Exception { - Assert.assertEquals(response.getStatusCode(), 200); - l.countDown(); - return response; + AsyncHttpClient p = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); + try { + final CountDownLatch l = new CountDownLatch(1); + Request request = new RequestBuilder("HEAD").setUrl("http://www.google.com/").build(); + + p.executeRequest(request, new AsyncCompletionHandlerAdapter() { + @Override + public Response onCompleted(Response response) throws Exception { + Assert.assertEquals(response.getStatusCode(), 200); + l.countDown(); + return response; + } + }).get(); + + if (!l.await(5, TimeUnit.SECONDS)) { + Assert.fail("Timeout out"); } - }).get(); - - if (!l.await(5, TimeUnit.SECONDS)) { - Assert.fail("Timeout out"); + } finally { + p.close(); } - p.close(); } - @Test(groups = {"online", "default_provider"}, enabled = false) + @Test(groups = { "online", "default_provider" }, enabled = false) public void invalidStreamTest2() throws Throwable { - AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder() - .setRequestTimeoutInMs(10000) - .setFollowRedirects(true) - .setAllowPoolingConnection(false) - .setMaximumNumberOfRedirects(6) - .build(); + AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(10000).setFollowRedirects(true).setAllowPoolingConnection(false).setMaximumNumberOfRedirects(6).build(); AsyncHttpClient c = getAsyncHttpClient(config); try { @@ -149,131 +158,143 @@ public void invalidStreamTest2() throws Throwable { t.printStackTrace(); assertNotNull(t.getCause()); assertEquals(t.getCause().getMessage(), "invalid version format: ICY"); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void asyncFullBodyProperlyRead() throws Throwable { - final AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - Response r = client.prepareGet("http://www.cyberpresse.ca/").execute().get(); + final AsyncHttpClient client = getAsyncHttpClient(null); + try { + Response r = client.prepareGet("http://www.cyberpresse.ca/").execute().get(); - InputStream stream = r.getResponseBodyAsStream(); - int available = stream.available(); - int[] lengthWrapper = new int[1]; - byte[] bytes = AsyncHttpProviderUtils.readFully(stream, lengthWrapper); - int byteToRead = lengthWrapper[0]; + InputStream stream = r.getResponseBodyAsStream(); + int available = stream.available(); + int[] lengthWrapper = new int[1]; + AsyncHttpProviderUtils.readFully(stream, lengthWrapper); + int byteToRead = lengthWrapper[0]; - Assert.assertEquals(available, byteToRead); - client.close(); + Assert.assertEquals(available, byteToRead); + } finally { + client.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testUrlRequestParametersEncoding() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); - String requestUrl2 = URL + URLEncoder.encode(REQUEST_PARAM, "UTF-8"); - log.info(String.format("Executing request [%s] ...", requestUrl2)); - Response response = client.prepareGet(requestUrl2).execute().get(); - Assert.assertEquals(response.getStatusCode(), 301); + try { + String requestUrl2 = URL + URLEncoder.encode(REQUEST_PARAM, "UTF-8"); + log.info(String.format("Executing request [%s] ...", requestUrl2)); + Response response = client.prepareGet(requestUrl2).execute().get(); + Assert.assertEquals(response.getStatusCode(), 301); + } finally { + client.close(); + } } /** - * See https://issues.sonatype.org/browse/AHC-61 + * See https://issues.sonatype.org/browse/AHC-61 + * * @throws Throwable */ - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void testAHC60() throws Throwable { AsyncHttpClient client = getAsyncHttpClient(null); - Response response = client.prepareGet("http://www.meetup.com/stackoverflow/Mountain-View-CA/").execute().get(); - Assert.assertEquals(response.getStatusCode(), 200); + try { + Response response = client.prepareGet("http://www.meetup.com/stackoverflow/Mountain-View-CA/").execute().get(); + Assert.assertEquals(response.getStatusCode(), 200); + } finally { + client.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void stripQueryStringTest() throws Throwable { AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); - Response response = c.prepareGet("http://www.freakonomics.com/?p=55846") - .execute().get(); + Response response = c.prepareGet("http://www.freakonomics.com/?p=55846").execute().get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); - c.close(); } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void stripQueryStringNegativeTest() throws Throwable { - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder() - .setRemoveQueryParamsOnRedirect(false).setFollowRedirects(true).build(); + AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setRemoveQueryParamsOnRedirect(false).setFollowRedirects(true).build(); AsyncHttpClient c = getAsyncHttpClient(cg); + try { + Response response = c.prepareGet("http://www.freakonomics.com/?p=55846").execute().get(); - Response response = c.prepareGet("http://www.freakonomics.com/?p=55846") - .execute().get(); - - assertNotNull(response); - assertEquals(response.getStatusCode(), 301); - - - c.close(); + assertNotNull(response); + assertEquals(response.getStatusCode(), 301); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}) + @Test(groups = { "online", "default_provider" }) public void evilCoookieTest() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(null); - - RequestBuilder builder2 = new RequestBuilder("GET"); - builder2.setFollowRedirects(true); - builder2.setUrl("http://www.google.com/"); - builder2.addHeader("Content-Type", "text/plain"); - builder2.addCookie(new com.ning.http.client.Cookie(".google.com", "evilcookie", "test", "/", 10, false)); - com.ning.http.client.Request request2 = builder2.build(); - Response response = c.executeRequest(request2).get(); - - assertNotNull(response); - assertEquals(response.getStatusCode(), 200); - c.close(); + try { + RequestBuilder builder2 = new RequestBuilder("GET"); + builder2.setFollowRedirects(true); + builder2.setUrl("http://www.google.com/"); + builder2.addHeader("Content-Type", "text/plain"); + builder2.addCookie(new com.ning.http.client.Cookie(".google.com", "evilcookie", "test", "/", 10, false)); + com.ning.http.client.Request request2 = builder2.build(); + Response response = c.executeRequest(request2).get(); + + assertNotNull(response); + assertEquals(response.getStatusCode(), 200); + } finally { + c.close(); + } } - @Test(groups = {"online", "default_provider"}, enabled = false) + @Test(groups = { "online", "default_provider" }, enabled = false) public void testAHC62Com() throws Throwable { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); - // Works - Response response = c.prepareGet("http://api.crunchbase.com/v/1/financial-organization/kinsey-hills-group.js").execute(new AsyncHandler() { - - private Response.ResponseBuilder builder = new Response.ResponseBuilder(); - - public void onThrowable(Throwable t) { - t.printStackTrace(); - } - - public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - System.out.println(bodyPart.getBodyPartBytes().length); - builder.accumulate(bodyPart); - - return STATE.CONTINUE; - } - - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - builder.accumulate(responseStatus); - return STATE.CONTINUE; - } - - public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { - builder.accumulate(headers); - return STATE.CONTINUE; - } - - public Response onCompleted() throws Exception { - return builder.build(); - } - }).get(10, TimeUnit.SECONDS); - assertNotNull(response); - assertTrue(response.getResponseBody().length() >= 3870); + try { + Response response = c.prepareGet("http://api.crunchbase.com/v/1/financial-organization/kinsey-hills-group.js").execute(new AsyncHandler() { + + private Response.ResponseBuilder builder = new Response.ResponseBuilder(); + + public void onThrowable(Throwable t) { + t.printStackTrace(); + } + + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + System.out.println(bodyPart.getBodyPartBytes().length); + builder.accumulate(bodyPart); + + return STATE.CONTINUE; + } + + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + builder.accumulate(responseStatus); + return STATE.CONTINUE; + } + + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + builder.accumulate(headers); + return STATE.CONTINUE; + } + + public Response onCompleted() throws Exception { + return builder.build(); + } + }).get(10, TimeUnit.SECONDS); + assertNotNull(response); + assertTrue(response.getResponseBody().length() >= 3870); + } finally { + c.close(); + } } - } - diff --git a/src/test/java/com/ning/http/client/async/RetryNonBlockingIssue.java b/src/test/java/com/ning/http/client/async/RetryNonBlockingIssue.java index 247fe8c2a3..86eabd4ba4 100644 --- a/src/test/java/com/ning/http/client/async/RetryNonBlockingIssue.java +++ b/src/test/java/com/ning/http/client/async/RetryNonBlockingIssue.java @@ -259,6 +259,7 @@ public void testRetryBlocking() throws IOException, InterruptedException, } } + @SuppressWarnings("serial") public class MockExceptionServlet extends HttpServlet { private Map requests = new @@ -325,7 +326,6 @@ public void service(HttpServletRequest req, HttpServletResponse res) if (error != null && error.trim().length() > 0) res.sendError(500, "servlet process was 500"); } - } } diff --git a/src/test/java/com/ning/http/client/async/RetryRequestTest.java b/src/test/java/com/ning/http/client/async/RetryRequestTest.java index 3cd92523ba..6b6ccd86bd 100644 --- a/src/test/java/com/ning/http/client/async/RetryRequestTest.java +++ b/src/test/java/com/ning/http/client/async/RetryRequestTest.java @@ -29,9 +29,7 @@ public abstract class RetryRequestTest extends AbstractBasicTest { public static class SlowAndBigHandler extends AbstractHandler { - public void handle(String pathInContext, Request request, - HttpServletRequest httpRequest, HttpServletResponse httpResponse) - throws IOException, ServletException { + public void handle(String pathInContext, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { int load = 100; httpResponse.setStatus(200); @@ -40,7 +38,6 @@ public void handle(String pathInContext, Request request, httpResponse.flushBuffer(); - OutputStream os = httpResponse.getOutputStream(); for (int i = 0; i < load; i++) { os.write(i % 255); @@ -51,7 +48,6 @@ public void handle(String pathInContext, Request request, // nuku } - if (i > load / 10) { httpResponse.sendError(500); } @@ -71,8 +67,7 @@ public AbstractHandler configureHandler() throws Exception { return new SlowAndBigHandler(); } - - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testMaxRetry() throws Throwable { AsyncHttpClient ahc = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setMaxRequestRetry(0).build()); try { @@ -84,8 +79,8 @@ public void testMaxRetry() throws Throwable { if (!t.getCause().getMessage().startsWith("Remotely Closed")) { fail(); } + } finally { + ahc.close(); } - - ahc.close(); } } diff --git a/src/test/java/com/ning/http/client/async/SimpleAsyncClientErrorBehaviourTest.java b/src/test/java/com/ning/http/client/async/SimpleAsyncClientErrorBehaviourTest.java index 4206f6b96d..064b472e09 100644 --- a/src/test/java/com/ning/http/client/async/SimpleAsyncClientErrorBehaviourTest.java +++ b/src/test/java/com/ning/http/client/async/SimpleAsyncClientErrorBehaviourTest.java @@ -34,60 +34,57 @@ /** * @author Benjamin Hanzelmann - * + * */ public class SimpleAsyncClientErrorBehaviourTest extends AbstractBasicTest { - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testAccumulateErrorBody() throws Throwable { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl() + "/nonexistent").setErrorDocumentBehaviour( ErrorDocumentBehaviour.ACCUMULATE ).build(); - - ByteArrayOutputStream o = new ByteArrayOutputStream(10); - Future future = client.get(new OutputStreamBodyConsumer(o)); - - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 404); - assertEquals(o.toString(), ""); - assertTrue(response.getResponseBody().startsWith("")); - - client.close(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl() + "/nonexistent").setErrorDocumentBehaviour(ErrorDocumentBehaviour.ACCUMULATE).build(); + try { + ByteArrayOutputStream o = new ByteArrayOutputStream(10); + Future future = client.get(new OutputStreamBodyConsumer(o)); + + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 404); + assertEquals(o.toString(), ""); + assertTrue(response.getResponseBody().startsWith("")); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testOmitErrorBody() throws Throwable { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl() + "/nonexistent").setErrorDocumentBehaviour( ErrorDocumentBehaviour.OMIT ).build(); - - ByteArrayOutputStream o = new ByteArrayOutputStream(10); - Future future = client.get(new OutputStreamBodyConsumer(o)); - - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 404); - assertEquals(o.toString(), ""); - assertEquals(response.getResponseBody(), ""); - client.close(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl() + "/nonexistent").setErrorDocumentBehaviour(ErrorDocumentBehaviour.OMIT).build(); + try { + ByteArrayOutputStream o = new ByteArrayOutputStream(10); + Future future = client.get(new OutputStreamBodyConsumer(o)); + + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 404); + assertEquals(o.toString(), ""); + assertEquals(response.getResponseBody(), ""); + } finally { + client.close(); + } } @Override - public AsyncHttpClient getAsyncHttpClient( AsyncHttpClientConfig config ) - { + public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { // disabled return null; } @Override - public AbstractHandler configureHandler() - throws Exception - { + public AbstractHandler configureHandler() throws Exception { return new AbstractHandler() { - - public void handle( String target, org.eclipse.jetty.server.Request baseRequest, - HttpServletRequest request, HttpServletResponse response ) - throws IOException, ServletException - { - response.sendError( 404 ); - baseRequest.setHandled( true ); + + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + response.sendError(404); + baseRequest.setHandled(true); } }; } diff --git a/src/test/java/com/ning/http/client/async/SimpleAsyncHttpClientTest.java b/src/test/java/com/ning/http/client/async/SimpleAsyncHttpClientTest.java index 2a6cfcfd15..f2874cf73c 100644 --- a/src/test/java/com/ning/http/client/async/SimpleAsyncHttpClientTest.java +++ b/src/test/java/com/ning/http/client/async/SimpleAsyncHttpClientTest.java @@ -21,12 +21,14 @@ import com.ning.http.client.generators.InputStreamBodyGenerator; import com.ning.http.client.simple.HeaderMap; import com.ning.http.client.simple.SimpleAHCTransferListener; + import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import static junit.framework.Assert.assertTrue; @@ -38,71 +40,74 @@ public abstract class SimpleAsyncHttpClientTest extends AbstractBasicTest { private final static String MY_MESSAGE = "my message"; + + public abstract String getProviderClass(); @Test(groups = { "standalone", "default_provider" }) - public void inpuStreamBodyConsumerTest() throws Throwable { - - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50) - .setRequestTimeoutInMs(5 * 60 * 1000).setUrl(getTargetUrl()).setHeader("Content-Type", "text/html").build(); - - Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes()))); + public void inputStreamBodyConsumerTest() throws Throwable { - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(response.getResponseBody(), MY_MESSAGE); - - client.close(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50).setRequestTimeoutInMs(5 * 60 * 1000).setUrl(getTargetUrl()).setHeader("Content-Type", "text/html").build(); + try { + Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes()))); + + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getResponseBody(), MY_MESSAGE); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) - public void StringBufferBodyConsumerTest() throws Throwable { + public void stringBuilderBodyConsumerTest() throws Throwable { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50) - .setRequestTimeoutInMs(5 * 60 * 1000).setUrl(getTargetUrl()).setHeader("Content-Type", "text/html").build(); - - StringBuilder s = new StringBuilder(); - Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new AppendableBodyConsumer(s)); - - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(s.toString(), MY_MESSAGE); - - client.close(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50).setRequestTimeoutInMs(5 * 60 * 1000).setUrl(getTargetUrl()).setHeader("Content-Type", "text/html").build(); + try { + StringBuilder s = new StringBuilder(); + Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new AppendableBodyConsumer(s)); + + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(s.toString(), MY_MESSAGE); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) - public void ByteArrayOutputStreamBodyConsumerTest() throws Throwable { - - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50) - .setRequestTimeoutInMs(5 * 60 * 1000).setUrl(getTargetUrl()).setHeader("Content-Type", "text/html").build(); - - ByteArrayOutputStream o = new ByteArrayOutputStream(10); - Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new OutputStreamBodyConsumer(o)); + public void byteArrayOutputStreamBodyConsumerTest() throws Throwable { - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(o.toString(), MY_MESSAGE); - - client.close(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50).setRequestTimeoutInMs(5 * 60 * 1000).setUrl(getTargetUrl()).setHeader("Content-Type", "text/html").build(); + try { + ByteArrayOutputStream o = new ByteArrayOutputStream(10); + Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new OutputStreamBodyConsumer(o)); + + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(o.toString(), MY_MESSAGE); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) - public void RequestByteArrayOutputStreamBodyConsumerTest() throws Throwable { + public void requestByteArrayOutputStreamBodyConsumerTest() throws Throwable { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl()).build(); - - ByteArrayOutputStream o = new ByteArrayOutputStream(10); - Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new OutputStreamBodyConsumer(o)); - - System.out.println("waiting for response"); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(o.toString(), MY_MESSAGE); - - client.close(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl(getTargetUrl()).build(); + try { + ByteArrayOutputStream o = new ByteArrayOutputStream(10); + Future future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new OutputStreamBodyConsumer(o)); + + System.out.println("waiting for response"); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(o.toString(), MY_MESSAGE); + } finally { + client.close(); + } } /** @@ -110,51 +115,55 @@ public void RequestByteArrayOutputStreamBodyConsumerTest() throws Throwable { */ @Test(groups = { "standalone", "default_provider" }, enabled = true) public void testPutZeroBytesFileTest() throws Throwable { - System.err.println("setting up client"); - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50) - .setRequestTimeoutInMs(5 * 1000).setUrl(getTargetUrl() + "/testPutZeroBytesFileTest.txt").setHeader("Content-Type", "text/plain").build(); - - File tmpfile = File.createTempFile("testPutZeroBytesFile", ".tmp"); - tmpfile.deleteOnExit(); - - Future future = client.put(new FileBodyGenerator(tmpfile)); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setIdleConnectionInPoolTimeoutInMs(100).setMaximumConnectionsTotal(50).setRequestTimeoutInMs(5 * 1000).setUrl(getTargetUrl() + "/testPutZeroBytesFileTest.txt").setHeader("Content-Type", "text/plain") + .build(); + try { + File tmpfile = File.createTempFile("testPutZeroBytesFile", ".tmp"); + tmpfile.deleteOnExit(); - System.out.println("waiting for response"); - Response response = future.get(); + Future future = client.put(new FileBodyGenerator(tmpfile)); - tmpfile.delete(); + Response response = future.get(); - assertEquals(response.getStatusCode(), 200); + tmpfile.delete(); - client.close(); + assertEquals(response.getStatusCode(), 200); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) public void testDerive() throws Exception { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().build(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).build(); SimpleAsyncHttpClient derived = client.derive().build(); - - assertNotSame(derived, client); + try { + assertNotSame(derived, client); + } finally { + client.close(); + derived.close(); + } } @Test(groups = { "standalone", "default_provider" }) public void testDeriveOverrideURL() throws Exception { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl("http://invalid.url/").build(); - ByteArrayOutputStream o = new ByteArrayOutputStream(10); - - InputStreamBodyGenerator generator = new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())); - OutputStreamBodyConsumer consumer = new OutputStreamBodyConsumer(o); - + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl("http://invalid.url/").build(); SimpleAsyncHttpClient derived = client.derive().setUrl(getTargetUrl()).build(); + try { + ByteArrayOutputStream o = new ByteArrayOutputStream(10); - Future future = derived.post(generator, consumer); + InputStreamBodyGenerator generator = new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())); + OutputStreamBodyConsumer consumer = new OutputStreamBodyConsumer(o); - Response response = future.get(); - assertEquals(response.getStatusCode(), 200); - assertEquals(o.toString(), MY_MESSAGE); + Future future = derived.post(generator, consumer); - client.close(); - derived.close(); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(o.toString(), MY_MESSAGE); + } finally { + client.close(); + derived.close(); + } } @Test(groups = { "standalone", "default_provider" }) @@ -190,47 +199,53 @@ public void onBytesReceived(String url, long amount, long current, long total) { } }; - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl()).setHeader("Custom", "custom").setListener(listener).build(); - ByteArrayOutputStream o = new ByteArrayOutputStream(10); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl(getTargetUrl()).setHeader("Custom", "custom").setListener(listener).build(); + try { + ByteArrayOutputStream o = new ByteArrayOutputStream(10); - InputStreamBodyGenerator generator = new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())); - OutputStreamBodyConsumer consumer = new OutputStreamBodyConsumer(o); + InputStreamBodyGenerator generator = new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())); + OutputStreamBodyConsumer consumer = new OutputStreamBodyConsumer(o); - Future future = client.post(generator, consumer); + Future future = client.post(generator, consumer); - Response response = future.get(); - client.close(); - assertEquals(response.getStatusCode(), 200); - assertEquals(o.toString(), MY_MESSAGE); + Response response = future.get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(o.toString(), MY_MESSAGE); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) public void testNullUrl() throws Exception { + SimpleAsyncHttpClient c = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).build().derive().build(); try { - new SimpleAsyncHttpClient.Builder().build().derive().build(); assertTrue(true); - } catch (NullPointerException ex) { - fail(); + } finally { + c.close(); } } @Test(groups = { "standalone", "default_provider" }) public void testCloseDerivedValidMaster() throws Exception { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl()).build(); - SimpleAsyncHttpClient derived = client.derive().build(); - - derived.get().get(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl(getTargetUrl()).build(); + try { + SimpleAsyncHttpClient derived = client.derive().build(); + derived.get().get(); - derived.close(); + derived.close(); - Response response = client.get().get(); + Response response = client.get().get(); - assertEquals(response.getStatusCode(), 200); + assertEquals(response.getStatusCode(), 200); + } finally { + client.close(); + } } @Test(groups = { "standalone", "default_provider" }) public void testCloseMasterInvalidDerived() throws Exception { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl()).build(); + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl(getTargetUrl()).build(); SimpleAsyncHttpClient derived = client.derive().build(); client.close(); @@ -239,52 +254,56 @@ public void testCloseMasterInvalidDerived() throws Exception { derived.get().get(); fail("Expected closed AHC"); } catch (IOException e) { - // expected + // expected -- Seems to me that this behavior conflicts with the requirements of Future.get() } } @Test(groups = { "standalone", "default_provider" }) public void testMultiPartPut() throws Exception { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl() + "/multipart").build(); - - Response response = client.put(new ByteArrayPart("baPart", "fileName", "testMultiPart".getBytes("utf-8"), "application/test", "utf-8")).get(); - - String body = response.getResponseBody(); - String contentType = response.getHeader("X-Content-Type"); - - assertTrue(contentType.contains("multipart/form-data")); - - String boundary = contentType.substring(contentType.lastIndexOf("=") + 1); - - assertTrue(body.startsWith("--" + boundary)); - assertTrue(body.trim().endsWith("--" + boundary + "--")); - assertTrue(body.contains("Content-Disposition:")); - assertTrue(body.contains("Content-Type: application/test")); - assertTrue(body.contains("name=\"baPart")); - assertTrue(body.contains("filename=\"fileName")); - + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl(getTargetUrl() + "/multipart").build(); + try { + Response response = client.put(new ByteArrayPart("baPart", "fileName", "testMultiPart".getBytes("utf-8"), "application/test", "utf-8")).get(); + + String body = response.getResponseBody(); + String contentType = response.getHeader("X-Content-Type"); + + assertTrue(contentType.contains("multipart/form-data")); + + String boundary = contentType.substring(contentType.lastIndexOf("=") + 1); + + assertTrue(body.startsWith("--" + boundary)); + assertTrue(body.trim().endsWith("--" + boundary + "--")); + assertTrue(body.contains("Content-Disposition:")); + assertTrue(body.contains("Content-Type: application/test")); + assertTrue(body.contains("name=\"baPart")); + assertTrue(body.contains("filename=\"fileName")); + } finally { + client.close(); + } } - + @Test(groups = { "standalone", "default_provider" }) public void testMultiPartPost() throws Exception { - SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setUrl(getTargetUrl() + "/multipart").build(); - - Response response = client.post(new ByteArrayPart("baPart", "fileName", "testMultiPart".getBytes("utf-8"), "application/test", "utf-8")).get(); - - String body = response.getResponseBody(); - String contentType = response.getHeader("X-Content-Type"); - - assertTrue(contentType.contains("multipart/form-data")); - - String boundary = contentType.substring(contentType.lastIndexOf("=") + 1); - - assertTrue(body.startsWith("--" + boundary)); - assertTrue(body.trim().endsWith("--" + boundary + "--")); - assertTrue(body.contains("Content-Disposition:")); - assertTrue(body.contains("Content-Type: application/test")); - assertTrue(body.contains("name=\"baPart")); - assertTrue(body.contains("filename=\"fileName")); - + SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder().setProviderClass(getProviderClass()).setUrl(getTargetUrl() + "/multipart").build(); + try { + Response response = client.post(new ByteArrayPart("baPart", "fileName", "testMultiPart".getBytes("utf-8"), "application/test", "utf-8")).get(); + + String body = response.getResponseBody(); + String contentType = response.getHeader("X-Content-Type"); + + assertTrue(contentType.contains("multipart/form-data")); + + String boundary = contentType.substring(contentType.lastIndexOf("=") + 1); + + assertTrue(body.startsWith("--" + boundary)); + assertTrue(body.trim().endsWith("--" + boundary + "--")); + assertTrue(body.contains("Content-Disposition:")); + assertTrue(body.contains("Content-Type: application/test")); + assertTrue(body.contains("name=\"baPart")); + assertTrue(body.contains("filename=\"fileName")); + } finally { + client.close(); + } } } diff --git a/src/test/java/com/ning/http/client/async/TransferListenerTest.java b/src/test/java/com/ning/http/client/async/TransferListenerTest.java index e2b80690c7..894f1150dd 100644 --- a/src/test/java/com/ning/http/client/async/TransferListenerTest.java +++ b/src/test/java/com/ning/http/client/async/TransferListenerTest.java @@ -40,15 +40,11 @@ import static org.testng.Assert.fail; public abstract class TransferListenerTest extends AbstractBasicTest { - private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" - + UUID.randomUUID().toString().substring(0, 8)); + private static final File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc-tests-" + UUID.randomUUID().toString().substring(0, 8)); private class BasicHandler extends AbstractHandler { - public void handle(String s, - org.eclipse.jetty.server.Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, org.eclipse.jetty.server.Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { Enumeration e = httpRequest.getHeaderNames(); String param; @@ -78,10 +74,8 @@ public AbstractHandler configureHandler() throws Exception { return new BasicHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicGetTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); - final AtomicReference throwable = new AtomicReference(); final AtomicReference hSent = new AtomicReference(); final AtomicReference hRead = new AtomicReference(); @@ -115,9 +109,9 @@ public void onThrowable(Throwable t) { } }); + AsyncHttpClient c = getAsyncHttpClient(null); try { - Response response = c.prepareGet(getTargetUrl()) - .execute(tl).get(); + Response response = c.prepareGet(getTargetUrl()).execute(tl).get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); @@ -127,13 +121,13 @@ public void onThrowable(Throwable t) { assertNull(throwable.get()); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicPutTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); final AtomicReference throwable = new AtomicReference(); final AtomicReference hSent = new AtomicReference(); @@ -175,9 +169,9 @@ public void onThrowable(Throwable t) { } }); + AsyncHttpClient c = getAsyncHttpClient(null); try { - Response response = c.preparePut(getTargetUrl()).setBody(largeFile) - .execute(tl).get(); + Response response = c.preparePut(getTargetUrl()).setBody(largeFile).execute(tl).get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); @@ -187,13 +181,13 @@ public void onThrowable(Throwable t) { assertEquals(bbSentLenght.get(), largeFile.length()); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicPutBodyTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(null); final AtomicReference throwable = new AtomicReference(); final AtomicReference hSent = new AtomicReference(); @@ -235,9 +229,9 @@ public void onThrowable(Throwable t) { } }); + AsyncHttpClient c = getAsyncHttpClient(null); try { - Response response = c.preparePut(getTargetUrl()).setBody(new FileBodyGenerator(largeFile)) - .execute(tl).get(); + Response response = c.preparePut(getTargetUrl()).setBody(new FileBodyGenerator(largeFile)).execute(tl).get(); assertNotNull(response); assertEquals(response.getStatusCode(), 200); @@ -247,16 +241,16 @@ public void onThrowable(Throwable t) { assertEquals(bbSentLenght.get(), largeFile.length()); } catch (IOException ex) { fail("Should have timed out"); + } finally { + c.close(); } - c.close(); } public String getTargetUrl() { return String.format("http://127.0.0.1:%d/foo/test", port1); } - public static File createTempFile(byte[] pattern, int repeat) - throws IOException { + public static File createTempFile(byte[] pattern, int repeat) throws IOException { TMP.mkdirs(); TMP.deleteOnExit(); File tmpFile = File.createTempFile("tmpfile-", ".data", TMP); @@ -265,8 +259,7 @@ public static File createTempFile(byte[] pattern, int repeat) return tmpFile; } - public static void write(byte[] pattern, int repeat, File file) - throws IOException { + public static void write(byte[] pattern, int repeat, File file) throws IOException { file.deleteOnExit(); file.getParentFile().mkdirs(); FileOutputStream out = null; @@ -275,8 +268,7 @@ public static void write(byte[] pattern, int repeat, File file) for (int i = 0; i < repeat; i++) { out.write(pattern); } - } - finally { + } finally { if (out != null) { out.close(); } diff --git a/src/test/java/com/ning/http/client/async/WebDavBasicTest.java b/src/test/java/com/ning/http/client/async/WebDavBasicTest.java index 77b69dcf67..720cd5adb5 100644 --- a/src/test/java/com/ning/http/client/async/WebDavBasicTest.java +++ b/src/test/java/com/ning/http/client/async/WebDavBasicTest.java @@ -38,7 +38,6 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; - public abstract class WebDavBasicTest extends AbstractBasicTest { public Embedded embedded; @@ -94,91 +93,100 @@ public void tearDownGlobal() throws InterruptedException, Exception { embedded.stop(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void mkcolWebDavTest1() throws InterruptedException, IOException, ExecutionException { AsyncHttpClient c = getAsyncHttpClient(null); - Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build(); - Response response = c.executeRequest(mkcolRequest).get(); - - assertEquals(response.getStatusCode(), 201); - - c.close(); + try { + Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build(); + Response response = c.executeRequest(mkcolRequest).get(); + + assertEquals(response.getStatusCode(), 201); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void mkcolWebDavTest2() throws InterruptedException, IOException, ExecutionException { AsyncHttpClient c = getAsyncHttpClient(null); - - Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl() + "/folder2").build(); - Response response = c.executeRequest(mkcolRequest).get(); - assertEquals(response.getStatusCode(), 409); - c.close(); + try { + Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl() + "/folder2").build(); + Response response = c.executeRequest(mkcolRequest).get(); + assertEquals(response.getStatusCode(), 409); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void basicPropFindWebDavTest() throws InterruptedException, IOException, ExecutionException { AsyncHttpClient c = getAsyncHttpClient(null); - Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(getTargetUrl()).build(); - Response response = c.executeRequest(propFindRequest).get(); - - assertEquals(response.getStatusCode(), 404); - c.close(); + try { + Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(getTargetUrl()).build(); + Response response = c.executeRequest(propFindRequest).get(); + + assertEquals(response.getStatusCode(), 404); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void propFindWebDavTest() throws InterruptedException, IOException, ExecutionException { AsyncHttpClient c = getAsyncHttpClient(null); - - Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build(); - Response response = c.executeRequest(mkcolRequest).get(); - assertEquals(response.getStatusCode(), 201); - - Request putRequest = new RequestBuilder("PUT").setUrl(String.format("http://127.0.0.1:%s/folder1/Test.txt", port1)).setBody("this is a test").build(); - response = c.executeRequest(putRequest).get(); - assertEquals(response.getStatusCode(), 201); - - Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(String.format("http://127.0.0.1:%s/folder1/Test.txt", port1)).build(); - response = c.executeRequest(propFindRequest).get(); - - assertEquals(response.getStatusCode(), 207); - assertTrue(response.getResponseBody().contains("HTTP/1.1 200 OK")); - c.close(); - + try { + Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build(); + Response response = c.executeRequest(mkcolRequest).get(); + assertEquals(response.getStatusCode(), 201); + + Request putRequest = new RequestBuilder("PUT").setUrl(String.format("http://127.0.0.1:%s/folder1/Test.txt", port1)).setBody("this is a test").build(); + response = c.executeRequest(putRequest).get(); + assertEquals(response.getStatusCode(), 201); + + Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(String.format("http://127.0.0.1:%s/folder1/Test.txt", port1)).build(); + response = c.executeRequest(propFindRequest).get(); + + assertEquals(response.getStatusCode(), 207); + assertTrue(response.getResponseBody().contains("HTTP/1.1 200 OK")); + } finally { + c.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void propFindCompletionHandlerWebDavTest() throws InterruptedException, IOException, ExecutionException { AsyncHttpClient c = getAsyncHttpClient(null); - - Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build(); - Response response = c.executeRequest(mkcolRequest).get(); - assertEquals(response.getStatusCode(), 201); - - Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(getTargetUrl()).build(); - WebDavResponse webDavResponse = c.executeRequest(propFindRequest, new WebDavCompletionHandlerBase() { - /** - * {@inheritDoc} - */ - /* @Override */ - public void onThrowable(Throwable t) { - - t.printStackTrace(); - } - - @Override - public WebDavResponse onCompleted(WebDavResponse response) throws Exception { - return response; - } - }).get(); - - assertNotNull(webDavResponse); - assertEquals(webDavResponse.getStatusCode(), 200); - c.close(); + try { + Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build(); + Response response = c.executeRequest(mkcolRequest).get(); + assertEquals(response.getStatusCode(), 201); + + Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(getTargetUrl()).build(); + WebDavResponse webDavResponse = c.executeRequest(propFindRequest, new WebDavCompletionHandlerBase() { + /** + * {@inheritDoc} + */ + /* @Override */ + public void onThrowable(Throwable t) { + + t.printStackTrace(); + } + + @Override + public WebDavResponse onCompleted(WebDavResponse response) throws Exception { + return response; + } + }).get(); + + assertNotNull(webDavResponse); + assertEquals(webDavResponse.getStatusCode(), 200); + } finally { + c.close(); + } } - } diff --git a/src/test/java/com/ning/http/client/async/ZeroCopyFileTest.java b/src/test/java/com/ning/http/client/async/ZeroCopyFileTest.java index 64efdb4c29..b5400b183e 100644 --- a/src/test/java/com/ning/http/client/async/ZeroCopyFileTest.java +++ b/src/test/java/com/ning/http/client/async/ZeroCopyFileTest.java @@ -47,10 +47,7 @@ public abstract class ZeroCopyFileTest extends AbstractBasicTest { private class ZeroCopyHandler extends AbstractHandler { - public void handle(String s, - Request r, - HttpServletRequest httpRequest, - HttpServletResponse httpResponse) throws IOException, ServletException { + public void handle(String s, Request r, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { int size = 10 * 1024; if (httpRequest.getContentLength() > 0) { @@ -67,59 +64,62 @@ public void handle(String s, } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void zeroCopyPostTest() throws IOException, ExecutionException, TimeoutException, InterruptedException, URISyntaxException { AsyncHttpClient client = getAsyncHttpClient(null); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + final AtomicBoolean headerSent = new AtomicBoolean(false); + final AtomicBoolean operationCompleted = new AtomicBoolean(false); + + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/").setBody(file).execute(new AsyncCompletionHandler() { + + public STATE onHeaderWriteCompleted() { + headerSent.set(true); + return STATE.CONTINUE; + } - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - final AtomicBoolean headerSent = new AtomicBoolean(false); - final AtomicBoolean operationCompleted = new AtomicBoolean(false); - - Future f = client.preparePost("http://127.0.0.1/" + port1 + "/").setBody(file).execute(new AsyncCompletionHandler() { - - public STATE onHeaderWriteCompleted() { - headerSent.set(true); - return STATE.CONTINUE; - } - - public STATE onContentWriteCompleted() { - operationCompleted.set(true); - return STATE.CONTINUE; - } + public STATE onContentWriteCompleted() { + operationCompleted.set(true); + return STATE.CONTINUE; + } - @Override - public Object onCompleted(Response response) throws Exception { - return response; - } - }); - Response resp = f.get(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), "This is a simple test file"); - assertTrue(operationCompleted.get()); - assertTrue(headerSent.get()); - client.close(); + @Override + public Response onCompleted(Response response) throws Exception { + return response; + } + }); + Response resp = f.get(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), "This is a simple test file"); + assertTrue(operationCompleted.get()); + assertTrue(headerSent.get()); + } finally { + client.close(); + } } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void zeroCopyPutTest() throws IOException, ExecutionException, TimeoutException, InterruptedException, URISyntaxException { AsyncHttpClient client = getAsyncHttpClient(null); - - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - - Future f = client.preparePut("http://127.0.0.1/" + port1 + "/").setBody(file).execute(); - Response resp = f.get(); - assertNotNull(resp); - assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); - assertEquals(resp.getResponseBody(), "This is a simple test file"); - client.close(); - + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + Future f = client.preparePut("http://127.0.0.1/" + port1 + "/").setBody(file).execute(); + Response resp = f.get(); + assertNotNull(resp); + assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK); + assertEquals(resp.getResponseBody(), "This is a simple test file"); + } finally { + client.close(); + } } @Override @@ -127,92 +127,92 @@ public AbstractHandler configureHandler() throws Exception { return new ZeroCopyHandler(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void zeroCopyFileTest() throws IOException, ExecutionException, TimeoutException, InterruptedException, URISyntaxException { AsyncHttpClient client = getAsyncHttpClient(null); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + File tmp = new File(System.getProperty("java.io.tmpdir") + File.separator + "zeroCopy.txt"); + tmp.deleteOnExit(); + final FileOutputStream stream = new FileOutputStream(tmp); + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/").setBody(file).execute(new AsyncHandler() { + public void onThrowable(Throwable t) { + } - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - - File tmp = new File(System.getProperty("java.io.tmpdir") + File.separator + "zeroCopy.txt"); - tmp.deleteOnExit(); - final FileOutputStream stream = new FileOutputStream(tmp); - Future f = client.preparePost("http://127.0.0.1/" + port1 + "/").setBody(file).execute(new AsyncHandler() { - public void onThrowable(Throwable t) { - } - - public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - bodyPart.writeTo(stream); - return STATE.CONTINUE; - } - - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - return STATE.CONTINUE; - } + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + bodyPart.writeTo(stream); + return STATE.CONTINUE; + } - public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { - return STATE.CONTINUE; - } + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + return STATE.CONTINUE; + } - public Response onCompleted() throws Exception { - return null; - } - }); - Response resp = f.get(); - stream.close(); - assertNull(resp); - assertEquals(file.length(), tmp.length()); - client.close(); + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + return STATE.CONTINUE; + } + public Response onCompleted() throws Exception { + return null; + } + }); + Response resp = f.get(); + stream.close(); + assertNull(resp); + assertEquals(file.length(), tmp.length()); + } finally { + client.close(); + } } - - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void zeroCopyFileWithBodyManipulationTest() throws IOException, ExecutionException, TimeoutException, InterruptedException, URISyntaxException { AsyncHttpClient client = getAsyncHttpClient(null); + try { + ClassLoader cl = getClass().getClassLoader(); + // override system properties + URL url = cl.getResource("SimpleTextFile.txt"); + File file = new File(url.toURI()); + + File tmp = new File(System.getProperty("java.io.tmpdir") + File.separator + "zeroCopy.txt"); + tmp.deleteOnExit(); + final FileOutputStream stream = new FileOutputStream(tmp); + Future f = client.preparePost("http://127.0.0.1/" + port1 + "/").setBody(file).execute(new AsyncHandler() { + public void onThrowable(Throwable t) { + } - ClassLoader cl = getClass().getClassLoader(); - // override system properties - URL url = cl.getResource("SimpleTextFile.txt"); - File file = new File(url.toURI()); - - File tmp = new File(System.getProperty("java.io.tmpdir") + File.separator + "zeroCopy.txt"); - tmp.deleteOnExit(); - final FileOutputStream stream = new FileOutputStream(tmp); - Future f = client.preparePost("http://127.0.0.1/" + port1 + "/").setBody(file).execute(new AsyncHandler() { - public void onThrowable(Throwable t) { - } + public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { + bodyPart.writeTo(stream); - public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { - bodyPart.writeTo(stream); + if (bodyPart.getBodyPartBytes().length == 0) { + return STATE.ABORT; + } - if (bodyPart.getBodyPartBytes().length == 0) { - return STATE.ABORT; + return STATE.CONTINUE; } - - return STATE.CONTINUE; - } - - public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { - return STATE.CONTINUE; - } - public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { - return STATE.CONTINUE; - } + public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception { + return STATE.CONTINUE; + } - public Response onCompleted() throws Exception { - return null; - } - }); - Response resp = f.get(); - stream.close(); - assertNull(resp); - assertEquals(file.length(), tmp.length()); - client.close(); + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + return STATE.CONTINUE; + } + public Response onCompleted() throws Exception { + return null; + } + }); + Response resp = f.get(); + stream.close(); + assertNull(resp); + assertEquals(file.length(), tmp.length()); + } finally { + client.close(); + } } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncProviderBasicTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncProviderBasicTest.java index c718e2e031..739bb8f4f1 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncProviderBasicTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncProviderBasicTest.java @@ -13,46 +13,36 @@ package com.ning.http.client.async.grizzly; +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; + +import org.glassfish.grizzly.filterchain.FilterChainBuilder; +import org.glassfish.grizzly.nio.transport.TCPNIOTransport; +import org.glassfish.grizzly.strategies.SameThreadIOStrategy; +import org.testng.annotations.Test; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProviderConfig; -import com.ning.http.client.FluentCaseInsensitiveStringsMap; -import com.ning.http.client.Response; import com.ning.http.client.async.AsyncProvidersBasicTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig; import com.ning.http.client.providers.grizzly.TransportCustomizer; -import org.glassfish.grizzly.filterchain.FilterChainBuilder; -import org.glassfish.grizzly.nio.transport.TCPNIOTransport; -import org.glassfish.grizzly.strategies.SameThreadIOStrategy; -import org.testng.Assert; -import org.testng.annotations.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; -import static org.testng.Assert.assertEquals; public class GrizzlyAsyncProviderBasicTest extends AsyncProvidersBasicTest { - @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Override @Test public void asyncHeaderPOSTTest() throws Throwable { - super.asyncHeaderPOSTTest(); //To change body of overridden methods use File | Settings | File Templates. + super.asyncHeaderPOSTTest(); // To change body of overridden methods use File | Settings | File Templates. } @Override - protected AsyncHttpProviderConfig getProviderConfig() { + protected AsyncHttpProviderConfig getProviderConfig() { final GrizzlyAsyncHttpProviderConfig config = new GrizzlyAsyncHttpProviderConfig(); config.addProperty(TRANSPORT_CUSTOMIZER, new TransportCustomizer() { @Override @@ -64,7 +54,7 @@ public void customize(TCPNIOTransport transport, FilterChainBuilder builder) { return config; } - @Test(groups = {"standalone", "default_provider", "async"}, enabled = false) + @Test(groups = { "standalone", "default_provider", "async" }, enabled = false) public void asyncDoPostBasicGZIPTest() throws Throwable { } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamHandlerTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamHandlerTest.java index c9bb4de004..cce3263723 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamHandlerTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamHandlerTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.AsyncStreamHandlerTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyAsyncStreamHandlerTest extends AsyncStreamHandlerTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamLifecycleTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamLifecycleTest.java index b2d376d690..094eecb133 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamLifecycleTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAsyncStreamLifecycleTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.AsyncStreamLifecycleTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyAsyncStreamLifecycleTest extends AsyncStreamLifecycleTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAuthTimeoutTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAuthTimeoutTest.java index c0224564ca..631d25c9df 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAuthTimeoutTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyAuthTimeoutTest.java @@ -16,17 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.AuthTimeoutTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyAuthTimeoutTest extends AuthTimeoutTest { - @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicAuthTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicAuthTest.java index 4f0dc2634e..7cca1aa82c 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicAuthTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicAuthTest.java @@ -16,17 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.BasicAuthTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyBasicAuthTest extends BasicAuthTest { - @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicHttpsTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicHttpsTest.java index d5e27c68f2..2ebeedc182 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicHttpsTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBasicHttpsTest.java @@ -16,20 +16,17 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.BasicHttpsTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyBasicHttpsTest extends BasicHttpsTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Override public void zeroCopyPostTest() throws Throwable { - super.zeroCopyPostTest(); //To change body of overridden methods use File | Settings | File Templates. + super.zeroCopyPostTest(); // To change body of overridden methods use File | Settings | File Templates. } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyChunkTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyChunkTest.java index 643628e64f..9a58678980 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyChunkTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyChunkTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.BodyChunkTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyBodyChunkTest extends BodyChunkTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyDeferringAsyncHandlerTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyDeferringAsyncHandlerTest.java index 889f49ad89..5b0810feea 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyDeferringAsyncHandlerTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyBodyDeferringAsyncHandlerTest.java @@ -16,15 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.BodyDeferringAsyncHandlerTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyBodyDeferringAsyncHandlerTest extends BodyDeferringAsyncHandlerTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyByteBufferCapacityTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyByteBufferCapacityTest.java index b875ce10f4..d3522541b2 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyByteBufferCapacityTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyByteBufferCapacityTest.java @@ -13,23 +13,21 @@ package com.ning.http.client.async.grizzly; +import org.testng.annotations.Test; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ByteBufferCapacityTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; -import org.testng.annotations.Test; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyByteBufferCapacityTest extends ByteBufferCapacityTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } - @Test(groups = {"standalone", "default_provider"}, enabled=false) + @Test(groups = { "standalone", "default_provider" }, enabled = false) public void basicByteBufferTest() throws Throwable { } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyChunkingTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyChunkingTest.java index 153f80c80b..9f07b7d498 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyChunkingTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyChunkingTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ChunkingTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyChunkingTest extends ChunkingTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyComplexClientTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyComplexClientTest.java index 0b8b4a9d18..5a5bd7a0f0 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyComplexClientTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyComplexClientTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ComplexClientTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyComplexClientTest extends ComplexClientTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyConnectionPoolTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyConnectionPoolTest.java index 37395056c1..c810843d6f 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyConnectionPoolTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyConnectionPoolTest.java @@ -13,63 +13,57 @@ package com.ning.http.client.async.grizzly; -import com.ning.http.client.AsyncCompletionHandler; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; + +import java.util.concurrent.TimeUnit; + +import org.glassfish.grizzly.Connection; +import org.testng.annotations.Test; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.ConnectionsPool; import com.ning.http.client.Response; import com.ning.http.client.async.ConnectionPoolTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; -import org.glassfish.grizzly.Connection; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.testng.Assert.*; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyConnectionPoolTest extends ConnectionPoolTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Override @Test public void testMaxTotalConnectionsException() { - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setAllowPoolingConnection(true) - .setMaximumConnectionsTotal(1) - .build() - ); - - String url = getTargetUrl(); - int i; - Exception exception = null; - for (i = 0; i < 20; i++) { - try { - log.info("{} requesting url [{}]...", i, url); - - if (i < 5) { - client.prepareGet(url).execute().get(); - } else { - client.prepareGet(url).execute(); + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true).setMaximumConnectionsTotal(1).build()); + try { + String url = getTargetUrl(); + int i; + Exception exception = null; + for (i = 0; i < 20; i++) { + try { + log.info("{} requesting url [{}]...", i, url); + + if (i < 5) { + client.prepareGet(url).execute().get(); + } else { + client.prepareGet(url).execute(); + } + } catch (Exception ex) { + exception = ex; + break; } - } catch (Exception ex) { - exception = ex; - break; } + assertNotNull(exception); + assertNotNull(exception.getMessage()); + } finally { + client.close(); } - assertNotNull(exception); - assertNotNull(exception.getMessage()); - } @Override @@ -97,24 +91,22 @@ public void destroy() { } }; - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionsPool(cp) - .build() - ); - - Exception exception = null; + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionsPool(cp).build()); try { - client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; + Exception exception = null; + try { + client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNull(exception); + } finally { + client.close(); } - assertNull(exception); - client.close(); } - @Test(groups = {"standalone", "default_provider"}) + @Test(groups = { "standalone", "default_provider" }) public void testInvalidConnectionsPool() { ConnectionsPool cp = new ConnectionsPool() { @@ -140,84 +132,46 @@ public void destroy() { } }; - AsyncHttpClient client = getAsyncHttpClient( - new AsyncHttpClientConfig.Builder() - .setConnectionsPool(cp) - .build() - ); - - Exception exception = null; + AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setConnectionsPool(cp).build()); try { - client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; + Exception exception = null; + try { + client.prepareGet(getTargetUrl()).execute().get(TIMEOUT, TimeUnit.SECONDS); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNotNull(exception); + } finally { + client.close(); } - assertNotNull(exception); - client.close(); } @Override @Test public void multipleMaxConnectionOpenTest() throws Throwable { - AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true) - .setConnectionTimeoutInMs(5000).setMaximumConnectionsTotal(1).build(); + AsyncHttpClientConfig cg = new AsyncHttpClientConfig.Builder().setAllowPoolingConnection(true).setConnectionTimeoutInMs(5000).setMaximumConnectionsTotal(1).build(); AsyncHttpClient c = getAsyncHttpClient(cg); - - String body = "hello there"; - - // once - Response response = c.preparePost(getTargetUrl()) - .setBody(body) - .execute().get(TIMEOUT, TimeUnit.SECONDS); - - assertEquals(response.getResponseBody(), body); - - // twice - Exception exception = null; try { - c.preparePost(String.format("http://127.0.0.1:%d/foo/test", port2)).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); - fail("Should throw exception. Too many connections issued."); - } catch (Exception ex) { - ex.printStackTrace(); - exception = ex; - } - assertNotNull(exception); - c.close(); - } + String body = "hello there"; + // once + Response response = c.preparePost(getTargetUrl()).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); - @Override - @Test - public void win7DisconnectTest() throws Throwable { - final AtomicInteger count = new AtomicInteger(0); - - AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - AsyncCompletionHandler handler = new - AsyncCompletionHandlerAdapter() { - - @Override - public Response onCompleted(Response response) throws - Exception { - - count.incrementAndGet(); - StackTraceElement e = new StackTraceElement("sun.nio.ch.SocketDispatcher", "read0", null, -1); - IOException t = new IOException(); - t.setStackTrace(new StackTraceElement[]{e}); - throw t; - } - }; + assertEquals(response.getResponseBody(), body); - try { - client.prepareGet(getTargetUrl()).execute(handler).get(); - fail("Must have received an exception"); - } catch (ExecutionException ex) { - assertNotNull(ex); - assertNotNull(ex.getCause()); - assertEquals(ex.getCause().getClass(), IOException.class); - assertEquals(count.get(), 1); + // twice + Exception exception = null; + try { + c.preparePost(String.format("http://127.0.0.1:%d/foo/test", port2)).setBody(body).execute().get(TIMEOUT, TimeUnit.SECONDS); + fail("Should throw exception. Too many connections issued."); + } catch (Exception ex) { + ex.printStackTrace(); + exception = ex; + } + assertNotNull(exception); + } finally { + c.close(); } - client.close(); } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyDigestAuthTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyDigestAuthTest.java index 95f2f8879a..77805bedf6 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyDigestAuthTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyDigestAuthTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.DigestAuthTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyDigestAuthTest extends DigestAuthTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyEmptyBodyTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyEmptyBodyTest.java index a6a88a4239..308177d336 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyEmptyBodyTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyEmptyBodyTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.EmptyBodyTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyEmptyBodyTest extends EmptyBodyTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyErrorResponseTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyErrorResponseTest.java index 33c0ff2e7b..5ff47a0be2 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyErrorResponseTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyErrorResponseTest.java @@ -16,16 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ErrorResponseTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyErrorResponseTest extends ErrorResponseTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyExpectContinue100Test.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyExpectContinue100Test.java index 09307e7185..0b8b085591 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyExpectContinue100Test.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyExpectContinue100Test.java @@ -16,16 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.Expect100ContinueTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; -public class GrizzlyExpectContinue100Test extends Expect100ContinueTest{ +public class GrizzlyExpectContinue100Test extends Expect100ContinueTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFeedableBodyGeneratorTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFeedableBodyGeneratorTest.java new file mode 100644 index 0000000000..df46368153 --- /dev/null +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFeedableBodyGeneratorTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2013 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package com.ning.http.client.async.grizzly; + +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.RequestBuilder; +import com.ning.http.client.providers.grizzly.FeedableBodyGenerator; +import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import org.glassfish.grizzly.Buffer; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.memory.Buffers; +import org.glassfish.grizzly.ssl.SSLContextConfigurator; +import org.glassfish.grizzly.ssl.SSLEngineConfigurator; +import org.glassfish.grizzly.utils.Charsets; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.glassfish.grizzly.http.server.NetworkListener.DEFAULT_NETWORK_HOST; +import static org.glassfish.grizzly.memory.MemoryManager.DEFAULT_MEMORY_MANAGER; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; +import static org.testng.AssertJUnit.assertEquals; + +public class GrizzlyFeedableBodyGeneratorTest { + + private static final byte[] DATA = + "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ".getBytes(Charsets.ASCII_CHARSET); + private static final int TEMP_FILE_SIZE = 2 * 1024 * 1024; + private static final int NON_SECURE_PORT = 9991; + private static final int SECURE_PORT = 9992; + + + private HttpServer server; + private File tempFile; + + + // ------------------------------------------------------------------- Setup + + + @BeforeTest + public void setup() throws Exception { + generateTempFile(); + server = new HttpServer(); + NetworkListener nonSecure = + new NetworkListener("nonsecure", + DEFAULT_NETWORK_HOST, + NON_SECURE_PORT); + NetworkListener secure = + new NetworkListener("secure", + DEFAULT_NETWORK_HOST, + SECURE_PORT); + secure.setSecure(true); + secure.setSSLEngineConfig(createSSLConfig()); + server.addListener(nonSecure); + server.addListener(secure); + server.getServerConfiguration().addHttpHandler(new ConsumingHandler(), "/test"); + server.start(); + } + + + // --------------------------------------------------------------- Tear Down + + + @AfterTest + public void tearDown() { + if (!tempFile.delete()) { + tempFile.deleteOnExit(); + } + tempFile = null; + server.shutdownNow(); + server = null; + } + + + // ------------------------------------------------------------ Test Methods + + + @Test + public void testSimpleFeederMultipleThreads() throws Exception { + doSimpleFeeder(false); + } + + @Test + public void testSimpleFeederOverSSLMultipleThreads() throws Exception { + doSimpleFeeder(true); + } + + + // --------------------------------------------------------- Private Methods + + + private void doSimpleFeeder(final boolean secure) { + final int threadCount = 10; + final CountDownLatch latch = new CountDownLatch(threadCount); + final int port = (secure ? SECURE_PORT : NON_SECURE_PORT); + final String scheme = (secure ? "https" : "http"); + ExecutorService service = Executors.newFixedThreadPool(threadCount); + + AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder() + .setMaximumConnectionsPerHost(60) + .setMaximumConnectionsTotal(60) + .build(); + final AsyncHttpClient client = + new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + final int[] statusCodes = new int[threadCount]; + final int[] totalsReceived = new int[threadCount]; + final Throwable[] errors = new Throwable[threadCount]; + for (int i = 0; i < threadCount; i++) { + final int idx = i; + service.execute(new Runnable() { + @Override + public void run() { + FeedableBodyGenerator generator = + new FeedableBodyGenerator(); + FeedableBodyGenerator.SimpleFeeder simpleFeeder = + new FeedableBodyGenerator.SimpleFeeder(generator) { + @Override + public void flush() throws IOException { + FileInputStream in = null; + try { + final byte[] bytesIn = new byte[2048]; + in = new FileInputStream(tempFile); + int read; + while ((read = in.read(bytesIn)) != -1) { + final Buffer b = + Buffers.wrap( + DEFAULT_MEMORY_MANAGER, + bytesIn, + 0, + read); + feed(b, false); + } + feed(Buffers.EMPTY_BUFFER, true); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ignored) { + } + } + } + } + }; + generator.setFeeder(simpleFeeder); + generator.setMaxPendingBytes(10000); + + RequestBuilder builder = new RequestBuilder("POST"); + builder.setUrl(scheme + "://localhost:" + port + "/test"); + builder.setBody(generator); + try { + client.executeRequest(builder.build(), + new AsyncCompletionHandler() { + @Override + public com.ning.http.client.Response onCompleted(com.ning.http.client.Response response) + throws Exception { + try { + totalsReceived[idx] = Integer.parseInt(response.getHeader("x-total")); + } catch (Exception e) { + errors[idx] = e; + } + statusCodes[idx] = response.getStatusCode(); + latch.countDown(); + return response; + } + + @Override + public void onThrowable(Throwable t) { + errors[idx] = t; + t.printStackTrace(); + latch.countDown(); + } + }); + } catch (IOException e) { + errors[idx] = e; + latch.countDown(); + } + } + }); + } + + try { + latch.await(1, TimeUnit.MINUTES); + } catch (InterruptedException e) { + fail("Latch interrupted"); + } + + for (int i = 0; i < threadCount; i++) { + assertEquals(200, statusCodes[i]); + assertNull(errors[i]); + assertEquals(tempFile.length(), totalsReceived[i]); + } + } + + + private static SSLEngineConfigurator createSSLConfig() + throws Exception { + final SSLContextConfigurator sslContextConfigurator = + new SSLContextConfigurator(); + final ClassLoader cl = GrizzlyFeedableBodyGeneratorTest.class.getClassLoader(); + // override system properties + final URL cacertsUrl = cl.getResource("ssltest-cacerts.jks"); + if (cacertsUrl != null) { + sslContextConfigurator.setTrustStoreFile(cacertsUrl.getFile()); + sslContextConfigurator.setTrustStorePass("changeit"); + } + + // override system properties + final URL keystoreUrl = cl.getResource("ssltest-keystore.jks"); + if (keystoreUrl != null) { + sslContextConfigurator.setKeyStoreFile(keystoreUrl.getFile()); + sslContextConfigurator.setKeyStorePass("changeit"); + } + + return new SSLEngineConfigurator( + sslContextConfigurator.createSSLContext(), + false, false, false); + } + + + private void generateTempFile() throws IOException { + tempFile = File.createTempFile("feedable", null); + int total = 0; + byte[] chunk = new byte[1024]; + Random r = new Random(System.currentTimeMillis()); + FileOutputStream out = new FileOutputStream(tempFile); + while (total < TEMP_FILE_SIZE) { + for (int i = 0; i < chunk.length; i++) { + chunk[i] = DATA[r.nextInt(DATA.length)]; + } + out.write(chunk); + total += chunk.length; + } + out.flush(); + out.close(); + } + + + // ---------------------------------------------------------- Nested Classes + + + private static final class ConsumingHandler extends HttpHandler { + + + // -------------------------------------------- Methods from HttpHandler + + + @Override + public void service(Request request, Response response) + throws Exception { + int total = 0; + byte[] bytesIn = new byte[2048]; + InputStream in = request.getInputStream(); + int read; + while ((read = in.read(bytesIn)) != -1) { + total += read; + Thread.sleep(5); + } + response.addHeader("X-Total", Integer.toString(total)); + } + + } // END ConsumingHandler + +} diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFilterTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFilterTest.java index 19a7d7ce21..c6587ebb63 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFilterTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFilterTest.java @@ -16,16 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.FilterTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyFilterTest extends FilterTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } - } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFollowingThreadTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFollowingThreadTest.java index 9835a67d79..74c6347ce7 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFollowingThreadTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyFollowingThreadTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.FollowingThreadTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyFollowingThreadTest extends FollowingThreadTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHead302Test.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHead302Test.java index a84023b1a2..70b6630f3b 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHead302Test.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHead302Test.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.Head302Test; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyHead302Test extends Head302Test { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHttpToHttpsRedirectTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHttpToHttpsRedirectTest.java index c6ea5c47d5..c6c1351aaf 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHttpToHttpsRedirectTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyHttpToHttpsRedirectTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.HttpToHttpsRedirectTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyHttpToHttpsRedirectTest extends HttpToHttpsRedirectTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyIdleStateHandlerTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyIdleStateHandlerTest.java index 1096adab69..9b6500bec5 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyIdleStateHandlerTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyIdleStateHandlerTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.IdleStateHandlerTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyIdleStateHandlerTest extends IdleStateHandlerTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyInputStreamTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyInputStreamTest.java index 6702035939..943a711aac 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyInputStreamTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyInputStreamTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.InputStreamTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyInputStreamTest extends InputStreamTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyListenableFutureTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyListenableFutureTest.java index 285e612721..5839c271cd 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyListenableFutureTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyListenableFutureTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ListenableFutureTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyListenableFutureTest extends ListenableFutureTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxConnectionsInThreadsTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxConnectionsInThreadsTest.java index d9a95eace4..c3982b645f 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxConnectionsInThreadsTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxConnectionsInThreadsTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.MaxConnectionsInThreads; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyMaxConnectionsInThreadsTest extends MaxConnectionsInThreads { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxTotalConnectionTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxTotalConnectionTest.java index eeb1c08bf4..7a2829c5ee 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxTotalConnectionTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMaxTotalConnectionTest.java @@ -16,15 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.MaxTotalConnectionTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyMaxTotalConnectionTest extends MaxTotalConnectionTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMultipleHeaderTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMultipleHeaderTest.java index 9635878ac9..4b58ba7440 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMultipleHeaderTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyMultipleHeaderTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.MultipleHeaderTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyMultipleHeaderTest extends MultipleHeaderTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNoNullResponseTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNoNullResponseTest.java index 2c4fac2577..63c10d687d 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNoNullResponseTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNoNullResponseTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.NoNullResponseTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyNoNullResponseTest extends NoNullResponseTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNonAsciiContentLengthTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNonAsciiContentLengthTest.java index 9ab8b96c3b..f9342f299e 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNonAsciiContentLengthTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyNonAsciiContentLengthTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.NonAsciiContentLengthTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyNonAsciiContentLengthTest extends NonAsciiContentLengthTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyParamEncodingTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyParamEncodingTest.java index 98fe02b2e7..be3a764092 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyParamEncodingTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyParamEncodingTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ParamEncodingTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyParamEncodingTest extends ParamEncodingTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestRelative302Test.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestRelative302Test.java index e52d331e14..e22cff3d03 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestRelative302Test.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestRelative302Test.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.PerRequestRelative302Test; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyPerRequestRelative302Test extends PerRequestRelative302Test { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestTimeoutTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestTimeoutTest.java index 0a7a16eb7e..6c1ce2d3b4 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestTimeoutTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPerRequestTimeoutTest.java @@ -13,24 +13,23 @@ package com.ning.http.client.async.grizzly; +import static org.testng.Assert.assertEquals; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.PerRequestTimeoutTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyPerRequestTimeoutTest extends PerRequestTimeoutTest { @Override - protected String getExpectedTimeoutMessage() { - return "Timeout exceeded"; + protected void checkTimeoutMessage(String message) { + assertEquals("Timeout exceeded", message); } @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostRedirectGetTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostRedirectGetTest.java index 54a6c78c24..269772a414 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostRedirectGetTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostRedirectGetTest.java @@ -16,15 +16,12 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.PostRedirectGetTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyPostRedirectGetTest extends PostRedirectGetTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostWithQSTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostWithQSTest.java index d9dd8b19b9..140f60562c 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostWithQSTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPostWithQSTest.java @@ -16,16 +16,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.PostWithQSTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; public class GrizzlyPostWithQSTest extends PostWithQSTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTest.java index 1bfbc7f472..63ab5cc82b 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.ProxyTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyProxyTest extends ProxyTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTunnelingTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTunnelingTest.java index 9033261a2c..8f112b2af0 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTunnelingTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyProxyTunnelingTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.ProxyyTunnellingTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyProxyTunnelingTest extends ProxyyTunnellingTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPutLargeFileTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPutLargeFileTest.java index c26efc51fc..47c8158c40 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPutLargeFileTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyPutLargeFileTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.PutLargeFileTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyPutLargeFileTest extends PutLargeFileTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyQueryParametersTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyQueryParametersTest.java index 8ee03ce466..6f46beba13 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyQueryParametersTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyQueryParametersTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.QueryParametersTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyQueryParametersTest extends QueryParametersTest{ @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRC10KTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRC10KTest.java index 837b10c43a..fbd14d7f96 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRC10KTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRC10KTest.java @@ -15,16 +15,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.RC10KTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyRC10KTest extends RC10KTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRedirectConnectionUsageTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRedirectConnectionUsageTest.java index 5397cf5fa1..f70cc2c92c 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRedirectConnectionUsageTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRedirectConnectionUsageTest.java @@ -13,27 +13,25 @@ package com.ning.http.client.async.grizzly; +import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; + +import org.glassfish.grizzly.filterchain.FilterChainBuilder; +import org.glassfish.grizzly.nio.transport.TCPNIOTransport; +import org.glassfish.grizzly.strategies.SameThreadIOStrategy; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.AsyncHttpProviderConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.RedirectConnectionUsageTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig; import com.ning.http.client.providers.grizzly.TransportCustomizer; -import org.glassfish.grizzly.filterchain.FilterChainBuilder; -import org.glassfish.grizzly.nio.transport.TCPNIOTransport; -import org.glassfish.grizzly.strategies.SameThreadIOStrategy; - -import static com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProviderConfig.Property.TRANSPORT_CUSTOMIZER; public class GrizzlyRedirectConnectionUsageTest extends RedirectConnectionUsageTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Override diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRelative302Test.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRelative302Test.java index 684f758353..33d65c5af9 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRelative302Test.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRelative302Test.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.Relative302Test; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyRelative302Test extends Relative302Test { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRemoteSiteTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRemoteSiteTest.java index 23b8b217bb..21b6472715 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRemoteSiteTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRemoteSiteTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.RemoteSiteTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyRemoteSiteTest extends RemoteSiteTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRetryRequestTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRetryRequestTest.java index 20b7cca957..d89247f60a 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRetryRequestTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyRetryRequestTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.RetryRequestTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyRetryRequestTest extends RetryRequestTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlySimpleAsyncHttpClientTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlySimpleAsyncHttpClientTest.java index 34709e2d62..291c40844c 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlySimpleAsyncHttpClientTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlySimpleAsyncHttpClientTest.java @@ -15,6 +15,7 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.SimpleAsyncHttpClientTest; import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; @@ -22,10 +23,10 @@ public class GrizzlySimpleAsyncHttpClientTest extends SimpleAsyncHttpClientTest @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } + public String getProviderClass() { + return GrizzlyAsyncHttpProvider.class.getName(); + } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyTransferListenerTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyTransferListenerTest.java index 90181ac0a5..3be998d25b 100644 --- a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyTransferListenerTest.java +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyTransferListenerTest.java @@ -15,17 +15,14 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.async.TransferListenerTest; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; public class GrizzlyTransferListenerTest extends TransferListenerTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/async/grizzly/GrizzlyUnexpectingTimeoutTest.java b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyUnexpectingTimeoutTest.java new file mode 100644 index 0000000000..9fc1f1d09c --- /dev/null +++ b/src/test/java/com/ning/http/client/async/grizzly/GrizzlyUnexpectingTimeoutTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012-2013 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package com.ning.http.client.async.grizzly; + +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.Response; +import com.ning.http.client.async.AbstractBasicTest; +import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import org.eclipse.jetty.continuation.Continuation; +import org.eclipse.jetty.continuation.ContinuationSupport; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.annotations.Test; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; + +public class GrizzlyUnexpectingTimeoutTest extends AbstractBasicTest { + + private static final String MSG = "Unauthorized without WWW-Authenticate header"; + + protected String getExpectedTimeoutMessage() { + return "401 response received, but no WWW-Authenticate header was present"; + } + + @Override + public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { + if (config == null) { + config = new AsyncHttpClientConfig.Builder().build(); + } + return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + } + + @Override + public AbstractHandler configureHandler() throws Exception { + return new ExpectExceptionHandler(); + } + + private class ExpectExceptionHandler extends AbstractHandler { + public void handle(String target, Request baseRequest, HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + final Continuation continuation = ContinuationSupport.getContinuation(request); + continuation.suspend(); + new Thread(new Runnable() { + public void run() { + try { + response.getOutputStream().print(MSG); + response.getOutputStream().flush(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + }).start(); + baseRequest.setHandled(true); + } + } + + @Test(groups = {"standalone", "default_provider"}) + public void unexpectedTimeoutTest() throws IOException { + final AtomicInteger counts = new AtomicInteger(); + final int timeout = 100; + + final AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(timeout).build()); + Future responseFuture = + client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandler() { + @Override + public Response onCompleted(Response response) throws Exception { + counts.incrementAndGet(); + return response; + } + + @Override + public void onThrowable(Throwable t) { + counts.incrementAndGet(); + super.onThrowable(t); + } + }); + // currently, an exception is expected + // because the grizzly provider would throw IllegalStateException if WWW-Authenticate header doesn't exist with 401 response status. + try { + Response response = responseFuture.get(); + assertNull(response); + } catch (InterruptedException e) { + fail("Interrupted.", e); + } catch (ExecutionException e) { + assertFalse(e.getCause() instanceof TimeoutException); + assertEquals(e.getCause().getMessage(), getExpectedTimeoutMessage()); + } + // wait for timeout again. + try { + Thread.sleep(timeout*2); + } catch (InterruptedException e) { + fail("Interrupted.", e); + } + // the result should be either onCompleted or onThrowable. + assertEquals(1, counts.get(), "result should be one"); + client.close(); + } +} diff --git a/src/test/java/com/ning/http/client/async/netty/NettyAsyncProviderBasicTest.java b/src/test/java/com/ning/http/client/async/netty/NettyAsyncProviderBasicTest.java index 4292c85f6f..bf713d309c 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyAsyncProviderBasicTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyAsyncProviderBasicTest.java @@ -27,7 +27,7 @@ public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { } @Override - protected AsyncHttpProviderConfig getProviderConfig() { + protected AsyncHttpProviderConfig getProviderConfig() { final NettyAsyncHttpProviderConfig config = new NettyAsyncHttpProviderConfig(); config.addProperty("tcpNoDelay", true); diff --git a/src/test/java/com/ning/http/client/async/netty/NettyBasicAuthTest.java b/src/test/java/com/ning/http/client/async/netty/NettyBasicAuthTest.java index 0dc441b15d..feb1f6115c 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyBasicAuthTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyBasicAuthTest.java @@ -16,10 +16,6 @@ import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.BasicAuthTest; import com.ning.http.client.async.ProviderUtil; -import org.testng.annotations.Test; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; public class NettyBasicAuthTest extends BasicAuthTest { @@ -27,10 +23,4 @@ public class NettyBasicAuthTest extends BasicAuthTest { public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { return ProviderUtil.nettyProvider(config); } - - @Override - @Test - public void redirectAndBasicAuthTest() throws Exception, ExecutionException, TimeoutException, InterruptedException { - super.redirectAndBasicAuthTest(); //To change body of overridden methods use File | Settings | File Templates. - } } diff --git a/src/test/java/com/ning/http/client/async/netty/NettyBodyDeferringAsyncHandlerTest.java b/src/test/java/com/ning/http/client/async/netty/NettyBodyDeferringAsyncHandlerTest.java index 8e71e559fb..45ad55e296 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyBodyDeferringAsyncHandlerTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyBodyDeferringAsyncHandlerTest.java @@ -17,8 +17,7 @@ import com.ning.http.client.async.BodyDeferringAsyncHandlerTest; import com.ning.http.client.async.ProviderUtil; -public class NettyBodyDeferringAsyncHandlerTest extends - BodyDeferringAsyncHandlerTest { +public class NettyBodyDeferringAsyncHandlerTest extends BodyDeferringAsyncHandlerTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { diff --git a/src/test/java/com/ning/http/client/async/netty/NettyByteBufferCapacityTest.java b/src/test/java/com/ning/http/client/async/netty/NettyByteBufferCapacityTest.java index 0d1d9ef43b..e3c2326412 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyByteBufferCapacityTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyByteBufferCapacityTest.java @@ -14,7 +14,6 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.async.AbstractBasicTest; import com.ning.http.client.async.ByteBufferCapacityTest; import com.ning.http.client.async.ProviderUtil; diff --git a/src/test/java/com/ning/http/client/async/netty/NettyPerRequestTimeoutTest.java b/src/test/java/com/ning/http/client/async/netty/NettyPerRequestTimeoutTest.java index 6b74df734b..aa42d7f46f 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyPerRequestTimeoutTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyPerRequestTimeoutTest.java @@ -12,12 +12,19 @@ */ package com.ning.http.client.async.netty; +import static org.testng.Assert.assertTrue; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.PerRequestTimeoutTest; import com.ning.http.client.async.ProviderUtil; public class NettyPerRequestTimeoutTest extends PerRequestTimeoutTest { + + protected void checkTimeoutMessage(String message) { + assertTrue(message.startsWith("Request reached time out of 100 ms after ")); + } + @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { return ProviderUtil.nettyProvider(config); diff --git a/src/test/java/com/ning/http/client/async/netty/NettyProxyTest.java b/src/test/java/com/ning/http/client/async/netty/NettyProxyTest.java index 2a3326320a..6d6babc83a 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyProxyTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyProxyTest.java @@ -22,7 +22,6 @@ public class NettyProxyTest extends ProxyTest { public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { return ProviderUtil.nettyProvider(config); } - } diff --git a/src/test/java/com/ning/http/client/async/netty/NettyRedirectConnectionUsageTest.java b/src/test/java/com/ning/http/client/async/netty/NettyRedirectConnectionUsageTest.java index 201b13e28a..c179b9d63d 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyRedirectConnectionUsageTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyRedirectConnectionUsageTest.java @@ -26,7 +26,7 @@ public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { } @Override - protected AsyncHttpProviderConfig getProviderConfig() { + protected AsyncHttpProviderConfig getProviderConfig() { final NettyAsyncHttpProviderConfig config = new NettyAsyncHttpProviderConfig(); if (System.getProperty("blockingio") != null) { diff --git a/src/test/java/com/ning/http/client/async/netty/NettyRequestThrottleTimeoutTest.java b/src/test/java/com/ning/http/client/async/netty/NettyRequestThrottleTimeoutTest.java new file mode 100644 index 0000000000..9608846d46 --- /dev/null +++ b/src/test/java/com/ning/http/client/async/netty/NettyRequestThrottleTimeoutTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.http.client.async.netty; + +import com.ning.http.client.*; +import com.ning.http.client.async.AbstractBasicTest; +import com.ning.http.client.async.ProviderUtil; +import org.eclipse.jetty.continuation.Continuation; +import org.eclipse.jetty.continuation.ContinuationSupport; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.testng.annotations.Test; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +import static org.testng.Assert.*; + +public class NettyRequestThrottleTimeoutTest extends AbstractBasicTest { + private static final String MSG = "Enough is enough."; + private static final int SLEEPTIME_MS = 1000; + + @Override + public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { + return ProviderUtil.nettyProvider(config); + } + + @Override + public AbstractHandler configureHandler() throws Exception { + return new SlowHandler(); + } + + private class SlowHandler extends AbstractHandler { + public void handle(String target, Request baseRequest, HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_OK); + final Continuation continuation = ContinuationSupport.getContinuation(request); + continuation.suspend(); + new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(SLEEPTIME_MS); + response.getOutputStream().print(MSG); + response.getOutputStream().flush(); + continuation.complete(); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + }).start(); + baseRequest.setHandled(true); + } + } + + @Test(groups = {"standalone", "netty_provider"}) + public void testRequestTimeout() throws IOException { + final Semaphore requestThrottle = new Semaphore(1); + + final AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder() + .setCompressionEnabled(true) + .setAllowPoolingConnection(true) + .setMaximumConnectionsTotal(1).build()); + + final CountDownLatch latch = new CountDownLatch(2); + + final List tooManyConnections = new ArrayList(2); + for(int i=0;i<2;i++) { + new Thread(new Runnable() { + + public void run() { + try { + requestThrottle.acquire(); + PerRequestConfig requestConfig = new PerRequestConfig(); + requestConfig.setRequestTimeoutInMs(SLEEPTIME_MS/2); + Future responseFuture = null; + try { + responseFuture = + client.prepareGet(getTargetUrl()).setPerRequestConfig(requestConfig).execute(new AsyncCompletionHandler() { + + @Override + public Response onCompleted(Response response) throws Exception { + requestThrottle.release(); + return response; + } + + @Override + public void onThrowable(Throwable t) { + requestThrottle.release(); + } + }); + } catch(Exception e) { + tooManyConnections.add(e); + } + + if(responseFuture!=null) + responseFuture.get(); + } catch (Exception e) { + } finally { + latch.countDown(); + } + + } + }).start(); + + + } + + try { + latch.await(30,TimeUnit.SECONDS); + } catch (Exception e) { + fail("failed to wait for requests to complete"); + } + + assertTrue(tooManyConnections.size()==0,"Should not have any connection errors where too many connections have been attempted"); + + client.close(); + } +} \ No newline at end of file diff --git a/src/test/java/com/ning/http/client/async/netty/NettySimpleAsyncHttpClientTest.java b/src/test/java/com/ning/http/client/async/netty/NettySimpleAsyncHttpClientTest.java index 249e0ebbdf..c470500643 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettySimpleAsyncHttpClientTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettySimpleAsyncHttpClientTest.java @@ -15,6 +15,7 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.SimpleAsyncHttpClientTest; +import com.ning.http.client.providers.netty.NettyAsyncHttpProvider; public class NettySimpleAsyncHttpClientTest extends SimpleAsyncHttpClientTest { @@ -28,4 +29,7 @@ public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { return null; } + public String getProviderClass() { + return NettyAsyncHttpProvider.class.getName(); + } } diff --git a/src/test/java/com/ning/http/client/async/netty/NettyZeroCopyFileTest.java b/src/test/java/com/ning/http/client/async/netty/NettyZeroCopyFileTest.java index c1cc524802..0bec536814 100644 --- a/src/test/java/com/ning/http/client/async/netty/NettyZeroCopyFileTest.java +++ b/src/test/java/com/ning/http/client/async/netty/NettyZeroCopyFileTest.java @@ -15,7 +15,6 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ProviderUtil; -import com.ning.http.client.async.TransferListenerTest; import com.ning.http.client.async.ZeroCopyFileTest; public class NettyZeroCopyFileTest extends ZeroCopyFileTest { diff --git a/src/test/java/com/ning/http/client/generators/ByteArrayBodyGeneratorTest.java b/src/test/java/com/ning/http/client/generators/ByteArrayBodyGeneratorTest.java index a2c3937839..105d750751 100644 --- a/src/test/java/com/ning/http/client/generators/ByteArrayBodyGeneratorTest.java +++ b/src/test/java/com/ning/http/client/generators/ByteArrayBodyGeneratorTest.java @@ -13,16 +13,15 @@ package com.ning.http.client.generators; -import com.ning.http.client.Body; - -import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Random; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import org.testng.annotations.Test; + +import com.ning.http.client.Body; /** * @author Bryan Davis bpd@keynetics.com diff --git a/src/test/java/com/ning/http/client/resumable/PropertiesBasedResumableProcesserTest.java b/src/test/java/com/ning/http/client/resumable/PropertiesBasedResumableProcesserTest.java index 41d0d68947..e9969a599b 100644 --- a/src/test/java/com/ning/http/client/resumable/PropertiesBasedResumableProcesserTest.java +++ b/src/test/java/com/ning/http/client/resumable/PropertiesBasedResumableProcesserTest.java @@ -23,7 +23,7 @@ * @author Benjamin Hanzelmann */ public class PropertiesBasedResumableProcesserTest { - @Test (enabled = false) + @Test public void testSaveLoad() throws Exception { PropertiesBasedResumableProcessor p = new PropertiesBasedResumableProcessor(); diff --git a/src/test/java/com/ning/http/client/websocket/ByteMessageTest.java b/src/test/java/com/ning/http/client/websocket/ByteMessageTest.java index 0566cd9ad4..1811a3895b 100644 --- a/src/test/java/com/ning/http/client/websocket/ByteMessageTest.java +++ b/src/test/java/com/ning/http/client/websocket/ByteMessageTest.java @@ -66,184 +66,195 @@ public org.eclipse.jetty.websocket.WebSocket doWebSocketConnect(HttpServletReque @Test public void echoByte() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(new byte[0]); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(new byte[0]); - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { - @Override - public void onOpen(WebSocket websocket) { - } + @Override + public void onOpen(WebSocket websocket) { + } - @Override - public void onClose(WebSocket websocket) { - latch.countDown(); - } + @Override + public void onClose(WebSocket websocket) { + latch.countDown(); + } - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } - @Override - public void onMessage(byte[] message) { - text.set(message); - latch.countDown(); - } + @Override + public void onMessage(byte[] message) { + text.set(message); + latch.countDown(); + } - @Override - public void onFragment(byte[] fragment, boolean last) { - } - }).build()).get(); + @Override + public void onFragment(byte[] fragment, boolean last) { + } + }).build()).get(); - websocket.sendMessage("ECHO".getBytes()); + websocket.sendMessage("ECHO".getBytes()); - latch.await(); - assertEquals(text.get(), "ECHO".getBytes()); + latch.await(); + assertEquals(text.get(), "ECHO".getBytes()); + } finally { + c.close(); + } } @Test public void echoTwoMessagesTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(2); - final AtomicReference text = new AtomicReference(null); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(2); + final AtomicReference text = new AtomicReference(null); - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { - @Override - public void onOpen(WebSocket websocket) { - } + @Override + public void onOpen(WebSocket websocket) { + } - @Override - public void onClose(WebSocket websocket) { - latch.countDown(); - } + @Override + public void onClose(WebSocket websocket) { + latch.countDown(); + } - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } - @Override - public void onMessage(byte[] message) { - if (text.get() == null) { - text.set(message); - } else { - byte[] n = new byte[text.get().length + message.length]; - System.arraycopy(text.get(), 0, n, 0, text.get().length); - System.arraycopy(message, 0, n, text.get().length, message.length); - text.set(n); - } - latch.countDown(); + @Override + public void onMessage(byte[] message) { + if (text.get() == null) { + text.set(message); + } else { + byte[] n = new byte[text.get().length + message.length]; + System.arraycopy(text.get(), 0, n, 0, text.get().length); + System.arraycopy(message, 0, n, text.get().length, message.length); + text.set(n); } + latch.countDown(); + } - @Override - public void onFragment(byte[] fragment, boolean last) { - } - }).build()).get(); + @Override + public void onFragment(byte[] fragment, boolean last) { + } + }).build()).get(); - websocket.sendMessage("ECHO".getBytes()).sendMessage("ECHO".getBytes()); + websocket.sendMessage("ECHO".getBytes()).sendMessage("ECHO".getBytes()); - latch.await(); - assertEquals(text.get(), "ECHOECHO".getBytes()); + latch.await(); + assertEquals(text.get(), "ECHOECHO".getBytes()); + } finally { + c.close(); + } } @Test public void echoOnOpenMessagesTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); + AsyncHttpClient c = getAsyncHttpClient(null); + try { final CountDownLatch latch = new CountDownLatch(2); final AtomicReference text = new AtomicReference(null); - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { - - @Override - public void onOpen(WebSocket websocket) { - websocket.sendMessage("ECHO".getBytes()).sendMessage("ECHO".getBytes()); - } - - @Override - public void onClose(WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - - @Override - public void onMessage(byte[] message) { - if (text.get() == null) { - text.set(message); - } else { - byte[] n = new byte[text.get().length + message.length]; - System.arraycopy(text.get(), 0, n, 0, text.get().length); - System.arraycopy(message, 0, n, text.get().length, message.length); - text.set(n); - } - latch.countDown(); - } - - @Override - public void onFragment(byte[] fragment, boolean last) { - } - }).build()).get(); + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { + + @Override + public void onOpen(WebSocket websocket) { + websocket.sendMessage("ECHO".getBytes()).sendMessage("ECHO".getBytes()); + } + + @Override + public void onClose(WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + + @Override + public void onMessage(byte[] message) { + if (text.get() == null) { + text.set(message); + } else { + byte[] n = new byte[text.get().length + message.length]; + System.arraycopy(text.get(), 0, n, 0, text.get().length); + System.arraycopy(message, 0, n, text.get().length, message.length); + text.set(n); + } + latch.countDown(); + } + + @Override + public void onFragment(byte[] fragment, boolean last) { + } + }).build()).get(); latch.await(); assertEquals(text.get(), "ECHOECHO".getBytes()); + } finally { + c.close(); + } } - public void echoFragments() throws Exception { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(null); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { - - @Override - public void onOpen(WebSocket websocket) { - } - - @Override - public void onClose(WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - - @Override - public void onMessage(byte[] message) { - if (text.get() == null) { - text.set(message); - } else { - byte[] n = new byte[text.get().length + message.length]; - System.arraycopy(text.get(), 0, n, 0, text.get().length); - System.arraycopy(message, 0, n, text.get().length, message.length); - text.set(n); - } - latch.countDown(); - } - - @Override - public void onFragment(byte[] fragment, boolean last) { - } - }).build()).get(); - websocket.stream("ECHO".getBytes(), false); - websocket.stream("ECHO".getBytes(), true); - latch.await(); - assertEquals(text.get(), "ECHOECHO".getBytes()); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(null); + + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketByteListener() { + + @Override + public void onOpen(WebSocket websocket) { + } + + @Override + public void onClose(WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + + @Override + public void onMessage(byte[] message) { + if (text.get() == null) { + text.set(message); + } else { + byte[] n = new byte[text.get().length + message.length]; + System.arraycopy(text.get(), 0, n, 0, text.get().length); + System.arraycopy(message, 0, n, text.get().length, message.length); + text.set(n); + } + latch.countDown(); + } + + @Override + public void onFragment(byte[] fragment, boolean last) { + } + }).build()).get(); + websocket.stream("ECHO".getBytes(), false); + websocket.stream("ECHO".getBytes(), true); + latch.await(); + assertEquals(text.get(), "ECHOECHO".getBytes()); + } finally { + c.close(); + } } } diff --git a/src/test/java/com/ning/http/client/websocket/CloseCodeReasonMessageTest.java b/src/test/java/com/ning/http/client/websocket/CloseCodeReasonMessageTest.java index 9329973e03..d1ff9fa5ce 100644 --- a/src/test/java/com/ning/http/client/websocket/CloseCodeReasonMessageTest.java +++ b/src/test/java/com/ning/http/client/websocket/CloseCodeReasonMessageTest.java @@ -13,16 +13,11 @@ package com.ning.http.client.websocket; import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.websocket.TextMessageTest; import com.ning.http.client.websocket.WebSocket; import com.ning.http.client.websocket.WebSocketCloseCodeReasonListener; import com.ning.http.client.websocket.WebSocketListener; import com.ning.http.client.websocket.WebSocketUpgradeHandler; -import com.ning.http.client.websocket.netty.NettyTextMessageTest; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; @@ -35,36 +30,42 @@ public abstract class CloseCodeReasonMessageTest extends TextMessageTest { @Test(timeOut = 60000) public void onCloseWithCode() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new Listener(latch, text)).build()).get(); + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new Listener(latch, text)).build()).get(); - websocket.close(); + websocket.close(); - latch.await(); - assertTrue(text.get().startsWith("1000")); + latch.await(); + assertTrue(text.get().startsWith("1000")); + } finally { + c.close(); + } } @Test(timeOut = 60000) public void onCloseWithCodeServerClose() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new Listener(latch, text)).build()).get(); - - latch.await(); - final String[] parts = text.get().split(" "); - assertEquals(parts.length, 5); - assertEquals(parts[0], "1000-Idle"); - assertEquals(parts[1], "for"); - assertTrue(Integer.parseInt(parts[2].substring(0, parts[2].indexOf('m'))) > 10000); - assertEquals(parts[3], ">"); - assertEquals(parts[4], "10000ms"); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); + + c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new Listener(latch, text)).build()).get(); + + latch.await(); + final String[] parts = text.get().split(" "); + assertEquals(parts.length, 5); + assertEquals(parts[0], "1000-Idle"); + assertEquals(parts[1], "for"); + assertTrue(Integer.parseInt(parts[2].substring(0, parts[2].indexOf('m'))) > 10000); + assertEquals(parts[3], ">"); + assertEquals(parts[4], "10000ms"); + } finally { + c.close(); + } } public final static class Listener implements WebSocketListener, WebSocketCloseCodeReasonListener { @@ -77,11 +78,11 @@ public Listener(CountDownLatch latch, AtomicReference text) { this.text = text; } - //@Override + // @Override public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { } - //@Override + // @Override public void onClose(com.ning.http.client.websocket.WebSocket websocket) { } @@ -90,7 +91,7 @@ public void onClose(WebSocket websocket, int code, String reason) { latch.countDown(); } - //@Override + // @Override public void onError(Throwable t) { t.printStackTrace(); latch.countDown(); diff --git a/src/test/java/com/ning/http/client/websocket/RedirectTest.java b/src/test/java/com/ning/http/client/websocket/RedirectTest.java index 25febcf53f..215cfce306 100644 --- a/src/test/java/com/ning/http/client/websocket/RedirectTest.java +++ b/src/test/java/com/ning/http/client/websocket/RedirectTest.java @@ -13,26 +13,25 @@ package com.ning.http.client.websocket; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; -import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import static org.testng.Assert.assertEquals; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClientConfig; public abstract class RedirectTest extends AbstractBasicTest { @@ -50,9 +49,6 @@ public void setUpGlobal() throws Exception { addConnector(_connector); - - - port2 = findFreePort(); final SelectChannelConnector connector2 = new SelectChannelConnector(); connector2.setPort(port2); @@ -60,13 +56,13 @@ public void setUpGlobal() throws Exception { WebSocketHandler _wsHandler = getWebSocketHandler(); HandlerList list = new HandlerList(); list.addHandler(new AbstractHandler() { - @Override - public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { - if (request.getLocalPort() == port2) { - httpServletResponse.sendRedirect(getTargetUrl()); - } - } - }); + @Override + public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { + if (request.getLocalPort() == port2) { + httpServletResponse.sendRedirect(getTargetUrl()); + } + } + }); list.addHandler(_wsHandler); setHandler(list); @@ -89,39 +85,39 @@ public org.eclipse.jetty.websocket.WebSocket doWebSocketConnect(HttpServletReque @Test(timeOut = 60000) public void testRedirectToWSResource() throws Exception { AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setFollowRedirects(true).build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getRedirectURL()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - text.set("OnOpen"); - latch.countDown(); - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - - latch.await(); - assertEquals(text.get(), "OnOpen"); - websocket.close(); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); + + WebSocket websocket = c.prepareGet(getRedirectURL()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + text.set("OnOpen"); + latch.countDown(); + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + latch.await(); + assertEquals(text.get(), "OnOpen"); + websocket.close(); + } finally { + c.close(); + } } - // --------------------------------------------------------- Private Methods - private String getRedirectURL() { return String.format("ws://127.0.0.1:%d/", port2); } diff --git a/src/test/java/com/ning/http/client/websocket/TextMessageTest.java b/src/test/java/com/ning/http/client/websocket/TextMessageTest.java index 2b1b154770..219502d387 100644 --- a/src/test/java/com/ning/http/client/websocket/TextMessageTest.java +++ b/src/test/java/com/ning/http/client/websocket/TextMessageTest.java @@ -13,7 +13,6 @@ package com.ning.http.client.websocket; import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; import org.testng.annotations.Test; import javax.servlet.http.HttpServletRequest; @@ -66,321 +65,339 @@ public org.eclipse.jetty.websocket.WebSocket doWebSocketConnect(HttpServletReque }; } - - @Test(timeOut = 60000) public void onOpen() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - text.set("OnOpen"); - latch.countDown(); - } + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - } + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + text.set("OnOpen"); + latch.countDown(); + } - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + } + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); - latch.await(); - assertEquals(text.get(), "OnOpen"); + latch.await(); + assertEquals(text.get(), "OnOpen"); + } finally { + c.close(); + } } @Test(timeOut = 60000) public void onEmptyListenerTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - - WebSocket websocket = null; + AsyncHttpClient c = getAsyncHttpClient(null); try { - websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().build()).get(); - } catch (Throwable t) { - fail(); + WebSocket websocket = null; + try { + websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().build()).get(); + } catch (Throwable t) { + fail(); + } + assertTrue(websocket != null); + } finally { + c.close(); } - assertTrue(websocket != null); } @Test(timeOut = 60000) public void onFailureTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = null; - Throwable t = null; + AsyncHttpClient c = getAsyncHttpClient(null); try { - websocket = c.prepareGet("ws://abcdefg") - .execute(new WebSocketUpgradeHandler.Builder().build()).get(); - } catch (Throwable t2) { - t = t2; + Throwable t = null; + try { + c.prepareGet("ws://abcdefg").execute(new WebSocketUpgradeHandler.Builder().build()).get(); + } catch (Throwable t2) { + t = t2; + } + assertTrue(t != null); + } finally { + c.close(); } - assertTrue(t != null); } @Test(timeOut = 60000) public void onTimeoutCloseTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - text.set("OnClose"); - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - latch.await(); - assertEquals(text.get(), "OnClose"); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); + + c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + text.set("OnClose"); + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + latch.await(); + assertEquals(text.get(), "OnClose"); + } finally { + c.close(); + } } @Test(timeOut = 60000) public void onClose() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - text.set("OnClose"); - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - websocket.close(); - - latch.await(); - assertEquals(text.get(), "OnClose"); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); + + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() { + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + text.set("OnClose"); + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + websocket.close(); + + latch.await(); + assertEquals(text.get(), "OnClose"); + } finally { + c.close(); + } } @Test(timeOut = 60000) public void echoText() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { - - @Override - public void onMessage(String message) { - text.set(message); - latch.countDown(); - } - - @Override - public void onFragment(String fragment, boolean last) { - } - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - websocket.sendTextMessage("ECHO"); - - latch.await(); - assertEquals(text.get(), "ECHO"); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); + + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { + + @Override + public void onMessage(String message) { + text.set(message); + latch.countDown(); + } + + @Override + public void onFragment(String fragment, boolean last) { + } + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + websocket.sendTextMessage("ECHO"); + + latch.await(); + assertEquals(text.get(), "ECHO"); + } finally { + c.close(); + } } @Test(timeOut = 60000) public void echoDoubleListenerText() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(2); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { - - @Override - public void onMessage(String message) { - text.set(message); - latch.countDown(); - } - - @Override - public void onFragment(String fragment, boolean last) { - } - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).addWebSocketListener(new WebSocketTextListener() { - - @Override - public void onMessage(String message) { - text.set(text.get() + message); - latch.countDown(); - } - - @Override - public void onFragment(String fragment, boolean last) { - } - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - websocket.sendTextMessage("ECHO"); - - latch.await(); - assertEquals(text.get(), "ECHOECHO"); + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(2); + final AtomicReference text = new AtomicReference(""); + + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { + + @Override + public void onMessage(String message) { + text.set(message); + latch.countDown(); + } + + @Override + public void onFragment(String fragment, boolean last) { + } + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).addWebSocketListener(new WebSocketTextListener() { + + @Override + public void onMessage(String message) { + text.set(text.get() + message); + latch.countDown(); + } + + @Override + public void onFragment(String fragment, boolean last) { + } + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + websocket.sendTextMessage("ECHO"); + + latch.await(); + assertEquals(text.get(), "ECHOECHO"); + } finally { + c.close(); + } } @Test public void echoTwoMessagesTest() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(2); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { - - @Override - public void onMessage(String message) { - text.set(text.get() + message); - latch.countDown(); - } - - @Override - public void onFragment(String fragment, boolean last) { - } - - boolean t = false; - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - websocket.sendTextMessage("ECHO").sendTextMessage("ECHO"); - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - latch.await(); - assertEquals(text.get(), "ECHOECHO"); - } + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(2); + final AtomicReference text = new AtomicReference(""); + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { - public void echoFragments() throws Throwable { - AsyncHttpClient c = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().build()); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference text = new AtomicReference(""); - - WebSocket websocket = c.prepareGet(getTargetUrl()) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { - - @Override - public void onMessage(String message) { - text.set(message); - latch.countDown(); - } - - @Override - public void onFragment(String fragment, boolean last) { - } - - @Override - public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { - } - - @Override - public void onClose(com.ning.http.client.websocket.WebSocket websocket) { - latch.countDown(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - latch.countDown(); - } - }).build()).get(); - - websocket.streamText("ECHO", false); - websocket.streamText("ECHO", true); - - latch.await(); - assertEquals(text.get(), "ECHOECHO"); + @Override + public void onMessage(String message) { + text.set(text.get() + message); + latch.countDown(); + } + + @Override + public void onFragment(String fragment, boolean last) { + } + + boolean t = false; + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + websocket.sendTextMessage("ECHO").sendTextMessage("ECHO"); + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + latch.await(); + assertEquals(text.get(), "ECHOECHO"); + } finally { + c.close(); + } } + public void echoFragments() throws Throwable { + AsyncHttpClient c = getAsyncHttpClient(null); + try { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference text = new AtomicReference(""); + + WebSocket websocket = c.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { + + @Override + public void onMessage(String message) { + text.set(message); + latch.countDown(); + } + + @Override + public void onFragment(String fragment, boolean last) { + } + + @Override + public void onOpen(com.ning.http.client.websocket.WebSocket websocket) { + } + + @Override + public void onClose(com.ning.http.client.websocket.WebSocket websocket) { + latch.countDown(); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + latch.countDown(); + } + }).build()).get(); + + websocket.streamText("ECHO", false); + websocket.streamText("ECHO", true); + + latch.await(); + assertEquals(text.get(), "ECHOECHO"); + } finally { + c.close(); + } + } } diff --git a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyByteMessageTest.java b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyByteMessageTest.java index 5497ba0e6e..37b1429e3e 100644 --- a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyByteMessageTest.java +++ b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyByteMessageTest.java @@ -12,20 +12,17 @@ */ package com.ning.http.client.websocket.grizzly; +import org.testng.annotations.Test; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ProviderUtil; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; import com.ning.http.client.websocket.ByteMessageTest; -import org.testng.annotations.Test; public class GrizzlyByteMessageTest extends ByteMessageTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Test(timeOut = 60000) diff --git a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyCloseCodeReasonMsgTest.java b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyCloseCodeReasonMsgTest.java index c767e55080..941de2bfbc 100644 --- a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyCloseCodeReasonMsgTest.java +++ b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyCloseCodeReasonMsgTest.java @@ -13,25 +13,23 @@ package com.ning.http.client.websocket.grizzly; +import org.testng.annotations.Test; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.websocket.CloseCodeReasonMessageTest; -import org.testng.annotations.Test; public class GrizzlyCloseCodeReasonMsgTest extends CloseCodeReasonMessageTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Override @Test public void onCloseWithCode() throws Throwable { - super.onCloseWithCode(); //To change body of overridden methods use File | Settings | File Templates. + super.onCloseWithCode(); // To change body of overridden methods use File | Settings | File Templates. } } diff --git a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyRedirectTest.java b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyRedirectTest.java index 2cdda3be98..15cd220c8b 100644 --- a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyRedirectTest.java +++ b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyRedirectTest.java @@ -15,16 +15,13 @@ import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; +import com.ning.http.client.async.ProviderUtil; import com.ning.http.client.websocket.RedirectTest; public class GrizzlyRedirectTest extends RedirectTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } } diff --git a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyTextMessageTest.java b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyTextMessageTest.java index bef60cb991..7499f39c31 100644 --- a/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyTextMessageTest.java +++ b/src/test/java/com/ning/http/client/websocket/grizzly/GrizzlyTextMessageTest.java @@ -12,25 +12,22 @@ */ package com.ning.http.client.websocket.grizzly; +import org.testng.annotations.Test; + import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.async.ProviderUtil; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; import com.ning.http.client.websocket.ByteMessageTest; -import org.testng.annotations.Test; public class GrizzlyTextMessageTest extends ByteMessageTest { @Override public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) { - if (config == null) { - config = new AsyncHttpClientConfig.Builder().build(); - } - return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); + return ProviderUtil.grizzlyProvider(config); } @Test(timeOut = 60000) @Override public void echoFragments() throws Exception { - super.echoFragments(); //To change body of overridden methods use File | Settings | File Templates. + super.echoFragments(); // To change body of overridden methods use File | Settings | File Templates. } } diff --git a/src/test/java/com/ning/http/util/AsyncHttpProviderUtilsTest.java b/src/test/java/com/ning/http/util/AsyncHttpProviderUtilsTest.java new file mode 100644 index 0000000000..7a1bdd26c5 --- /dev/null +++ b/src/test/java/com/ning/http/util/AsyncHttpProviderUtilsTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.http.util; + +import java.net.URI; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class AsyncHttpProviderUtilsTest { + + @Test(groups = "fast") + public void getRedirectUriShouldHandleProperlyEncodedLocation() { + + String url = "http://www.ebay.de/sch/sis.html;jsessionid=92D73F80262E3EBED7E115ED01035DDA?_nkw=FSC%20Lifebook%20E8310%20Core2Duo%20T8100%202%201GHz%204GB%20DVD%20RW&_itemId=150731406505"; + URI uri = AsyncHttpProviderUtils.getRedirectUri(URI.create("http://www.ebay.de/"), url); + Assert.assertEquals("http://www.ebay.de/sch/sis.html;jsessionid=92D73F80262E3EBED7E115ED01035DDA?_nkw=FSC%20Lifebook%20E8310%20Core2Duo%20T8100%202%201GHz%204GB%20DVD%20RW&_itemId=150731406505", uri.toString()); + } + + @Test(groups = "fast") + public void getRedirectUriShouldHandleRawQueryParamsLocation() { + + String url = "http://www.ebay.de/sch/sis.html;jsessionid=92D73F80262E3EBED7E115ED01035DDA?_nkw=FSC%20Lifebook%20E8310%20Core2Duo%20T8100%202%201GHz%204GB%20DVD%20RW&_itemId=150731406505"; + URI uri = AsyncHttpProviderUtils.getRedirectUri(URI.create("http://www.ebay.de/"), url); + Assert.assertEquals("http://www.ebay.de/sch/sis.html;jsessionid=92D73F80262E3EBED7E115ED01035DDA?_nkw=FSC%20Lifebook%20E8310%20Core2Duo%20T8100%202%201GHz%204GB%20DVD%20RW&_itemId=150731406505", uri.toString()); + } + + @Test(groups = "fast") + public void getRedirectUriShouldHandleRelativeLocation() { + + String url = "/sch/sis.html;jsessionid=92D73F80262E3EBED7E115ED01035DDA?_nkw=FSC Lifebook E8310 Core2Duo T8100 2 1GHz 4GB DVD RW&_itemId=150731406505"; + URI uri = AsyncHttpProviderUtils.getRedirectUri(URI.create("http://www.ebay.de/"), url); + Assert.assertEquals("http://www.ebay.de/sch/sis.html;jsessionid=92D73F80262E3EBED7E115ED01035DDA?_nkw=FSC%20Lifebook%20E8310%20Core2Duo%20T8100%202%201GHz%204GB%20DVD%20RW&_itemId=150731406505", uri.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/ning/http/util/TestUTF8UrlCodec.java b/src/test/java/com/ning/http/util/TestUTF8UrlCodec.java index e675a1a611..a0cd0f3c34 100644 --- a/src/test/java/com/ning/http/util/TestUTF8UrlCodec.java +++ b/src/test/java/com/ning/http/util/TestUTF8UrlCodec.java @@ -27,4 +27,15 @@ public void testBasics() Assert.assertEquals(UTF8UrlEncoder.encode("a&b"), "a%26b"); Assert.assertEquals(UTF8UrlEncoder.encode("a+b"), "a%2Bb"); } + + @Test(groups="fast") + public void testNonBmp() + { + // Plane 1 + Assert.assertEquals(UTF8UrlEncoder.encode("\uD83D\uDCA9"), "%F0%9F%92%A9"); + // Plane 2 + Assert.assertEquals(UTF8UrlEncoder.encode("\ud84c\uddc8 \ud84f\udfef"), "%F0%A3%87%88%20%F0%A3%BF%AF"); + // Plane 15 + Assert.assertEquals(UTF8UrlEncoder.encode("\udb80\udc01"), "%F3%B0%80%81"); + } } diff --git a/src/test/java/com/ning/org/jboss/netty/handler/codec/http/CookieDecoderTest.java b/src/test/java/com/ning/org/jboss/netty/handler/codec/http/CookieDecoderTest.java new file mode 100644 index 0000000000..5d2f9cb09b --- /dev/null +++ b/src/test/java/com/ning/org/jboss/netty/handler/codec/http/CookieDecoderTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.ning.org.jboss.netty.handler.codec.http; + +import java.util.Set; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.ning.http.client.Cookie; + +public class CookieDecoderTest { + + @Test(groups = "fast") + public void testDecodeUnquoted() { + Set cookies = CookieDecoder.decode("foo=value; domain=/; path=/"); + Assert.assertEquals(cookies.size(), 1); + + Cookie first = cookies.iterator().next(); + Assert.assertEquals(first.getValue(), "value"); + Assert.assertEquals(first.getDomain(), "/"); + Assert.assertEquals(first.getPath(), "/"); + } + + @Test(groups = "fast") + public void testDecodeQuoted() { + Set cookies = CookieDecoder.decode("ALPHA=\"VALUE1\"; Domain=docs.foo.com; Path=/accounts; Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; HttpOnly"); + Assert.assertEquals(cookies.size(), 1); + + Cookie first = cookies.iterator().next(); + Assert.assertEquals(first.getValue(), "VALUE1"); + } + + @Test(groups = "fast") + public void testDecodeQuotedContainingEscapedQuote() { + Set cookies = CookieDecoder.decode("ALPHA=\"VALUE1\\\"\"; Domain=docs.foo.com; Path=/accounts; Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; HttpOnly"); + Assert.assertEquals(cookies.size(), 1); + + Cookie first = cookies.iterator().next(); + Assert.assertEquals(first.getValue(), "VALUE1\""); + } +}