Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
JettyRequestIpResolver.java
Go to the documentation of this file.
1package dev.rafex.ether.http.jetty12;
2
3/*-
4 * #%L
5 * ether-http-jetty12
6 * %%
7 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
8 * %%
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 * #L%
27 */
28
29import java.util.List;
30
31import org.eclipse.jetty.server.Request;
32
33import dev.rafex.ether.http.security.proxy.TrustedProxyPolicy;
34
35final class JettyRequestIpResolver {
36
37 private JettyRequestIpResolver() {
38 }
39
40 static String resolve(final Request request, final TrustedProxyPolicy policy) {
41 final var remoteAddress = Request.getRemoteAddr(request);
42 if (policy == null || !policy.trustForwardedHeader() || !policy.isTrusted(remoteAddress)) {
43 return remoteAddress;
44 }
45
46 if (policy.forwardedOnly()) {
47 final var forwarded = request.getHeaders().get("Forwarded");
48 final var forwardedFor = resolveForwarded(forwarded, policy.preferRightMostForwardedFor());
49 return forwardedFor == null ? remoteAddress : forwardedFor;
50 }
51
52 final var xForwardedFor = request.getHeaders().get("X-Forwarded-For");
53 final var forwardedFor = selectFromCsv(xForwardedFor, policy.preferRightMostForwardedFor());
54 return forwardedFor == null ? remoteAddress : forwardedFor;
55 }
56
57 private static String resolveForwarded(final String forwarded, final boolean preferRightMost) {
58 if (forwarded == null || forwarded.isBlank()) {
59 return null;
60 }
61 final List<String> entries = List.of(forwarded.split(","));
62 if (entries.isEmpty()) {
63 return null;
64 }
65 final int start = preferRightMost ? entries.size() - 1 : 0;
66 final int step = preferRightMost ? -1 : 1;
67 for (int i = start; i >= 0 && i < entries.size(); i += step) {
68 final var parts = entries.get(i).trim().split(";");
69 for (final var part : parts) {
70 final var token = part.trim();
71 if (token.regionMatches(true, 0, "for=", 0, 4)) {
72 return stripAddressDecorators(token.substring(4).trim());
73 }
74 }
75 }
76 return null;
77 }
78
79 private static String selectFromCsv(final String raw, final boolean preferRightMost) {
80 if (raw == null || raw.isBlank()) {
81 return null;
82 }
83 final var parts = raw.split(",");
84 if (parts.length == 0) {
85 return null;
86 }
87 final var candidate = preferRightMost ? parts[parts.length - 1] : parts[0];
88 return stripAddressDecorators(candidate.trim());
89 }
90
91 private static String stripAddressDecorators(final String value) {
92 if (value == null || value.isBlank()) {
93 return null;
94 }
95 var sanitized = value;
96 if (sanitized.startsWith("\"") && sanitized.endsWith("\"") && sanitized.length() > 1) {
97 sanitized = sanitized.substring(1, sanitized.length() - 1);
98 }
99 return sanitized;
100 }
101}