/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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 io.undertow.server.handlers;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import io.undertow.io.IoCallback;
import io.undertow.io.Sender;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.testutils.DefaultServer;
import io.undertow.testutils.HttpClientUtils;
import io.undertow.testutils.TestHttpClient;
import io.undertow.util.Headers;
import io.undertow.util.StatusCodes;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* @author Stuart Douglas
*/
@RunWith(DefaultServer.class)
public class SenderTestCase {
public static final int SENDS = 10000;
public static final int TXS = 1000;
public static final String HELLO_WORLD = "Hello World";
@BeforeClass
public static void setup() {
HttpHandler lotsOfSendsHandler = new HttpHandler() {
@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
boolean blocking = exchange.getQueryParameters().get("blocking").getFirst().equals("true");
if (blocking) {
if (exchange.isInIoThread()) {
exchange.startBlocking();
exchange.dispatch(this);
return;
}
}
final Sender sender = exchange.getResponseSender();
class SendClass implements Runnable, IoCallback {
int sent = 0;
@Override
public void run() {
sent++;
sender.send("a", this);
}
@Override
public void onComplete(final HttpServerExchange exchange, final Sender sender) {
if (sent++ == SENDS) {
sender.close();
return;
}
sender.send("a", this);
}
@Override
public void onException(final HttpServerExchange exchange, final Sender sender, final IOException exception) {
exception.printStackTrace();
exchange.endExchange();
}
}
new SendClass().run();
}
};
HttpHandler lotsOfTransferHandler = new HttpHandler() {
@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
boolean blocking = exchange.getQueryParameters().get("blocking").getFirst().equals("true");
if (blocking) {
if (exchange.isInIoThread()) {
exchange.startBlocking();
exchange.dispatch(this);
return;
}
}
URI uri = SenderTestCase.class.getResource(SenderTestCase.class.getSimpleName() + ".class").toURI();
Path file = Paths.get(uri);
final FileChannel channel = FileChannel.open(file, StandardOpenOption.READ);
exchange.setResponseContentLength(channel.size() * TXS);
final Sender sender = exchange.getResponseSender();
class SendClass implements Runnable, IoCallback {
int sent = 0;
@Override
public void run() {
sent++;
try {
channel.position(0);
} catch (IOException e) {
}
sender.transferFrom(channel, this);
}
@Override
public void onComplete(final HttpServerExchange exchange, final Sender sender) {
if (sent++ == TXS) {
sender.close();
return;
}
try {
channel.position(0);
} catch (IOException e) {
}
sender.transferFrom(channel, this);
}
@Override
public void onException(final HttpServerExchange exchange, final Sender sender, final IOException exception) {
exception.printStackTrace();
exchange.endExchange();
}
}
new SendClass().run();
}
};
final HttpHandler fixedLengthSender = new HttpHandler() {
@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send(HELLO_WORLD);
}
};
PathHandler handler = new PathHandler().addPrefixPath("/lots", lotsOfSendsHandler)
.addPrefixPath("/fixed", fixedLengthSender)
.addPrefixPath("/transfer", lotsOfTransferHandler);
DefaultServer.setRootHandler(handler);
}
@Test
public void testAsyncSender() throws IOException {
StringBuilder sb = new StringBuilder(SENDS);
for (int i = 0; i < SENDS; ++i) {
sb.append("a");
}
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/lots?blocking=false");
TestHttpClient client = new TestHttpClient();
try {
HttpResponse result = client.execute(get);
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
Assert.assertEquals(sb.toString(), HttpClientUtils.readResponse(result));
} finally {
client.getConnectionManager().shutdown();
}
}
@Test
public void testAsyncTransfer() throws Exception {
StringBuilder sb = new StringBuilder(TXS);
for (int i = 0; i < TXS; ++i) {
sb.append("a");
}
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/transfer?blocking=false");
TestHttpClient client = new TestHttpClient();
try {
HttpResponse result = client.execute(get);
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
Path file = Paths.get(SenderTestCase.class.getResource(SenderTestCase.class.getSimpleName() + ".class").toURI());
long length = Files.size(file);
byte[] data = new byte[(int) length * TXS];
for (int i = 0; i < TXS; i++) {
try(DataInputStream is = new DataInputStream(Files.newInputStream(file))) {
is.readFully(data, (int) (i * length), (int) length);
}
}
Assert.assertArrayEquals(data, HttpClientUtils.readRawResponse(result));
} finally {
client.getConnectionManager().shutdown();
}
}
@Test
public void testSyncTransfer() throws Exception {
StringBuilder sb = new StringBuilder(TXS);
for (int i = 0; i < TXS; ++i) {
sb.append("a");
}
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/transfer?blocking=true");
TestHttpClient client = new TestHttpClient();
try {
HttpResponse result = client.execute(get);
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
Path file = Paths.get(SenderTestCase.class.getResource(SenderTestCase.class.getSimpleName() + ".class").toURI());
long length = Files.size(file);
byte[] data = new byte[(int) length * TXS];
for (int i = 0; i < TXS; i++) {
try(DataInputStream is = new DataInputStream(Files.newInputStream(file))) {
is.readFully(data, (int) (i * length), (int) length);
}
}
Assert.assertArrayEquals(data, HttpClientUtils.readRawResponse(result));
} finally {
client.getConnectionManager().shutdown();
}
}
@Test
public void testBlockingSender() throws IOException {
StringBuilder sb = new StringBuilder(SENDS);
for (int i = 0; i < SENDS; ++i) {
sb.append("a");
}
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/lots?blocking=true");
TestHttpClient client = new TestHttpClient();
try {
HttpResponse result = client.execute(get);
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
Assert.assertEquals(sb.toString(), HttpClientUtils.readResponse(result));
} finally {
client.getConnectionManager().shutdown();
}
}
@Test
public void testSenderSetsContentLength() throws IOException {
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/fixed");
TestHttpClient client = new TestHttpClient();
try {
HttpResponse result = client.execute(get);
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
Assert.assertEquals(HELLO_WORLD, HttpClientUtils.readResponse(result));
Header[] header = result.getHeaders(Headers.CONTENT_LENGTH_STRING);
Assert.assertEquals(1, header.length);
Assert.assertEquals("" + HELLO_WORLD.length(), header[0].getValue());
} finally {
client.getConnectionManager().shutdown();
}
}
}