Mercurial > hg > monetdb-java
diff src/main/java/org/monetdb/mcl/net/MonetUrlParser.java @ 834:5aa19bbed0d6 monetdbs
Comments and formatting
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Wed, 13 Dec 2023 15:39:47 +0100 (16 months ago) |
parents | 6b7778153d23 |
children | 9d21c6e7ed26 |
line wrap: on
line diff
--- a/src/main/java/org/monetdb/mcl/net/MonetUrlParser.java +++ b/src/main/java/org/monetdb/mcl/net/MonetUrlParser.java @@ -6,270 +6,275 @@ import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; +/** + * Helper class to keep the URL parsing code separate from the rest of + * the {@link Target} class. + */ public class MonetUrlParser { - private final Target target; - private final String urlText; - private final URI url; + private final Target target; + private final String urlText; + private final URI url; - public MonetUrlParser(Target target, String url) throws URISyntaxException { - this.target = target; - this.urlText = url; - // we want to accept monetdb:// but the Java URI parser rejects that. - switch (url) { - case "monetdb:-": - case "monetdbs:-": - throw new URISyntaxException(url, "invalid MonetDB URL"); - case "monetdb://": - case "monetdbs://": - url += "-"; - break; - } - this.url = new URI(url); - } + private MonetUrlParser(Target target, String url) throws URISyntaxException { + this.target = target; + this.urlText = url; + // we want to accept monetdb:// but the Java URI parser rejects that. + switch (url) { + case "monetdb:-": + case "monetdbs:-": + throw new URISyntaxException(url, "invalid MonetDB URL"); + case "monetdb://": + case "monetdbs://": + url += "-"; + break; + } + this.url = new URI(url); + } - public static void parse(Target target, String url) throws URISyntaxException, ValidationError { - if (url.equals("monetdb://")) { - // deal with peculiarity of Java's URI parser - url = "monetdb:///"; - } + public static void parse(Target target, String url) throws URISyntaxException, ValidationError { + if (url.equals("monetdb://")) { + // deal with peculiarity of Java's URI parser + url = "monetdb:///"; + } - target.barrier(); - if (url.startsWith("mapi:")) { - try { - MonetUrlParser parser = new MonetUrlParser(target, url.substring(5)); - parser.parseClassic(); - } catch (URISyntaxException e) { - URISyntaxException exc = new URISyntaxException(e.getInput(), e.getReason(), -1); - exc.setStackTrace(e.getStackTrace()); - throw exc; - } - } else { - MonetUrlParser parser = new MonetUrlParser(target, url); - parser.parseModern(); - } - target.barrier(); - } + target.barrier(); + if (url.startsWith("mapi:")) { + try { + MonetUrlParser parser = new MonetUrlParser(target, url.substring(5)); + parser.parseClassic(); + } catch (URISyntaxException e) { + URISyntaxException exc = new URISyntaxException(e.getInput(), e.getReason(), -1); + exc.setStackTrace(e.getStackTrace()); + throw exc; + } + } else { + MonetUrlParser parser = new MonetUrlParser(target, url); + parser.parseModern(); + } + target.barrier(); + } - public static String percentDecode(String context, String text) throws URISyntaxException { - try { - return URLDecoder.decode(text, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("should be unreachable: UTF-8 unknown??", e); - } catch (IllegalArgumentException e) { - throw new URISyntaxException(text, context + ": invalid percent escape"); - } - } + public static String percentDecode(String context, String text) throws URISyntaxException { + try { + return URLDecoder.decode(text, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("should be unreachable: UTF-8 unknown??", e); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(text, context + ": invalid percent escape"); + } + } - public static String percentEncode(String text) { - try { - return URLEncoder.encode(text, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } + public static String percentEncode(String text) { + try { + return URLEncoder.encode(text, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } - private void parseModern() throws URISyntaxException, ValidationError { - clearBasic(); + private void parseModern() throws URISyntaxException, ValidationError { + clearBasic(); - String scheme = url.getScheme(); - if (scheme == null) throw new URISyntaxException(urlText, "URL scheme must be monetdb:// or monetdbs://"); - switch (scheme) { - case "monetdb": - target.setTls(false); - break; - case "monetdbs": - target.setTls(true); - break; - default: - throw new URISyntaxException(urlText, "URL scheme must be monetdb:// or monetdbs://"); - } + String scheme = url.getScheme(); + if (scheme == null) + throw new URISyntaxException(urlText, "URL scheme must be monetdb:// or monetdbs://"); + switch (scheme) { + case "monetdb": + target.setTls(false); + break; + case "monetdbs": + target.setTls(true); + break; + default: + throw new URISyntaxException(urlText, "URL scheme must be monetdb:// or monetdbs://"); + } - // The built-in getHost and getPort methods do strange things - // in edge cases such as percent-encoded host names and - // invalid port numbers - String authority = url.getAuthority(); - String host; - String remainder; - int pos; - if (authority == null) { - if (!url.getRawSchemeSpecificPart().startsWith("//")) { - throw new URISyntaxException(urlText, "expected //"); - } - host = ""; - remainder = ""; - } else if (authority.equals("-")) { - host = ""; - remainder = ""; - } else { - if (authority.startsWith("[")) { - // IPv6 - pos = authority.indexOf(']'); - if (pos < 0) - throw new URISyntaxException(urlText, "unmatched '['"); - host = authority.substring(1, pos); - remainder = authority.substring(pos + 1); - } else if ((pos = authority.indexOf(':')) >= 0) { - host = authority.substring(0, pos); - remainder = authority.substring(pos); - } else { - host = authority; - remainder = ""; - } - } - host = Target.unpackHost(host); - target.setHost(host); + // The built-in getHost and getPort methods do strange things + // in edge cases such as percent-encoded host names and + // invalid port numbers + String authority = url.getAuthority(); + String host; + String remainder; + int pos; + if (authority == null) { + if (!url.getRawSchemeSpecificPart().startsWith("//")) { + throw new URISyntaxException(urlText, "expected //"); + } + host = ""; + remainder = ""; + } else if (authority.equals("-")) { + host = ""; + remainder = ""; + } else { + if (authority.startsWith("[")) { + // IPv6 + pos = authority.indexOf(']'); + if (pos < 0) + throw new URISyntaxException(urlText, "unmatched '['"); + host = authority.substring(1, pos); + remainder = authority.substring(pos + 1); + } else if ((pos = authority.indexOf(':')) >= 0) { + host = authority.substring(0, pos); + remainder = authority.substring(pos); + } else { + host = authority; + remainder = ""; + } + } + host = Target.unpackHost(host); + target.setHost(host); - if (remainder.isEmpty()) { - // do nothing - } else if (remainder.startsWith(":")) { - String portStr = remainder.substring(1); - try { - int port = Integer.parseInt(portStr); - if (port <= 0 || port > 65535) - portStr = null; - } catch (NumberFormatException e) { - portStr = null; - } - if (portStr == null) - throw new ValidationError(urlText, "invalid port number"); - target.setString(Parameter.PORT, portStr); - } + if (remainder.isEmpty()) { + // do nothing + } else if (remainder.startsWith(":")) { + String portStr = remainder.substring(1); + try { + int port = Integer.parseInt(portStr); + if (port <= 0 || port > 65535) + portStr = null; + } catch (NumberFormatException e) { + portStr = null; + } + if (portStr == null) + throw new ValidationError(urlText, "invalid port number"); + target.setString(Parameter.PORT, portStr); + } - String path = url.getRawPath(); - String[] parts = path.split("/", 4); - // <0: empty before leading slash> / <1: database> / <2: tableschema> / <3: table> / <4: should not exist> - switch (parts.length) { - case 4: - target.setString(Parameter.TABLE, percentDecode(Parameter.TABLE.name, parts[3])); - // fallthrough - case 3: - target.setString(Parameter.TABLESCHEMA, percentDecode(Parameter.TABLESCHEMA.name, parts[2])); - // fallthrough - case 2: - target.setString(Parameter.DATABASE, percentDecode(Parameter.DATABASE.name, parts[1])); - case 1: - case 0: - // fallthrough - break; - } + String path = url.getRawPath(); + String[] parts = path.split("/", 4); + // <0: empty before leading slash> / <1: database> / <2: tableschema> / <3: table> / <4: should not exist> + switch (parts.length) { + case 4: + target.setString(Parameter.TABLE, percentDecode(Parameter.TABLE.name, parts[3])); + // fallthrough + case 3: + target.setString(Parameter.TABLESCHEMA, percentDecode(Parameter.TABLESCHEMA.name, parts[2])); + // fallthrough + case 2: + target.setString(Parameter.DATABASE, percentDecode(Parameter.DATABASE.name, parts[1])); + case 1: + case 0: + // fallthrough + break; + } - final String query = url.getRawQuery(); - if (query != null) { - final String args[] = query.split("&"); - for (int i = 0; i < args.length; i++) { - pos = args[i].indexOf('='); - if (pos <= 0) { - throw new URISyntaxException(args[i], "invalid key=value pair"); - } - String key = args[i].substring(0, pos); - key = percentDecode(key, key); - Parameter parm = Parameter.forName(key); - if (parm != null && parm.isCore) - throw new URISyntaxException(key, key + "= is not allowed as a query parameter"); + final String query = url.getRawQuery(); + if (query != null) { + final String[] args = query.split("&"); + for (int i = 0; i < args.length; i++) { + pos = args[i].indexOf('='); + if (pos <= 0) { + throw new URISyntaxException(args[i], "invalid key=value pair"); + } + String key = args[i].substring(0, pos); + key = percentDecode(key, key); + Parameter parm = Parameter.forName(key); + if (parm != null && parm.isCore) + throw new URISyntaxException(key, key + "= is not allowed as a query parameter"); - String value = args[i].substring(pos + 1); - target.setString(key, percentDecode(key, value)); - } - } - } + String value = args[i].substring(pos + 1); + target.setString(key, percentDecode(key, value)); + } + } + } - private void parseClassic() throws URISyntaxException, ValidationError { - if (!url.getRawSchemeSpecificPart().startsWith("//")) { - throw new URISyntaxException(urlText, "expected //"); - } + private void parseClassic() throws URISyntaxException, ValidationError { + if (!url.getRawSchemeSpecificPart().startsWith("//")) { + throw new URISyntaxException(urlText, "expected //"); + } - String scheme = url.getScheme(); - if (scheme == null) - scheme = ""; - switch (scheme) { - case "monetdb": - parseClassicAuthorityAndPath(); - break; - case "merovingian": - String authority = url.getRawAuthority(); - // authority must be "proxy" ignore authority and path - boolean valid = urlText.startsWith("merovingian://proxy?") || urlText.equals("merovingian://proxy"); - if (!valid) - throw new URISyntaxException(urlText, "with mapi:merovingian:, only //proxy is supported"); - break; - default: - throw new URISyntaxException(urlText, "URL scheme must be mapi:monetdb:// or mapi:merovingian://"); - } + String scheme = url.getScheme(); + if (scheme == null) + scheme = ""; + switch (scheme) { + case "monetdb": + parseClassicAuthorityAndPath(); + break; + case "merovingian": + String authority = url.getRawAuthority(); + // authority must be "proxy" ignore authority and path + boolean valid = urlText.startsWith("merovingian://proxy?") || urlText.equals("merovingian://proxy"); + if (!valid) + throw new URISyntaxException(urlText, "with mapi:merovingian:, only //proxy is supported"); + break; + default: + throw new URISyntaxException(urlText, "URL scheme must be mapi:monetdb:// or mapi:merovingian://"); + } - final String query = url.getRawQuery(); - if (query != null) { - final String args[] = query.split("&"); - for (int i = 0; i < args.length; i++) { - String arg = args[i]; - if (arg.startsWith("language=")) { - String language = arg.substring(9); - target.setString(Parameter.LANGUAGE, language); - } else if (arg.startsWith("database=")) { - String database = arg.substring(9); - target.setString(Parameter.DATABASE, database); - } else { - // ignore - } - } - } - } + final String query = url.getRawQuery(); + if (query != null) { + final String[] args = query.split("&"); + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg.startsWith("language=")) { + String language = arg.substring(9); + target.setString(Parameter.LANGUAGE, language); + } else if (arg.startsWith("database=")) { + String database = arg.substring(9); + target.setString(Parameter.DATABASE, database); + } else { + // ignore + } + } + } + } - private void parseClassicAuthorityAndPath() throws URISyntaxException, ValidationError { - clearBasic(); - String authority = url.getRawAuthority(); - String host; - String portStr; - int pos; - if (authority == null) { - host = ""; - portStr = ""; - } else if (authority.indexOf('@') >= 0) { - throw new URISyntaxException(urlText, "user@host syntax is not allowed"); - } else if ((pos = authority.indexOf(':')) >= 0) { - host = authority.substring(0, pos); - portStr = authority.substring(pos + 1); - } else { - host = authority; - portStr = ""; - } + private void parseClassicAuthorityAndPath() throws URISyntaxException, ValidationError { + clearBasic(); + String authority = url.getRawAuthority(); + String host; + String portStr; + int pos; + if (authority == null) { + host = ""; + portStr = ""; + } else if (authority.indexOf('@') >= 0) { + throw new URISyntaxException(urlText, "user@host syntax is not allowed"); + } else if ((pos = authority.indexOf(':')) >= 0) { + host = authority.substring(0, pos); + portStr = authority.substring(pos + 1); + } else { + host = authority; + portStr = ""; + } - if (!portStr.isEmpty()) { - int port; - try { - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - port = -1; - } - if (port <= 0) { - throw new ValidationError(urlText, "invalid port number"); - } - target.setString(Parameter.PORT, portStr); - } + if (!portStr.isEmpty()) { + int port; + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + port = -1; + } + if (port <= 0) { + throw new ValidationError(urlText, "invalid port number"); + } + target.setString(Parameter.PORT, portStr); + } - String path = url.getRawPath(); - if (host.isEmpty() && portStr.isEmpty()) { - // socket - target.clear(Parameter.HOST); - target.setString(Parameter.SOCK, path != null ? path : ""); - } else { - // tcp - target.clear(Parameter.SOCK); - target.setString(Parameter.HOST, host); - if (path == null || path.isEmpty()) { - // do nothing - } else if (!path.startsWith("/")) { - throw new URISyntaxException(urlText, "expect path to start with /"); - } else { - String database = path.substring(1); - target.setString(Parameter.DATABASE, database); - } - } - } + String path = url.getRawPath(); + if (host.isEmpty() && portStr.isEmpty()) { + // socket + target.clear(Parameter.HOST); + target.setString(Parameter.SOCK, path != null ? path : ""); + } else { + // tcp + target.clear(Parameter.SOCK); + target.setString(Parameter.HOST, host); + if (path == null || path.isEmpty()) { + // do nothing + } else if (!path.startsWith("/")) { + throw new URISyntaxException(urlText, "expect path to start with /"); + } else { + String database = path.substring(1); + target.setString(Parameter.DATABASE, database); + } + } + } - private void clearBasic() { - target.clear(Parameter.TLS); - target.clear(Parameter.HOST); - target.clear(Parameter.PORT); - target.clear(Parameter.DATABASE); - } + private void clearBasic() { + target.clear(Parameter.TLS); + target.clear(Parameter.HOST); + target.clear(Parameter.PORT); + target.clear(Parameter.DATABASE); + } }