Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class DnsClientOptionsConverter {
public static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, DnsClientOptions obj) {
for (java.util.Map.Entry<String, Object> member : json) {
switch (member.getKey()) {
case "activityLogFormat":
if (member.getValue() instanceof String) {
obj.setActivityLogFormat(io.netty.handler.logging.ByteBufFormat.valueOf((String)member.getValue()));
}
break;
case "host":
if (member.getValue() instanceof String) {
obj.setHost((String)member.getValue());
Expand Down Expand Up @@ -54,6 +59,9 @@ public static void toJson(DnsClientOptions obj, JsonObject json) {
}

public static void toJson(DnsClientOptions obj, java.util.Map<String, Object> json) {
if (obj.getActivityLogFormat() != null) {
json.put("activityLogFormat", obj.getActivityLogFormat().name());
}
if (obj.getHost() != null) {
json.put("host", obj.getHost());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, EventBu
obj.setAcceptBacklog(((Number)member.getValue()).intValue());
}
break;
case "activityLogDataFormat":
if (member.getValue() instanceof String) {
obj.setActivityLogDataFormat(io.netty.handler.logging.ByteBufFormat.valueOf((String)member.getValue()));
}
break;
case "clientAuth":
if (member.getValue() instanceof String) {
obj.setClientAuth(io.vertx.core.http.ClientAuth.valueOf((String)member.getValue()));
Expand Down Expand Up @@ -129,6 +134,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, EventBu
obj.setOpenSslEngineOptions(new io.vertx.core.net.OpenSSLEngineOptions((io.vertx.core.json.JsonObject)member.getValue()));
}
break;
case "pcapCaptureFile":
if (member.getValue() instanceof String) {
obj.setPcapCaptureFile((String)member.getValue());
}
break;
case "pemKeyCertOptions":
if (member.getValue() instanceof JsonObject) {
obj.setPemKeyCertOptions(new io.vertx.core.net.PemKeyCertOptions((io.vertx.core.json.JsonObject)member.getValue()));
Expand Down Expand Up @@ -269,6 +279,9 @@ static void toJson(EventBusOptions obj, JsonObject json) {

static void toJson(EventBusOptions obj, java.util.Map<String, Object> json) {
json.put("acceptBacklog", obj.getAcceptBacklog());
if (obj.getActivityLogDataFormat() != null) {
json.put("activityLogDataFormat", obj.getActivityLogDataFormat().name());
}
if (obj.getClientAuth() != null) {
json.put("clientAuth", obj.getClientAuth().name());
}
Expand Down Expand Up @@ -319,6 +332,9 @@ static void toJson(EventBusOptions obj, java.util.Map<String, Object> json) {
if (obj.getOpenSslEngineOptions() != null) {
json.put("openSslEngineOptions", obj.getOpenSslEngineOptions().toJson());
}
if (obj.getPcapCaptureFile() != null) {
json.put("pcapCaptureFile", obj.getPcapCaptureFile());
}
if (obj.getPemKeyCertOptions() != null) {
json.put("pemKeyCertOptions", obj.getPemKeyCertOptions().toJson());
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/generated/io/vertx/core/net/NetworkOptionsConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,21 @@ public class NetworkOptionsConverter {
static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, NetworkOptions obj) {
for (java.util.Map.Entry<String, Object> member : json) {
switch (member.getKey()) {
case "activityLogDataFormat":
if (member.getValue() instanceof String) {
obj.setActivityLogDataFormat(io.netty.handler.logging.ByteBufFormat.valueOf((String)member.getValue()));
}
break;
case "logActivity":
if (member.getValue() instanceof Boolean) {
obj.setLogActivity((Boolean)member.getValue());
}
break;
case "pcapCaptureFile":
if (member.getValue() instanceof String) {
obj.setPcapCaptureFile((String)member.getValue());
}
break;
case "receiveBufferSize":
if (member.getValue() instanceof Number) {
obj.setReceiveBufferSize(((Number)member.getValue()).intValue());
Expand Down Expand Up @@ -59,7 +69,13 @@ static void toJson(NetworkOptions obj, JsonObject json) {
}

static void toJson(NetworkOptions obj, java.util.Map<String, Object> json) {
if (obj.getActivityLogDataFormat() != null) {
json.put("activityLogDataFormat", obj.getActivityLogDataFormat().name());
}
json.put("logActivity", obj.getLogActivity());
if (obj.getPcapCaptureFile() != null) {
json.put("pcapCaptureFile", obj.getPcapCaptureFile());
}
json.put("receiveBufferSize", obj.getReceiveBufferSize());
json.put("reuseAddress", obj.isReuseAddress());
json.put("reusePort", obj.isReusePort());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ public DatagramSocketOptions setReusePort(boolean reusePort) {
return (DatagramSocketOptions) super.setReusePort(reusePort);
}

@Override
public DatagramSocketOptions setPcapCaptureFile(String pcapCaptureFile) {
return (DatagramSocketOptions) super.setPcapCaptureFile(pcapCaptureFile);
}

@Override
public int getTrafficClass() {
return super.getTrafficClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io.vertx.core.buffer.Buffer;
import io.vertx.core.datagram.DatagramSocket;
import io.vertx.core.datagram.DatagramSocketOptions;
import io.vertx.core.http.impl.VertxPcapWriteHandler;
import io.vertx.core.impl.AddressResolver;
import io.vertx.core.impl.Arguments;
import io.vertx.core.impl.ContextInternal;
Expand Down Expand Up @@ -78,6 +79,9 @@ private DatagramSocketImpl(VertxInternal vertx, DatagramSocketOptions options) {
if (options.getLogActivity()) {
channel.pipeline().addLast("logging", new LoggingHandler(options.getActivityLogDataFormat()));
}
if ((options.getPcapCaptureFile() != null) && !options.getPcapCaptureFile().isEmpty()) {
channel.pipeline().addLast("pcapCapturing", new VertxPcapWriteHandler(options.getPcapCaptureFile()));
}
VertxMetrics metrics = vertx.metricsSPI();
this.metrics = metrics != null ? metrics.createDatagramSocketMetrics(options) : null;
this.channel = channel;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/vertx/core/eventbus/EventBusOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ public EventBusOptions setReusePort(boolean reusePort) {
return this;
}

@Override
public EventBusOptions setPcapCaptureFile(String pcapCaptureFile) {
super.setPcapCaptureFile(pcapCaptureFile);
return this;
}

@Override
public EventBusOptions setSendBufferSize(int sendBufferSize) {
super.setSendBufferSize(sendBufferSize);
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/vertx/core/http/HttpClientOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@ public HttpClientOptions setReusePort(boolean reusePort) {
return this;
}

@Override
public HttpClientOptions setPcapCaptureFile(String pcapCaptureFile) {
super.setPcapCaptureFile(pcapCaptureFile);
return this;
}

@Override
public HttpClientOptions setTrafficClass(int trafficClass) {
super.setTrafficClass(trafficClass);
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/vertx/core/http/HttpServerOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ public HttpServerOptions setReusePort(boolean reusePort) {
return this;
}

@Override
public HttpServerOptions setPcapCaptureFile(String pcapCaptureFile) {
super.setPcapCaptureFile(pcapCaptureFile);
return this;
}

@Override
public HttpServerOptions setTrafficClass(int trafficClass) {
super.setTrafficClass(trafficClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class HttpChannelConnector {
private final HttpVersion version;
private final SocketAddress peerAddress;
private final SocketAddress server;
private final boolean enablePcapCapture;

public HttpChannelConnector(HttpClientImpl client,
NetClientImpl netClient,
Expand All @@ -77,6 +78,7 @@ public HttpChannelConnector(HttpClientImpl client,
this.version = version;
this.peerAddress = peerAddress;
this.server = server;
this.enablePcapCapture = (options.getPcapCaptureFile() != null) && !options.getPcapCaptureFile().isEmpty();
}

public SocketAddress server() {
Expand Down Expand Up @@ -164,6 +166,9 @@ private void applyHttp1xConnectionOptions(ChannelPipeline pipeline) {
if (options.getLogActivity()) {
pipeline.addLast("logging", new LoggingHandler(options.getActivityLogDataFormat()));
}
if (enablePcapCapture) {
pipeline.addLast("pcapCapturing", new VertxPcapWriteHandler(options.getPcapCaptureFile()));
}
pipeline.addLast("codec", new HttpClientCodec(
options.getMaxInitialLineLength(),
options.getMaxHeaderSize(),
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/vertx/core/http/impl/HttpServerWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
import io.vertx.core.net.impl.HAProxyMessageCompletionHandler;
import io.vertx.core.spi.metrics.HttpServerMetrics;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.function.Supplier;

Expand All @@ -55,6 +59,7 @@ public class HttpServerWorker implements Handler<Channel> {
private final String serverOrigin;
private final boolean logEnabled;
private final boolean disableH2C;
private final boolean enablePcapCapture;
final Handler<HttpServerConnection> connectionHandler;
private final Handler<Throwable> exceptionHandler;

Expand All @@ -77,6 +82,7 @@ public HttpServerWorker(EventLoopContext context,
this.serverOrigin = serverOrigin;
this.logEnabled = options.getLogActivity();
this.disableH2C = disableH2C;
this.enablePcapCapture = (options.getPcapCaptureFile() != null) && !options.getPcapCaptureFile().isEmpty();
this.connectionHandler = connectionHandler;
this.exceptionHandler = exceptionHandler;
}
Expand Down Expand Up @@ -252,6 +258,9 @@ private void configureHttp1OrH2C(ChannelPipeline pipeline) {
if (logEnabled) {
pipeline.addLast("logging", new LoggingHandler(options.getActivityLogDataFormat()));
}
if (enablePcapCapture) {
pipeline.addLast("pcapCapturing", new VertxPcapWriteHandler(options.getPcapCaptureFile()));
}
if (HttpServerImpl.USE_FLASH_POLICY_HANDLER) {
pipeline.addLast("flashpolicy", new FlashPolicyHandler());
}
Expand Down
119 changes: 119 additions & 0 deletions src/main/java/io/vertx/core/http/impl/VertxPcapWriteHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package io.vertx.core.http.impl;

import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.pcap.PcapWriteHandler;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
* A handler that simply delegates to the built in {@link PcapWriteHandler}.
* Vert.x needs this because the handler might not have been added to the processing pipeline
* when the {@code channelRead} method is invoked, and thus the necessary setup is performed
* in the {@code channelRegistered} method.
* Furthermore, we want to support capturing the output of multiple Netty pipelines into a single file,
* so for example both the output of an HTTP server and an HTTP Client can be inspected via the same file.
*/
public class VertxPcapWriteHandler extends ChannelDuplexHandler implements Closeable {

private static final Logger log = LoggerFactory.getLogger(VertxPcapWriteHandler.class);

/**
* The idea of this map is to control the usage of each throughout the entire Vert.x application.
* When the same file is configured for multiple pipelines, we want each pipeline to write to the same
* OutputStream, but we only want to close it when the last pipeline has been closed.
*/
private static final ConcurrentMap<String, Metadata> fileToMetadata = new ConcurrentHashMap<>();

private final PcapWriteHandler delegate;
private final String pcapCaptureFile;

public VertxPcapWriteHandler(String pcapCaptureFile) {
this.pcapCaptureFile = pcapCaptureFile;
Metadata metadata = fileToMetadata.computeIfAbsent(pcapCaptureFile, Metadata::new);
// pcap contains a global header section that should only be written by the first handler
int openedCount = metadata.openedCount.getAndIncrement();
this.delegate = new PcapWriteHandler(metadata.outputStream, false, openedCount == 0);
}

@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
delegate.channelActive(ctx);
}

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
delegate.channelActive(ctx);
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
delegate.channelRead(ctx, msg);
}

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
delegate.write(ctx, msg, promise);
}

@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
int openedCount = fileToMetadata.get(pcapCaptureFile).openedCount.decrementAndGet();
if (openedCount == 0) {
delegate.handlerRemoved(ctx);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
delegate.exceptionCaught(ctx, cause);
}

@Override
public void close() throws IOException {
delegate.close();
}

private static class Metadata {

final AtomicInteger openedCount;
final OutputStream outputStream;

Metadata(String pcapFile) {
openedCount = new AtomicInteger(0);
outputStream = getOutputStream(pcapFile);
}

private OutputStream getOutputStream(String pcapFile) {
try {
return new FileOutputStream(pcapFile);
} catch (FileNotFoundException e) {
log.warn("Unable to open capture file for writing, so no capture information will be recorded.", e);
return NullOutputStream.INSTANCE;
}
}

private static class NullOutputStream extends OutputStream {

static final NullOutputStream INSTANCE = new NullOutputStream();

private NullOutputStream() {
}

@Override
public void write(int b) throws IOException {

}
}
}

}
5 changes: 5 additions & 0 deletions src/main/java/io/vertx/core/net/ClientOptionsBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ public ClientOptionsBase setReusePort(boolean reusePort) {
return (ClientOptionsBase) super.setReusePort(reusePort);
}

@Override
public ClientOptionsBase setPcapCaptureFile(String pcapCaptureFile) {
return (ClientOptionsBase) super.setPcapCaptureFile(pcapCaptureFile);
}

@Override
public ClientOptionsBase setTrafficClass(int trafficClass) {
return (ClientOptionsBase) super.setTrafficClass(trafficClass);
Expand Down
Loading