Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e18e18d
RAV-2958 - Initial Proxy Protocol processing
bplessis-swi Oct 27, 2025
887da06
add spec link
bplessis-swi Oct 27, 2025
a34382d
fix tls constructor
bplessis-swi Oct 27, 2025
b3e5389
cleanup test
bplessis-swi Oct 27, 2025
74755f0
only test supported java releases
bplessis-swi Oct 27, 2025
b74f019
cleanup end of files spaces
bplessis-swi Nov 6, 2025
a4751e6
add test suite for basic cache
bplessis-swi Nov 6, 2025
f260ccd
update readme
bplessis-swi Nov 6, 2025
5b8a9bb
Split decoder in functions
bplessis-swi Nov 7, 2025
9d7cd72
Switch to external library for encoding headers in tests
bplessis-swi Nov 7, 2025
50e65a2
whitespaces
bplessis-swi Nov 7, 2025
f4292cf
Update README.md
bplessis-swi Nov 17, 2025
c47fb02
Fix license file, use leshan compatible BSD-3-Clause
bplessis-swi Nov 17, 2025
47e6df3
Update proxy-socket-core/src/main/java/net/airvantage/proxysocket/cor…
bplessis-swi Nov 17, 2025
9029e73
Update proxy-socket-core/src/main/java/net/airvantage/proxysocket/cor…
bplessis-swi Nov 17, 2025
340dde3
Update proxy-socket-core/src/main/java/net/airvantage/proxysocket/cor…
bplessis-swi Nov 17, 2025
eda9898
take in account simon's review
bplessis-swi Nov 18, 2025
b23bb8f
add serialVersionUID
bplessis-swi Nov 18, 2025
4770613
remove unused
bplessis-swi Nov 18, 2025
128bcac
add optional TLV parsing flag
bplessis-swi Nov 18, 2025
dbfe0f8
remove uneeded import
bplessis-swi Nov 18, 2025
8876188
remove uneeded import
bplessis-swi Nov 18, 2025
b1ccf69
remove uneeded import
bplessis-swi Nov 18, 2025
beb2682
cleanup null test
bplessis-swi Nov 18, 2025
e05039f
rename variable
bplessis-swi Nov 19, 2025
e0902be
last cleanups
bplessis-swi Nov 19, 2025
30ef605
cleanups
bplessis-swi Nov 19, 2025
3098cb2
remove suggested unused imports
bplessis-swi Nov 20, 2025
175c850
expand the throw to a block
bplessis-swi Nov 20, 2025
77c6d8b
Take in account simon's review
bplessis-swi Nov 21, 2025
236e1ab
Use java 16 records
bplessis-swi Nov 24, 2025
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
43 changes: 43 additions & 0 deletions .github/workflows/pull_requests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Pull Request
on:
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build and Test (Java ${{ matrix.java }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
java: [ '17', '21' ]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ matrix.java }}
cache: maven

- name: Build and test
run: |
mvn -B -e --fail-at-end clean verify

- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-java-${{ matrix.java }}
path: |
./**/target/surefire-reports/**
./**/target/failsafe-reports/**
674 changes: 0 additions & 674 deletions LICENSE

This file was deleted.

27 changes: 27 additions & 0 deletions LICENSE.BSD-3-Clause
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2025, Semtech Corporation.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

* Neither the name of the Eclipse Foundation, Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 changes: 62 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,62 @@
# proxy-socket-java
Java Library to handle ProxyProtocol v2 on standard java sockets
# Proxy Socket Java (UDP + TCP, Java 17)

## Overview

Library providing HAProxy Proxy Protocol v2 support for UDP and TCP. Multi-module layout:

- proxy-socket-core: zero dependencies, parser, models, interfaces
- proxy-socket-udp: DatagramSocket wrapper
- proxy-socket-tcp: ServerSocket/Socket wrappers
- proxy-socket-guava: optional Guava-based cache
- proxy-socket-examples: runnable samples

Reference: [HAProxy Proxy Protocol Specifications](https://www.haproxy.org/download/3.3/doc/proxy-protocol.txt)

## Quick start (UDP)

```java
var socket = new net.airvantage.proxysocket.udp.ProxyDatagramSocket.Builder()
.maxEntries(10_000)
.ttl(java.time.Duration.ofMinutes(5))
.metrics(new MyMetrics())
.build();
socket.bind(new java.net.InetSocketAddress(9999));
var buf = new byte[2048];
var packet = new java.net.DatagramPacket(buf, buf.length);
socket.receive(packet); // header stripped, source set to real client
socket.send(packet); // destination rewritten to LB if cached
```

## Quick start (TCP)

```java
try (var server = new net.airvantage.proxysocket.tcp.ProxyServerSocket(9998)) {
for (;;) {
var s = (net.airvantage.proxysocket.tcp.ProxySocket) server.accept();
var header = s.getHeader();
// header.getSourceAddress() is the real client address
}
}
```

## License

MIT License © 2025 Semtech. See `LICENSE`.

## Metrics hook

Implement `net.airvantage.proxysocket.core.ProxySocketMetricsListener` and pass it via UDP builder or TCP server ctor.

## Thread safety

- UDP/TCP wrappers follow JDK `DatagramSocket`/`ServerSocket`/`Socket` thread-safety; caches and listeners must be thread-safe.
- Core parser is stateless and thread-safe.

## Configuration

- UDP cache defaults: 10k entries, 5 min TTL if Guava present; otherwise concurrent map (no TTL).
- TCP: blocking header read on accept with configurable timeout.

## Examples

See `proxy-socket-examples` module: `UdpEchoWithProxyProtocol`, `TcpEchoWithProxyProtocol`.
82 changes: 82 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>net.airvantage</groupId>
<artifactId>proxysocket-java</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

<description>ProxyProtocol Java implementation.</description>

<properties>

<!-- Dependency versions -->
<junit.version>5.10.3</junit.version>

</properties>

<modules>
<module>proxy-socket-core</module>
</modules>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<encoding>UTF-8</encoding>
<release>17</release>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<useModulePath>false</useModulePath>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.10</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>
38 changes: 38 additions & 0 deletions proxy-socket-core/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.airvantage</groupId>
<artifactId>proxysocket-java</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>proxy-socket-core</artifactId>
<name>Proxy Protocol - Core</name>
<packaging>jar</packaging>

<properties>
<maven.compiler.release>17</maven.compiler.release>
</properties>

<dependencies>
<!-- Core must have zero required dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- AWS ProProt library for test validation against known encoder -->
<dependency>
<groupId>com.amazonaws.proprot</groupId>
<artifactId>proprot</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>


Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* BSD-3-Clause License.
* Copyright (c) 2025 Semtech
*/
package net.airvantage.proxysocket.core;

import java.net.InetSocketAddress;

/**
* Thread-safe cache abstraction mapping real client addresses to proxy/load-balancer addresses.
*/
public interface ProxyAddressCache {
void put(InetSocketAddress clientAddr, InetSocketAddress proxyAddr);
InetSocketAddress get(InetSocketAddress clientAddr);
void invalidate(InetSocketAddress clientAddr);
void clear();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* BSD-3-Clause License.
* Copyright (c) 2025 Semtech
*/
package net.airvantage.proxysocket.core;

public class ProxyProtocolException extends Exception {
private static final long serialVersionUID = 1L;

public ProxyProtocolException(String message) {
super(message);
}
public ProxyProtocolException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* BSD-3-Clause License.
* Copyright (c) 2025 Semtech
*/
package net.airvantage.proxysocket.core;

import net.airvantage.proxysocket.core.v2.ProxyHeader;
import java.net.InetSocketAddress;

/**
* Metrics/observability callbacks for Proxy Protocol processing.
* Implementations must be thread-safe.
*/
public interface ProxyProtocolMetricsListener {
default void onHeaderParsed(ProxyHeader header) {}
default void onParseError(Exception e) {}
default void onCacheHit(InetSocketAddress client) {}
default void onCacheMiss(InetSocketAddress client) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* BSD-3-Clause License.
* Copyright (c) 2025 Semtech
*/
package net.airvantage.proxysocket.core;

public final class ProxyProtocolParseException extends ProxyProtocolException {
private static final long serialVersionUID = 1L;

public ProxyProtocolParseException(String message) {
super(message);
}
public ProxyProtocolParseException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* BSD-3-Clause License.
* Copyright (c) 2025 Semtech
*/
package net.airvantage.proxysocket.core.cache;

import net.airvantage.proxysocket.core.ProxyAddressCache;
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* Simple thread-safe cache backed by {@link ConcurrentHashMap}.
*/
public final class ConcurrentMapProxyAddressCache implements ProxyAddressCache {
private final ConcurrentMap<InetSocketAddress, InetSocketAddress> map = new ConcurrentHashMap<>();

@Override
public void put(InetSocketAddress clientAddr, InetSocketAddress proxyAddr) {
map.put(clientAddr, proxyAddr);
}

@Override
public InetSocketAddress get(InetSocketAddress clientAddr) {
return map.get(clientAddr);
}

@Override
public void invalidate(InetSocketAddress clientAddr) {
map.remove(clientAddr);
}

@Override
public void clear() {
map.clear();
}
}
Loading
Loading