package org.webpieces.router.impl;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.webpieces.ctx.api.HttpMethod;
import org.webpieces.ctx.api.RouterRequest;
import org.webpieces.router.api.dto.RouteType;
public class StaticRoute implements Route {
private String urlPath;
private String fileSystemPath;
private boolean isFile = false;
private Pattern patternToMatch;
private boolean isOnClassPath;
private List<String> pathParamNames = new ArrayList<>();
private File targetCacheLocation;
private Properties hashMeta;
public StaticRoute(UrlPath url, String fileSystemPath, boolean isOnClassPath, File cachedCompressedDirectory) {
this.fileSystemPath = fileSystemPath;
this.isOnClassPath = isOnClassPath;
String urlSubPath = url.getSubPath();
this.urlPath = url.getFullPath();
//very big conflict between domain/path/path/path
if(!urlSubPath.startsWith("/"))
throw new IllegalArgumentException("static resource url paths must start with / and can't have domain name at this time="+urlSubPath);
else if(isOnClassPath) {
if(!fileSystemPath.startsWith("/"))
throw new IllegalArgumentException("Classpath resources must start with a / and be absolute on the classpath");
} else {//on filesystem
if(!fileSystemPath.startsWith("/")) {
String path = System.getProperty("user.dir");
this.fileSystemPath = path + "/"+fileSystemPath;
}
}
File f = new File(this.fileSystemPath);
if(!f.exists())
throw new IllegalArgumentException("File="+getCanonicalPath(f)+" does not exist");
if(isDirectory(urlSubPath)) {
this.isFile = false;
if(!isDirectory(fileSystemPath))
throw new IllegalArgumentException("Static directory so fileSystemPath must end with a /");
else if(!f.isDirectory())
throw new IllegalArgumentException("file="+getCanonicalPath(f)+" is not a directory and must be for static directories");
this.patternToMatch = Pattern.compile("^"+urlSubPath+"(?<resource>.*)$");
this.pathParamNames.add("resource");
} else {
this.isFile = true;
if(isDirectory(fileSystemPath))
throw new IllegalArgumentException("Static file so fileSystemPath must NOT end with a /");
else if(!f.isFile())
throw new IllegalArgumentException("file="+getCanonicalPath(f)+" is not a file and must be for static file route");
this.patternToMatch = Pattern.compile("^"+urlSubPath+"$");
}
String relativePath = urlSubPath.substring(1);
this.targetCacheLocation = new File(cachedCompressedDirectory, relativePath);
}
private boolean isDirectory(String urlSubPath) {
return urlSubPath.endsWith("/");
}
private String getCanonicalPath(File f) {
try {
return f.getCanonicalPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public String getFullPath() {
return urlPath;
}
@Override
public boolean matchesMethod(HttpMethod method) {
if(method == HttpMethod.GET || method == HttpMethod.HEAD)
return true;
return false;
}
@Override
public Matcher matches(RouterRequest request, String subPath) {
if(!matchesMethod(request.method)) {
return null;
}
Matcher matcher = patternToMatch.matcher(subPath);
String hash = null;
List<String> list = request.queryParams.get("hash");
if(list != null && list.size() > 0)
hash = list.get(0);
if(hashMeta != null) {
//MUST be in production mode if we are here as only production sets hashMeta
if(matcher.matches() && hash != null) {
//DO NOT allow a browser to cache a file not matching the hash because that is unfixable once
//customers have that file until it expires and generally all expires should be infinite since
//we hash every file for you so browsers always load latest
String filesHash = hashMeta.getProperty(request.relativePath);
if(!hash.equals(filesHash))
return null;
}
}
return matcher;
}
@Override
public String getControllerMethodString() {
throw new UnsupportedOperationException("should not call this");
}
@Override
public List<String> getPathParamNames() {
return pathParamNames ;
}
@Override
public RouteType getRouteType() {
return RouteType.STATIC;
}
@Override
public boolean isPostOnly() {
return false;
}
@Override
public boolean isCheckSecureToken() {
return false;
}
public boolean getIsOnClassPath() {
return isOnClassPath;
}
public String getFileSystemPath() {
return fileSystemPath;
}
@Override
public String toString() {
return "\nStaticRoute [\n urlPath=" + urlPath + ",\n fileSystemPath=" + fileSystemPath + ",\n isFile=" + isFile
+ ",\n patternToMatch=" + patternToMatch + ",\n isOnClassPath=" + isOnClassPath + ",\n pathParamNames="
+ pathParamNames + "]";
}
public boolean isFile() {
return isFile;
}
public File getTargetCacheLocation() {
return this.targetCacheLocation;
}
@Override
public boolean isHttpsRoute() {
throw new UnsupportedOperationException("This method is not necessary as there are no filters for static routes at this time");
}
public void setHashMeta(Properties hashMeta) {
this.hashMeta = hashMeta;
}
@Override
public String getMethod() {
return "GET";
}
}