diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index b1593cf637..099c62d91f 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.security.Principal; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -55,11 +56,13 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.ObjectUtils; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; @@ -87,10 +90,6 @@ public class MockHttpServletRequest implements HttpServletRequest { private static final String HTTPS = "https"; - private static final String CONTENT_TYPE_HEADER = "Content-Type"; - - private static final String HOST_HEADER = "Host"; - private static final String CHARSET_PREFIX = "charset="; private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -219,6 +218,8 @@ public class MockHttpServletRequest implements HttpServletRequest { private Cookie[] cookies; + private boolean cookieHeaderSet; + private final Map headers = new LinkedCaseInsensitiveMap<>(); private String method; @@ -387,7 +388,7 @@ public class MockHttpServletRequest implements HttpServletRequest { StringUtils.hasLength(this.characterEncoding)) { sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); } - doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); + doAddHeaderValue(HttpHeaders.CONTENT_TYPE, sb.toString(), true); } } @@ -633,7 +634,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public String getServerName() { - String host = getHeader(HOST_HEADER); + String host = getHeader(HttpHeaders.HOST); if (host != null) { host = host.trim(); if (host.startsWith("[")) { @@ -655,7 +656,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public int getServerPort() { - String host = getHeader(HOST_HEADER); + String host = getHeader(HttpHeaders.HOST); if (host != null) { host = host.trim(); int idx; @@ -922,6 +923,11 @@ public class MockHttpServletRequest implements HttpServletRequest { public void setCookies(Cookie... cookies) { this.cookies = cookies; + if (!this.cookieHeaderSet && !ObjectUtils.isEmpty(cookies)) { + Arrays.stream(cookies) + .map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue())) + .forEach(value -> doAddHeaderValue(HttpHeaders.COOKIE, value, false)); + } } @Override @@ -945,10 +951,11 @@ public class MockHttpServletRequest implements HttpServletRequest { * @see #getDateHeader */ public void addHeader(String name, Object value) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name) && !this.headers.containsKey(CONTENT_TYPE_HEADER)) { + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name) && !this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) { setContentType(value.toString()); } else { + this.cookieHeaderSet = HttpHeaders.COOKIE.equalsIgnoreCase(name); doAddHeaderValue(name, value, false); } } diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java index 1c278d1a10..1060991609 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java @@ -37,9 +37,11 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.util.StringUtils; import org.springframework.web.util.WebUtils; /** @@ -56,12 +58,6 @@ public class MockHttpServletResponse implements HttpServletResponse { private static final String CHARSET_PREFIX = "charset="; - private static final String CONTENT_TYPE_HEADER = "Content-Type"; - - private static final String CONTENT_LENGTH_HEADER = "Content-Length"; - - private static final String LOCATION_HEADER = "Location"; - private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -102,6 +98,8 @@ public class MockHttpServletResponse implements HttpServletResponse { private final List cookies = new ArrayList<>(); + private boolean cookieHeaderSet; + private final Map headers = new LinkedCaseInsensitiveMap<>(); private int status = HttpServletResponse.SC_OK; @@ -168,7 +166,7 @@ public class MockHttpServletResponse implements HttpServletResponse { if (!this.contentType.toLowerCase().contains(CHARSET_PREFIX) && this.charset) { sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); } - doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); + doAddHeaderValue(HttpHeaders.CONTENT_TYPE, sb.toString(), true); } } @@ -208,7 +206,7 @@ public class MockHttpServletResponse implements HttpServletResponse { @Override public void setContentLength(int contentLength) { this.contentLength = contentLength; - doAddHeaderValue(CONTENT_LENGTH_HEADER, contentLength, true); + doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true); } public int getContentLength() { @@ -218,7 +216,7 @@ public class MockHttpServletResponse implements HttpServletResponse { @Override public void setContentLengthLong(long contentLength) { this.contentLength = contentLength; - doAddHeaderValue(CONTENT_LENGTH_HEADER, contentLength, true); + doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true); } public long getContentLengthLong() { @@ -322,6 +320,36 @@ public class MockHttpServletResponse implements HttpServletResponse { public void addCookie(Cookie cookie) { Assert.notNull(cookie, "Cookie must not be null"); this.cookies.add(cookie); + if (!this.cookieHeaderSet) { + doAddHeaderValue(HttpHeaders.SET_COOKIE, getCookieHeader(cookie), false); + } + } + + private String getCookieHeader(Cookie cookie) { + StringBuilder buf = new StringBuilder(); + buf.append(cookie.getName()).append('=').append(cookie.getValue() == null ? "" : cookie.getValue()); + if (StringUtils.hasText(cookie.getPath())) { + buf.append(";Path=").append(cookie.getPath()); + } + if (StringUtils.hasText(cookie.getDomain())) { + buf.append(";Domain=").append(cookie.getDomain()); + } + int maxAge = cookie.getMaxAge(); + if (maxAge >= 0) { + buf.append(";Max-Age=").append(maxAge); + buf.append(";Expires="); + HttpHeaders headers = new HttpHeaders(); + headers.setExpires(maxAge > 0 ? System.currentTimeMillis() + 1000L * maxAge : 0); + buf.append(headers.getFirst(HttpHeaders.EXPIRES)); + } + + if (cookie.getSecure()) { + buf.append(";Secure"); + } + if (cookie.isHttpOnly()) { + buf.append(";HttpOnly"); + } + return buf.toString(); } public Cookie[] getCookies() { @@ -466,13 +494,13 @@ public class MockHttpServletResponse implements HttpServletResponse { public void sendRedirect(String url) throws IOException { Assert.state(!isCommitted(), "Cannot send redirect - response is already committed"); Assert.notNull(url, "Redirect URL must not be null"); - setHeader(LOCATION_HEADER, url); + setHeader(HttpHeaders.LOCATION, url); setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); setCommitted(true); } public String getRedirectedUrl() { - return getHeader(LOCATION_HEADER); + return getHeader(HttpHeaders.LOCATION); } @Override @@ -505,11 +533,13 @@ public class MockHttpServletResponse implements HttpServletResponse { @Override public void setHeader(String name, String value) { + this.cookieHeaderSet = HttpHeaders.SET_COOKIE.equalsIgnoreCase(name); setHeaderValue(name, value); } @Override public void addHeader(String name, String value) { + this.cookieHeaderSet = HttpHeaders.SET_COOKIE.equalsIgnoreCase(name); addHeaderValue(name, value); } @@ -538,11 +568,11 @@ public class MockHttpServletResponse implements HttpServletResponse { } private boolean setSpecialHeader(String name, Object value) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) { + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) { setContentType(value.toString()); return true; } - else if (CONTENT_LENGTH_HEADER.equalsIgnoreCase(name)) { + else if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) { setContentLength(value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(value.toString())); return true; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java index 3b44432f8c..6ae42fb18e 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import com.gargoylesoftware.htmlunit.WebConnection; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.util.Cookie; +import org.apache.http.impl.cookie.BasicClientCookie; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; @@ -161,7 +162,7 @@ public final class MockMvcWebConnection implements WebConnection { if (cookie.getDomain() == null) { cookie.setDomain(webRequest.getUrl().getHost()); } - Cookie toManage = MockWebResponseBuilder.createCookie(cookie); + Cookie toManage = createCookie(cookie); Date expires = toManage.getExpires(); if (expires == null || expires.after(now)) { cookieManager.addCookie(toManage); @@ -172,6 +173,23 @@ public final class MockMvcWebConnection implements WebConnection { } } + private static com.gargoylesoftware.htmlunit.util.Cookie createCookie(javax.servlet.http.Cookie cookie) { + Date expires = null; + if (cookie.getMaxAge() > -1) { + expires = new Date(System.currentTimeMillis() + cookie.getMaxAge() * 1000); + } + BasicClientCookie result = new BasicClientCookie(cookie.getName(), cookie.getValue()); + result.setDomain(cookie.getDomain()); + result.setComment(cookie.getComment()); + result.setExpiryDate(expires); + result.setPath(cookie.getPath()); + result.setSecure(cookie.getSecure()); + if (cookie.isHttpOnly()) { + result.setAttribute("httponly", "true"); + } + return new com.gargoylesoftware.htmlunit.util.Cookie(result); + } + @Override public void close() { } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java index 60b207e68e..2ee99eaf53 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,17 +19,13 @@ package org.springframework.test.web.servlet.htmlunit; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; -import javax.servlet.http.Cookie; - import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.WebResponseData; import com.gargoylesoftware.htmlunit.util.NameValuePair; -import org.apache.http.impl.cookie.BasicClientCookie; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.util.Assert; @@ -105,30 +101,7 @@ final class MockWebResponseBuilder { if (location != null) { responseHeaders.add(new NameValuePair("Location", location)); } - for (Cookie cookie : this.response.getCookies()) { - responseHeaders.add(new NameValuePair("Set-Cookie", valueOfCookie(cookie))); - } return responseHeaders; } - private String valueOfCookie(Cookie cookie) { - return createCookie(cookie).toString(); - } - - static com.gargoylesoftware.htmlunit.util.Cookie createCookie(Cookie cookie) { - Date expires = null; - if (cookie.getMaxAge() > -1) { - expires = new Date(System.currentTimeMillis() + cookie.getMaxAge() * 1000); - } - BasicClientCookie result = new BasicClientCookie(cookie.getName(), cookie.getValue()); - result.setDomain(cookie.getDomain()); - result.setComment(cookie.getComment()); - result.setExpiryDate(expires); - result.setPath(cookie.getPath()); - result.setSecure(cookie.getSecure()); - if (cookie.isHttpOnly()) { - result.setAttribute("httponly", "true"); - } - return new com.gargoylesoftware.htmlunit.util.Cookie(result); - } } diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java index 1ec2d9b1df..af1bef380a 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java @@ -251,12 +251,14 @@ public class MockHttpServletRequestTests { request.setCookies(cookie1, cookie2); Cookie[] cookies = request.getCookies(); + List cookieHeaders = Collections.list(request.getHeaders("Cookie")); assertEquals(2, cookies.length); assertEquals("foo", cookies[0].getName()); assertEquals("bar", cookies[0].getValue()); assertEquals("baz", cookies[1].getName()); assertEquals("qux", cookies[1].getValue()); + assertEquals(Arrays.asList("foo=bar", "baz=qux"), cookieHeaders); } @Test diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java index 96b55e8111..5d4b5d2a76 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java @@ -19,10 +19,14 @@ package org.springframework.mock.web; import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.junit.Test; +import org.springframework.http.HttpHeaders; import org.springframework.web.util.WebUtils; import static org.junit.Assert.*; @@ -155,6 +159,22 @@ public class MockHttpServletResponseTests { assertEquals("HTTP header casing not being preserved", headerName, responseHeaders.iterator().next()); } + @Test + public void cookies() { + Cookie cookie = new Cookie("foo", "bar"); + cookie.setPath("/path"); + cookie.setDomain("example.com"); + cookie.setMaxAge(0); + cookie.setSecure(true); + cookie.setHttpOnly(true); + + response.addCookie(cookie); + + assertEquals("foo=bar;Path=/path;Domain=example.com;" + + "Max-Age=0;Expires=Thu, 01 Jan 1970 00:00:00 GMT;" + + "Secure;HttpOnly", response.getHeader(HttpHeaders.SET_COOKIE)); + } + @Test public void servletOutputStreamCommittedWhenBufferSizeExceeded() throws IOException { assertFalse(response.isCommitted()); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java index 33ab3dd207..bb62afbede 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java @@ -116,8 +116,8 @@ public class MockWebResponseBuilderTests { assertThat(header.getValue(), equalTo("value")); header = responseHeaders.get(2); assertThat(header.getName(), equalTo("Set-Cookie")); - assertThat(header.getValue(), startsWith("cookieA=valueA;domain=domain;path=/path;expires=")); - assertThat(header.getValue(), endsWith(";secure;httpOnly")); + assertThat(header.getValue(), startsWith("cookieA=valueA;Path=/path;Domain=domain;Max-Age=1800;Expires=")); + assertThat(header.getValue(), endsWith(";Secure;HttpOnly")); } // SPR-14169 diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java index 543cd89471..5a2a210faf 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java @@ -19,8 +19,8 @@ package org.springframework.test.web.servlet.result; import java.net.URI; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; - import javax.servlet.http.Cookie; import org.junit.Test; @@ -40,7 +40,8 @@ import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.ModelAndView; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Unit tests for {@link PrintingResultHandler}. @@ -114,10 +115,18 @@ public class PrintingResultHandlerTests { this.handler.handle(this.mvcResult); + // Manually validate cookie values since maxAge changes... + List cookieValues = this.response.getHeaders("Set-Cookie"); + assertEquals(2, cookieValues.size()); + assertEquals("cookie=cookieValue", cookieValues.get(0)); + assertTrue("Actual: " + cookieValues.get(1), cookieValues.get(1).startsWith( + "enigma=42;Path=/crumbs;Domain=.example.com;Max-Age=1234;Expires=")); + HttpHeaders headers = new HttpHeaders(); headers.set("header", "headerValue"); headers.setContentType(MediaType.TEXT_PLAIN); headers.setLocation(new URI("/redirectFoo")); + headers.put("Set-Cookie", cookieValues); String heading = "MockHttpServletResponse"; assertValue(heading, "Status", this.response.getStatus()); diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java index 896c174288..100a18c83b 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.security.Principal; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -55,11 +56,13 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.ObjectUtils; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; @@ -87,10 +90,6 @@ public class MockHttpServletRequest implements HttpServletRequest { private static final String HTTPS = "https"; - private static final String CONTENT_TYPE_HEADER = "Content-Type"; - - private static final String HOST_HEADER = "Host"; - private static final String CHARSET_PREFIX = "charset="; private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -219,6 +218,8 @@ public class MockHttpServletRequest implements HttpServletRequest { private Cookie[] cookies; + private boolean cookieHeaderSet; + private final Map headers = new LinkedCaseInsensitiveMap<>(); private String method; @@ -387,7 +388,7 @@ public class MockHttpServletRequest implements HttpServletRequest { StringUtils.hasLength(this.characterEncoding)) { sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); } - doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); + doAddHeaderValue(HttpHeaders.CONTENT_TYPE, sb.toString(), true); } } @@ -633,7 +634,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public String getServerName() { - String host = getHeader(HOST_HEADER); + String host = getHeader(HttpHeaders.HOST); if (host != null) { host = host.trim(); if (host.startsWith("[")) { @@ -655,7 +656,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public int getServerPort() { - String host = getHeader(HOST_HEADER); + String host = getHeader(HttpHeaders.HOST); if (host != null) { host = host.trim(); int idx; @@ -922,6 +923,11 @@ public class MockHttpServletRequest implements HttpServletRequest { public void setCookies(Cookie... cookies) { this.cookies = cookies; + if (!this.cookieHeaderSet && !ObjectUtils.isEmpty(cookies)) { + Arrays.stream(cookies) + .map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue())) + .forEach(value -> doAddHeaderValue(HttpHeaders.COOKIE, value, false)); + } } @Override @@ -945,10 +951,11 @@ public class MockHttpServletRequest implements HttpServletRequest { * @see #getDateHeader */ public void addHeader(String name, Object value) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name) && !this.headers.containsKey(CONTENT_TYPE_HEADER)) { + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name) && !this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) { setContentType(value.toString()); } else { + this.cookieHeaderSet = HttpHeaders.COOKIE.equalsIgnoreCase(name); doAddHeaderValue(name, value, false); } } diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java index bc667a8e60..17c5fe2974 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java +++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java @@ -37,9 +37,11 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.util.StringUtils; import org.springframework.web.util.WebUtils; /** @@ -56,12 +58,6 @@ public class MockHttpServletResponse implements HttpServletResponse { private static final String CHARSET_PREFIX = "charset="; - private static final String CONTENT_TYPE_HEADER = "Content-Type"; - - private static final String CONTENT_LENGTH_HEADER = "Content-Length"; - - private static final String LOCATION_HEADER = "Location"; - private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -102,6 +98,8 @@ public class MockHttpServletResponse implements HttpServletResponse { private final List cookies = new ArrayList<>(); + private boolean cookieHeaderSet; + private final Map headers = new LinkedCaseInsensitiveMap<>(); private int status = HttpServletResponse.SC_OK; @@ -168,7 +166,7 @@ public class MockHttpServletResponse implements HttpServletResponse { if (!this.contentType.toLowerCase().contains(CHARSET_PREFIX) && this.charset) { sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); } - doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); + doAddHeaderValue(HttpHeaders.CONTENT_TYPE, sb.toString(), true); } } @@ -208,7 +206,7 @@ public class MockHttpServletResponse implements HttpServletResponse { @Override public void setContentLength(int contentLength) { this.contentLength = contentLength; - doAddHeaderValue(CONTENT_LENGTH_HEADER, contentLength, true); + doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true); } public int getContentLength() { @@ -218,7 +216,7 @@ public class MockHttpServletResponse implements HttpServletResponse { @Override public void setContentLengthLong(long contentLength) { this.contentLength = contentLength; - doAddHeaderValue(CONTENT_LENGTH_HEADER, contentLength, true); + doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true); } public long getContentLengthLong() { @@ -322,6 +320,36 @@ public class MockHttpServletResponse implements HttpServletResponse { public void addCookie(Cookie cookie) { Assert.notNull(cookie, "Cookie must not be null"); this.cookies.add(cookie); + if (!this.cookieHeaderSet) { + doAddHeaderValue(HttpHeaders.SET_COOKIE, getCookieHeader(cookie), false); + } + } + + private String getCookieHeader(Cookie cookie) { + StringBuilder buf = new StringBuilder(); + buf.append(cookie.getName()).append('=').append(cookie.getValue() == null ? "" : cookie.getValue()); + if (StringUtils.hasText(cookie.getPath())) { + buf.append(";Path=").append(cookie.getPath()); + } + if (StringUtils.hasText(cookie.getDomain())) { + buf.append(";Domain=").append(cookie.getDomain()); + } + int maxAge = cookie.getMaxAge(); + if (maxAge >= 0) { + buf.append(";Max-Age=").append(maxAge); + buf.append(";Expires="); + HttpHeaders headers = new HttpHeaders(); + headers.setExpires(maxAge > 0 ? System.currentTimeMillis() + 1000L * maxAge : 0); + buf.append(headers.getFirst(HttpHeaders.EXPIRES)); + } + + if (cookie.getSecure()) { + buf.append(";Secure"); + } + if (cookie.isHttpOnly()) { + buf.append(";HttpOnly"); + } + return buf.toString(); } public Cookie[] getCookies() { @@ -466,13 +494,13 @@ public class MockHttpServletResponse implements HttpServletResponse { public void sendRedirect(String url) throws IOException { Assert.state(!isCommitted(), "Cannot send redirect - response is already committed"); Assert.notNull(url, "Redirect URL must not be null"); - setHeader(LOCATION_HEADER, url); + setHeader(HttpHeaders.LOCATION, url); setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); setCommitted(true); } public String getRedirectedUrl() { - return getHeader(LOCATION_HEADER); + return getHeader(HttpHeaders.LOCATION); } @Override @@ -505,11 +533,13 @@ public class MockHttpServletResponse implements HttpServletResponse { @Override public void setHeader(String name, String value) { + this.cookieHeaderSet = HttpHeaders.SET_COOKIE.equalsIgnoreCase(name); setHeaderValue(name, value); } @Override public void addHeader(String name, String value) { + this.cookieHeaderSet = HttpHeaders.SET_COOKIE.equalsIgnoreCase(name); addHeaderValue(name, value); } @@ -538,11 +568,11 @@ public class MockHttpServletResponse implements HttpServletResponse { } private boolean setSpecialHeader(String name, Object value) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) { + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) { setContentType(value.toString()); return true; } - else if (CONTENT_LENGTH_HEADER.equalsIgnoreCase(name)) { + else if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) { setContentLength(value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(value.toString())); return true;