mirror of https://github.com/qortal/qortal
Browse Source
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.qdn
CalDescent
3 years ago
2 changed files with 187 additions and 137 deletions
@ -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