Skip to content

Commit e98bf06

Browse files
committed
Port changes for AsyncHttpClient#244 from master to 1.7.
1 parent c541ad0 commit e98bf06

File tree

2 files changed

+148
-5
lines changed

2 files changed

+148
-5
lines changed

src/main/java/com/ning/http/client/providers/grizzly/GrizzlyResponseFuture.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
public class GrizzlyResponseFuture<V> extends AbstractListenableFuture<V> {
3939

4040
private final AtomicBoolean done = new AtomicBoolean(false);
41+
private final AtomicBoolean cancelled = new AtomicBoolean(false);
4142
private final AsyncHandler handler;
4243
private final GrizzlyAsyncHttpProvider provider;
4344
private final Request request;
@@ -66,17 +67,28 @@ public class GrizzlyResponseFuture<V> extends AbstractListenableFuture<V> {
6667

6768
public void done(Callable callable) {
6869

69-
done.compareAndSet(false, true);
70-
super.done();
70+
if (!done.compareAndSet(false, true) || cancelled.get()) {
71+
return;
72+
}
73+
done();
74+
7175

7276
}
7377

7478

7579
public void abort(Throwable t) {
7680

81+
if (done.get() || !cancelled.compareAndSet(false, true)) {
82+
return;
83+
}
84+
7785
delegate.failure(t);
7886
if (handler != null) {
79-
handler.onThrowable(t);
87+
try {
88+
handler.onThrowable(t);
89+
} catch (Throwable ignore) {
90+
}
91+
8092
}
8193
closeConnection();
8294
done();
@@ -121,7 +133,15 @@ public boolean getAndSetWriteBody(boolean writeBody) {
121133

122134
public boolean cancel(boolean mayInterruptIfRunning) {
123135

124-
handler.onThrowable(new CancellationException());
136+
if (done.get() || !cancelled.compareAndSet(false, true)) {
137+
return false;
138+
}
139+
if (handler != null) {
140+
try {
141+
handler.onThrowable(new CancellationException());
142+
} catch (Throwable ignore) {
143+
}
144+
}
125145
done();
126146
return delegate.cancel(mayInterruptIfRunning);
127147

@@ -182,7 +202,7 @@ void setDelegate(final FutureImpl<V> delegate) {
182202

183203
private void closeConnection() {
184204

185-
if (connection != null && !connection.isOpen()) {
205+
if (connection != null && connection.isOpen()) {
186206
connection.close().markForRecycle(true);
187207
}
188208

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2012-2013 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
package com.ning.http.client.async.grizzly;
15+
16+
import com.ning.http.client.AsyncCompletionHandler;
17+
import com.ning.http.client.AsyncHttpClient;
18+
import com.ning.http.client.AsyncHttpClientConfig;
19+
import com.ning.http.client.Response;
20+
import com.ning.http.client.async.AbstractBasicTest;
21+
import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider;
22+
import org.eclipse.jetty.continuation.Continuation;
23+
import org.eclipse.jetty.continuation.ContinuationSupport;
24+
import org.eclipse.jetty.server.Request;
25+
import org.eclipse.jetty.server.handler.AbstractHandler;
26+
import org.testng.annotations.Test;
27+
28+
import javax.servlet.ServletException;
29+
import javax.servlet.http.HttpServletRequest;
30+
import javax.servlet.http.HttpServletResponse;
31+
import java.io.IOException;
32+
import java.util.concurrent.ExecutionException;
33+
import java.util.concurrent.Future;
34+
import java.util.concurrent.TimeoutException;
35+
import java.util.concurrent.atomic.AtomicInteger;
36+
37+
import static org.testng.Assert.assertEquals;
38+
import static org.testng.Assert.assertFalse;
39+
import static org.testng.Assert.assertNull;
40+
import static org.testng.Assert.fail;
41+
42+
public class GrizzlyUnexpectingTimeoutTest extends AbstractBasicTest {
43+
44+
private static final String MSG = "Unauthorized without WWW-Authenticate header";
45+
46+
protected String getExpectedTimeoutMessage() {
47+
return "401 response received, but no WWW-Authenticate header was present";
48+
}
49+
50+
@Override
51+
public AsyncHttpClient getAsyncHttpClient(AsyncHttpClientConfig config) {
52+
if (config == null) {
53+
config = new AsyncHttpClientConfig.Builder().build();
54+
}
55+
return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config);
56+
}
57+
58+
@Override
59+
public AbstractHandler configureHandler() throws Exception {
60+
return new ExpectExceptionHandler();
61+
}
62+
63+
private class ExpectExceptionHandler extends AbstractHandler {
64+
public void handle(String target, Request baseRequest, HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException {
65+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
66+
final Continuation continuation = ContinuationSupport.getContinuation(request);
67+
continuation.suspend();
68+
new Thread(new Runnable() {
69+
public void run() {
70+
try {
71+
response.getOutputStream().print(MSG);
72+
response.getOutputStream().flush();
73+
} catch (IOException e) {
74+
log.error(e.getMessage(), e);
75+
}
76+
}
77+
}).start();
78+
baseRequest.setHandled(true);
79+
}
80+
}
81+
82+
@Test(groups = {"standalone", "default_provider"})
83+
public void unexpectedTimeoutTest() throws IOException {
84+
final AtomicInteger counts = new AtomicInteger();
85+
final int timeout = 100;
86+
87+
final AsyncHttpClient client = getAsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(timeout).build());
88+
Future<Response> responseFuture =
89+
client.prepareGet(getTargetUrl()).execute(new AsyncCompletionHandler<Response>() {
90+
@Override
91+
public Response onCompleted(Response response) throws Exception {
92+
counts.incrementAndGet();
93+
return response;
94+
}
95+
96+
@Override
97+
public void onThrowable(Throwable t) {
98+
counts.incrementAndGet();
99+
super.onThrowable(t);
100+
}
101+
});
102+
// currently, an exception is expected
103+
// because the grizzly provider would throw IllegalStateException if WWW-Authenticate header doesn't exist with 401 response status.
104+
try {
105+
Response response = responseFuture.get();
106+
assertNull(response);
107+
} catch (InterruptedException e) {
108+
fail("Interrupted.", e);
109+
} catch (ExecutionException e) {
110+
assertFalse(e.getCause() instanceof TimeoutException);
111+
assertEquals(e.getCause().getMessage(), getExpectedTimeoutMessage());
112+
}
113+
// wait for timeout again.
114+
try {
115+
Thread.sleep(timeout*2);
116+
} catch (InterruptedException e) {
117+
fail("Interrupted.", e);
118+
}
119+
// the result should be either onCompleted or onThrowable.
120+
assertEquals(1, counts.get(), "result should be one");
121+
client.close();
122+
}
123+
}

0 commit comments

Comments
 (0)