Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
GlowrootJettyExtractors.java
Go to the documentation of this file.
1package dev.rafex.ether.glowroot.jetty12;
2
3import java.util.function.Function;
4
5/*-
6 * #%L
7 * ether-glowroot-jetty12
8 * %%
9 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
10 * %%
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 * #L%
29 */
30
31import dev.rafex.ether.http.core.HttpExchange;
32import dev.rafex.ether.http.jetty12.JettyAuthHandler;
33import dev.rafex.ether.http.jetty12.exchange.JettyHttpExchange;
34
35/**
36 * Factory of Jetty-specific {@link HttpExchange} extractor functions for use
37 * with {@link GlowrootAuthUserMiddleware} and
38 * {@link GlowrootRequestIdMiddleware}.
39 *
40 * <p>
41 * All extractors safely return {@code null} when the exchange is not a
42 * {@link JettyHttpExchange} (e.g. during unit tests with a test double).
43 * </p>
44 *
45 * <p>
46 * Usage:
47 * </p>
48 *
49 * <pre>{@code
50 * // Set authenticated user from JWT auth context
51 * middlewareRegistry.add(new GlowrootAuthUserMiddleware(GlowrootJettyExtractors.authUser()));
52 *
53 * // Capture X-Request-Id header (generate if absent)
54 * middlewareRegistry.add(new GlowrootRequestIdMiddleware(GlowrootJettyExtractors.xRequestId(), true));
55 *
56 * // Capture any custom header
57 * middlewareRegistry.add(new GlowrootRequestIdMiddleware(GlowrootJettyExtractors.header("X-Correlation-Id"), false));
58 * }</pre>
59 */
60public final class GlowrootJettyExtractors {
61
62 private GlowrootJettyExtractors() {
63 }
64
65 /**
66 * Returns an extractor that reads the authenticated user from the
67 * {@link JettyAuthHandler#REQ_ATTR_AUTH} request attribute set by
68 * {@link JettyAuthHandler} after successful JWT verification.
69 *
70 * <p>
71 * The raw {@code context} object is converted via {@code toString()};
72 * implementors of {@link dev.rafex.ether.http.jetty12.TokenVerifier} should
73 * ensure the context object returns the user subject from {@code toString()}.
74 * </p>
75 *
76 * @return extractor function that yields the authenticated user, or
77 * {@code null}
78 */
79 public static Function<HttpExchange, String> authUser() {
80 return exchange -> {
81 if (exchange instanceof final JettyHttpExchange jettyExchange) {
82 final var ctx = jettyExchange.request().getAttribute(JettyAuthHandler.REQ_ATTR_AUTH);
83 return ctx == null ? null : ctx.toString();
84 }
85 return null;
86 };
87 }
88
89 /**
90 * Returns an extractor that reads the value of a specific HTTP request header.
91 *
92 * @param headerName the header name (case-insensitive per HTTP spec)
93 * @return extractor function that yields the header value, or {@code null} if
94 * absent
95 */
96 public static Function<HttpExchange, String> header(final String headerName) {
97 return exchange -> {
98 if (exchange instanceof final JettyHttpExchange jettyExchange) {
99 return jettyExchange.request().getHeaders().get(headerName);
100 }
101 return null;
102 };
103 }
104
105 /**
106 * Returns an extractor for the {@code X-Request-Id} header, the de-facto
107 * standard for HTTP request correlation.
108 *
109 * @return extractor function that yields the {@code X-Request-Id} header value,
110 * or {@code null}
111 */
112 public static Function<HttpExchange, String> xRequestId() {
113 return header("X-Request-Id");
114 }
115
116 /**
117 * Returns an extractor for the client IP address, preferring
118 * {@code X-Forwarded-For} (set by reverse proxies / load-balancers) over the
119 * direct remote address.
120 *
121 * @return extractor function that yields the client IP address, or {@code null}
122 */
123 public static Function<HttpExchange, String> clientIp() {
124 return exchange -> {
125 if (exchange instanceof final JettyHttpExchange jettyExchange) {
126 final var forwarded = jettyExchange.request().getHeaders().get("X-Forwarded-For");
127 if (forwarded != null && !forwarded.isBlank()) {
128 // X-Forwarded-For may contain a comma-separated list; first entry is the client
129 final int comma = forwarded.indexOf(',');
130 return comma > 0 ? forwarded.substring(0, comma).trim() : forwarded.trim();
131 }
132 final var addr = jettyExchange.request().getConnectionMetaData().getRemoteSocketAddress();
133 return addr == null ? null : addr.toString();
134 }
135 return null;
136 };
137 }
138}
static Function< HttpExchange, String > header(final String headerName)
Returns an extractor that reads the value of a specific HTTP request header.
static Function< HttpExchange, String > authUser()
Returns an extractor that reads the authenticated user from the JettyAuthHandler#REQ_ATTR_AUTH reques...
static Function< HttpExchange, String > clientIp()
Returns an extractor for the client IP address, preferring X-Forwarded-For (set by reverse proxies / ...
static Function< HttpExchange, String > xRequestId()
Returns an extractor for the X-Request-Id header, the de-facto standard for HTTP request correlation.