Skip to content

Commit fcee646

Browse files
committed
Updated HTTP server to completely use blocking IO and Java 21 virtual threads.
1 parent 3cc242b commit fcee646

File tree

69 files changed

+2942
-20992
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2942
-20992
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- uses: actions/setup-java@v4
1818
with:
1919
distribution: 'temurin'
20-
java-version: 17
20+
java-version: 21
2121
- name: Install Savant Build
2222
run: |
2323
mkdir -p ~/dev/savant
@@ -28,7 +28,7 @@ jobs:
2828
ln -s savant-2.0.0-RC.7 current
2929
rm savant.tar.gz
3030
cat <<EOF > ~/.savant/plugins/org.savantbuild.plugin.java.properties
31-
17=${JAVA_HOME_17_X64}
31+
21=${JAVA_HOME_21_X64}
3232
EOF
3333
shell: bash
3434
- name: Run the build

build.savant

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jackson5Version = "3.0.1"
1717
restifyVersion = "4.2.1"
1818
testngVersion = "7.10.2"
1919

20-
project(group: "io.fusionauth", name: "java-http", version: "0.3.5", licenses: ["ApacheV2_0"]) {
20+
project(group: "io.fusionauth", name: "java-http", version: "0.4.0-RC.1", licenses: ["ApacheV2_0"]) {
2121
workflow {
2222
fetch {
2323
// Dependency resolution order:
@@ -61,9 +61,9 @@ idea = loadPlugin(id: "org.savantbuild.plugin:idea:2.0.0-RC.7")
6161
release = loadPlugin(id: "org.savantbuild.plugin:release-git:2.0.0-RC.6")
6262
pom = loadPlugin(id: "org.savantbuild.plugin:pom:2.0.0-RC.6")
6363

64-
java.settings.javaVersion = "17"
64+
java.settings.javaVersion = "21"
6565
java.settings.compilerArguments = "--add-exports java.base/sun.security.x509=ALL-UNNAMED --add-exports java.base/sun.security.util=ALL-UNNAMED -XDignore.symbol.file"
66-
javaTestNG.settings.javaVersion = "17"
66+
javaTestNG.settings.javaVersion = "21"
6767
javaTestNG.settings.jvmArguments = "--add-exports java.base/sun.security.x509=ALL-UNNAMED --add-exports java.base/sun.security.util=ALL-UNNAMED"
6868
javaTestNG.settings.testngArguments = "-listener io.fusionauth.http.BaseTest\$TestListener"
6969

java-http.iml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
</CLASSES>
4444
<JAVADOC />
4545
<SOURCES>
46-
<root url="jar://$MODULE_DIR$/.savant/cache/com/fasterxml/jackson/core/jackson-databind/2.15.4/jackson-databind-2.15.4-src.jar!/" />
46+
<root url="jar://$MODULE_DIR$/.savant/cache/com/fasterxml/jackson/core/jackson-databind/2.15.4/jackson-databind-2.15.4-sources.jar!/" />
4747
</SOURCES>
4848
</library>
4949
</orderEntry>
@@ -54,7 +54,7 @@
5454
</CLASSES>
5555
<JAVADOC />
5656
<SOURCES>
57-
<root url="jar://$MODULE_DIR$/.savant/cache/com/fasterxml/jackson/core/jackson-annotations/2.15.4/jackson-annotations-2.15.4-src.jar!/" />
57+
<root url="jar://$MODULE_DIR$/.savant/cache/com/fasterxml/jackson/core/jackson-annotations/2.15.4/jackson-annotations-2.15.4-sources.jar!/" />
5858
</SOURCES>
5959
</library>
6060
</orderEntry>
@@ -65,7 +65,7 @@
6565
</CLASSES>
6666
<JAVADOC />
6767
<SOURCES>
68-
<root url="jar://$MODULE_DIR$/.savant/cache/com/fasterxml/jackson/core/jackson-core/2.15.4/jackson-core-2.15.4-src.jar!/" />
68+
<root url="jar://$MODULE_DIR$/.savant/cache/com/fasterxml/jackson/core/jackson-core/2.15.4/jackson-core-2.15.4-sources.jar!/" />
6969
</SOURCES>
7070
</library>
7171
</orderEntry>
@@ -76,7 +76,7 @@
7676
</CLASSES>
7777
<JAVADOC />
7878
<SOURCES>
79-
<root url="jar://$MODULE_DIR$/.savant/cache/org/testng/testng/7.10.2/testng-7.10.2-src.jar!/" />
79+
<root url="jar://$MODULE_DIR$/.savant/cache/org/testng/testng/7.10.2/testng-7.10.2-sources.jar!/" />
8080
</SOURCES>
8181
</library>
8282
</orderEntry>
@@ -109,7 +109,7 @@
109109
</CLASSES>
110110
<JAVADOC />
111111
<SOURCES>
112-
<root url="jar://$MODULE_DIR$/.savant/cache/org/webjars/jquery/3.7.1/jquery-3.7.1-src.jar!/" />
112+
<root url="jar://$MODULE_DIR$/.savant/cache/org/webjars/jquery/3.7.1/jquery-3.7.1-sources.jar!/" />
113113
</SOURCES>
114114
</library>
115115
</orderEntry>

java-http.ipr

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@
1313
<profile version="1.0">
1414
<option name="myName" value="Project Default" />
1515
<inspection_tool class="HttpUrlsUsage" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
16+
<inspection_tool class="NullableProblems" enabled="true" level="WARNING" enabled_by_default="true">
17+
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
18+
<option name="REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL" value="true" />
19+
<option name="REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE" value="true" />
20+
<option name="REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL" value="true" />
21+
<option name="REPORT_NOT_ANNOTATED_GETTER" value="true" />
22+
<option name="IGNORE_EXTERNAL_SUPER_NOTNULL" value="true" />
23+
<option name="REPORT_NOT_ANNOTATED_SETTER_PARAMETER" value="true" />
24+
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
25+
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
26+
</inspection_tool>
1627
<inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true">
1728
<option name="ignoredTypes">
1829
<set>
@@ -1391,7 +1402,7 @@
13911402
<module fileurl="file://$PROJECT_DIR$/load-tests/tomcat/tomcat.iml" filepath="$PROJECT_DIR$/load-tests/tomcat/tomcat.iml" />
13921403
</modules>
13931404
</component>
1394-
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="Java 17" project-jdk-type="JavaSDK">
1405+
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="Java 21" project-jdk-type="JavaSDK">
13951406
<output url="file://$PROJECT_DIR$/out" />
13961407
</component>
13971408
<component name="VcsDirectoryMappings">

load-tests/self/build.savant

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ import java.nio.file.Paths
1515
* either express or implied. See the License for the specific
1616
* language governing permissions and limitations under the License.
1717
*/
18-
restifyVersion = "4.1.2"
19-
testngVersion = "7.6.1"
20-
2118
project(group: "io.fusionauth", name: "self", version: "0.1.0", licenses: ["Apache-2.0"]) {
2219
workflow {
2320
fetch {
@@ -35,7 +32,7 @@ project(group: "io.fusionauth", name: "self", version: "0.1.0", licenses: ["Apac
3532

3633
dependencies {
3734
group(name: "compile") {
38-
dependency(id: "io.fusionauth:java-http:0.1.8")
35+
dependency(id: "io.fusionauth:java-http:0.4.0-{integration}")
3936
}
4037
}
4138

@@ -52,7 +49,7 @@ idea = loadPlugin(id: "org.savantbuild.plugin:idea:2.0.0-RC.4")
5249
tomcat = loadPlugin(id: "org.savantbuild.plugin:tomcat:2.0.0-RC.4")
5350
webapp = loadPlugin(id: "org.savantbuild.plugin:webapp:2.0.0-RC.5.{integration}")
5451

55-
java.settings.javaVersion = "17"
52+
java.settings.javaVersion = "21"
5653
java.settings.compilerArguments = "--add-exports java.base/sun.security.x509=ALL-UNNAMED --add-exports java.base/sun.security.util=ALL-UNNAMED -XDignore.symbol.file"
5754
tomcat.settings.buildDirectory = Paths.get("build/dist/tomcat")
5855
tomcat.settings.buildWebDirectory = Paths.get("build/dist/tomcat/web")
@@ -72,15 +69,15 @@ target(name: "jar", description: "Builds the project JARs", dependsOn: ["compile
7269

7370
target(name: "app", description: "Builds the app", dependsOn: ["jar"]) {
7471
// Copy all compile dependencies to be able to run command line
75-
dependency.copy(to: "build/lib") {
72+
dependency.copy(to: "build/dist/lib") {
7673
dependencies(group: "compile", transitive: true, fetchSource: false)
7774
}
7875

79-
file.copy(to: "build/lib") {
76+
file.copy(to: "build/dist/lib") {
8077
fileSet(dir: "build/jars")
8178
}
8279

83-
file.copy(to: "build") {
80+
file.copy(to: "build/dist") {
8481
fileSet(dir: "src/main/script")
8582
}
8683
}

load-tests/self/self.iml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
<orderEntry type="module-library">
1111
<library>
1212
<CLASSES>
13-
<root url="jar://$USER_HOME$/.savant/cache/io/fusionauth/java-http/0.1.8/java-http-0.1.8.jar!/" />
13+
<root url="jar://$USER_HOME$/.savant/cache/io/fusionauth/java-http/0.4.0-{integration}/java-http-0.4.0-{integration}.jar!/" />
1414
</CLASSES>
1515
<JAVADOC />
1616
<SOURCES>
17-
<root url="jar://$USER_HOME$/.savant/cache/io/fusionauth/java-http/0.1.8/java-http-0.1.8-src.jar!/" />
17+
<root url="jar://$USER_HOME$/.savant/cache/io/fusionauth/java-http/0.4.0-{integration}/java-http-0.4.0-{integration}-src.jar!/" />
1818
</SOURCES>
1919
</library>
2020
</orderEntry>

load-tests/self/src/main/java/io/fusionauth/http/load/Main.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,35 @@
1515
*/
1616
package io.fusionauth.http.load;
1717

18+
import java.time.Duration;
19+
20+
import io.fusionauth.http.log.Level;
21+
import io.fusionauth.http.log.SystemOutLoggerFactory;
22+
import io.fusionauth.http.server.CountingInstrumenter;
1823
import io.fusionauth.http.server.HTTPListenerConfiguration;
1924
import io.fusionauth.http.server.HTTPServer;
2025

2126
public class Main {
2227
public static void main(String[] args) throws Exception {
28+
SystemOutLoggerFactory.FACTORY.getLogger(Object.class).setLevel(Level.Debug);
29+
2330
System.out.println("Starting java-http server");
31+
CountingInstrumenter instrumenter = new CountingInstrumenter();
2432
try (HTTPServer ignore = new HTTPServer().withHandler(new LoadHandler())
33+
.withClientTimeout(Duration.ofSeconds(100L))
2534
.withCompressByDefault(false)
35+
.withInstrumenter(instrumenter)
2636
.withListener(new HTTPListenerConfiguration(8080))
27-
.withNumberOfWorkerThreads(200)
37+
.withLoggerFactory(SystemOutLoggerFactory.FACTORY)
2838
.start()) {
29-
Thread.sleep(1_000_000);
39+
40+
for (int i = 0; i < 1_000; i++) {
41+
Thread.sleep(10_000);
42+
System.out.printf("Current stats. Bad requests [%s]. Bytes read [%s]. Bytes written [%s]. Chunked requests [%s]. Chunked responses [%s]. Closed connections [%s]. Connections [%s]. Started [%s].\n",
43+
instrumenter.getBadRequests(), instrumenter.getBytesRead(), instrumenter.getBytesWritten(), instrumenter.getChunkedRequests(),
44+
instrumenter.getChunkedResponses(), instrumenter.getClosedConnections(), instrumenter.getConnections(), instrumenter.getStartedCount());
45+
}
46+
3047
System.out.println("Shutting down java-http server");
3148
}
3249
}

load-tests/self/src/main/script/start.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ if [[ $# -gt 1 && $1 == "--suspend" ]]; then
3333
shift
3434
fi
3535

36-
~/dev/java/current17/bin/java ${suspend} -cp "${CLASSPATH}" io.fusionauth.http.load.Main
36+
~/dev/java/current21/bin/java ${suspend} -cp "${CLASSPATH}" io.fusionauth.http.load.Main

src/main/java/io/fusionauth/http/ClientSSLHandshakeException.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/main/java/io/fusionauth/http/ConnectionClosedException.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@
1616
package io.fusionauth.http;
1717

1818
/**
19-
* An exception thrown when the server determines the request is too slow, has timed out or something like that.
19+
* An exception thrown when the client closes the socket and the server should handle it gracefully.
2020
*
2121
* @author Daniel DeGroff
2222
*/
2323
public class ConnectionClosedException extends RuntimeException {
24-
public ConnectionClosedException(Throwable cause) {
25-
super(cause);
26-
}
2724
}

src/main/java/io/fusionauth/http/Cookie.java

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,25 @@
2020
import java.util.List;
2121
import java.util.Objects;
2222

23+
import io.fusionauth.http.HTTPValues.CookieAttributes;
2324
import io.fusionauth.http.util.DateTools;
2425

26+
@SuppressWarnings("unused")
2527
public class Cookie implements Buildable<Cookie> {
28+
public static final String DomainPrefix = "; " + CookieAttributes.Domain + "=";
29+
30+
public static final String ExpiresPrefix = "; " + CookieAttributes.Expires + "=";
31+
32+
public static final String HTTPOnlyPrefix = "; " + CookieAttributes.HttpOnly;
33+
34+
public static final String MaxAgePrefix = "; " + CookieAttributes.MaxAge + "=";
35+
36+
public static final String PathPrefix = "; " + CookieAttributes.Path + "=";
37+
38+
public static final String SameSitePrefix = "; " + CookieAttributes.SameSite + "=";
39+
40+
public static final String SecurePrefix = "; " + CookieAttributes.Secure;
41+
2642
public String domain;
2743

2844
public ZonedDateTime expires;
@@ -93,7 +109,7 @@ public static List<Cookie> fromRequestHeader(String header) {
93109
start = i + 1;
94110
} else if (c == ';' && inValue) {
95111
value = new String(chars, start, i - start);
96-
if (name.trim().length() > 0 && value.trim().length() > 0) {
112+
if (!name.trim().isEmpty() && !value.trim().isEmpty()) {
97113
cookies.add(new Cookie(name, value));
98114
}
99115

@@ -116,7 +132,7 @@ public static List<Cookie> fromRequestHeader(String header) {
116132
value = header.substring(start);
117133
}
118134

119-
if (name != null && value != null && name.trim().length() > 0 && value.trim().length() > 0) {
135+
if (name != null && value != null && !name.trim().isEmpty() && !value.trim().isEmpty()) {
120136
cookies.add(new Cookie(name, value));
121137
}
122138

@@ -208,7 +224,7 @@ public static Cookie fromResponseHeader(String header) {
208224
if (inAttributes) {
209225
cookie.addAttribute(name, value);
210226
} else {
211-
if (name == null || value == null || name.trim().length() == 0) {
227+
if (name == null || value == null || name.trim().isEmpty()) {
212228
return null;
213229
}
214230

@@ -261,10 +277,9 @@ public boolean equals(Object o) {
261277
if (this == o) {
262278
return true;
263279
}
264-
if (!(o instanceof Cookie)) {
280+
if (!(o instanceof Cookie cookie)) {
265281
return false;
266282
}
267-
Cookie cookie = (Cookie) o;
268283
return httpOnly == cookie.httpOnly &&
269284
secure == cookie.secure &&
270285
Objects.equals(domain, cookie.domain) &&
@@ -357,14 +372,34 @@ public String toRequestHeader() {
357372
}
358373

359374
public String toResponseHeader() {
360-
return name + "=" + value
361-
+ (domain != null ? ("; " + HTTPValues.CookieAttributes.Domain + "=" + domain) : "")
362-
+ (expires != null ? ("; " + HTTPValues.CookieAttributes.Expires + "=" + DateTools.format(expires)) : "")
363-
+ (httpOnly ? ("; " + HTTPValues.CookieAttributes.HttpOnly) : "")
364-
+ (maxAge != null ? ("; " + HTTPValues.CookieAttributes.MaxAge + "=" + maxAge) : "")
365-
+ (path != null ? ("; " + HTTPValues.CookieAttributes.Path + "=" + path) : "")
366-
+ (sameSite != null ? ("; " + HTTPValues.CookieAttributes.SameSite + "=" + sameSite.name()) : "")
367-
+ (secure ? ("; " + HTTPValues.CookieAttributes.Secure) : "");
375+
var build = new StringBuilder();
376+
build.append(name).append("=");
377+
if (value != null) {
378+
build.append(value);
379+
}
380+
if (domain != null) {
381+
build.append(DomainPrefix).append(domain);
382+
}
383+
if (expires != null) {
384+
build.append(ExpiresPrefix).append(DateTools.format(expires));
385+
}
386+
if (httpOnly) {
387+
build.append(HTTPOnlyPrefix);
388+
}
389+
if (maxAge != null) {
390+
build.append(MaxAgePrefix).append(maxAge);
391+
}
392+
if (path != null) {
393+
build.append(PathPrefix).append(path);
394+
}
395+
if (sameSite != null) {
396+
build.append(SameSitePrefix).append(sameSite.name());
397+
}
398+
if (secure) {
399+
build.append(SecurePrefix);
400+
}
401+
402+
return build.toString();
368403
}
369404

370405
public enum SameSite {

0 commit comments

Comments
 (0)