Skip to content

Commit c40907d

Browse files
committed
Add support for HA Proxy Protocol TLV's
TLV's are supported in netty, but currently not accessible to vert.x users. Here the TLV's are exposed in `NetSocket#tlvs`
1 parent 1170f63 commit c40907d

File tree

12 files changed

+146
-9
lines changed

12 files changed

+146
-9
lines changed

vertx-core/src/main/java/io/vertx/core/http/HttpConnection.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.time.Duration;
2424
import java.util.Arrays;
2525
import java.util.List;
26+
import java.util.Map;
2627
import java.util.concurrent.TimeUnit;
2728

2829
/**
@@ -298,4 +299,11 @@ default List<Certificate> peerCertificates() throws SSLPeerUnverifiedException {
298299
*/
299300
String indicatedServerName();
300301

302+
/**
303+
* @return the type-length-values present in the TCP header as map where the key contains the TLV type and
304+
* the value contains the TLV value, mainly used for HA Proxy Protocol v2
305+
*/
306+
@GenIgnore()
307+
Map<Buffer, Buffer> tlvs();
308+
301309
}

vertx-core/src/main/java/io/vertx/core/http/impl/HttpNetSocket.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import io.vertx.core.Handler;
1717
import io.vertx.core.buffer.Buffer;
1818
import io.vertx.core.http.HttpClosedException;
19-
import io.vertx.core.http.StreamResetException;
2019
import io.vertx.core.internal.ContextInternal;
2120
import io.vertx.core.net.NetSocket;
2221
import io.vertx.core.net.SSLOptions;
@@ -26,6 +25,7 @@
2625

2726
import javax.net.ssl.SSLSession;
2827
import java.time.Duration;
28+
import java.util.Map;
2929

3030
/**
3131
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
@@ -229,6 +229,11 @@ public SocketAddress localAddress(boolean real) {
229229
return stream.connection().localAddress(real);
230230
}
231231

232+
@Override
233+
public Map<Buffer, Buffer> tlvs() {
234+
return stream.connection().tlvs();
235+
}
236+
232237
@Override
233238
public Future<Void> close() {
234239
return end();

vertx-core/src/main/java/io/vertx/core/http/impl/UnpooledHttpClientConnection.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import java.time.Duration;
2727
import java.util.ArrayDeque;
2828
import java.util.Deque;
29-
import java.util.concurrent.TimeUnit;
29+
import java.util.Map;
3030

3131
/**
3232
* An un-pooled HTTP client connection that maintains a queue for pending requests that cannot be served
@@ -207,6 +207,11 @@ public String indicatedServerName() {
207207
return actual.indicatedServerName();
208208
}
209209

210+
@Override
211+
public Map<Buffer, Buffer> tlvs() {
212+
return actual.tlvs();
213+
}
214+
210215
/**
211216
* Create an HTTP stream.
212217
*

vertx-core/src/main/java/io/vertx/core/http/impl/http3/Http3Connection.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import java.util.Iterator;
3434
import java.util.List;
3535
import java.util.Map;
36-
import java.util.concurrent.TimeUnit;
3736

3837
/**
3938
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
@@ -332,4 +331,9 @@ public HttpConnection pingHandler(@Nullable Handler<Buffer> handler) {
332331
public HttpConnection exceptionHandler(Handler<Throwable> handler) {
333332
return this;
334333
}
334+
335+
@Override
336+
public Map<Buffer, Buffer> tlvs() {
337+
return connection.tlvs();
338+
}
335339
}

vertx-core/src/main/java/io/vertx/core/http/impl/tcp/Http2UpgradeClientConnection.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@
3434
import io.vertx.core.net.HostAndPort;
3535
import io.vertx.core.net.SocketAddress;
3636
import io.vertx.core.spi.metrics.ClientMetrics;
37-
import io.vertx.core.spi.metrics.HttpClientMetrics;
37+
3838

3939
import javax.net.ssl.SSLSession;
4040
import java.time.Duration;
41+
import java.util.Map;
4142

4243
/**
4344
* A connection that attempts to perform a protocol upgrade to H2C. The connection might use HTTP/1 or H2C
@@ -901,6 +902,11 @@ public String indicatedServerName() {
901902
return current.indicatedServerName();
902903
}
903904

905+
@Override
906+
public Map<Buffer, Buffer> tlvs() {
907+
return current.tlvs();
908+
}
909+
904910
@Override
905911
public String toString() {
906912
return getClass().getSimpleName() + "[current=" + current.getClass().getSimpleName() + "]";

vertx-core/src/main/java/io/vertx/core/net/NetSocket.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import io.vertx.codegen.annotations.VertxGen;
1919
import io.vertx.core.Future;
2020
import io.vertx.core.Handler;
21-
import io.vertx.core.VertxException;
2221
import io.vertx.core.buffer.Buffer;
2322
import io.vertx.core.streams.ReadStream;
2423
import io.vertx.core.streams.WriteStream;
@@ -29,6 +28,7 @@
2928
import java.time.Duration;
3029
import java.util.Arrays;
3130
import java.util.List;
31+
import java.util.Map;
3232

3333
/**
3434
* Represents a socket-like interface to a TCP connection on either the
@@ -316,5 +316,12 @@ default List<Certificate> peerCertificates() throws SSLPeerUnverifiedException {
316316
*/
317317
String applicationLayerProtocol();
318318

319+
/**
320+
* @return the type-length-values present in the TCP header as map where the key contains the TLV type and
321+
* the value contains the TLV value, mainly used for HA Proxy Protocol v2
322+
*/
323+
@GenIgnore()
324+
Map<Buffer, Buffer> tlvs();
325+
319326
}
320327

vertx-core/src/main/java/io/vertx/core/net/QuicConnection.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import javax.net.ssl.SSLSession;
2020
import java.time.Duration;
21+
import java.util.Map;
2122

2223
/**
2324
* <p>A Quic connection between a client and a server, providing support for handling or creating Quic {@link QuicStream streams}</p>
@@ -176,4 +177,11 @@ default Future<Void> shutdown(Duration timeout) {
176177
*/
177178
SocketAddress localAddress();
178179

180+
/**
181+
* @return the type-length-values present in the TCP header as map where the key contains the TLV type and
182+
* the value contains the TLV value, mainly used for HA Proxy Protocol v2
183+
*/
184+
@GenIgnore()
185+
Map<Buffer, Buffer> tlvs();
186+
179187
}

vertx-core/src/main/java/io/vertx/core/net/impl/ConnectionBase.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.netty.util.AttributeKey;
2020
import io.netty.util.concurrent.FutureListener;
2121
import io.vertx.core.*;
22+
import io.vertx.core.buffer.Buffer;
2223
import io.vertx.core.internal.ContextInternal;
2324
import io.vertx.core.internal.PromiseInternal;
2425
import io.vertx.core.internal.VertxInternal;
@@ -32,6 +33,7 @@
3233

3334
import javax.net.ssl.SSLSession;
3435
import java.net.InetSocketAddress;
36+
import java.util.Map;
3537

3638
/**
3739
* Abstract base class for connections managed by a vertx instance. This base implementation does not handle
@@ -53,6 +55,7 @@ public abstract class ConnectionBase {
5355
public static final VertxException CLOSED_EXCEPTION = NetSocketInternal.CLOSED_EXCEPTION;
5456
public static final AttributeKey<SocketAddress> REMOTE_ADDRESS_OVERRIDE = AttributeKey.valueOf("RemoteAddressOverride");
5557
public static final AttributeKey<SocketAddress> LOCAL_ADDRESS_OVERRIDE = AttributeKey.valueOf("LocalAddressOverride");
58+
public static final AttributeKey<Map<Buffer, Buffer>> TLVS = AttributeKey.valueOf("TLVS");
5659
private static final Logger log = LoggerFactory.getLogger(ConnectionBase.class);
5760

5861
protected final VertxInternal vertx;
@@ -426,4 +429,8 @@ public SocketAddress localAddress(boolean real) {
426429
}
427430
}
428431

432+
public Map<Buffer, Buffer> tlvs() {
433+
return channel.hasAttr(TLVS) ? channel.attr(TLVS).getAndSet(null) : null;
434+
}
435+
429436
}

vertx-core/src/main/java/io/vertx/core/net/impl/HAProxyMessageCompletionHandler.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
package io.vertx.core.net.impl;
22

3+
import io.netty.buffer.ByteBufUtil;
34
import io.netty.channel.Channel;
45
import io.netty.channel.ChannelHandlerContext;
56
import io.netty.handler.codec.MessageToMessageDecoder;
67
import io.netty.handler.codec.haproxy.HAProxyMessage;
78
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
9+
import io.netty.handler.codec.haproxy.HAProxyTLV;
810
import io.netty.handler.timeout.IdleState;
911
import io.netty.handler.timeout.IdleStateEvent;
1012
import io.netty.util.concurrent.Promise;
13+
import io.vertx.core.buffer.Buffer;
1114
import io.vertx.core.internal.logging.Logger;
1215
import io.vertx.core.internal.logging.LoggerFactory;
1316
import io.vertx.core.net.SocketAddress;
1417

1518
import java.io.IOException;
1619
import java.util.List;
20+
import java.util.Map;
21+
import java.util.Objects;
22+
import java.util.stream.Collectors;
1723

1824
public class HAProxyMessageCompletionHandler extends MessageToMessageDecoder<HAProxyMessage> {
1925
//Public because its used in tests
@@ -71,6 +77,11 @@ protected void decode(ChannelHandlerContext ctx, HAProxyMessage msg, List<Object
7177
ctx.channel().attr(ConnectionBase.LOCAL_ADDRESS_OVERRIDE)
7278
.set(createAddress(protocol, msg.destinationAddress(), msg.destinationPort()));
7379
}
80+
81+
if (msg.tlvs() != null) {
82+
ctx.channel().attr(ConnectionBase.TLVS)
83+
.set(createTLVs(msg.tlvs()));
84+
}
7485
}
7586
ctx.pipeline().remove(this);
7687
promise.setSuccess(ctx.channel());
@@ -103,4 +114,11 @@ private SocketAddress createAddress(HAProxyProxiedProtocol protocol, String sour
103114
throw new IllegalStateException("Should never happen");
104115
}
105116
}
117+
118+
private Map<Buffer, Buffer> createTLVs(List<HAProxyTLV> haProxyTLVs) {
119+
return haProxyTLVs.stream()
120+
.filter(Objects::nonNull)
121+
.collect(Collectors.toMap(tlv -> Buffer.buffer().appendByte(tlv.typeByteValue()),
122+
tlv -> Buffer.buffer(ByteBufUtil.getBytes(tlv.content()))));
123+
}
106124
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2011-2026 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.net.impl;
12+
13+
import io.netty.buffer.ByteBufUtil;
14+
import io.vertx.core.buffer.Buffer;
15+
import io.vertx.core.net.TLV;
16+
17+
public class HAProxyTLV implements TLV {
18+
19+
private final Buffer type;
20+
private final Buffer value;
21+
22+
public HAProxyTLV(Buffer type, Buffer value) {
23+
this.type = type;
24+
this.value = value;
25+
}
26+
27+
public static TLV from(io.netty.handler.codec.haproxy.HAProxyTLV haProxyTLV) {
28+
Buffer type = Buffer.buffer().appendByte(haProxyTLV.typeByteValue());
29+
Buffer value = Buffer.buffer(ByteBufUtil.getBytes(haProxyTLV.content()));
30+
return new HAProxyTLV(type, value);
31+
}
32+
33+
@Override
34+
public Buffer type() {
35+
return type;
36+
}
37+
38+
@Override
39+
public Buffer value() {
40+
return value;
41+
}
42+
}

0 commit comments

Comments
 (0)