/* * Copyright 2002-2016 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.servlet.support; import static org.junit.Assert.*; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.web.servlet.FlashMap; import org.springframework.web.util.WebUtils; /** * Test fixture for testing {@link AbstractFlashMapManager} methods. * * @author Rossen Stoyanchev */ public class FlashMapManagerTests { private TestFlashMapManager flashMapManager; private MockHttpServletRequest request; private MockHttpServletResponse response; @Before public void setup() { this.flashMapManager = new TestFlashMapManager(); this.request = new MockHttpServletRequest(); this.response = new MockHttpServletResponse(); } @Test public void retrieveAndUpdateMatchByPath() { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/path"); this.flashMapManager.setFlashMaps(Arrays.asList(flashMap)); this.request.setRequestURI("/path"); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMap, inputFlashMap); } // SPR-8779 @Test public void retrieveAndUpdateMatchByOriginatingPath() { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/accounts"); this.flashMapManager.setFlashMaps(Arrays.asList(flashMap)); this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts"); this.request.setRequestURI("/mvc/accounts"); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMap, inputFlashMap); assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size()); } @Test public void retrieveAndUpdateMatchWithTrailingSlash() { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/path"); this.flashMapManager.setFlashMaps(Arrays.asList(flashMap)); this.request.setRequestURI("/path/"); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMap, inputFlashMap); assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size()); } @Test public void retrieveAndUpdateMatchByParams() { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.addTargetRequestParam("number", "one"); this.flashMapManager.setFlashMaps(Arrays.asList(flashMap)); this.request.setQueryString("number="); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertNull(inputFlashMap); assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size()); this.request.setQueryString("number=two"); inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertNull(inputFlashMap); assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size()); this.request.setQueryString("number=one"); inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMap, inputFlashMap); assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size()); } // SPR-8798 @Test public void retrieveAndUpdateMatchWithMultiValueParam() { FlashMap flashMap = new FlashMap(); flashMap.put("name", "value"); flashMap.addTargetRequestParam("id", "1"); flashMap.addTargetRequestParam("id", "2"); this.flashMapManager.setFlashMaps(Arrays.asList(flashMap)); this.request.setQueryString("id=1"); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertNull(inputFlashMap); assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size()); this.request.setQueryString("id=1&id=2"); inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMap, inputFlashMap); assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size()); } @Test public void retrieveAndUpdateSortMultipleMatches() { FlashMap emptyFlashMap = new FlashMap(); FlashMap flashMapOne = new FlashMap(); flashMapOne.put("key1", "value1"); flashMapOne.setTargetRequestPath("/one"); FlashMap flashMapTwo = new FlashMap(); flashMapTwo.put("key1", "value1"); flashMapTwo.put("key2", "value2"); flashMapTwo.setTargetRequestPath("/one/two"); this.flashMapManager.setFlashMaps(Arrays.asList(emptyFlashMap, flashMapOne, flashMapTwo)); this.request.setRequestURI("/one/two"); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMapTwo, inputFlashMap); assertEquals("Input FlashMap should have been removed", 2, this.flashMapManager.getFlashMaps().size()); } @Test public void retrieveAndUpdateRemoveExpired() throws InterruptedException { List<FlashMap> flashMaps = new ArrayList<>(); for (int i = 0; i < 5; i++) { FlashMap expiredFlashMap = new FlashMap(); expiredFlashMap.startExpirationPeriod(-1); flashMaps.add(expiredFlashMap); } this.flashMapManager.setFlashMaps(flashMaps); this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals("Expired instances should be removed even if the saved FlashMap is empty", 0, this.flashMapManager.getFlashMaps().size()); } @Test public void saveOutputFlashMapEmpty() throws InterruptedException { FlashMap flashMap = new FlashMap(); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); List<FlashMap> allMaps = this.flashMapManager.getFlashMaps(); assertNull(allMaps); } @Test public void saveOutputFlashMap() throws InterruptedException { FlashMap flashMap = new FlashMap(); flashMap.put("name", "value"); this.flashMapManager.setFlashMapTimeout(-1); // expire immediately so we can check expiration started this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); List<FlashMap> allMaps = this.flashMapManager.getFlashMaps(); assertNotNull(allMaps); assertSame(flashMap, allMaps.get(0)); assertTrue(flashMap.isExpired()); } @Test public void saveOutputFlashMapDecodeTargetPath() throws InterruptedException { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/once%20upon%20a%20time"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); assertEquals("/once upon a time", flashMap.getTargetRequestPath()); } @Test public void saveOutputFlashMapNormalizeTargetPath() throws InterruptedException { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("."); this.request.setRequestURI("/once/upon/a/time"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); assertEquals("/once/upon/a", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath("./"); this.request.setRequestURI("/once/upon/a/time"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); assertEquals("/once/upon/a/", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath(".."); this.request.setRequestURI("/once/upon/a/time"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); assertEquals("/once/upon", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath("../"); this.request.setRequestURI("/once/upon/a/time"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); assertEquals("/once/upon/", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath("../../only"); this.request.setRequestURI("/once/upon/a/time"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); assertEquals("/once/only", flashMap.getTargetRequestPath()); } // SPR-9657, SPR-11504 @Test public void saveOutputFlashMapDecodeParameters() throws Exception { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/path"); flashMap.addTargetRequestParam("param", "%D0%90%D0%90"); flashMap.addTargetRequestParam("param", "%D0%91%D0%91"); flashMap.addTargetRequestParam("param", "%D0%92%D0%92"); flashMap.addTargetRequestParam("%3A%2F%3F%23%5B%5D%40", "value"); this.request.setCharacterEncoding("UTF-8"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); MockHttpServletRequest requestAfterRedirect = new MockHttpServletRequest("GET", "/path"); requestAfterRedirect.setQueryString("param=%D0%90%D0%90¶m=%D0%91%D0%91¶m=%D0%92%D0%92&%3A%2F%3F%23%5B%5D%40=value"); requestAfterRedirect.addParameter("param", "\u0410\u0410"); requestAfterRedirect.addParameter("param", "\u0411\u0411"); requestAfterRedirect.addParameter("param", "\u0412\u0412"); requestAfterRedirect.addParameter(":/?#[]@", "value"); flashMap = this.flashMapManager.retrieveAndUpdate(requestAfterRedirect, new MockHttpServletResponse()); assertNotNull(flashMap); assertEquals(1, flashMap.size()); assertEquals("value", flashMap.get("key")); } // SPR-12569 @Test public void flashAttributesWithQueryParamsWithSpace() throws Exception { String encodedValue = URLEncoder.encode("1 2", "UTF-8"); FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/path"); flashMap.addTargetRequestParam("param", encodedValue); this.request.setCharacterEncoding("UTF-8"); this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response); MockHttpServletRequest requestAfterRedirect = new MockHttpServletRequest("GET", "/path"); requestAfterRedirect.setQueryString("param=" + encodedValue); requestAfterRedirect.addParameter("param", "1 2"); flashMap = this.flashMapManager.retrieveAndUpdate(requestAfterRedirect, new MockHttpServletResponse()); assertNotNull(flashMap); assertEquals(1, flashMap.size()); assertEquals("value", flashMap.get("key")); } @Test // SPR-15505 public void retrieveAndUpdateMatchByOriginatingPathAndQueryString() { FlashMap flashMap = new FlashMap(); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/accounts"); flashMap.addTargetRequestParam("a", "b"); this.flashMapManager.setFlashMaps(Collections.singletonList(flashMap)); this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts"); this.request.setAttribute(WebUtils.FORWARD_QUERY_STRING_ATTRIBUTE, "a=b"); this.request.setRequestURI("/mvc/accounts"); this.request.setQueryString("x=y"); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response); assertEquals(flashMap, inputFlashMap); assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size()); } private static class TestFlashMapManager extends AbstractFlashMapManager { private List<FlashMap> flashMaps; public void setFlashMaps(List<FlashMap> flashMaps) { this.flashMaps = new CopyOnWriteArrayList<>(flashMaps); } public List<FlashMap> getFlashMaps() { return this.flashMaps; } @Override protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) { return this.flashMaps; } @Override protected void updateFlashMaps(List<FlashMap> maps, HttpServletRequest request, HttpServletResponse response) { this.flashMaps = maps; } } }