diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/RouteValueParser.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/RouteValueParser.java index 998f44208..67e329516 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/RouteValueParser.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/RouteValueParser.java @@ -6,15 +6,18 @@ import java.util.concurrent.Callable; import java.util.regex.Matcher; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFRoute; +import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.ConnectionException; +import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException; import org.springframework.ide.vscode.commons.util.RegexpParser; import org.springframework.ide.vscode.commons.util.ValueParseException; public class RouteValueParser extends RegexpParser { - private static final String ROUTE_REGEX = "^([\\da-z\\.-]+)(:\\d{1,4})?((\\/[\\dA-Za-z\\.-]+)*\\/?)?$"; + private static final String ROUTE_REGEX = "^([\\da-z\\.-]+)(:\\d{1,5})?((\\/[\\dA-Za-z\\.-]+)*\\/?)?$"; private static final String ROUTE_TYPE_NAME = "Route"; private static final String ROUTE_DESCRIPTION = "HTTP or TCP application root route"; + private static final int MAX_PORT_NUMBER = 65535; private Callable> domains; @@ -22,12 +25,24 @@ public class RouteValueParser extends RegexpParser { super(ROUTE_REGEX, ROUTE_TYPE_NAME, ROUTE_DESCRIPTION); this.domains = domains; } - - @Override - public Object parse(String str) throws Exception { - Matcher matcher = (Matcher) super.parse(str); - if (matcher != null) { - Collection cloudDomains = domains == null ? Collections.emptyList() : domains.call(); + + private Matcher staticValidation(String str) throws Exception { + return (Matcher) super.parse(str); + } + + private Object dynamicValidation(String str, Matcher matcher) throws Exception { + try { + Collection cloudDomains = Collections.emptyList(); + try { + cloudDomains = domains == null ? Collections.emptyList() : domains.call(); + } catch (ValueParseException e) { + /* + * If domains hint provider throws exception it is + * ValueParserException not NoTargetsException. This means no + * communication with CF -> abort dyncamic validation + */ + return matcher; + } // Ensure cloud domains is empty list instead of null if (cloudDomains == null) { cloudDomains = Collections.emptyList(); @@ -40,11 +55,28 @@ public class RouteValueParser extends RegexpParser { throw new ValueParseException( "Unable to determine type of route. HTTP port may have a path but no port. TCP route may have port but no path."); } + if (route.getPort() > MAX_PORT_NUMBER) { + String portAndColumn = matcher.group(2); + int start = str.indexOf(portAndColumn) + 1; + int end = start + portAndColumn.length() - 1; + throw new ValueParseException("Invalid port number. Port range must be between 1 and " + MAX_PORT_NUMBER, start, end); + } if (!cloudDomains.contains(route.getDomain())) { String hostDomain = matcher.group(1); throw new ReconcileException("Unknown domain", ManifestYamlSchemaProblemsTypes.UNKNOWN_DOMAIN_PROBLEM, hostDomain.lastIndexOf(route.getDomain()), hostDomain.length()); } return route; + } catch (ConnectionException | NoTargetsException e) { + // No connection to CF? Abort dynamic validation + return matcher; + } + } + + @Override + public Object parse(String str) throws Exception { + Matcher matcher = staticValidation(str); + if (matcher != null) { + return dynamicValidation(str, matcher); } return null; } diff --git a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java index 4e6fe6b15..573d82b8f 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java +++ b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java @@ -1021,9 +1021,7 @@ public class ManifestYamlEditorTest { " routes:\n" + " - route: http://springsource.org\n"); editor.assertProblems("http://springsource.org|is not a valid 'Route'"); - Diagnostic problem = editor.assertProblem("http://springsource.org"); - assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); editor = harness.newEditor( @@ -1032,9 +1030,7 @@ public class ManifestYamlEditorTest { " routes:\n" + " - route: spring source.org\n"); editor.assertProblems("spring source.org|is not a valid 'Route'"); - problem = editor.assertProblem("spring source.org"); - assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); editor = harness.newEditor( @@ -1043,9 +1039,7 @@ public class ManifestYamlEditorTest { " routes:\n" + " - route: springsource.org:kuku\n"); editor.assertProblems("springsource.org:kuku|is not a valid 'Route'"); - problem = editor.assertProblem("springsource.org:kuku"); - assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); @@ -1055,9 +1049,16 @@ public class ManifestYamlEditorTest { " routes:\n" + " - route: springsource.org/kuku?p=23\n"); editor.assertProblems("springsource.org/kuku?p=23|is not a valid 'Route'"); - problem = editor.assertProblem("springsource.org/kuku?p=23"); + assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); + editor = harness.newEditor( + "applications:\n" + + "- name: foo\n" + + " routes:\n" + + " - route: springsource.org:645788\n"); + editor.assertProblems("springsource.org:645788|is not a valid 'Route'"); + problem = editor.assertProblem("springsource.org:645788"); assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); } @@ -1069,9 +1070,16 @@ public class ManifestYamlEditorTest { " routes:\n" + " - route: springsource.org:8765/path\n"); editor.assertProblems("springsource.org:8765/path|Unable to determine type of route"); - Diagnostic problem = editor.assertProblem("springsource.org:8765/path"); - + assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); + + editor = harness.newEditor( + "applications:\n" + + "- name: foo\n" + + " routes:\n" + + " - route: host.springsource.org:66000\n"); + editor.assertProblems("66000|Invalid port"); + problem = editor.assertProblem("66000"); assertEquals(DiagnosticSeverity.Error, problem.getSeverity()); editor = harness.newEditor( @@ -1080,9 +1088,7 @@ public class ManifestYamlEditorTest { " routes:\n" + " - route: host.springsource.org\n"); editor.assertProblems("springsource.org|Unknown domain"); - problem = editor.assertProblem("springsource.org"); - assertEquals(DiagnosticSeverity.Warning, problem.getSeverity()); }