diff --git a/README.md b/README.md index 4252f65..fed1191 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # SimpleHttpServer -[![Maven Central](https://img.shields.io/maven-central/v/com.kttdevelopment/simplehttpserver)](https://mvnrepository.com/artifact/com.kttdevelopment/simplehttpserver) [![Deploy](https://github.com/Ktt-Development/simplehttpserver/workflows/Deploy/badge.svg)](https://github.com/orgs/Ktt-Development/packages?repo_name=simplehttpserver) [![Java CI](https://github.com/Ktt-Development/simplehttpserver/workflows/Java%20CI/badge.svg)](https://github.com/Ktt-Development/simplehttpserver/actions?query=workflow%3A%22Java+CI%22) +[![Maven Central](https://img.shields.io/maven-central/v/com.kttdevelopment/simplehttpserver)](https://mvnrepository.com/artifact/com.kttdevelopment/simplehttpserver) [![version](https://img.shields.io/github/v/release/ktt-development/simplehttpserver?include_prereleases)](https://github.com/Ktt-Development/simplehttpserver/releases) [![license](https://img.shields.io/github/license/Ktt-Development/simplehttpserver)](https://github.com/Ktt-Development/simplehttpserver/blob/main/LICENSE) diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java index 0cff18a..f7e9a96 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java @@ -4,8 +4,7 @@ import com.kttdevelopment.simplehttpserver.var.RequestMethod; import com.sun.net.httpserver.*; -import java.io.IOException; -import java.io.OutputStream; +import java.io.*; import java.net.InetSocketAddress; import java.net.URI; import java.util.Map; @@ -46,7 +45,7 @@ * * @see HttpExchange * @since 02.00.00 - * @version 03.04.03 + * @version 4.0.0 * @author Ktt Development */ @SuppressWarnings("SpellCheckingInspection") @@ -386,6 +385,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String, boolean) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -406,6 +409,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String, boolean) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -427,6 +434,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String, boolean) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -448,6 +459,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String, boolean) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -470,6 +485,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String, boolean) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -490,6 +509,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String, boolean) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -511,6 +534,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String) * @see #send(String, int) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -532,6 +559,10 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String) * @see #send(String, boolean) * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ @@ -554,11 +585,115 @@ static SimpleHttpExchange create(final HttpExchange exchange){ * @see #send(String) * @see #send(String, boolean) * @see #send(String, int) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) * @since 02.00.00 * @author Ktt Development */ public abstract void send(final String response, final int responseCode, final boolean gzip) throws IOException; + /** + * Sends a file to the client. + * + * @param file file to send + * @throws IOException internal server error or file read error + * + * @see #sendResponseHeaders(int, long) + * @see #send(int) + * @see #send(byte[]) + * @see #send(byte[], boolean) + * @see #send(byte[], int) + * @see #send(byte[], int, boolean) + * @see #send(String) + * @see #send(String, boolean) + * @see #send(String, int) + * @see #send(String, int, boolean) + * @see #send(File, boolean) + * @see #send(File, int) + * @see #send(File, int, boolean) + * @since 4.0.0 + * @author Ktt Development + */ + public abstract void send(final File file) throws IOException; + + /** + * Sends a file to the client. + * + * @param file file to send + * @param gzip if the response should be gziped before sending it to the client + * @throws IOException internal server error or file read error + * + * @see #sendResponseHeaders(int, long) + * @see #send(int) + * @see #send(byte[]) + * @see #send(byte[], boolean) + * @see #send(byte[], int) + * @see #send(byte[], int, boolean) + * @see #send(String) + * @see #send(String, boolean) + * @see #send(String, int) + * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, int) + * @see #send(File, int, boolean) + * @since 4.0.0 + * @author Ktt Development + */ + public abstract void send(final File file, final boolean gzip) throws IOException; + + /** + * Sends a file with response code to the client. + * + * @param file file to send + * @param responseCode response code + * @throws IOException internal server error or file read error + * + * @see #sendResponseHeaders(int, long) + * @see #send(int) + * @see #send(byte[]) + * @see #send(byte[], boolean) + * @see #send(byte[], int) + * @see #send(byte[], int, boolean) + * @see #send(String) + * @see #send(String, boolean) + * @see #send(String, int) + * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int, boolean) + * @since 4.0.0 + * @author Ktt Development + */ + public abstract void send(final File file, final int responseCode) throws IOException; + + /** + * Sends a file with response code to the client. + * + * @param file file to send + * @param responseCode response code + * @param gzip if the response should be gziped before sending it to the client + * @throws IOException internal server error or file read error + * + * @see #sendResponseHeaders(int, long) + * @see #send(int) + * @see #send(byte[]) + * @see #send(byte[], boolean) + * @see #send(byte[], int) + * @see #send(byte[], int, boolean) + * @see #send(String) + * @see #send(String, boolean) + * @see #send(String, int) + * @see #send(String, int, boolean) + * @see #send(File) + * @see #send(File, boolean) + * @see #send(File, int) + * @since 4.0.0 + * @author Ktt Development + */ + public abstract void send(final File file, final int responseCode, final boolean gzip) throws IOException; + // /** diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java index 1e1bd3d..7fae8ed 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java @@ -6,6 +6,7 @@ import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; @@ -17,7 +18,7 @@ * * @see SimpleHttpExchange * @since 02.00.00 - * @version 03.05.00 + * @version 4.0.0 * @author Ktt Development */ @SuppressWarnings("SpellCheckingInspection") @@ -390,6 +391,26 @@ public final void send(final String response, final int responseCode, final bool send(response.getBytes(StandardCharsets.UTF_8),responseCode,gzip); } + @Override + public final void send(final File file) throws IOException{ + send(Files.readAllBytes(file.toPath())); + } + + @Override + public final void send(final File file, final boolean gzip) throws IOException{ + send(Files.readAllBytes(file.toPath()), true); + } + + @Override + public final void send(final File file, final int responseCode) throws IOException{ + send(Files.readAllBytes(file.toPath()),responseCode); + } + + @Override + public final void send(final File file, final int responseCode, final boolean gzip) throws IOException{ + send(Files.readAllBytes(file.toPath()),responseCode,gzip); + } + // @Override diff --git a/src/test/java/com/kttdevelopment/simplehttpserver/simplehttpexchange/io/SimpleHttpExchangeSendTest.java b/src/test/java/com/kttdevelopment/simplehttpserver/simplehttpexchange/io/SimpleHttpExchangeSendTest.java new file mode 100644 index 0000000..95da841 --- /dev/null +++ b/src/test/java/com/kttdevelopment/simplehttpserver/simplehttpexchange/io/SimpleHttpExchangeSendTest.java @@ -0,0 +1,171 @@ +package com.kttdevelopment.simplehttpserver.simplehttpexchange.io; + +import com.kttdevelopment.simplehttpserver.SimpleHttpHandler; +import com.kttdevelopment.simplehttpserver.SimpleHttpServer; +import com.kttdevelopment.simplehttpserver.var.HttpCode; +import org.junit.*; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.*; +import java.nio.file.Files; +import java.util.concurrent.ExecutionException; + +@SuppressWarnings("OptionalGetWithoutIsPresent") +public class SimpleHttpExchangeSendTest { + + @Rule + public final TemporaryFolder directory = new TemporaryFolder(new File(".")); + + @Test + public final void sendTest() throws IOException{ + final int port = 8080; + final SimpleHttpServer server = SimpleHttpServer.create(port); + final String context = ""; + + final int testCode = HttpCode.HTTP_Accepted; + final String testContent = String.valueOf(System.currentTimeMillis()); + + server.createContext("code", (SimpleHttpHandler) exchange -> { + exchange.send(testCode); + exchange.close(); + }); + server.createContext("bytes", (SimpleHttpHandler) exchange -> exchange.send(testContent.getBytes())); + server.createContext("bytes/gzip", (SimpleHttpHandler) exchange -> exchange.send(testContent.getBytes(),true)); + server.createContext("string", (SimpleHttpHandler) exchange -> exchange.send(testContent)); + server.createContext("string/gzip", (SimpleHttpHandler) exchange -> exchange.send(testContent,true)); + + final File testFile = directory.newFile(); + Files.write(testFile.toPath(),testContent.getBytes()); + server.createContext("file", (SimpleHttpHandler) exchange -> exchange.send(testFile)); + server.createContext("file/gzip", (SimpleHttpHandler) exchange -> exchange.send(testFile,true)); + + server.start(); + + // response code + { + final String url = "http://localhost:" + port + context + '/' + "code"; + final HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + try{ + final int response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::statusCode).get(); + Assert.assertEquals("Client data did not match server data for " + url, testCode, response); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + // bytes + { + final String url = "http://localhost:" + port + context + '/' + "bytes"; + final HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + try{ + final String response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body).get(); + Assert.assertEquals("Client data did not match server data for " + url, testContent, response); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + // bytes gzip + { + final String url = "http://localhost:" + port + context + '/' + "bytes" + '/' + "gzip"; + final HttpRequest request = HttpRequest.newBuilder() + .header("Accept-Encoding","deflate, gzip") + .uri(URI.create(url)) + .build(); + + try{ + final HttpHeaders response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::headers).get(); + Assert.assertEquals("Client data did not match server data for " + url, "gzip", response.firstValue("Content-Encoding").get()); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + // string + { + final String url = "http://localhost:" + port + context + '/' + "string"; + final HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + try{ + final String response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body).get(); + Assert.assertEquals("Client data did not match server data for " + url, testContent, response); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + // string gzip + { + final String url = "http://localhost:" + port + context + '/' + "string" + '/' + "gzip"; + final HttpRequest request = HttpRequest.newBuilder() + .header("Accept-Encoding","deflate, gzip") + .uri(URI.create(url)) + .build(); + + try{ + final HttpHeaders response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::headers).get(); + Assert.assertEquals("Client data did not match server data for " + url, "gzip", response.firstValue("Content-Encoding").get()); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + // file + { + final String url = "http://localhost:" + port + context + '/' + "file"; + final HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + try{ + final String response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body).get(); + Assert.assertEquals("Client data did not match server data for " + url, testContent, response); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + // file gzip + { + final String url = "http://localhost:" + port + context + '/' + "file" + '/' + "gzip"; + final HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + try{ + final HttpHeaders response = HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::headers).get(); + Assert.assertEquals("Client data did not match server data for " + url, "gzip", response.firstValue("Content-Encoding").get()); + }catch(final InterruptedException | ExecutionException ignored){ + Assert.fail("Failed to read context for " + url); + } + } + + server.stop(); + } + +}