From 833a01d6a5de8b5b343de23697df04df0099057a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 13 Jul 2015 18:05:39 +0100 Subject: [PATCH] Add a Host: header to HTTP request snippets HTTP 1.1 requires a Host: header in all requests. This commit updates the HTTP request snippet to ensure that such a header is always present. Closes gh-85 --- .../restdocs/http/HttpDocumentation.java | 18 ++++-- .../restdocs/http/HttpDocumentationTests.java | 61 +++++++++++++------ 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java index 5c569f1c..781024bd 100644 --- a/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java @@ -100,13 +100,15 @@ public abstract class HttpDocumentation { this.result.getRequest()); this.writer.printf("%s %s HTTP/1.1%n", request.getMethod(), request.getRequestUriWithQueryString()); - + if (requiresHostHeader(request)) { + writeHeader(HttpHeaders.HOST, request.getHost()); + } for (Entry> header : request.getHeaders().entrySet()) { for (String value : header.getValue()) { if (header.getKey() == HttpHeaders.CONTENT_TYPE && request.isMultipartRequest()) { - this.writer.printf("%s: %s; boundary=%s%n", header.getKey(), - value, MULTIPART_BOUNDARY); + writeHeader(header.getKey(), String.format("%s; boundary=%s", + value, MULTIPART_BOUNDARY)); } else { this.writer.printf("%s: %s%n", header.getKey(), value); @@ -115,7 +117,7 @@ public abstract class HttpDocumentation { } } if (requiresFormEncodingContentType(request)) { - this.writer.printf("%s: %s%n", HttpHeaders.CONTENT_TYPE, + writeHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); } this.writer.println(); @@ -133,6 +135,10 @@ public abstract class HttpDocumentation { } } + private boolean requiresHostHeader(DocumentableHttpServletRequest request) { + return request.getHeaders().get(HttpHeaders.HOST) == null; + } + private boolean requiresFormEncodingContentType( DocumentableHttpServletRequest request) { return request.getHeaders().getContentType() == null @@ -140,6 +146,10 @@ public abstract class HttpDocumentation { && StringUtils.hasText(request.getParameterMapAsQueryString()); } + private void writeHeader(String name, String value) { + this.writer.printf("%s: %s%n", name, value); + } + private void writeParts(DocumentableHttpServletRequest request) throws IOException { for (Entry> entry : request.getMultipartFiles() diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/http/HttpDocumentationTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/http/HttpDocumentationTests.java index 73a639e1..b55ddec8 100644 --- a/spring-restdocs/src/test/java/org/springframework/restdocs/http/HttpDocumentationTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/http/HttpDocumentationTests.java @@ -35,7 +35,9 @@ import java.io.IOException; import org.junit.Rule; import org.junit.Test; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.test.ExpectedSnippet; @@ -54,7 +56,8 @@ public class HttpDocumentationTests { @Test public void getRequest() throws IOException { this.snippet.expectHttpRequest("get-request").withContents( - httpRequest(GET, "/foo").header("Alpha", "a")); + httpRequest(GET, "/foo").header(HttpHeaders.HOST, "localhost").header( + "Alpha", "a")); documentHttpRequest("get-request").handle( result(get("/foo").header("Alpha", "a"))); @@ -63,7 +66,7 @@ public class HttpDocumentationTests { @Test public void getRequestWithQueryString() throws IOException { this.snippet.expectHttpRequest("get-request-with-query-string").withContents( - httpRequest(GET, "/foo?bar=baz")); + httpRequest(GET, "/foo?bar=baz").header(HttpHeaders.HOST, "localhost")); documentHttpRequest("get-request-with-query-string").handle( result(get("/foo?bar=baz"))); @@ -72,7 +75,7 @@ public class HttpDocumentationTests { @Test public void getRequestWithParameter() throws IOException { this.snippet.expectHttpRequest("get-request-with-parameter").withContents( - httpRequest(GET, "/foo?b%26r=baz")); + httpRequest(GET, "/foo?b%26r=baz").header(HttpHeaders.HOST, "localhost")); documentHttpRequest("get-request-with-parameter").handle( result(get("/foo").param("b&r", "baz"))); @@ -81,8 +84,8 @@ public class HttpDocumentationTests { @Test public void postRequestWithContent() throws IOException { this.snippet.expectHttpRequest("post-request-with-content").withContents( - httpRequest(POST, "/foo") // - .content("Hello, world")); + httpRequest(POST, "/foo").header(HttpHeaders.HOST, "localhost").content( + "Hello, world")); documentHttpRequest("post-request-with-content").handle( result(post("/foo").content("Hello, world"))); @@ -91,8 +94,8 @@ public class HttpDocumentationTests { @Test public void postRequestWithParameter() throws IOException { this.snippet.expectHttpRequest("post-request-with-parameter").withContents( - httpRequest(POST, "/foo") // - .header("Content-Type", "application/x-www-form-urlencoded") // + httpRequest(POST, "/foo").header(HttpHeaders.HOST, "localhost") + .header("Content-Type", "application/x-www-form-urlencoded") .content("b%26r=baz&a=alpha")); documentHttpRequest("post-request-with-parameter").handle( @@ -102,8 +105,8 @@ public class HttpDocumentationTests { @Test public void putRequestWithContent() throws IOException { this.snippet.expectHttpRequest("put-request-with-content").withContents( - httpRequest(PUT, "/foo") // - .content("Hello, world")); + httpRequest(PUT, "/foo").header(HttpHeaders.HOST, "localhost").content( + "Hello, world")); documentHttpRequest("put-request-with-content").handle( result(put("/foo").content("Hello, world"))); @@ -112,8 +115,8 @@ public class HttpDocumentationTests { @Test public void putRequestWithParameter() throws IOException { this.snippet.expectHttpRequest("put-request-with-parameter").withContents( - httpRequest(PUT, "/foo") // - .header("Content-Type", "application/x-www-form-urlencoded") // + httpRequest(PUT, "/foo").header(HttpHeaders.HOST, "localhost") + .header("Content-Type", "application/x-www-form-urlencoded") .content("b%26r=baz&a=alpha")); documentHttpRequest("put-request-with-parameter").handle( @@ -164,9 +167,11 @@ public class HttpDocumentationTests { String expectedContent = String.format("--%s%nContent-Disposition: form-data; " + "name=image%n%n<< data >>%n--%s--", boundary, boundary); this.snippet.expectHttpRequest("multipart-post").withContents( - httpRequest(POST, "/upload").header("Content-Type", - "multipart/form-data; boundary=" + boundary).content( - expectedContent)); + httpRequest(POST, "/upload") + .header(HttpHeaders.HOST, "localhost") + .header("Content-Type", + "multipart/form-data; boundary=" + boundary) + .content(expectedContent)); MockMultipartFile multipartFile = new MockMultipartFile("image", "documents/images/example.png", null, "<< data >>".getBytes()); documentHttpRequest("multipart-post").handle( @@ -180,13 +185,35 @@ public class HttpDocumentationTests { + "name=image%nContent-Type: image/png%n%n<< data >>%n--%s--", boundary, boundary); this.snippet.expectHttpRequest("multipart-post-with-content-type").withContents( - httpRequest(POST, "/upload").header("Content-Type", - "multipart/form-data; boundary=" + boundary).content( - expectedContent)); + httpRequest(POST, "/upload") + .header(HttpHeaders.HOST, "localhost") + .header("Content-Type", + "multipart/form-data; boundary=" + boundary) + .content(expectedContent)); MockMultipartFile multipartFile = new MockMultipartFile("image", "documents/images/example.png", MediaType.IMAGE_PNG_VALUE, "<< data >>".getBytes()); documentHttpRequest("multipart-post-with-content-type").handle( result(fileUpload("/upload").file(multipartFile))); } + + @Test + public void getRequestWithCustomServerName() throws IOException { + this.snippet.expectHttpRequest("get-request-custom-server-name").withContents( + httpRequest(GET, "/foo").header(HttpHeaders.HOST, "api.example.com")); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); + request.setServerName("api.example.com"); + + documentHttpRequest("get-request-custom-server-name").handle(result(request)); + } + + @Test + public void getRequestWithCustomHost() throws IOException { + this.snippet.expectHttpRequest("get-request-custom-host").withContents( + httpRequest(GET, "/foo").header(HttpHeaders.HOST, "api.example.com")); + + documentHttpRequest("get-request-custom-host").handle( + result(get("/foo").header(HttpHeaders.HOST, "api.example.com"))); + } }