Skip to content

Commit 6b85cef

Browse files
committed
Be smarter about resolve the port on the baseURL.
1 parent 511dc9c commit 6b85cef

File tree

5 files changed

+139
-20
lines changed

5 files changed

+139
-20
lines changed

build.savant

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jackson5Version = "3.0.1"
1717
restifyVersion = "4.2.0"
1818
testngVersion = "7.8.0"
1919

20-
project(group: "io.fusionauth", name: "java-http", version: "0.3.1", licenses: ["ApacheV2_0"]) {
20+
project(group: "io.fusionauth", name: "java-http", version: "0.3.2", licenses: ["ApacheV2_0"]) {
2121
workflow {
2222
fetch {
2323
// Dependency resolution order:

java-http.ipr

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true">
1616
<option name="ignoredTypes">
1717
<set>
18+
<option value="java.lang.String" />
1819
<option value="java.util.List" />
1920
</set>
2021
</option>
@@ -184,7 +185,7 @@
184185
</option>
185186
<option name="RIGHT_MARGIN" value="140" />
186187
<option name="WRAP_COMMENTS" value="true" />
187-
<DB2CodeStyleSettings version="6">
188+
<DB2CodeStyleSettings version="7">
188189
<option name="USE_GENERAL_STYLE" value="false" />
189190
<option name="TYPE_CASE" value="3" />
190191
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -221,7 +222,7 @@
221222
<option name="EXPR_CASE_THEN_WRAP" value="true" />
222223
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
223224
</DB2CodeStyleSettings>
224-
<DerbyCodeStyleSettings version="6">
225+
<DerbyCodeStyleSettings version="7">
225226
<option name="USE_GENERAL_STYLE" value="false" />
226227
<option name="TYPE_CASE" value="3" />
227228
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -262,7 +263,7 @@
262263
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" />
263264
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" />
264265
</GroovyCodeStyleSettings>
265-
<H2CodeStyleSettings version="6">
266+
<H2CodeStyleSettings version="7">
266267
<option name="USE_GENERAL_STYLE" value="false" />
267268
<option name="TYPE_CASE" value="3" />
268269
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -299,7 +300,7 @@
299300
<option name="EXPR_CASE_THEN_WRAP" value="true" />
300301
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
301302
</H2CodeStyleSettings>
302-
<HSQLCodeStyleSettings version="6">
303+
<HSQLCodeStyleSettings version="7">
303304
<option name="USE_GENERAL_STYLE" value="false" />
304305
<option name="TYPE_CASE" value="3" />
305306
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -359,7 +360,7 @@
359360
</option>
360361
<option name="JD_INDENT_ON_CONTINUATION" value="true" />
361362
</JavaCodeStyleSettings>
362-
<MSSQLCodeStyleSettings version="6">
363+
<MSSQLCodeStyleSettings version="7">
363364
<option name="USE_GENERAL_STYLE" value="false" />
364365
<option name="TYPE_CASE" value="3" />
365366
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -402,7 +403,7 @@
402403
<MarkdownNavigatorCodeStyleSettings>
403404
<option name="RIGHT_MARGIN" value="72" />
404405
</MarkdownNavigatorCodeStyleSettings>
405-
<MySQLCodeStyleSettings version="6">
406+
<MySQLCodeStyleSettings version="7">
406407
<option name="USE_GENERAL_STYLE" value="false" />
407408
<option name="TYPE_CASE" value="3" />
408409
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -439,7 +440,7 @@
439440
<option name="EXPR_CASE_THEN_WRAP" value="true" />
440441
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
441442
</MySQLCodeStyleSettings>
442-
<OracleCodeStyleSettings version="6">
443+
<OracleCodeStyleSettings version="7">
443444
<option name="USE_GENERAL_STYLE" value="false" />
444445
<option name="TYPE_CASE" value="3" />
445446
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -476,7 +477,7 @@
476477
<option name="EXPR_CASE_THEN_WRAP" value="true" />
477478
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
478479
</OracleCodeStyleSettings>
479-
<PostgresCodeStyleSettings version="6">
480+
<PostgresCodeStyleSettings version="7">
480481
<option name="USE_GENERAL_STYLE" value="false" />
481482
<option name="TYPE_CASE" value="3" />
482483
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -513,7 +514,7 @@
513514
<option name="EXPR_CASE_THEN_WRAP" value="true" />
514515
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
515516
</PostgresCodeStyleSettings>
516-
<SQLiteCodeStyleSettings version="6">
517+
<SQLiteCodeStyleSettings version="7">
517518
<option name="USE_GENERAL_STYLE" value="false" />
518519
<option name="TYPE_CASE" value="3" />
519520
<option name="CUSTOM_TYPE_CASE" value="3" />
@@ -550,7 +551,7 @@
550551
<option name="EXPR_CASE_THEN_WRAP" value="true" />
551552
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
552553
</SQLiteCodeStyleSettings>
553-
<SqlCodeStyleSettings version="6">
554+
<SqlCodeStyleSettings version="7">
554555
<option name="TYPE_CASE" value="3" />
555556
<option name="CUSTOM_TYPE_CASE" value="3" />
556557
<option name="ALIAS_CASE" value="4" />
@@ -591,7 +592,7 @@
591592
<option name="WRAP_INSIDE_WHERE" value="5" />
592593
<option name="WRAP_INSIDE_SET" value="5" />
593594
</SqlCodeStyleSettings>
594-
<SybaseCodeStyleSettings version="6">
595+
<SybaseCodeStyleSettings version="7">
595596
<option name="USE_GENERAL_STYLE" value="false" />
596597
<option name="TYPE_CASE" value="3" />
597598
<option name="CUSTOM_TYPE_CASE" value="3" />

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>io.fusionauth</groupId>
44
<artifactId>java-http</artifactId>
5-
<version>0.3.1</version>
5+
<version>0.3.2</version>
66
<packaging>jar</packaging>
77

88
<name>Java HTTP library (client and server)</name>

src/main/java/io/fusionauth/http/server/HTTPRequest.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2023, FusionAuth, All Rights Reserved
2+
* Copyright (c) 2022-2024, FusionAuth, All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717

1818
import java.io.IOException;
1919
import java.io.InputStream;
20+
import java.net.URI;
2021
import java.nio.charset.Charset;
2122
import java.nio.charset.StandardCharsets;
2223
import java.time.Instant;
@@ -249,11 +250,7 @@ public String getBaseURL() {
249250
}
250251

251252
String serverName = getHost().toLowerCase();
252-
int serverPort = getPort();
253-
// Ignore port 80 for http
254-
if (getScheme().equalsIgnoreCase("http") && serverPort == 80) {
255-
serverPort = -1;
256-
}
253+
int serverPort = getBaseURLServerPort();
257254

258255
String uri = scheme + "://" + serverName;
259256
if (serverPort > 0) {
@@ -768,4 +765,52 @@ private void decodeHeader(String name, String value) {
768765
break;
769766
}
770767
}
768+
769+
/**
770+
* Try and infer the port if the X-Forwarded-Port header is not present.
771+
*
772+
* @return the server port
773+
*/
774+
private int getBaseURLServerPort() {
775+
// Ignore port 80 for http
776+
int serverPort = getPort();
777+
if (scheme.equalsIgnoreCase("http") && serverPort == 80) {
778+
serverPort = -1;
779+
}
780+
781+
// See if we can infer a better choice for the port than the current serverPort.
782+
783+
// If we already have an X-Forwarded-Port header, nothing to do here.
784+
if (getHeader(Headers.XForwardedPort) != null) {
785+
return serverPort;
786+
}
787+
788+
// If we don't have an X-Forwarded-Proto header, or it is not https, nothing to do here.
789+
if (!"https".equals(getHeader(Headers.XForwardedProto))) {
790+
return serverPort;
791+
}
792+
793+
// If we don't have a host header, nothing to do here.
794+
String hostHeader = getHeader(Headers.XForwardedHost);
795+
if (hostHeader == null) {
796+
return serverPort;
797+
}
798+
799+
// If we can pull the port from the X-Forwarded-Host header, let's do that.
800+
// - This is effectively the same as X-Forwarded-Port
801+
try {
802+
int hostPort = URI.create("https://" + hostHeader).getPort();
803+
if (hostPort != -1) {
804+
return hostPort;
805+
}
806+
} catch (Exception ignore) {
807+
// If we can't parse the hostHeader, keep the existing resolved port
808+
return serverPort;
809+
}
810+
811+
// If we made this far, we have met all conditions for assuming port 443.
812+
// - We are missing the X-Forwarded-Port header, we have an X-Forwarded-Proto header of https, and we have an X-Forwarded-Host
813+
// header value that has not defined a port, and it has defined a port.
814+
return 443;
815+
}
771816
}

src/test/java/io/fusionauth/http/HTTPRequestTest.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, FusionAuth, All Rights Reserved
2+
* Copyright (c) 2022-2024, FusionAuth, All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,12 +18,14 @@
1818
import java.nio.charset.StandardCharsets;
1919
import java.util.List;
2020
import java.util.Map;
21+
import java.util.function.Consumer;
2122

2223
import io.fusionauth.http.HTTPValues.Headers;
2324
import io.fusionauth.http.server.HTTPRequest;
2425
import org.testng.annotations.Test;
2526
import static org.testng.Assert.assertEquals;
2627
import static org.testng.Assert.assertTrue;
28+
import static org.testng.Assert.fail;
2729

2830
/**
2931
* Tests the HTTPRequest.
@@ -51,6 +53,51 @@ public void decodeHeaders() {
5153
assertEquals(request.getCharacterEncoding(), StandardCharsets.UTF_8);
5254
}
5355

56+
@Test
57+
public void getBaseURL() {
58+
// Use case 1: missing X-Forwarded-Port, infer it from https
59+
assertBaseURL("https://acme.com",
60+
"X-Forwarded-Host", "acme.com",
61+
"X-Forwarded-Proto", "https");
62+
63+
// Use case 2: Set a port on the Host, we will use that.
64+
assertBaseURL("https://acme.com:8192",
65+
"X-Forwarded-Host", "acme.com:8192",
66+
"X-Forwarded-Proto", "https");
67+
68+
// Use case 3: Set port from the X-Forwarded-Port header
69+
assertBaseURL("https://acme.com",
70+
"X-Forwarded-Host", "acme.com",
71+
"X-Forwarded-Port", "443",
72+
"X-Forwarded-Proto", "https");
73+
74+
// Use case 4: Missing X-Forwarded-Proto header, cannot infer 443
75+
assertBaseURL("http://acme.com:8080",
76+
"X-Forwarded-Host", "acme.com");
77+
78+
// Use case 5: Malformed X-Forwarded-Host header, so we'll ignore the port on the -Host header.
79+
assertBaseURL("https://acme.com:8080",
80+
"X-Forwarded-Host", "acme.com:##",
81+
"X-Forwarded-Proto", "https");
82+
83+
// Use case 6: Missing X-Forwarded-Host
84+
assertBaseURL("https://localhost:8080",
85+
"X-Forwarded-Proto", "https");
86+
87+
// Use case 7: http and port 80
88+
assertBaseURL("https://localhost",
89+
request -> request.setPort(80),
90+
"X-Forwarded-Proto", "https");
91+
92+
// Use case 8: https and port 80
93+
assertBaseURL("http://localhost",
94+
request -> {
95+
request.setPort(80);
96+
request.setScheme("https");
97+
},
98+
"X-Forwarded-Proto", "http");
99+
}
100+
54101
@Test
55102
public void hostHeaderPortHandling() {
56103
// positive cases
@@ -124,6 +171,32 @@ public void queryString() {
124171
assertEquals(request.getURLParameters(), Map.of("name", List.of("")));
125172
}
126173

174+
private void assertBaseURL(String expected, Consumer<HTTPRequest> consumer, String... headers) {
175+
HTTPRequest request = new HTTPRequest();
176+
177+
request.setScheme("http");
178+
request.setHost("localhost");
179+
request.setPort(8080);
180+
181+
if (consumer != null) {
182+
consumer.accept(request);
183+
}
184+
185+
if (headers.length % 2 != 0) {
186+
fail("You need to provide pairs.");
187+
}
188+
189+
for (int i = 0; i < headers.length; i = i + 2) {
190+
request.setHeader(headers[i], headers[i + 1]);
191+
}
192+
193+
assertEquals(request.getBaseURL(), expected);
194+
}
195+
196+
private void assertBaseURL(String expected, String... headers) {
197+
assertBaseURL(expected, null, headers);
198+
}
199+
127200
private void assertURLs(String scheme, String source, String host, int port, String baseURL) {
128201
HTTPRequest request = new HTTPRequest();
129202
request.setScheme(scheme);

0 commit comments

Comments
 (0)