forked from Qortal/qortal
Website serving code moved to a new class called ArbitraryDataRenderer
The process of serving resources to a browser will likely be needed for more than just websites (e.g. it will be needed for apps too) so it makes sense to abstract it to its own class.
This commit is contained in:
parent
7e30bf4197
commit
a9af5bcec4
@ -7,41 +7,26 @@ import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.api.ApiError;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.HTMLParser;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.arbitrary.ArbitraryDataTransactionBuilder;
|
||||
import org.qortal.arbitrary.*;
|
||||
import org.qortal.arbitrary.exception.MissingDataException;
|
||||
import org.qortal.data.transaction.ArbitraryTransactionData;
|
||||
import org.qortal.data.transaction.ArbitraryTransactionData.*;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.settings.Settings;
|
||||
import org.qortal.arbitrary.ArbitraryDataFile;
|
||||
import org.qortal.arbitrary.ArbitraryDataFile.*;
|
||||
import org.qortal.arbitrary.ArbitraryDataReader;
|
||||
import org.qortal.arbitrary.ArbitraryDataWriter;
|
||||
import org.qortal.transform.TransformationException;
|
||||
import org.qortal.transform.transaction.ArbitraryTransactionTransformer;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
|
||||
@ -168,132 +153,15 @@ public class WebsiteResource {
|
||||
if (domainMap != null && domainMap.containsKey(request.getServerName())) {
|
||||
return this.get(domainMap.get(request.getServerName()), ResourceIdType.NAME, inPath, null, "", false, true);
|
||||
}
|
||||
return this.getResponse(404, "Error 404: File Not Found");
|
||||
return ArbitraryDataRenderer.getResponse(response, 404, "Error 404: File Not Found");
|
||||
}
|
||||
|
||||
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, String inPath, String secret58,
|
||||
String prefix, boolean usePrefix, boolean async) {
|
||||
if (!inPath.startsWith(File.separator)) {
|
||||
inPath = File.separator + inPath;
|
||||
}
|
||||
|
||||
Service service = Service.WEBSITE;
|
||||
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, null);
|
||||
arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only
|
||||
try {
|
||||
if (!arbitraryDataReader.isCachedDataAvailable()) {
|
||||
// If async is requested, show a loading screen whilst build is in progress
|
||||
if (async) {
|
||||
arbitraryDataReader.loadAsynchronously();
|
||||
return this.getLoadingResponse();
|
||||
}
|
||||
|
||||
// Otherwise, hang the request until the build completes
|
||||
arbitraryDataReader.loadSynchronously(false);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.info(String.format("Unable to load %s %s: %s", service, resourceId, e.getMessage()));
|
||||
return this.getResponse(500, "Error 500: Internal Server Error");
|
||||
}
|
||||
|
||||
java.nio.file.Path path = arbitraryDataReader.getFilePath();
|
||||
if (path == null) {
|
||||
return this.getResponse(404, "Error 404: File Not Found");
|
||||
}
|
||||
String unzippedPath = path.toString();
|
||||
|
||||
try {
|
||||
String filename = this.getFilename(unzippedPath.toString(), inPath);
|
||||
String filePath = unzippedPath + File.separator + filename;
|
||||
|
||||
if (HTMLParser.isHtmlFile(filename)) {
|
||||
// HTML file - needs to be parsed
|
||||
byte[] data = Files.readAllBytes(Paths.get(filePath)); // TODO: limit file size that can be read into memory
|
||||
HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, usePrefix);
|
||||
data = htmlParser.replaceRelativeLinks(filename, data);
|
||||
response.setContentType(context.getMimeType(filename));
|
||||
response.setContentLength(data.length);
|
||||
response.getOutputStream().write(data);
|
||||
}
|
||||
else {
|
||||
// Regular file - can be streamed directly
|
||||
File file = new File(filePath);
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
response.setContentType(context.getMimeType(filename));
|
||||
int bytesRead, length = 0;
|
||||
byte[] buffer = new byte[10240];
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
response.getOutputStream().write(buffer, 0, bytesRead);
|
||||
length += bytesRead;
|
||||
}
|
||||
response.setContentLength(length);
|
||||
inputStream.close();
|
||||
}
|
||||
return response;
|
||||
} catch (FileNotFoundException | NoSuchFileException e) {
|
||||
LOGGER.info("Unable to serve file: {}", e.getMessage());
|
||||
if (inPath.equals("/")) {
|
||||
// Delete the unzipped folder if no index file was found
|
||||
try {
|
||||
FileUtils.deleteDirectory(new File(unzippedPath));
|
||||
} catch (IOException ioException) {
|
||||
LOGGER.info("Unable to delete directory: {}", unzippedPath, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Unable to serve file at path: {}", inPath, e);
|
||||
}
|
||||
|
||||
return this.getResponse(404, "Error 404: File Not Found");
|
||||
}
|
||||
|
||||
private String getFilename(String directory, String userPath) {
|
||||
if (userPath == null || userPath.endsWith("/") || userPath.equals("")) {
|
||||
// Locate index file
|
||||
List<String> indexFiles = this.indexFiles();
|
||||
for (String indexFile : indexFiles) {
|
||||
String filePath = directory + File.separator + indexFile;
|
||||
if (Files.exists(Paths.get(filePath))) {
|
||||
return userPath + indexFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
return userPath;
|
||||
}
|
||||
|
||||
private HttpServletResponse getLoadingResponse() {
|
||||
String responseString = null;
|
||||
URL url = Resources.getResource("loading/index.html");
|
||||
try {
|
||||
responseString = Resources.toString(url, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Unable to show loading screen: {}", e.getMessage());
|
||||
}
|
||||
return this.getResponse(503, responseString);
|
||||
}
|
||||
|
||||
private HttpServletResponse getResponse(int responseCode, String responseString) {
|
||||
try {
|
||||
byte[] responseData = responseString.getBytes();
|
||||
response.setStatus(responseCode);
|
||||
response.setContentLength(responseData.length);
|
||||
response.getOutputStream().write(responseData);
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Error writing {} response", responseCode);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private List<String> indexFiles() {
|
||||
List<String> indexFiles = new ArrayList<>();
|
||||
indexFiles.add("index.html");
|
||||
indexFiles.add("index.htm");
|
||||
indexFiles.add("default.html");
|
||||
indexFiles.add("default.htm");
|
||||
indexFiles.add("home.html");
|
||||
indexFiles.add("home.htm");
|
||||
return indexFiles;
|
||||
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, inPath, secret58,
|
||||
prefix, usePrefix, async, request, response, context);
|
||||
return renderer.render();
|
||||
}
|
||||
|
||||
}
|
||||
|
182
src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java
Normal file
182
src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java
Normal file
@ -0,0 +1,182 @@
|
||||
package org.qortal.arbitrary;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.api.HTMLParser;
|
||||
import org.qortal.arbitrary.ArbitraryDataFile.*;
|
||||
import org.qortal.data.transaction.ArbitraryTransactionData;
|
||||
import org.qortal.data.transaction.ArbitraryTransactionData.*;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ArbitraryDataRenderer {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataRenderer.class);
|
||||
|
||||
private String resourceId;
|
||||
private ResourceIdType resourceIdType;
|
||||
private String inPath;
|
||||
private String secret58;
|
||||
private String prefix;
|
||||
private boolean usePrefix;
|
||||
private boolean async;
|
||||
private HttpServletRequest request;
|
||||
private HttpServletResponse response;
|
||||
private ServletContext context;
|
||||
|
||||
public ArbitraryDataRenderer(String resourceId, ResourceIdType resourceIdType, String inPath, String secret58,
|
||||
String prefix, boolean usePrefix, boolean async,
|
||||
HttpServletRequest request, HttpServletResponse response, ServletContext context) {
|
||||
|
||||
this.resourceId = resourceId;
|
||||
this.resourceIdType = resourceIdType;
|
||||
this.inPath = inPath;
|
||||
this.secret58 = secret58;
|
||||
this.prefix = prefix;
|
||||
this.usePrefix = usePrefix;
|
||||
this.async = async;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public HttpServletResponse render() {
|
||||
if (!inPath.startsWith(File.separator)) {
|
||||
inPath = File.separator + inPath;
|
||||
}
|
||||
|
||||
ArbitraryTransactionData.Service service = Service.WEBSITE;
|
||||
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, null);
|
||||
arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only
|
||||
try {
|
||||
if (!arbitraryDataReader.isCachedDataAvailable()) {
|
||||
// If async is requested, show a loading screen whilst build is in progress
|
||||
if (async) {
|
||||
arbitraryDataReader.loadAsynchronously();
|
||||
return this.getLoadingResponse();
|
||||
}
|
||||
|
||||
// Otherwise, hang the request until the build completes
|
||||
arbitraryDataReader.loadSynchronously(false);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.info(String.format("Unable to load %s %s: %s", service, resourceId, e.getMessage()));
|
||||
return this.getResponse(response, 500, "Error 500: Internal Server Error");
|
||||
}
|
||||
|
||||
java.nio.file.Path path = arbitraryDataReader.getFilePath();
|
||||
if (path == null) {
|
||||
return this.getResponse(response, 404, "Error 404: File Not Found");
|
||||
}
|
||||
String unzippedPath = path.toString();
|
||||
|
||||
try {
|
||||
String filename = this.getFilename(unzippedPath.toString(), inPath);
|
||||
String filePath = unzippedPath + File.separator + filename;
|
||||
|
||||
if (HTMLParser.isHtmlFile(filename)) {
|
||||
// HTML file - needs to be parsed
|
||||
byte[] data = Files.readAllBytes(Paths.get(filePath)); // TODO: limit file size that can be read into memory
|
||||
HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, usePrefix);
|
||||
data = htmlParser.replaceRelativeLinks(filename, data);
|
||||
response.setContentType(context.getMimeType(filename));
|
||||
response.setContentLength(data.length);
|
||||
response.getOutputStream().write(data);
|
||||
}
|
||||
else {
|
||||
// Regular file - can be streamed directly
|
||||
File file = new File(filePath);
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
response.setContentType(context.getMimeType(filename));
|
||||
int bytesRead, length = 0;
|
||||
byte[] buffer = new byte[10240];
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
response.getOutputStream().write(buffer, 0, bytesRead);
|
||||
length += bytesRead;
|
||||
}
|
||||
response.setContentLength(length);
|
||||
inputStream.close();
|
||||
}
|
||||
return response;
|
||||
} catch (FileNotFoundException | NoSuchFileException e) {
|
||||
LOGGER.info("Unable to serve file: {}", e.getMessage());
|
||||
if (inPath.equals("/")) {
|
||||
// Delete the unzipped folder if no index file was found
|
||||
try {
|
||||
FileUtils.deleteDirectory(new File(unzippedPath));
|
||||
} catch (IOException ioException) {
|
||||
LOGGER.info("Unable to delete directory: {}", unzippedPath, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Unable to serve file at path: {}", inPath, e);
|
||||
}
|
||||
|
||||
return this.getResponse(response, 404, "Error 404: File Not Found");
|
||||
}
|
||||
|
||||
private String getFilename(String directory, String userPath) {
|
||||
if (userPath == null || userPath.endsWith("/") || userPath.equals("")) {
|
||||
// Locate index file
|
||||
List<String> indexFiles = this.indexFiles();
|
||||
for (String indexFile : indexFiles) {
|
||||
String filePath = directory + File.separator + indexFile;
|
||||
if (Files.exists(Paths.get(filePath))) {
|
||||
return userPath + indexFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
return userPath;
|
||||
}
|
||||
|
||||
private HttpServletResponse getLoadingResponse() {
|
||||
String responseString = null;
|
||||
URL url = Resources.getResource("loading/index.html");
|
||||
try {
|
||||
responseString = Resources.toString(url, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Unable to show loading screen: {}", e.getMessage());
|
||||
}
|
||||
return this.getResponse(response, 503, responseString);
|
||||
}
|
||||
|
||||
public static HttpServletResponse getResponse(HttpServletResponse response, int responseCode, String responseString) {
|
||||
try {
|
||||
byte[] responseData = responseString.getBytes();
|
||||
response.setStatus(responseCode);
|
||||
response.setContentLength(responseData.length);
|
||||
response.getOutputStream().write(responseData);
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Error writing {} response", responseCode);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private List<String> indexFiles() {
|
||||
List<String> indexFiles = new ArrayList<>();
|
||||
indexFiles.add("index.html");
|
||||
indexFiles.add("index.htm");
|
||||
indexFiles.add("default.html");
|
||||
indexFiles.add("default.htm");
|
||||
indexFiles.add("home.html");
|
||||
indexFiles.add("home.htm");
|
||||
return indexFiles;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user