/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.framework.util; import java.io.File; import java.net.URL; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import com.google.common.base.Charsets; import com.google.common.io.Resources; import com.google.common.net.HttpHeaders; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.*; public class HttpServletRangeUtilTest { private File INPUT_FILE; public HttpServletRangeUtilTest() throws Exception { final URL url = getClass().getResource( "input.dat" ); INPUT_FILE = File.createTempFile( "range-test", "dat" ); FileUtils.writeStringToFile( INPUT_FILE, Resources.toString( url, Charsets.UTF_8 ) ); INPUT_FILE.deleteOnExit(); } @Test public void test_range_regular_expression() throws Exception { assertTrue( "range cannot be parsed!", "bytes=0-0,-1".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); assertTrue( "range cannot be parsed!", "bytes=0-5".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); assertTrue( "range cannot be parsed!", "bytes=0-5,6-10,11-20".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); assertTrue( "range cannot be parsed!", "bytes=0-5, 6-10, 11-20".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); assertTrue( "range cannot be parsed!", "bytes=-500".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); assertTrue( "range cannot be parsed!", "bytes=9500-".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); assertTrue( "range cannot be parsed!", "bytes=500-600,601-999".matches( HttpServletRangeUtil.PATTERN_RANGE ) ); } @Test public void test_bad_symbols_in_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=bad" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, mockHttpServletResponse.getStatus() ); } @Test public void test_bad_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=5-1" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, mockHttpServletResponse.getStatus() ); } @Test public void test_out_of_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=50000-50100" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, mockHttpServletResponse.getStatus() ); } @Test public void test_out_of_range_in_multipart() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=0-5, 50000-50100" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, mockHttpServletResponse.getStatus() ); } @Test public void test_no_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( readFromFile( "input.dat" ), mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_OK, mockHttpServletResponse.getStatus() ); assertEquals( "52", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } @Test public void test_process_request_multipart_zero_to_zero() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=0-0,-1" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( readFromFile( "response1.dat" ), mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "multipart/byteranges; boundary=THIS_STRING_SEPARATES", mockHttpServletResponse.getContentType() ); assertEquals( "attachment;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); } @Test public void test_process_request_plain_one_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=0-5" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "AaBbCc", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "application/pdf", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); assertEquals( "6", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } @Test public void test_process_request_multipart_three_ranges() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=0-5,6-10,11-20" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( readFromFile( "response3.dat" ), mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "multipart/byteranges; boundary=THIS_STRING_SEPARATES", mockHttpServletResponse.getContentType() ); assertEquals( "attachment;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); } @Test public void test_process_request_multipart_three_ranges_with_spaces() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=0-5, 6-10, 11-20" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( readFromFile( "response3.dat" ), mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "multipart/byteranges; boundary=THIS_STRING_SEPARATES", mockHttpServletResponse.getContentType() ); assertEquals( "attachment;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); } @Test public void test_process_request_plain_minus_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=-50" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "BbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "application/pdf", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); assertEquals( "50", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } @Test public void test_process_request_plain_some_range() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=-48" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "CcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "application/pdf", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); assertEquals( "48", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } @Test public void test_process_request_plain_range_minus() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=50-" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "Zz", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "application/pdf", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); assertEquals( "2", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } @Test public void test_process_request_plain_last_range_minus() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.dat" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=51-" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.dat", "application/pdf", INPUT_FILE, false ); assertEquals( "z", mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "application/pdf", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.dat\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); assertEquals( "1", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } @Test public void test_gzip_ranges() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.js" ); httpServletRequest.addHeader( HttpHeaders.RANGE, "bytes=0-0,-1" ); httpServletRequest.addHeader( HttpHeaders.ACCEPT_ENCODING, "gzip" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.js", "application/javascript", INPUT_FILE, false ); assertEquals( readFromFile( "response4.dat" ), mockHttpServletResponse.getContentAsString() ); assertEquals( HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus() ); assertEquals( "multipart/byteranges; boundary=THIS_STRING_SEPARATES", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.js\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); } @Test public void test_gzip_plain() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod( "GET" ); httpServletRequest.setPathInfo( "/input.js" ); httpServletRequest.addHeader( HttpHeaders.ACCEPT_ENCODING, "gzip" ); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest( httpServletRequest, mockHttpServletResponse, "input.js", "application/javascript", INPUT_FILE, false ); assertTrue( mockHttpServletResponse.getContentAsByteArray().length > 0 ); assertEquals( HttpServletResponse.SC_OK, mockHttpServletResponse.getStatus() ); assertEquals( "application/javascript", mockHttpServletResponse.getContentType() ); assertEquals( "inline;filename=\"input.js\"", mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_DISPOSITION ) ); assertEquals( null, mockHttpServletResponse.getHeader( HttpHeaders.CONTENT_LENGTH ) ); } private String readFromFile( final String fileName ) throws Exception { final URL url = getClass().getResource( fileName ); if ( url == null ) { throw new IllegalArgumentException( "Resource file [" + fileName + "] not found" ); } final String s = Resources.toString( url, Charsets.UTF_8 ); return s.replaceAll( "\\r\\n", "\n" ).replaceAll( "\\n", "\r\n" ); } }