org.zodiac.sdk.nio.http.server.FileServerProtocol Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zodiac-sdk-nio Show documentation
Show all versions of zodiac-sdk-nio Show documentation
Zodiac SDK NIO2(New Non-Blocking IO)
package org.zodiac.sdk.nio.http.server;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.MalformedInputException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.zodiac.sdk.nio.http.ApplicationProtocol;
import org.zodiac.sdk.nio.http.common.HTTPRequest;
import org.zodiac.sdk.nio.http.common.HTTPResponse;
import org.zodiac.sdk.nio.http.common.HttpHeaders;
import org.zodiac.sdk.nio.http.common.HttpResponseStatusEnum;
public class FileServerProtocol implements ApplicationProtocol.Response {
final Path path;
String pathAsString;
public FileServerProtocol() throws IOException {
this(Paths.get("").toAbsolutePath().toString());
}
public FileServerProtocol(final String directory) throws IOException {
path = Paths.get(directory);
if (!path.toFile().exists() || !path.toFile().isDirectory()) {
throw new IllegalStateException("The directory specified does not exist: " + directory);
} else {
pathAsString = path.toFile().getCanonicalPath();
}
}
@Override
public HTTPResponse response(final HTTPRequest request) throws IOException {
return dispatchResponse(request);
}
@Override
public ApplicationProtocol.Response copy() throws IOException {
return new FileServerProtocol(pathAsString);
}
private HTTPResponse dispatchResponse(final HTTPRequest request) {
switch (request.method()) {
case GET:
return get(request);
case POST:
return post(request);
default:
throw new IllegalArgumentException("Invalid http method specified: " + request.method());
}
}
private HTTPResponse get(final HTTPRequest request) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept("*/*");
if (request.path().equalsIgnoreCase("/")) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.OK)
.statusMessage(HttpResponseStatusEnum.OK)
.headers(httpHeaders)
.body(String.join("\n", fileNames(pathAsString)))
.build();
} else {
try {
final String fileContent = read(request.path());
if (fileContent == null) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.NOT_FOUND)
.statusMessage(HttpResponseStatusEnum.NOT_FOUND)
.headers(httpHeaders)
.body("Could not find the specified file: " + request.path())
.build();
}
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.OK)
.statusMessage(HttpResponseStatusEnum.OK)
.headers(httpHeaders)
.body(fileContent)
.build();
} catch (final MalformedInputException e) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.INTERNAL_SERVER_ERROR)
.statusMessage(HttpResponseStatusEnum.INTERNAL_SERVER_ERROR)
.headers(httpHeaders)
.body("Contents of file contain non UTF-8 character encodings: " + request.path() + "\n" + e.getMessage())
.build();
} catch (final IOException e) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.INTERNAL_SERVER_ERROR)
.statusMessage(HttpResponseStatusEnum.INTERNAL_SERVER_ERROR)
.headers(httpHeaders)
.body("The specified file could not be read: " + request.path() + "\n" + e.getMessage())
.build();
} catch (final FileServerProtocol.Error e) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.UNAUTHORIZED)
.statusMessage(HttpResponseStatusEnum.UNAUTHORIZED)
.headers(httpHeaders)
.body(e.getMessage())
.build();
}
}
}
private HTTPResponse post(final HTTPRequest request) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept("*/*");
try {
write(request.path(), request.body());
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.OK)
.statusMessage(HttpResponseStatusEnum.OK)
.headers(httpHeaders)
.body("File contents successfully written to: " + request.path() + "\n" + read(request.path()))
.build();
} catch (final IOException e) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.INTERNAL_SERVER_ERROR)
.statusMessage(HttpResponseStatusEnum.INTERNAL_SERVER_ERROR)
.headers(httpHeaders)
.body("Could not write to file: " + request.path() + "\n" + e.getMessage())
.build();
} catch (final FileServerProtocol.Error e) {
return HTTPResponse.builder()
.statusCode(HttpResponseStatusEnum.UNAUTHORIZED)
.statusMessage(HttpResponseStatusEnum.UNAUTHORIZED)
.headers(httpHeaders)
.body(e.getMessage())
.build();
}
}
private List files() {
return ls(pathAsString);
}
private File file(final String relativeFilePath) {
return files().stream().filter(e -> e.getAbsolutePath().equals(pathAsString + relativeFilePath)).findFirst().orElse(null);
}
private String read(final String relativeFilePath) throws IOException, FileServerProtocol.Error {
final Path pathToFile = Paths.get(pathAsString + relativeFilePath);
if (isUnauthorizedPathAccess(pathToFile)) {
throw new Error("Unauthorized access to path outside root working directory: " + pathAsString);
}
if (Files.isDirectory(pathToFile)) {
return readDirectory(relativeFilePath);
} else {
return readFile(relativeFilePath);
}
}
private String readFile(final String relativeFilePath) throws IOException {
final File file = file(relativeFilePath);
if (file == null) {
return null;
}
return String.join("\n", Files.readAllLines(file.toPath()));
}
private String readDirectory(final String relativeDirectoryPath) {
return ls(pathAsString + relativeDirectoryPath).stream()
.map(e -> e.getAbsolutePath().replace(pathAsString, ""))
.collect(Collectors.joining("\n"));
}
private void write(final String relativeFilePath, final String content) throws IOException, FileServerProtocol.Error {
final Path pathToFile = Paths.get(pathAsString + relativeFilePath);
if (isUnauthorizedPathAccess(pathToFile)) {
throw new Error("Unauthorized access to path outside root working directory: " + pathAsString);
}
if (!Files.exists(pathToFile.getParent())) {
Files.createDirectory(pathToFile.getParent());
}
final File file = new File(pathToFile.toAbsolutePath().toString());
try (final FileWriter fw = new FileWriter(file, false)) {
fw.write(content);
}
}
private List fileNames(final String directoryName) {
final List files = ls(directoryName);
return files.stream()
.map(File::getAbsolutePath)
.map(e -> e.replace(pathAsString, ""))
.collect(Collectors.toList());
}
private List ls(final String directoryName) {
return lsRec(directoryName, new ArrayList<>());
}
private List lsRec(final String directoryName, final List files) {
final File[] directoryFiles = new File(directoryName).listFiles();
if (directoryFiles != null) {
for (final File file : directoryFiles) {
if (file.isFile()) {
files.add(file);
} else if (file.isDirectory()) {
lsRec(file.getAbsolutePath(), files);
}
}
}
return files;
}
private boolean isUnauthorizedPathAccess(final Path pathRequested) throws IOException {
return !(pathRequested.toFile().getCanonicalPath().startsWith(pathAsString));
}
public static class Error extends Throwable {
public Error(final String message) {
super(message);
}
}
}