/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.test.core;
import io.vertx.core.Context;
import io.vertx.core.VertxOptions;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.metrics.MetricsOptions;
import io.vertx.test.fakemetrics.FakeHttpClientMetrics;
import io.vertx.test.fakemetrics.FakeHttpServerMetrics;
import io.vertx.test.fakemetrics.FakeMetricsBase;
import io.vertx.test.fakemetrics.FakeMetricsFactory;
import io.vertx.test.fakemetrics.HttpClientMetric;
import io.vertx.test.fakemetrics.HttpServerMetric;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class HttpMetricsTest extends HttpTestBase {
@Override
protected VertxOptions getOptions() {
VertxOptions options = super.getOptions();
options.setMetricsOptions(new MetricsOptions().setEnabled(true).setFactory(new FakeMetricsFactory()));
return options;
}
@Test
public void testHttp1MetricsLifecycle() throws Exception {
testHttpMetricsLifecycle(HttpVersion.HTTP_1_1);
}
@Test
public void testHttp2MetricsLifecycle() throws Exception {
testHttpMetricsLifecycle(HttpVersion.HTTP_2);
}
private void testHttpMetricsLifecycle(HttpVersion protocol) throws Exception {
waitFor(2);
int numBuffers = 10;
int contentLength = numBuffers * 1000;
AtomicReference<HttpServerMetric> serverMetric = new AtomicReference<>();
server.requestHandler(req -> {
assertEquals(protocol, req.version());
FakeHttpServerMetrics serverMetrics = FakeMetricsBase.getMetrics(server);
assertNotNull(serverMetrics);
serverMetric.set(serverMetrics.getMetric(req));
assertNotNull(serverMetric.get());
assertNotNull(serverMetric.get().socket);
assertTrue(serverMetric.get().socket.connected.get());
req.bodyHandler(buff -> {
assertEquals(contentLength, buff.length());
HttpServerResponse resp = req.response().putHeader("Content-Length", "" + contentLength);
AtomicInteger numBuffer = new AtomicInteger(numBuffers);
vertx.setPeriodic(1, timerID -> {
if (numBuffer.getAndDecrement() == 0) {
resp.end();
assertNull(serverMetrics.getMetric(req));
vertx.cancelTimer(timerID);
} else {
resp.write(TestUtils.randomBuffer(1000));
}
});
});
});
startServer();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<HttpClientMetric> clientMetric = new AtomicReference<>();
client = vertx.createHttpClient(new HttpClientOptions().setProtocolVersion(protocol));
FakeHttpClientMetrics metrics = FakeMetricsBase.getMetrics(client);
Context ctx = vertx.getOrCreateContext();
ctx.runOnContext(v -> {
HttpClientRequest req = client.get(DEFAULT_HTTP_PORT, DEFAULT_HTTP_HOST, "/somepath").exceptionHandler(this::fail);
assertNull(metrics.getMetric(req));
req.setChunked(true).handler(resp -> {
clientMetric.set(metrics.getMetric(req));
assertNotNull(clientMetric.get());
assertNotNull(clientMetric.get().socket);
assertTrue(clientMetric.get().socket.connected.get());
resp.bodyHandler(buff -> {
assertNull(metrics.getMetric(req));
assertEquals(contentLength, buff.length());
latch.countDown();
});
});
for (int i = 0;i < numBuffers;i++) {
req.write(TestUtils.randomBuffer(1000));
}
req.end();
});
awaitLatch(latch);
client.close();
assertWaitUntil(() -> !serverMetric.get().socket.connected.get());
assertEquals(contentLength, serverMetric.get().socket.bytesRead.get());
assertEquals(contentLength, serverMetric.get().socket.bytesWritten.get());
assertWaitUntil(() -> !clientMetric.get().socket.connected.get());
assertEquals(contentLength, clientMetric.get().socket.bytesRead.get());
assertEquals(contentLength, clientMetric.get().socket.bytesWritten.get());
}
@Test
public void testHttp1ClientLifecycle() throws Exception {
testHttpClientLifecycle(HttpVersion.HTTP_1_1);
}
@Test
public void testHttp2ClientLifecycle() throws Exception {
testHttpClientLifecycle(HttpVersion.HTTP_2);
}
private void testHttpClientLifecycle(HttpVersion protocol) throws Exception {
HttpServer server = vertx.createHttpServer();
CountDownLatch requestBeginLatch = new CountDownLatch(1);
CountDownLatch requestBodyLatch = new CountDownLatch(1);
CountDownLatch requestEndLatch = new CountDownLatch(1);
CompletableFuture<Void> beginResponse = new CompletableFuture<>();
CompletableFuture<Void> endResponse = new CompletableFuture<>();
server.requestHandler(req -> {
assertEquals(protocol, req.version());
requestBeginLatch.countDown();
req.handler(buff -> {
requestBodyLatch.countDown();
});
req.endHandler(v -> {
requestEndLatch.countDown();
});
Context ctx = vertx.getOrCreateContext();
beginResponse.thenAccept(v1 -> {
ctx.runOnContext(v2 -> {
req.response().setChunked(true).write(TestUtils.randomAlphaString(1024));
});
});
endResponse.thenAccept(v1 -> {
ctx.runOnContext(v2 -> {
req.response().end();
});
});
});
CountDownLatch listenLatch = new CountDownLatch(1);
server.listen(8080, "localhost", onSuccess(s -> { listenLatch.countDown(); }));
awaitLatch(listenLatch);
HttpClient client = vertx.createHttpClient(new HttpClientOptions().setProtocolVersion(protocol));
FakeHttpClientMetrics clientMetrics = FakeMetricsBase.getMetrics(client);
CountDownLatch responseBeginLatch = new CountDownLatch(1);
CountDownLatch responseEndLatch = new CountDownLatch(1);
HttpClientRequest req = client.post(8080, "localhost", "/somepath", resp -> {
responseBeginLatch.countDown();
resp.endHandler(v -> {
responseEndLatch.countDown();
});
}).setChunked(true);
req.sendHead();
awaitLatch(requestBeginLatch);
HttpClientMetric reqMetric = clientMetrics.getMetric(req);
assertEquals(0, reqMetric.requestEnded.get());
assertEquals(0, reqMetric.responseBegin.get());
req.write(TestUtils.randomAlphaString(1024));
awaitLatch(requestBodyLatch);
assertEquals(0, reqMetric.requestEnded.get());
assertEquals(0, reqMetric.responseBegin.get());
req.end();
awaitLatch(requestEndLatch);
assertEquals(1, reqMetric.requestEnded.get());
assertEquals(0, reqMetric.responseBegin.get());
beginResponse.complete(null);
awaitLatch(responseBeginLatch);
assertEquals(1, reqMetric.requestEnded.get());
assertEquals(1, reqMetric.responseBegin.get());
endResponse.complete(null);
awaitLatch(responseEndLatch);
assertNull(clientMetrics.getMetric(req));
assertEquals(1, reqMetric.requestEnded.get());
assertEquals(1, reqMetric.responseBegin.get());
}
@Test
public void testHttp1ClientConnectionClosed() throws Exception {
testClientConnectionClosed(HttpVersion.HTTP_1_1);
}
@Test
public void testHttp2ClientConnectionClosed() throws Exception {
testClientConnectionClosed(HttpVersion.HTTP_2);
}
private void testClientConnectionClosed(HttpVersion protocol) throws Exception {
server.requestHandler(req -> {
req.response().setChunked(true).write(Buffer.buffer("some-data"));
});
startServer();
client = vertx.createHttpClient(new HttpClientOptions().setProtocolVersion(protocol).setIdleTimeout(2));
FakeHttpClientMetrics metrics = FakeMetricsBase.getMetrics(client);
HttpClientRequest req = client.get(DEFAULT_HTTP_PORT, DEFAULT_HTTP_HOST, "/somepath");
req.handler(resp -> {
HttpClientMetric metric = metrics.getMetric(req);
assertNotNull(metric);
assertFalse(metric.failed.get());
resp.exceptionHandler(err -> {
assertNull(metrics.getMetric(req));
assertTrue(metric.failed.get());
testComplete();
});
});
req.end();
await();
}
@Test
public void testHttp1ServerConnectionClosed() throws Exception {
testServerConnectionClosed(HttpVersion.HTTP_1_1);
}
@Test
public void testHttp2ServerConnectionClosed() throws Exception {
testServerConnectionClosed(HttpVersion.HTTP_2);
}
private void testServerConnectionClosed(HttpVersion protocol) throws Exception {
server.close();
server = vertx.createHttpServer(new HttpServerOptions().setPort(DEFAULT_HTTP_PORT).setHost(DEFAULT_HTTP_HOST).setIdleTimeout(2));
server.requestHandler(req -> {
FakeHttpServerMetrics metrics = FakeMetricsBase.getMetrics(server);
HttpServerMetric metric = metrics.getMetric(req);
assertNotNull(metric);
assertFalse(metric.failed.get());
req.response().closeHandler(v -> {
assertNull(metrics.getMetric(req));
assertTrue(metric.failed.get());
testComplete();
});
});
startServer();
client = vertx.createHttpClient(new HttpClientOptions().setProtocolVersion(protocol));
HttpClientRequest req = client.get(DEFAULT_HTTP_PORT, DEFAULT_HTTP_HOST, "/somepath");
req.handler(resp -> {
}).end();
await();
}
@Test
public void testPushPromise() throws Exception {
waitFor(2);
int numBuffers = 10;
int contentLength = numBuffers * 1000;
server.requestHandler(req -> {
req.response().push(HttpMethod.GET, "/wibble", ar -> {
HttpServerResponse pushedResp = ar.result();
FakeHttpServerMetrics serverMetrics = FakeMetricsBase.getMetrics(server);
HttpServerMetric serverMetric = serverMetrics.getMetric(pushedResp);
assertNotNull(serverMetric);
pushedResp.putHeader("content-length", "" + contentLength);
AtomicInteger numBuffer = new AtomicInteger(numBuffers);
vertx.setPeriodic(1, timerID -> {
if (numBuffer.getAndDecrement() == 0) {
pushedResp.end();
assertNull(serverMetrics.getMetric(pushedResp));
vertx.cancelTimer(timerID);
complete();
} else {
pushedResp.write(TestUtils.randomBuffer(1000));
}
});
});
});
startServer();
client = vertx.createHttpClient(new HttpClientOptions().setProtocolVersion(HttpVersion.HTTP_2));
FakeHttpClientMetrics metrics = FakeMetricsBase.getMetrics(client);
HttpClientRequest req = client.get(DEFAULT_HTTP_PORT, DEFAULT_HTTP_HOST, "/somepath", resp -> {
});
req.pushHandler(pushedReq -> {
HttpClientMetric metric = metrics.getMetric(pushedReq);
assertNotNull(metric);
assertSame(pushedReq, metric.request);
pushedReq.handler(resp -> {
resp.endHandler(v -> {
assertNull(metrics.getMetric(pushedReq));
complete();
});
});
});
req.end();
await();
}
}