/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.webserver; import com.liferay.document.library.kernel.service.DLAppLocalServiceUtil; import com.liferay.portal.kernel.repository.model.FileEntry; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.servlet.HttpHeaders; import com.liferay.portal.kernel.test.rule.AggregateTestRule; import com.liferay.portal.kernel.test.util.ServiceContextTestUtil; import com.liferay.portal.kernel.test.util.TestPropsValues; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.ContentTypes; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.webdav.methods.Method; import com.liferay.portal.test.rule.LiferayIntegrationTestRule; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletResponse; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.springframework.mock.web.MockHttpServletResponse; /** * @author Alexander Chow */ public class WebServerRangeTest extends BaseWebServerTestCase { @ClassRule @Rule public static final AggregateTestRule aggregateTestRule = new LiferayIntegrationTestRule(); @Test public void testBasic() throws Exception { testRange(null); } @Test public void testMultipartRange() throws Exception { String[] ranges = {"0-9", "25-25", "30-49", "70-79"}; String rangeHeader = "bytes="; for (int i = 0; i < ranges.length; i++) { rangeHeader += ranges[i]; if (i != (ranges.length - 1)) { rangeHeader += StringPool.COMMA; } } MockHttpServletResponse mockHttpServletResponse = testRange( rangeHeader); String contentType = mockHttpServletResponse.getContentType(); Assert.assertTrue(contentType.startsWith(_BOUNDARY_PREFACE)); String boundary = contentType.substring(_BOUNDARY_PREFACE.length()); String responseBody = mockHttpServletResponse.getContentAsString(); Assert.assertTrue( responseBody.startsWith("\r\n--" + boundary + "\r\n")); Assert.assertTrue(responseBody.endsWith("--" + boundary + "--\r\n")); String[] responseBodies = StringUtil.split(responseBody, boundary); Assert.assertEquals( Arrays.toString(responseBodies), ranges.length + 2, responseBodies.length); Assert.assertEquals(StringPool.DOUBLE_DASH, responseBodies[0]); Assert.assertEquals( StringPool.DOUBLE_DASH, responseBodies[ranges.length + 1]); for (int i = 0; i < ranges.length; i++) { String[] lines = StringUtil.split( responseBodies[i + 1], StringPool.RETURN_NEW_LINE); Assert.assertEquals("Content-Type: text/plain", lines[0]); Assert.assertEquals( "Content-Range: bytes " + ranges[i] + "/80", lines[1]); Assert.assertTrue(Validator.isNull(lines[2])); String[] rangePair = StringUtil.split(ranges[i], StringPool.DASH); int start = GetterUtil.getInteger(rangePair[0]); int end = GetterUtil.getInteger(rangePair[1]); byte[] bytes = ArrayUtil.subset( _SAMPLE_DATA.getBytes(), start, end + 1); Assert.assertArrayEquals(bytes, lines[3].getBytes("UTF-8")); Assert.assertEquals(StringPool.DOUBLE_DASH, lines[4]); } } @Test public void testSingleRangeByte() throws Exception { MockHttpServletResponse mockHttpServletResponse = testRange( "bytes=10-10"); Assert.assertEquals( "1", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH)); Assert.assertEquals( "bytes 10-10/80", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_RANGE)); Assert.assertEquals("B", mockHttpServletResponse.getContentAsString()); } @Test public void testSingleRangeFirst() throws Exception { MockHttpServletResponse mockHttpServletResponse = testRange( "bytes=0-9"); Assert.assertEquals( "10", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH)); Assert.assertEquals( "bytes 0-9/80", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_RANGE)); Assert.assertEquals( "A123456789", mockHttpServletResponse.getContentAsString()); } @Test public void testSingleRangeLast() throws Exception { MockHttpServletResponse mockHttpServletResponse = testRange( "bytes=70-79"); Assert.assertEquals( "10", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH)); Assert.assertEquals( "bytes 70-79/80", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_RANGE)); Assert.assertArrayEquals( _UNICODE_DATA.getBytes(), mockHttpServletResponse.getContentAsByteArray()); } @Override protected HttpServlet getServlet() { return new WebServerServlet() { @Override protected boolean isSupportsRangeHeader(String contentType) { return true; } }; } protected MockHttpServletResponse testRange(String rangeHeader) throws Exception { String fileName = "Test Range.txt"; ServiceContext serviceContext = ServiceContextTestUtil.getServiceContext( group.getGroupId(), TestPropsValues.getUserId()); FileEntry fileEntry = DLAppLocalServiceUtil.addFileEntry( TestPropsValues.getUserId(), group.getGroupId(), parentFolder.getFolderId(), fileName, ContentTypes.TEXT_PLAIN, _SAMPLE_DATA.getBytes(), serviceContext); String path = fileEntry.getGroupId() + "/" + fileEntry.getFolderId() + "/" + fileEntry.getTitle(); Map<String, String> headers = new HashMap<>(); if (Validator.isNotNull(rangeHeader)) { headers.put(HttpHeaders.RANGE, rangeHeader); } MockHttpServletResponse mockHttpServletResponse = service( Method.GET, path, headers, null, null, null); int status = mockHttpServletResponse.getStatus(); Assert.assertTrue( mockHttpServletResponse.containsHeader(HttpHeaders.ACCEPT_RANGES)); if (Validator.isNotNull(rangeHeader)) { Assert.assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, status); } else { Assert.assertEquals(HttpServletResponse.SC_OK, status); } String contentType = mockHttpServletResponse.getContentType(); if (Validator.isNotNull(rangeHeader) && rangeHeader.contains(StringPool.COMMA)) { Assert.assertTrue(contentType.startsWith("multipart/byteranges")); } else { Assert.assertEquals(ContentTypes.TEXT_PLAIN, contentType); } return mockHttpServletResponse; } private static final String _BOUNDARY_PREFACE = "multipart/byteranges; boundary="; private static final String _SAMPLE_DATA = "A123456789B123456789C123456789D123456789" + "E123456789F123456789G123456789" + WebServerRangeTest._UNICODE_DATA; private static final String _UNICODE_DATA = "H\u4e2d\u00e9\u00fc\u00f1"; }