/*
* Copyright 2010 Ning, Inc.
*
* This program is licensed 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 org.asynchttpclient;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.asynchttpclient.Dsl.config;
import static org.asynchttpclient.test.TestUtils.*;
import static org.asynchttpclient.util.ThrowableUtil.unknownStackTrace;
import static org.testng.Assert.*;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.asynchttpclient.test.TestUtils.AsyncHandlerAdapter;
import org.asynchttpclient.testserver.HttpServer;
import org.asynchttpclient.testserver.HttpTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class AsyncStreamHandlerTest extends HttpTest {
private static final String RESPONSE = "param_1_";
private static HttpServer server;
@BeforeClass
public static void start() throws Throwable {
server = new HttpServer();
server.start();
}
@AfterClass
public static void stop() throws Throwable {
server.close();
}
private static String getTargetUrl() {
return server.getHttpUrl() + "/foo/bar";
}
@Test
public void getWithOnHeadersReceivedAbort() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
client.prepareGet(getTargetUrl()).execute(new AsyncHandlerAdapter() {
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
assertContentTypesEquals(headers.get(CONTENT_TYPE), TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET);
return State.ABORT;
}
}).get(5, TimeUnit.SECONDS);
});
});
}
@Test
public void asyncStreamPOSTTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
String responseBody = client.preparePost(getTargetUrl())//
.setHeader(CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED)//
.addFormParam("param_1", "value_1")//
.execute(new AsyncHandlerAdapter() {
private StringBuilder builder = new StringBuilder();
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
assertContentTypesEquals(headers.get(CONTENT_TYPE), TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET);
return State.CONTINUE;
}
@Override
public State onBodyPartReceived(HttpResponseBodyPart content) throws Exception {
builder.append(new String(content.getBodyPartBytes(), US_ASCII));
return State.CONTINUE;
}
@Override
public String onCompleted() throws Exception {
return builder.toString().trim();
}
}).get(10, TimeUnit.SECONDS);
assertEquals(responseBody, RESPONSE);
});
});
}
@Test
public void asyncStreamInterruptTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
final AtomicBoolean onHeadersReceived = new AtomicBoolean();
final AtomicBoolean onBodyPartReceived = new AtomicBoolean();
final AtomicBoolean onThrowable = new AtomicBoolean();
client.preparePost(getTargetUrl())//
.setHeader(CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED)//
.addFormParam("param_1", "value_1")//
.execute(new AsyncHandlerAdapter() {
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
onHeadersReceived.set(true);
assertContentTypesEquals(headers.get(CONTENT_TYPE), TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET);
return State.ABORT;
}
@Override
public State onBodyPartReceived(final HttpResponseBodyPart content) throws Exception {
onBodyPartReceived.set(true);
return State.ABORT;
}
@Override
public void onThrowable(Throwable t) {
onThrowable.set(true);
}
}).get(5, TimeUnit.SECONDS);
assertTrue(onHeadersReceived.get(), "Headers weren't received");
assertFalse(onBodyPartReceived.get(), "Abort not working");
assertFalse(onThrowable.get(), "Shouldn't get an exception");
});
});
}
@Test
public void asyncStreamFutureTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
final AtomicBoolean onHeadersReceived = new AtomicBoolean();
final AtomicBoolean onThrowable = new AtomicBoolean();
String responseBody = client.preparePost(getTargetUrl())//
.addFormParam("param_1", "value_1")//
.execute(new AsyncHandlerAdapter() {
private StringBuilder builder = new StringBuilder();
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
assertContentTypesEquals(headers.get(CONTENT_TYPE), TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET);
onHeadersReceived.set(true);
return State.CONTINUE;
}
@Override
public State onBodyPartReceived(HttpResponseBodyPart content) throws Exception {
builder.append(new String(content.getBodyPartBytes()));
return State.CONTINUE;
}
@Override
public String onCompleted() throws Exception {
return builder.toString().trim();
}
@Override
public void onThrowable(Throwable t) {
onThrowable.set(true);
}
}).get(5, TimeUnit.SECONDS);
assertTrue(onHeadersReceived.get(), "Headers weren't received");
assertFalse(onThrowable.get(), "Shouldn't get an exception");
assertEquals(responseBody, RESPONSE, "Unexpected response body");
});
});
}
@Test
public void asyncStreamThrowableRefusedTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
final CountDownLatch l = new CountDownLatch(1);
client.prepareGet(getTargetUrl()).execute(new AsyncHandlerAdapter() {
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
throw unknownStackTrace(new RuntimeException("FOO"), AsyncStreamHandlerTest.class, "asyncStreamThrowableRefusedTest");
}
@Override
public void onThrowable(Throwable t) {
try {
if (t.getMessage() != null) {
assertEquals(t.getMessage(), "FOO");
}
} finally {
l.countDown();
}
}
});
if (!l.await(10, TimeUnit.SECONDS)) {
fail("Timed out");
}
});
});
}
@Test
public void asyncStreamReusePOSTTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
final AtomicReference<HttpHeaders> responseHeaders = new AtomicReference<>();
BoundRequestBuilder rb = client.preparePost(getTargetUrl())//
.setHeader(CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED)//
.addFormParam("param_1", "value_1");
Future<String> f = rb.execute(new AsyncHandlerAdapter() {
private StringBuilder builder = new StringBuilder();
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
responseHeaders.set(headers);
return State.CONTINUE;
}
@Override
public State onBodyPartReceived(HttpResponseBodyPart content) throws Exception {
builder.append(new String(content.getBodyPartBytes()));
return State.CONTINUE;
}
@Override
public String onCompleted() throws Exception {
return builder.toString();
}
});
String r = f.get(5, TimeUnit.SECONDS);
HttpHeaders h = responseHeaders.get();
assertNotNull(h, "Should receive non null headers");
assertContentTypesEquals(h.get(CONTENT_TYPE), TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET);
assertNotNull(r, "No response body");
assertEquals(r.trim(), RESPONSE, "Unexpected response body");
responseHeaders.set(null);
server.enqueueEcho();
// Let do the same again
f = rb.execute(new AsyncHandlerAdapter() {
private StringBuilder builder = new StringBuilder();
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
responseHeaders.set(headers);
return State.CONTINUE;
}
@Override
public State onBodyPartReceived(HttpResponseBodyPart content) throws Exception {
builder.append(new String(content.getBodyPartBytes()));
return State.CONTINUE;
}
@Override
public String onCompleted() throws Exception {
return builder.toString();
}
});
f.get(5, TimeUnit.SECONDS);
h = responseHeaders.get();
assertNotNull(h, "Should receive non null headers");
assertContentTypesEquals(h.get(CONTENT_TYPE), TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET);
assertNotNull(r, "No response body");
assertEquals(r.trim(), RESPONSE, "Unexpected response body");
});
});
}
@Test
public void asyncStream302RedirectWithBody() throws Throwable {
withClient(config().setFollowRedirect(true)).run(client -> {
withServer(server).run(server -> {
String originalUrl = server.getHttpUrl() + "/original";
String redirectUrl = server.getHttpUrl() + "/redirect";
server.enqueueResponse(response -> {
response.setStatus(302);
response.setHeader(LOCATION.toString(), redirectUrl);
response.getOutputStream().println("You are being asked to redirect to " + redirectUrl);
});
server.enqueueOk();
Response response = client.prepareGet(originalUrl).execute().get(20, TimeUnit.SECONDS);
assertEquals(response.getStatusCode(), 200);
assertTrue(response.getResponseBody().isEmpty());
});
});
}
@Test(timeOut = 3000)
public void asyncStreamJustStatusLine() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
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<Integer> statusCode = client.prepareGet(getTargetUrl()).execute(new AsyncHandler<Integer>() {
private int status = -1;
@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 onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
whatCalled[STATUS] = true;
status = responseStatus.getStatusCode();
latch.countDown();
return State.ABORT;
}
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
whatCalled[OTHER] = true;
latch.countDown();
return State.ABORT;
}
@Override
public Integer onCompleted() throws Exception {
whatCalled[COMPLETED] = true;
latch.countDown();
return status;
}
});
if (!latch.await(2, TimeUnit.SECONDS)) {
fail("Timeout");
return;
}
Integer status = statusCode.get(TIMEOUT, TimeUnit.SECONDS);
assertEquals((int) status, 200, "Expected status code failed.");
if (!whatCalled[STATUS]) {
fail("onStatusReceived not called.");
}
if (!whatCalled[COMPLETED]) {
fail("onCompleted not called.");
}
if (whatCalled[OTHER]) {
fail("Other method of AsyncHandler got called.");
}
});
});
}
@Test(groups = "online")
public void asyncOptionsTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
final AtomicReference<HttpHeaders> responseHeaders = new AtomicReference<>();
final String[] expected = { "GET", "HEAD", "OPTIONS", "POST" };
Future<String> f = client.prepareOptions("http://www.apache.org/").execute(new AsyncHandlerAdapter() {
@Override
public State onHeadersReceived(HttpHeaders headers) throws Exception {
responseHeaders.set(headers);
return State.ABORT;
}
@Override
public String onCompleted() throws Exception {
return "OK";
}
});
f.get(20, TimeUnit.SECONDS);
HttpHeaders h = responseHeaders.get();
assertNotNull(h);
String[] values = h.get(ALLOW).split(",|, ");
assertNotNull(values);
assertEquals(values.length, expected.length);
Arrays.sort(values);
assertEquals(values, expected);
});
});
}
@Test
public void closeConnectionTest() throws Throwable {
withClient().run(client -> {
withServer(server).run(server -> {
server.enqueueEcho();
Response r = client.prepareGet(getTargetUrl()).execute(new AsyncHandler<Response>() {
private Response.ResponseBuilder builder = new Response.ResponseBuilder();
public State onHeadersReceived(HttpHeaders headers) throws Exception {
builder.accumulate(headers);
return State.CONTINUE;
}
public void onThrowable(Throwable t) {
}
public State onBodyPartReceived(HttpResponseBodyPart content) throws Exception {
builder.accumulate(content);
return content.isLast() ? State.ABORT : State.CONTINUE;
}
public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
builder.accumulate(responseStatus);
return State.CONTINUE;
}
public Response onCompleted() throws Exception {
return builder.build();
}
}).get();
assertNotNull(r);
assertEquals(r.getStatusCode(), 200);
});
});
}
}