66 */
77package jetty ;
88
9+ import java .io .IOException ;
10+ import java .net .ServerSocket ;
11+ import java .net .URI ;
12+ import java .net .http .HttpClient ;
13+ import java .net .http .HttpRequest ;
14+ import java .net .http .HttpResponse ;
15+ import java .nio .ByteBuffer ;
16+ import java .nio .charset .StandardCharsets ;
17+ import java .nio .file .Path ;
18+ import java .time .Duration ;
19+
920import org .eclipse .jetty .ee10 .servlet .ServletContextHandler ;
1021import org .eclipse .jetty .ee10 .webapp .WebAppContext ;
22+ import org .eclipse .jetty .server .ConnectionFactory ;
23+ import org .eclipse .jetty .server .Connector ;
24+ import org .eclipse .jetty .server .ForwardedRequestCustomizer ;
1125import org .eclipse .jetty .server .Handler ;
26+ import org .eclipse .jetty .server .HttpConfiguration ;
1227import org .eclipse .jetty .server .Request ;
1328import org .eclipse .jetty .server .Response ;
1429import org .eclipse .jetty .server .Server ;
2035import org .junit .jupiter .api .Test ;
2136import org .junit .jupiter .api .io .TempDir ;
2237
23- import java .io .IOException ;
24- import java .net .URI ;
25- import java .net .http .HttpClient ;
26- import java .net .http .HttpRequest ;
27- import java .net .http .HttpResponse ;
28- import java .nio .ByteBuffer ;
29- import java .nio .charset .StandardCharsets ;
30- import java .nio .file .Path ;
31- import java .time .Duration ;
32-
3338import static org .assertj .core .api .Assertions .assertThat ;
3439
3540public class JettyTests {
3641
37- private static final int PORT = 8080 ;
42+ private static int port ;
3843
39- private static boolean DEBUG = false ;
44+ private static final boolean DEBUG = false ;
4045
4146 @ BeforeAll
42- static void beforeAll () {
47+ static void beforeAll () throws IOException {
48+ port = findAvailablePort ();
49+ System .out .println ("Using port " + port + " for Jetty" );
4350 System .setProperty ("org.slf4j.simpleLogger.defaultLogLevel" , DEBUG ? "debug" : "warn" );
4451 }
4552
53+ private static int findAvailablePort () throws IOException {
54+ try (ServerSocket socket = new ServerSocket ()) {
55+ socket .bind (null );
56+ return socket .getLocalPort ();
57+ }
58+ }
59+
4660 @ Test
4761 void typeUtilWorks () {
4862 assertThat (TypeUtil .fromName ("java.lang.String" )).isEqualTo (String .class );
4963 }
5064
5165 @ Test
5266 void http () throws Exception {
53- Server server = new Server (PORT );
67+ Server server = new Server (port );
5468 server .setHandler (new Handler .Abstract () {
5569 @ Override
5670 public boolean handle (Request request , Response response , Callback callback ) {
@@ -72,7 +86,7 @@ public boolean handle(Request request, Response response, Callback callback) {
7286
7387 @ Test
7488 void servlet () throws Exception {
75- Server server = new Server (PORT );
89+ Server server = new Server (port );
7690 ServletContextHandler handler = new ServletContextHandler ();
7791 handler .setContextPath ("/" );
7892 handler .addServlet (HelloWorldServlet .class , "/*" );
@@ -94,7 +108,7 @@ void webapp(@TempDir Path tempDir) throws Exception {
94108 // See https://github.com/eclipse/jetty.project/issues/9116
95109 ResourceFactory .registerResourceFactory ("resource" , new URLResourceFactory ());
96110
97- Server server = new Server (PORT );
111+ Server server = new Server (port );
98112 WebAppContext context = new WebAppContext ();
99113 // EnvConfiguration and PlusConfiguration uses JNDI, which is not what we want to include
100114 context .setBaseResourceAsPath (tempDir );
@@ -111,9 +125,40 @@ void webapp(@TempDir Path tempDir) throws Exception {
111125 }
112126 }
113127
128+ @ Test
129+ void forwardHeaders () throws Exception {
130+ Server server = new Server (port );
131+ for (Connector connector : server .getConnectors ()) {
132+ for (ConnectionFactory connectionFactory : connector .getConnectionFactories ()) {
133+ if (connectionFactory instanceof HttpConfiguration .ConnectionFactory ) {
134+ ((HttpConfiguration .ConnectionFactory ) connectionFactory ).getHttpConfiguration ()
135+ .addCustomizer (new ForwardedRequestCustomizer ());
136+ }
137+ }
138+ }
139+ server .setHandler (new Handler .Abstract () {
140+ @ Override
141+ public boolean handle (Request request , Response response , Callback callback ) {
142+ response .setStatus (200 );
143+ response .getHeaders ().add ("Content-Type" , "text/plain" );
144+ String content = "I am " + Request .getServerName (request ) + ":" + Request .getServerPort (request );
145+ response .write (true , ByteBuffer .wrap (content .getBytes (StandardCharsets .UTF_8 )), callback );
146+ return true ;
147+ }
148+ });
149+ server .start ();
150+ try {
151+ HttpResponse <String > response = doHttpRequest ("X-Forwarded-Host" , "some-host" , "X-Forwarded-Port" , "12345" );
152+ assertThat (response .statusCode ()).isEqualTo (200 );
153+ assertThat (response .body ()).isEqualTo ("I am some-host:12345" );
154+ } finally {
155+ server .stop ();
156+ }
157+ }
158+
114159 private static HttpResponse <String > doHttpRequest (String ... headers ) throws IOException , InterruptedException {
115160 HttpClient client = HttpClient .newBuilder ().connectTimeout (Duration .ofSeconds (1 )).build ();
116- HttpRequest .Builder request = HttpRequest .newBuilder (URI .create (String .format ("http://localhost:%d/" , PORT )))
161+ HttpRequest .Builder request = HttpRequest .newBuilder (URI .create (String .format ("http://localhost:%d/" , port )))
117162 .GET ().header ("Accept" , "text/plain" ).timeout (Duration .ofSeconds (1 ));
118163 if (headers .length > 0 ) {
119164 request .headers (headers );
0 commit comments