Ensure that ZipHeaderPeekIS doesn't miss bytes on partial header read
Closes gh-13032
This commit is contained in:
		
							parent
							
								
									b17a00d0c3
								
							
						
					
					
						commit
						34af02380c
					
				|  | @ -315,26 +315,34 @@ public class JarWriter implements LoaderClassesWriter { | |||
| 	/** | ||||
| 	 * {@link InputStream} that can peek ahead at zip header bytes. | ||||
| 	 */ | ||||
| 	private static class ZipHeaderPeekInputStream extends FilterInputStream { | ||||
| 	static class ZipHeaderPeekInputStream extends FilterInputStream { | ||||
| 
 | ||||
| 		private static final byte[] ZIP_HEADER = new byte[] { 0x50, 0x4b, 0x03, 0x04 }; | ||||
| 
 | ||||
| 		private final byte[] header; | ||||
| 
 | ||||
| 		private final int headerLength; | ||||
| 
 | ||||
| 		private int position; | ||||
| 
 | ||||
| 		private ByteArrayInputStream headerStream; | ||||
| 
 | ||||
| 		protected ZipHeaderPeekInputStream(InputStream in) throws IOException { | ||||
| 			super(in); | ||||
| 			this.header = new byte[4]; | ||||
| 			int len = in.read(this.header); | ||||
| 			this.headerStream = new ByteArrayInputStream(this.header, 0, len); | ||||
| 			this.headerLength = in.read(this.header); | ||||
| 			this.headerStream = new ByteArrayInputStream(this.header, 0, | ||||
| 					this.headerLength); | ||||
| 		} | ||||
| 
 | ||||
| 		@Override | ||||
| 		public int read() throws IOException { | ||||
| 			int read = (this.headerStream != null ? this.headerStream.read() : -1); | ||||
| 			if (read != -1) { | ||||
| 				this.position++; | ||||
| 				if (this.position >= this.headerLength) { | ||||
| 					this.headerStream = null; | ||||
| 				} | ||||
| 				return read; | ||||
| 			} | ||||
| 			return super.read(); | ||||
|  | @ -349,11 +357,20 @@ public class JarWriter implements LoaderClassesWriter { | |||
| 		public int read(byte[] b, int off, int len) throws IOException { | ||||
| 			int read = (this.headerStream != null ? this.headerStream.read(b, off, len) | ||||
| 					: -1); | ||||
| 			if (read != -1) { | ||||
| 				this.headerStream = null; | ||||
| 				return read; | ||||
| 			if (read > 0) { | ||||
| 				this.position += read; | ||||
| 			} | ||||
| 			return super.read(b, off, len); | ||||
| 			else { | ||||
| 				read = 0; | ||||
| 			} | ||||
| 			if (read < len) { | ||||
| 				read += super.read(b, off + read, len - read); | ||||
| 				this.position += read; | ||||
| 			} | ||||
| 			if (this.position >= this.headerLength) { | ||||
| 				this.headerStream = null; | ||||
| 			} | ||||
| 			return read; | ||||
| 		} | ||||
| 
 | ||||
| 		public boolean hasZipHeader() { | ||||
|  |  | |||
|  | @ -0,0 +1,145 @@ | |||
| /* | ||||
|  * Copyright 2012-2018 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.boot.loader.tools; | ||||
| 
 | ||||
| /** | ||||
|  * Tests for {@link ZipHeaderPeekInputStream}. | ||||
|  * | ||||
|  * @author Andy Wilkinson | ||||
|  */ | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
| 
 | ||||
| import org.springframework.boot.loader.tools.JarWriter.ZipHeaderPeekInputStream; | ||||
| 
 | ||||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| 
 | ||||
| public class ZipHeaderPeekInputStreamTests { | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void hasZipHeaderReturnsTrueWhenStreamStartsWithZipHeader() | ||||
| 			throws IOException { | ||||
| 		ZipHeaderPeekInputStream in = null; | ||||
| 		try { | ||||
| 			in = new ZipHeaderPeekInputStream(new ByteArrayInputStream( | ||||
| 					new byte[] { 0x50, 0x4b, 0x03, 0x04, 5, 6 })); | ||||
| 			assertThat(in.hasZipHeader()).isTrue(); | ||||
| 		} | ||||
| 		finally { | ||||
| 			if (in != null) { | ||||
| 				in.close(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void hasZipHeaderReturnsFalseWheStreamDoesNotStartWithZipHeader() | ||||
| 			throws IOException { | ||||
| 		ZipHeaderPeekInputStream in = null; | ||||
| 		try { | ||||
| 			in = new ZipHeaderPeekInputStream( | ||||
| 					new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5 })); | ||||
| 			assertThat(in.hasZipHeader()).isFalse(); | ||||
| 		} | ||||
| 		finally { | ||||
| 			if (in != null) { | ||||
| 				in.close(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void readIndividualBytes() throws IOException { | ||||
| 		ZipHeaderPeekInputStream in = null; | ||||
| 		try { | ||||
| 			in = new ZipHeaderPeekInputStream( | ||||
| 					new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5 })); | ||||
| 			assertThat(in.read()).isEqualTo(0); | ||||
| 			assertThat(in.read()).isEqualTo(1); | ||||
| 			assertThat(in.read()).isEqualTo(2); | ||||
| 			assertThat(in.read()).isEqualTo(3); | ||||
| 			assertThat(in.read()).isEqualTo(4); | ||||
| 			assertThat(in.read()).isEqualTo(5); | ||||
| 		} | ||||
| 		finally { | ||||
| 			if (in != null) { | ||||
| 				in.close(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void readMultipleBytes() throws IOException { | ||||
| 		ZipHeaderPeekInputStream in = null; | ||||
| 		try { | ||||
| 			in = new ZipHeaderPeekInputStream( | ||||
| 					new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5 })); | ||||
| 			byte[] bytes = new byte[3]; | ||||
| 			assertThat(in.read(bytes)).isEqualTo(3); | ||||
| 			assertThat(bytes).containsExactly(0, 1, 2); | ||||
| 			assertThat(in.read(bytes)).isEqualTo(3); | ||||
| 			assertThat(bytes).containsExactly(3, 4, 5); | ||||
| 			assertThat(in.read(bytes)).isEqualTo(-1); | ||||
| 		} | ||||
| 		finally { | ||||
| 			if (in != null) { | ||||
| 				in.close(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void readingMoreThanEntireStreamReadsToEndOfStream() throws IOException { | ||||
| 		ZipHeaderPeekInputStream in = null; | ||||
| 		try { | ||||
| 			in = new ZipHeaderPeekInputStream( | ||||
| 					new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5 })); | ||||
| 			byte[] bytes = new byte[8]; | ||||
| 			assertThat(in.read(bytes)).isEqualTo(6); | ||||
| 			assertThat(bytes).containsExactly(0, 1, 2, 3, 4, 5, 0, 0); | ||||
| 			assertThat(in.read(bytes)).isEqualTo(-1); | ||||
| 		} | ||||
| 		finally { | ||||
| 			if (in != null) { | ||||
| 				in.close(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void readOfSomeOfTheHeaderThenMoreThanEntireStreamReadsToEndOfStream() | ||||
| 			throws IOException { | ||||
| 		ZipHeaderPeekInputStream in = null; | ||||
| 		try { | ||||
| 			in = new ZipHeaderPeekInputStream( | ||||
| 					new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5 })); | ||||
| 			byte[] bytes = new byte[8]; | ||||
| 			assertThat(in.read(bytes, 0, 3)).isEqualTo(3); | ||||
| 			assertThat(bytes).containsExactly(0, 1, 2, 0, 0, 0, 0, 0); | ||||
| 			assertThat(in.read(bytes, 3, 5)).isEqualTo(3); | ||||
| 			assertThat(bytes).containsExactly(0, 1, 2, 3, 4, 5, 0, 0); | ||||
| 		} | ||||
| 		finally { | ||||
| 			if (in != null) { | ||||
| 				in.close(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
		Loading…
	
		Reference in New Issue