Skip to content

Commit fc987c5

Browse files
committed
Initial support for proxy protocol data V1 and V2. Minor changes to API to make protocol data available to application handlers.
1 parent 826cf87 commit fc987c5

File tree

10 files changed

+413
-2
lines changed

10 files changed

+413
-2
lines changed

common/socket/src/main/java/io/helidon/common/socket/SocketOptionsBlueprint.java

+9
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ interface SocketOptionsBlueprint {
109109
@ConfiguredOption("false")
110110
boolean tcpNoDelay();
111111

112+
/**
113+
* Enable support for proxy protocol for this socket.
114+
* Default is {@code false}.
115+
*
116+
* @return HAProxy support status
117+
*/
118+
@ConfiguredOption("false")
119+
boolean enableProxyProtocol();
120+
112121
/**
113122
* Configure socket with defined socket options.
114123
*

webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ServerRequest.java

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.InputStream;
2020
import java.util.Objects;
21+
import java.util.Optional;
2122
import java.util.function.Supplier;
2223
import java.util.function.UnaryOperator;
2324

@@ -38,6 +39,7 @@
3839
import io.helidon.http.media.ReadableEntity;
3940
import io.helidon.webserver.ConnectionContext;
4041
import io.helidon.webserver.ListenerContext;
42+
import io.helidon.webserver.ProxyProtocolData;
4143
import io.helidon.webserver.http.HttpSecurity;
4244
import io.helidon.webserver.http.RoutingRequest;
4345

@@ -220,6 +222,11 @@ public void streamFilter(UnaryOperator<InputStream> filterFunction) {
220222
this.streamFilter = it -> filterFunction.apply(current.apply(it));
221223
}
222224

225+
@Override
226+
public Optional<ProxyProtocolData> proxyProtocolData() {
227+
return Optional.ofNullable(ctx.proxyProtocolData());
228+
}
229+
223230
private UriInfo createUriInfo() {
224231
return ctx.listenerContext().config().requestedUriDiscoveryContext().uriInfo(remotePeer().address().toString(),
225232
localPeer().address().toString(),

webserver/webserver/src/main/java/io/helidon/webserver/ConnectionContext.java

+11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.helidon.common.buffers.DataReader;
2222
import io.helidon.common.buffers.DataWriter;
2323
import io.helidon.common.socket.SocketContext;
24+
import io.helidon.common.socket.SocketOptions;
2425

2526
/**
2627
* Server connection context.
@@ -60,4 +61,14 @@ public interface ConnectionContext extends SocketContext {
6061
* @return rouer
6162
*/
6263
Router router();
64+
65+
/**
66+
* Proxy protocol header data.
67+
*
68+
* @return header data or {@code null} if proxy protocol not enabled on socket
69+
* @see SocketOptions#enableProxyProtocol()
70+
*/
71+
default ProxyProtocolData proxyProtocolData() {
72+
return null;
73+
}
6374
}

webserver/webserver/src/main/java/io/helidon/webserver/ConnectionHandler.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.helidon.common.socket.HelidonSocket;
3434
import io.helidon.common.socket.PeerInfo;
3535
import io.helidon.common.socket.PlainSocket;
36+
import io.helidon.common.socket.SocketOptions;
3637
import io.helidon.common.socket.SocketWriter;
3738
import io.helidon.common.socket.TlsSocket;
3839
import io.helidon.common.task.InterruptableTask;
@@ -64,11 +65,13 @@ class ConnectionHandler implements InterruptableTask<Void>, ConnectionContext {
6465
private final String serverChannelId;
6566
private final Router router;
6667
private final Tls tls;
68+
private final SocketOptions connectionOptions;
6769

6870
private ServerConnection connection;
6971
private HelidonSocket helidonSocket;
7072
private DataReader reader;
7173
private SocketWriter writer;
74+
private ProxyProtocolData proxyProtocolData;
7275

7376
ConnectionHandler(ListenerContext listenerContext,
7477
Semaphore connectionSemaphore,
@@ -78,7 +81,8 @@ class ConnectionHandler implements InterruptableTask<Void>, ConnectionContext {
7881
Socket socket,
7982
String serverChannelId,
8083
Router router,
81-
Tls tls) {
84+
Tls tls,
85+
SocketOptions connectionOptions) {
8286
this.listenerContext = listenerContext;
8387
this.connectionSemaphore = connectionSemaphore;
8488
this.requestSemaphore = requestSemaphore;
@@ -89,6 +93,7 @@ class ConnectionHandler implements InterruptableTask<Void>, ConnectionContext {
8993
this.serverChannelId = serverChannelId;
9094
this.router = router;
9195
this.tls = tls;
96+
this.connectionOptions = connectionOptions;
9297
}
9398

9499
@Override
@@ -100,6 +105,12 @@ public boolean canInterrupt() {
100105
public final void run() {
101106
String channelId = "0x" + HexFormat.of().toHexDigits(System.identityHashCode(socket));
102107

108+
// proxy protocol before SSL handshake
109+
if (connectionOptions.enableProxyProtocol()) {
110+
ProxyProtocolHandler handler = new ProxyProtocolHandler(socket, channelId);
111+
proxyProtocolData = handler.get();
112+
}
113+
103114
// handle SSL and init helidonSocket, reader and writer
104115
try {
105116
if (tls.enabled()) {
@@ -226,6 +237,11 @@ public Router router() {
226237
return router;
227238
}
228239

240+
@Override
241+
public ProxyProtocolData proxyProtocolData() {
242+
return proxyProtocolData;
243+
}
244+
229245
private ServerConnection identifyConnection() {
230246
try {
231247
reader.ensureAvailable();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2023 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.helidon.webserver;
17+
18+
/**
19+
* Proxy protocol data parsed by {@link ProxyProtocolHandler}.
20+
*/
21+
public interface ProxyProtocolData {
22+
23+
/**
24+
* The protocol family options.
25+
*/
26+
enum ProtocolFamily {
27+
/**
28+
* TCP version 4.
29+
*/
30+
TCP4,
31+
32+
/**
33+
* TCP version 6.
34+
*/
35+
TCP6,
36+
37+
/**
38+
* Protocol family is unknown.
39+
*/
40+
UNKNOWN
41+
}
42+
43+
/**
44+
* Protocol family from protocol header.
45+
*
46+
* @return protocol family
47+
*/
48+
ProtocolFamily protocolFamily();
49+
50+
/**
51+
* Source address that is either IPv4 or IPv6 depending on {@link #protocolFamily()}}.
52+
*
53+
* @return source address
54+
*/
55+
String sourceAddress();
56+
57+
/**
58+
* Destination address that is either IPv4 or IPv6 depending on {@link #protocolFamily()}}.
59+
*
60+
* @return source address
61+
*/
62+
String destAddress();
63+
64+
/**
65+
* Source port number.
66+
*
67+
* @return source port.
68+
*/
69+
int sourcePort();
70+
71+
/**
72+
* Destination port number.
73+
*
74+
* @return port number.
75+
*/
76+
int destPort();
77+
}
78+
79+

0 commit comments

Comments
 (0)