package water.api;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
/**
*
*/
public class RequestUri {
private static Pattern version_pattern = Pattern.compile("^/(?:\\d+|EXPERIMENTAL|LATEST)/.*", Pattern.CASE_INSENSITIVE);
private static Set<String> http_methods = new HashSet<>(Arrays.asList("HEAD", "GET", "POST", "DELETE"));
private String method;
private String url;
private String[] path;
private boolean is_api_url;
public RequestUri(String request_method, String request_url) throws MalformedURLException {
if (!http_methods.contains(request_method))
throw new MalformedURLException("Bad HTTP method: " + request_method);
method = request_method;
url = request_url;
is_api_url = version_pattern.matcher(request_url).matches();
path = null;
}
public String getUrl() { return url; }
public boolean isApiUrl() { return is_api_url; }
public String getMethod() { return method; }
public boolean isGetMethod() { return method.equals("GET"); }
public boolean isPostMethod() { return method.equals("POST"); }
public boolean isHeadMethod() { return method.equals("HEAD"); }
public String[] getPath() {
computePathIfNeeded();
return path;
}
public String[] getParamsList() {
computePathIfNeeded();
ArrayList<String> params_list = new ArrayList<>();
for (int i = 2; i < path.length; i++)
if (path[i].startsWith("{") && path[i].endsWith("}"))
params_list.add(path[i].substring(1, path[i].length()-1));
return params_list.toArray(new String[params_list.size()]);
}
public int getVersion() {
computePathIfNeeded();
String ver = path[path.length - 1];
return ver.isEmpty()? 0 : Integer.parseInt(ver);
}
public String toString() {
return method + " " + url;
}
/**
* Convert the provided HTTP_method/URL pair into a "path" suitable for lookups in the RouteTree. This is mostly
* equivalent to url.split("/"), with a few caveats:
* - if the url contains "special" version (LATEST/EXPERIMENTAL), it will be replaced with its numeric value;
* - the order of url chunks is modified: the version is always moved to the end, its place taken by http_method;
* Examples:
* "GET", "/3/Models/{model_id}" => ["", "GET", "Models", "{model_id}", "3"]
* "GET", "/" => ["", "GET", ""]
* First chunk is always "" because that is the root of the RouteTree.
*/
private void computePathIfNeeded() {
if (path == null) {
// This will make sure path array has one extra element in the end, where we will store the version string.
// Pass -1 because otherwise split() removes any trailing empty strings.
path = (url + "/").split("/", -1);
assert path[0].isEmpty() && path.length >= 3;
String ver = path[1].toUpperCase();
if (ver.equals("EXPERIMENTAL")) ver = ((Integer) SchemaServer.getExperimentalVersion()).toString();
if (ver.equals("LATEST")) ver = ((Integer) SchemaServer.getLatestOrHighestSupportedVersion()).toString();
// Old clients (h2o-2) tend to append .json suffix to the endpoint's name -- fixing that
if (path[2].endsWith(".json"))
path[2] = path[2].substring(0, path[2].length() - 5);
path[1] = method;
path[path.length - 1] = ver;
}
}
}