瀏覽代碼

Add Java Microhttp Test (#7150)

* Add Microhttp test to Java suite. Includes plain-text and JSON tests. Depends on Microhttp 0.3 and Jackson 2.13.1. Docker image built atop OpenJDK 17.0.2.

* Replace tabs with four spaces in pom.xml.

* Update microhttp version to 0.4. Adjust event loop resolution time duration.

Co-authored-by: Elliot Barlas <[email protected]>
Elliot Barlas 3 年之前
父節點
當前提交
1c848dbcdb

+ 22 - 0
frameworks/Java/microhttp/README.md

@@ -0,0 +1,22 @@
+# Microhttp Benchmarking Test
+
+### Test Type Implementation Source Code
+
+* [JSON](src/main/java/hello/HelloWebServer.java)
+* [PLAINTEXT](src/main/java/hello/HelloWebServer.java)
+
+## Important Libraries
+
+The tests were run with:
+* [OpenJDK 17.0.2](http://openjdk.java.net/)
+* [Microhttp 0.3](https://github.com/ebarlas/microhttp)
+* [Jackson 2.13.1](https://github.com/FasterXML/jackson)
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext

+ 26 - 0
frameworks/Java/microhttp/benchmark_config.json

@@ -0,0 +1,26 @@
+{
+  "framework": "microhttp",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "None",
+        "framework": "microhttp",
+        "language": "Java",
+        "flavor": "None",
+        "orm": "None",
+        "platform": "Microhttp",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "microhttp",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 13 - 0
frameworks/Java/microhttp/microhttp.dockerfile

@@ -0,0 +1,13 @@
+FROM maven:3.8.4-openjdk-17-slim as maven
+WORKDIR /microhttp
+COPY pom.xml pom.xml
+COPY src src
+RUN mvn compile assembly:single -q
+
+FROM openjdk:17.0.2
+WORKDIR /microhttp
+COPY --from=maven /microhttp/target/microhttp-example-0.1-jar-with-dependencies.jar app.jar
+
+EXPOSE 8080
+
+CMD ["java", "-jar", "app.jar"]

+ 85 - 0
frameworks/Java/microhttp/pom.xml

@@ -0,0 +1,85 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.techempower</groupId>
+    <artifactId>microhttp-example</artifactId>
+    <version>0.1</version>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <microhttp.version>0.4</microhttp.version>
+        <jackson.version>2.13.1</jackson.version>
+        <junit.version>5.8.2</junit.version>
+    </properties>
+
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.microhttp</groupId>
+            <artifactId>microhttp</artifactId>
+            <version>${microhttp.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.8.2</version>
+            <scope>${junit.version}</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <inherited>true</inherited>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.10.0</version>
+                <configuration>
+                    <debug>false</debug>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M5</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>hello.HelloWebServer</mainClass>
+                        </manifest>
+                    </archive>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
+                        <phase>package</phase> <!-- bind to the packaging phase -->
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 130 - 0
frameworks/Java/microhttp/src/main/java/hello/HelloWebServer.java

@@ -0,0 +1,130 @@
+package hello;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.microhttp.EventLoop;
+import org.microhttp.Header;
+import org.microhttp.LogEntry;
+import org.microhttp.Logger;
+import org.microhttp.Options;
+import org.microhttp.Request;
+import org.microhttp.Response;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class HelloWebServer {
+
+    static final String MESSAGE = "Hello, World!";
+    static final byte[] TEXT_BYTES = MESSAGE.getBytes(StandardCharsets.UTF_8);
+
+    static final String SERVER = "microhttp";
+
+    static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC);
+
+    static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    final int port;
+
+    volatile String date = DATE_FORMATTER.format(Instant.now());
+
+    record JsonMessage(String message) {
+    }
+
+    HelloWebServer(int port) {
+        this.port = port;
+    }
+
+    void start() throws IOException {
+        startUpdater();
+        Options options = new Options()
+                .withHost(null) // wildcard any-address binding
+                .withPort(port)
+                .withReuseAddr(true)
+                .withReusePort(true)
+                .withAcceptLength(8_192)
+                .withMaxRequestSize(1_024 * 1_024)
+                .withReadBufferSize(1_024 * 64)
+                .withResolution(Duration.ofMillis(1_000))
+                .withSocketTimeout(Duration.ofSeconds(90));
+        EventLoop eventLoop = new EventLoop(options, new DisabledLogger(), this::handle);
+        eventLoop.start();
+    }
+
+    void startUpdater() {
+        Thread thread = new Thread(this::runDateUpdater);
+        thread.setDaemon(true);
+        thread.setPriority(Thread.MIN_PRIORITY);
+        thread.start();
+    }
+
+    void runDateUpdater() {
+        while (true) {
+            try {
+                Thread.sleep(1_000);
+            } catch (InterruptedException e) {
+                return;
+            }
+            date = DATE_FORMATTER.format(Instant.now());
+        }
+    }
+
+    void handle(Request request, Consumer<Response> callback) {
+        if (request.uri().equals("/plaintext")) {
+            List<Header> headers = List.of(
+                    new Header("Content-Type", "text/plain"),
+                    new Header("Date", date),
+                    new Header("Server", SERVER));
+            callback.accept(new Response(200, "OK", headers, TEXT_BYTES));
+        } else if (request.uri().equals("/json")) {
+            List<Header> headers = List.of(
+                    new Header("Content-Type", "application/json"),
+                    new Header("Date", date),
+                    new Header("Server", SERVER));
+            callback.accept(new Response(200, "OK", headers, jsonBody()));
+        } else {
+            List<Header> headers = List.of(
+                    new Header("Date", date),
+                    new Header("Server", SERVER));
+            callback.accept(new Response(404, "Not Found", headers, new byte[0]));
+        }
+    }
+
+    byte[] jsonBody() {
+        try {
+            return OBJECT_MAPPER.writeValueAsBytes(new JsonMessage(MESSAGE));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        int port = args.length > 0
+                ? Integer.parseInt(args[0])
+                : 8080;
+        new HelloWebServer(port).start();
+    }
+
+    static class DisabledLogger implements Logger {
+        @Override
+        public boolean enabled() {
+            return false;
+        }
+
+        @Override
+        public void log(LogEntry... logEntries) {
+
+        }
+
+        @Override
+        public void log(Exception e, LogEntry... logEntries) {
+
+        }
+    }
+
+}

+ 64 - 0
frameworks/Java/microhttp/src/test/java/hello/HelloWebServerTest.java

@@ -0,0 +1,64 @@
+package hello;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+class HelloWebServerTest {
+
+    @Test
+    void plainTextAndJson() throws IOException, InterruptedException {
+        HelloWebServer server = new HelloWebServer(8080);
+        Runnable task = () -> {
+            try {
+                server.start();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        };
+        Thread thread = new Thread(task);
+        thread.setDaemon(true);
+        thread.start();
+        HttpClient client = HttpClient.newBuilder()
+                .version(HttpClient.Version.HTTP_1_1)
+                .build();
+        verifyPlainText(client);
+        verifyJson(client);
+        verifyOther(client);
+    }
+
+    static void verifyPlainText(HttpClient client) throws IOException, InterruptedException {
+        HttpRequest request = HttpRequest.newBuilder()
+                .GET()
+                .uri(URI.create("http://localhost:8080/plaintext"))
+                .build();
+        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+        Assertions.assertEquals(200, response.statusCode());
+        Assertions.assertEquals("Hello, World!", response.body());
+    }
+
+    static void verifyJson(HttpClient client) throws IOException, InterruptedException {
+        HttpRequest request = HttpRequest.newBuilder()
+                .GET()
+                .uri(URI.create("http://localhost:8080/json"))
+                .build();
+        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+        Assertions.assertEquals(200, response.statusCode());
+        Assertions.assertEquals("{\"message\":\"Hello, World!\"}", response.body());
+    }
+
+    static void verifyOther(HttpClient client) throws IOException, InterruptedException {
+        HttpRequest request = HttpRequest.newBuilder()
+                .GET()
+                .uri(URI.create("http://localhost:8080/unknown"))
+                .build();
+        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+        Assertions.assertEquals(404, response.statusCode());
+    }
+
+}