From e40acf2447f36cff584e2088ddb2b31d9ddf74c3 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 15 Mar 2018 19:38:55 -0700 Subject: [PATCH 1/3] Polish --- .../boot/web/client/SampleWebClientConfiguration.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-boot-docs/src/test/java/org/springframework/boot/web/client/SampleWebClientConfiguration.java b/spring-boot-docs/src/test/java/org/springframework/boot/web/client/SampleWebClientConfiguration.java index d6a7a8c573..141be7ce50 100644 --- a/spring-boot-docs/src/test/java/org/springframework/boot/web/client/SampleWebClientConfiguration.java +++ b/spring-boot-docs/src/test/java/org/springframework/boot/web/client/SampleWebClientConfiguration.java @@ -37,12 +37,11 @@ import org.springframework.web.bind.annotation.RestController; */ @SpringBootConfiguration @ImportAutoConfiguration({ EmbeddedServletContainerAutoConfiguration.class, - ServerPropertiesAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, - JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class }) + ServerPropertiesAutoConfiguration.class, DispatcherServletAutoConfiguration.class, + WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class }) class SampleWebClientConfiguration { - @RestController private static class ExampleController { From e975dbe3f04371a1463437db56dda012bbb958c6 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 16 Mar 2018 11:56:34 -0700 Subject: [PATCH 2/3] Only use jar shortcut for matching URLs Update JAR `Handler` logic so that the existing `jarFile` is only used if the requested URL starts with the same path. Prior to this commit it was possible to construct a URL with another URL as context. This could mean that the `handler` was shared and the already resolved `jarFile` contained in the handler wasn't necessarily suitable. Fixes gh-12483 --- .../boot/loader/jar/Handler.java | 3 ++- .../boot/loader/jar/JarFileTests.java | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java index 991309413b..3d93b43061 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java @@ -92,7 +92,8 @@ public class Handler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { - if (this.jarFile != null) { + if (this.jarFile != null + && url.toString().startsWith(this.jarFile.getUrl().toString())) { return JarURLConnection.get(url, this.jarFile); } try { diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java index e4466b8b47..6cff3ec742 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java @@ -485,4 +485,24 @@ public class JarFileTests { assertThat(temp.delete()).isTrue(); } + @Test + public void createUrlFromStringWithContextWhenNotFound() throws Exception { + // gh-12483 + JarURLConnection.setUseFastExceptions(true); + try { + JarFile.registerUrlProtocolHandler(); + JarFile nested = this.jarFile + .getNestedJarFile(this.jarFile.getEntry("nested.jar")); + URL context = nested.getUrl(); + new URL(context, "jar:" + this.rootJarFile.toURI() + "!/nested.jar!/3.dat") + .openConnection().getInputStream().close(); + this.thrown.expect(FileNotFoundException.class); + new URL(context, "jar:" + this.rootJarFile.toURI() + "!/no.dat") + .openConnection().getInputStream().close(); + } + finally { + JarURLConnection.setUseFastExceptions(false); + } + } + } From 23892e33d6ed73a130850d27342dba631b9fb8d7 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 16 Mar 2018 14:30:42 -0700 Subject: [PATCH 3/3] Add text/plain error response support Refine `BasicErrorController` mappings so that only JSON and XML get structured responses. A simple string response is returned for all other media types. Fixes gh-12513 --- .../web/BasicErrorController.java | 29 +++++++++++++++---- .../BasicErrorControllerIntegrationTests.java | 14 +++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java index 41363125d1..4da5734491 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java @@ -31,7 +31,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; /** @@ -82,6 +81,16 @@ public class BasicErrorController extends AbstractErrorController { return this.errorProperties.getPath(); } + @RequestMapping(produces = { "application/xml", "text/xml", "application/json", + "application/*+xml", "application/*+json" }) + public ResponseEntity> errorStructured( + HttpServletRequest request) { + Map body = getErrorAttributes(request, + isIncludeStackTrace(request, MediaType.ALL)); + HttpStatus status = getStatus(request); + return new ResponseEntity>(body, status); + } + @RequestMapping(produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { @@ -94,12 +103,20 @@ public class BasicErrorController extends AbstractErrorController { } @RequestMapping - @ResponseBody - public ResponseEntity> error(HttpServletRequest request) { - Map body = getErrorAttributes(request, - isIncludeStackTrace(request, MediaType.ALL)); + public ResponseEntity errorText(HttpServletRequest request) { + Map attributes = getErrorAttributes(request, + isIncludeStackTrace(request, MediaType.TEXT_PLAIN)); + int padding = 0; + for (Map.Entry entry : attributes.entrySet()) { + padding = Math.max(padding, entry.getKey().length()); + } + StringBuffer body = new StringBuffer(); + for (Map.Entry entry : attributes.entrySet()) { + body.append(String.format("%-" + padding + "s : %s%n", entry.getKey(), + entry.getValue())); + } HttpStatus status = getStatus(request); - return new ResponseEntity>(body, status); + return new ResponseEntity(body.toString(), status); } /** diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTests.java index 932d174c86..3add527f78 100755 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/BasicErrorControllerIntegrationTests.java @@ -158,6 +158,7 @@ public class BasicErrorControllerIntegrationTests { load(); RequestEntity request = RequestEntity .post(URI.create(createUrl("/bodyValidation"))) + .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON).body("{}"); ResponseEntity entity = new TestRestTemplate().exchange(request, Map.class); String resp = entity.getBody().toString(); @@ -167,6 +168,19 @@ public class BasicErrorControllerIntegrationTests { assertThat(resp).contains(MethodArgumentNotValidException.class.getName()); } + @Test + public void testRequestBodyValidationForText() throws Exception { + load(); + RequestEntity request = RequestEntity.post(URI.create(createUrl("/"))) + .accept(MediaType.TEXT_PLAIN).build(); + ResponseEntity entity = new TestRestTemplate().exchange(request, + String.class); + String resp = entity.getBody().toString(); + assertThat(resp).contains("status"); + assertThat(resp).contains("error"); + assertThat(resp).contains(IllegalStateException.class.getName()); + } + @Test public void testConventionTemplateMapping() throws Exception { load();