Skip to content
This repository was archived by the owner on Mar 24, 2026. It is now read-only.

Commit 9674158

Browse files
committed
added httpserver (com.sun.net.httpserver) framework
1 parent 865098b commit 9674158

10 files changed

Lines changed: 386 additions & 0 deletions

File tree

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ env:
5151
- "TESTDIR=Java/grizzly"
5252
- "TESTDIR=Java/grizzly-jersey"
5353
- "TESTDIR=Java/helidon"
54+
- "TESTDIR=Java/httpserver"
5455
- "TESTDIR=Java/jawn"
5556
- "TESTDIR=Java/javalin"
5657
- "TESTDIR=Java/jetty"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# httpserver Benchmarking Test
2+
3+
This is the com.sun.net.httpserver portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
4+
5+
Package [com.sun.net.httpserver](https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html)
6+
provides a simple high-level Http server API, which can be used to build embedded HTTP servers.
7+
It is built-in to the Oracle JDK and OpenJDK (but is not part of the Java standard
8+
and is not available in other JDKs).
9+
10+
11+
12+
13+
### Test Type Implementation Source Code
14+
15+
* [JSON](src/main/java/benchmarks/Server.java)
16+
* [Plaintext](src/main/java/benchmarks/Server.java)
17+
* [Fortunes](src/main/java/benchmarks/Server.java)
18+
19+
## Important Libraries
20+
The tests were run with:
21+
* [Jackson](https://github.com/FasterXML/jackson)
22+
* [HikariCP](https://github.com/brettwooldridge/HikariCP)
23+
* [HTTL](https://httl.github.io/en/)
24+
25+
## Test URLs
26+
### JSON
27+
28+
http://localhost:8080/json
29+
30+
### Plaintext
31+
32+
http://localhost:8080/plaintext
33+
34+
### Fortunes
35+
36+
http://localhost:8080/fortunes
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"framework": "httpserver",
3+
"tests": [
4+
{
5+
"default": {
6+
"json_url": "/json",
7+
"plaintext_url": "/plaintext",
8+
"port": 8080,
9+
"approach": "Realistic",
10+
"classification": "Platform",
11+
"database": "None",
12+
"framework": "None",
13+
"language": "Java",
14+
"flavor": "None",
15+
"orm": "Raw",
16+
"platform": "httpserver",
17+
"webserver": "None",
18+
"os": "Linux",
19+
"database_os": "Linux",
20+
"display_name": "httpserver",
21+
"notes": "",
22+
"versus": ""
23+
},
24+
"postgres": {
25+
"fortune_url": "/fortunes",
26+
"port": 8080,
27+
"approach": "Realistic",
28+
"classification": "Platform",
29+
"database": "Postgres",
30+
"framework": "None",
31+
"language": "Java",
32+
"flavor": "None",
33+
"orm": "Raw",
34+
"platform": "httpserver",
35+
"webserver": "None",
36+
"os": "Linux",
37+
"database_os": "Linux",
38+
"display_name": "httpserver-postgres",
39+
"notes": "",
40+
"versus": ""
41+
}
42+
}
43+
]
44+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM maven:3.5.3-jdk-10-slim as maven
2+
WORKDIR /httpserver
3+
COPY pom.xml pom.xml
4+
COPY src src
5+
RUN mvn compile assembly:single -q
6+
7+
FROM openjdk:10-jre-slim
8+
WORKDIR /httpserver
9+
COPY --from=maven /httpserver/target/httpserver-1.0-jar-with-dependencies.jar app.jar
10+
CMD ["java", "-server", "-Xss256k", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-jar", "app.jar", "postgres"]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM maven:3.5.3-jdk-10-slim as maven
2+
WORKDIR /httpserver
3+
COPY pom.xml pom.xml
4+
COPY src src
5+
RUN mvn compile assembly:single -q
6+
7+
FROM openjdk:10-jre-slim
8+
WORKDIR /httpserver
9+
COPY --from=maven /httpserver/target/httpserver-1.0-jar-with-dependencies.jar app.jar
10+
CMD ["java", "-server", "-Xss256k", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-jar", "app.jar"]

frameworks/Java/httpserver/pom.xml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.techempower</groupId>
7+
<artifactId>httpserver</artifactId>
8+
<version>1.0</version>
9+
<packaging>jar</packaging>
10+
11+
<properties>
12+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13+
<maven.compiler.source>1.8</maven.compiler.source>
14+
<maven.compiler.target>1.8</maven.compiler.target>
15+
</properties>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>com.fasterxml.jackson.core</groupId>
20+
<artifactId>jackson-databind</artifactId>
21+
<version>2.9.7</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>com.fasterxml.jackson.module</groupId>
25+
<artifactId>jackson-module-afterburner</artifactId>
26+
<version>2.9.7</version>
27+
</dependency>
28+
29+
<dependency>
30+
<groupId>org.postgresql</groupId>
31+
<artifactId>postgresql</artifactId>
32+
<version>42.2.5</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>com.zaxxer</groupId>
36+
<artifactId>HikariCP</artifactId>
37+
<version>3.2.0</version>
38+
</dependency>
39+
40+
<dependency>
41+
<groupId>com.github.httl</groupId>
42+
<artifactId>httl</artifactId>
43+
<version>1.0.11</version>
44+
</dependency>
45+
46+
<dependency>
47+
<groupId>org.slf4j</groupId>
48+
<artifactId>slf4j-simple</artifactId>
49+
<version>1.7.25</version>
50+
</dependency>
51+
</dependencies>
52+
53+
<build>
54+
<plugins>
55+
<plugin>
56+
<inherited>true</inherited>
57+
<groupId>org.apache.maven.plugins</groupId>
58+
<artifactId>maven-compiler-plugin</artifactId>
59+
<version>3.8.0</version>
60+
<configuration>
61+
<debug>false</debug>
62+
</configuration>
63+
</plugin>
64+
<plugin>
65+
<artifactId>maven-assembly-plugin</artifactId>
66+
<version>3.1.0</version>
67+
<configuration>
68+
<archive>
69+
<manifest>
70+
<mainClass>benchmarks.Server</mainClass>
71+
</manifest>
72+
</archive>
73+
<descriptorRefs>
74+
<descriptorRef>jar-with-dependencies</descriptorRef>
75+
</descriptorRefs>
76+
</configuration>
77+
<executions>
78+
<execution>
79+
<id>make-assembly</id>
80+
<phase>package</phase>
81+
<goals>
82+
<goal>single</goal>
83+
</goals>
84+
</execution>
85+
</executions>
86+
</plugin>
87+
</plugins>
88+
</build>
89+
</project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package benchmarks;
2+
3+
public class Fortune implements Comparable<Fortune> {
4+
5+
private final int id;
6+
private final String message;
7+
8+
public Fortune(int id, String message) {
9+
this.id = id;
10+
this.message = message;
11+
}
12+
13+
public int getId() {
14+
return id;
15+
}
16+
17+
public String getMessage() {
18+
return message;
19+
}
20+
21+
@Override
22+
public int compareTo(Fortune other) {
23+
return message.compareTo(other.message);
24+
}
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package benchmarks;
2+
3+
public class Message {
4+
5+
private final String message;
6+
7+
public Message(String message) {
8+
this.message = message;
9+
}
10+
11+
public String getMessage() {
12+
return message;
13+
}
14+
15+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package benchmarks;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.IOException;
5+
import java.net.InetSocketAddress;
6+
import java.sql.Connection;
7+
import java.sql.PreparedStatement;
8+
import java.sql.ResultSet;
9+
import java.sql.SQLException;
10+
import java.text.ParseException;
11+
import java.util.*;
12+
import java.util.concurrent.SynchronousQueue;
13+
import java.util.concurrent.ThreadPoolExecutor;
14+
import java.util.concurrent.TimeUnit;
15+
import javax.sql.DataSource;
16+
import com.fasterxml.jackson.databind.ObjectMapper;
17+
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
18+
import com.sun.net.httpserver.HttpHandler;
19+
import com.sun.net.httpserver.HttpServer;
20+
import com.zaxxer.hikari.HikariConfig;
21+
import com.zaxxer.hikari.HikariDataSource;
22+
import httl.Engine;
23+
import httl.Template;
24+
25+
public class Server {
26+
27+
private static final String HELLO_TEXT = "Hello, World!";
28+
private static final byte[] HELLO_BYTES = HELLO_TEXT.getBytes();
29+
private static final int HELLO_LENGTH = HELLO_BYTES.length;
30+
private static final String SERVER_NAME = "httpserver";
31+
private static final ObjectMapper MAPPER = new ObjectMapper();
32+
33+
static {
34+
MAPPER.registerModule(new AfterburnerModule());
35+
}
36+
37+
private static List<Fortune> queryFortunes(DataSource ds) throws SQLException {
38+
List<Fortune> fortunes = new ArrayList<>();
39+
try (Connection conn = ds.getConnection();
40+
PreparedStatement statement = conn.prepareStatement("SELECT id, message FROM fortune");
41+
ResultSet resultSet = statement.executeQuery()) {
42+
while (resultSet.next())
43+
fortunes.add(new Fortune(resultSet.getInt(1), resultSet.getString(2)));
44+
}
45+
return fortunes;
46+
}
47+
48+
private static DataSource createPostgresDataSource() throws ClassNotFoundException {
49+
Class.forName("org.postgresql.Driver");
50+
HikariConfig config = new HikariConfig();
51+
config.setJdbcUrl("jdbc:postgresql://tfb-database:5432/hello_world");
52+
config.setUsername("benchmarkdbuser");
53+
config.setPassword("benchmarkdbpass");
54+
config.setMaximumPoolSize(64);
55+
return new HikariDataSource(config);
56+
}
57+
58+
private static Template loadTemplate(String filename) throws IOException, ParseException {
59+
Properties props = new Properties();
60+
props.put("import.packages", "java.util," + Fortune.class.getPackage().getName());
61+
props.put("input.encoding", "UTF-8");
62+
props.put("output.encoding", "UTF-8");
63+
props.put("precompiled", "false");
64+
Engine engine = Engine.getEngine(props);
65+
return engine.getTemplate(filename);
66+
}
67+
68+
private static HttpHandler createPlaintextHandler() {
69+
return t -> {
70+
t.getResponseHeaders().add("Content-Type", "text/plain");
71+
t.getResponseHeaders().add("Server", SERVER_NAME);
72+
t.sendResponseHeaders(200, HELLO_LENGTH);
73+
t.getResponseBody().write(HELLO_BYTES);
74+
t.getResponseBody().close();
75+
};
76+
}
77+
78+
private static HttpHandler createJSONHandler() {
79+
return t -> {
80+
// serialize message to JSON
81+
Message msg = new Message(HELLO_TEXT);
82+
byte[] bytes;
83+
try {
84+
bytes = MAPPER.writeValueAsBytes(msg);
85+
} catch (Exception e) {
86+
throw new RuntimeException(e);
87+
}
88+
// send response
89+
t.getResponseHeaders().add("Content-Type", "application/json");
90+
t.getResponseHeaders().add("Server", SERVER_NAME);
91+
t.sendResponseHeaders(200, bytes.length);
92+
t.getResponseBody().write(bytes);
93+
t.getResponseBody().close();
94+
};
95+
}
96+
97+
private static HttpHandler createFortunesHandler(DataSource ds) throws IOException, ParseException {
98+
Template template = loadTemplate("/fortunes.template.httl");
99+
return t -> {
100+
try {
101+
// query db
102+
List<Fortune> fortunes = queryFortunes(ds);
103+
fortunes.add(new Fortune(0, "Additional fortune added at request time."));
104+
Collections.sort(fortunes);
105+
// render template
106+
Map<String, Object> context = new HashMap<>(1);
107+
context.put("fortunes", fortunes);
108+
ByteArrayOutputStream out = new ByteArrayOutputStream();
109+
template.render(context, out);
110+
byte[] bytes = out.toByteArray();
111+
// send response
112+
t.getResponseHeaders().add("Content-Type", "text/html; charset=utf-8");
113+
t.getResponseHeaders().add("Server", SERVER_NAME);
114+
t.sendResponseHeaders(200, bytes.length);
115+
t.getResponseBody().write(bytes);
116+
t.getResponseBody().close();
117+
} catch (SQLException | ParseException e) {
118+
throw new IOException(e);
119+
}
120+
};
121+
}
122+
123+
public static void main(String[] args) throws Exception {
124+
// parse arguments
125+
String settings = args.length > 0 ? args[0] : "";
126+
int port = args.length > 1 ? Integer.parseInt(args[1]) : 8080;
127+
if (settings.contains("debug"))
128+
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
129+
// create server
130+
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
131+
server.setExecutor(new ThreadPoolExecutor(
132+
8, Integer.MAX_VALUE, 300, TimeUnit.SECONDS, new SynchronousQueue<>()));
133+
// add context handlers
134+
server.createContext("/plaintext", createPlaintextHandler());
135+
server.createContext("/json", createJSONHandler());
136+
if (settings.contains("postgres")) {
137+
DataSource ds = createPostgresDataSource();
138+
server.createContext("/fortunes", createFortunesHandler(ds));
139+
}
140+
// start server
141+
server.start();
142+
}
143+
}

0 commit comments

Comments
 (0)