protected InputStream getInputStream(URL packageURL) throws RetryAfterException, IOException { return new ContentRangeInputStream(getConnectionHandler(), packageURL); }
/** * Prepares the connection for the next chunk, if needed. * * @return <code>true</code> if the prepare was successful (there was a next chunk to be read), <code>false</code> * otherwise. * @throws IOException * in case of I/O exception. */ private boolean prepareNextChunk() throws IOException { if ((m_conn == null) && contentRemaining()) { m_conn = m_handler.getConnection(m_url); applyRangeHeader(m_conn); long[] contentInfo = getContentRangeInfo(m_conn); // No, not yet, update our local administration... if (m_contentInfo != null) { // verify the total size (paranoia sanity check)... if (m_contentInfo[1] >= 0 && contentInfo[1] >= 0 && m_contentInfo[1] != contentInfo[1]) { throw new IOException("Stream size mismatch between different chunks!"); } } m_contentInfo = contentInfo; } // Make sure there's still content remaining to be read... return (m_conn != null) && contentRemaining(); } }
@Override public int read() throws IOException { assertOpen(); if (!prepareNextChunk()) { return -1; } InputStream is = getInputStream(); int result = is.read(); if (result > 0) { m_readChunk++; m_readTotal++; } // End of chunk?! if ((m_contentInfo[0] > 0) && (m_readChunk >= m_contentInfo[0])) { closeChunk(); } return result; }
@Override public int available() throws IOException { assertOpen(); if (!prepareNextChunk()) { return 0; } long chunkSize = m_contentInfo[0]; if (chunkSize < 0) { // size not available (yet)... return 0; } return (int) ((chunkSize - m_readChunk) & 0xFFFFFFFFL); }
/** * Tests that we can read non-partial content and return the expected contents. */ @Test(expectedExceptions = IOException.class) public void testReadClosedStreamFail() throws Exception { ConnectionHandler handler = new TestConnectionHandler(new CompleteContentConnection(m_content, true)); ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL); is.close(); // simulate an early close... is.read(); // should fail! }
is = new ContentRangeInputStream(m_handle.getConnectionHandler(), m_handle.getURL(), targetLength); os = new BufferedOutputStream(new FileOutputStream(m_target, appendTarget)); read = is.read(buffer);
/** * Tests that we call {@link InputStream#close()} multiple times. */ @Test public void testDoubleClosedStreamOk() throws Exception { ConnectionHandler handler = new TestConnectionHandler(new CompleteContentConnection(m_content, true)); ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL); is.close(); // simulate an early close... is.close(); // not a problem... }
@Override public void close() throws IOException { if (m_state != ST_CLOSED) { m_state = ST_CLOSED; closeChunk(); } }
/** * @param conn * the URL connection to get the range information from, cannot be <code>null</code>. * @return an array of two elements containing: current chunk size & total size (in bytes). * @throws IOException * in case of I/O problems or unexpected content. */ private long[] getContentRangeInfo(URLConnection conn) throws IOException { if (conn instanceof HttpURLConnection) { return getHttpContentRangeInfo((HttpURLConnection) conn); } long totalBytes = conn.getContentLength(); return new long[] { totalBytes, totalBytes }; }
/** * Tests that we cannot read partial content if the server returns a complete body. */ @Test(expectedExceptions = IOException.class) public void testReadPartialContentWithChangingContentLengthFail() throws Exception { ConnectionHandler handler = new TestConnectionHandler(new FailingContentConnection(m_content, Failure.PARTIAL_CHANGING_CONTENT_LENGTH)); ContentRangeInputStream is = null; try { is = new ContentRangeInputStream(handler, m_testURL); is.read(new byte[1024]); // should succeed. is.read(new byte[1024]); // should fail! } finally { if (is != null) { is.close(); } } }
/** * Tests that we can read non-partial content and return the expected contents. */ @Test public void testReadNonPartialFileContentByteForByteWithOffsetOk() throws Exception { File file = File.createTempFile("cris", ".tmp"); file.deleteOnExit(); FileOutputStream fos = new FileOutputStream(file); fos.write(m_content.getBytes()); fos.close(); ConnectionHandler handler = new TestConnectionHandler(file.toURI().toURL().openConnection()); ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL, 48); assertEquals(slurpAsStringByteForByte(is), m_content.substring(48)); // try several additional reads, which should all return -1 (= EOF)... int tries = 5; while (--tries > 0) { assertEquals(is.read(), -1); } }
/** * @return the current input stream, never <code>null</code>. * @throws IOException * in case of I/O problems accessing the input stream. */ private InputStream getInputStream() throws IOException { try { return m_conn.getInputStream(); } catch (IOException exception) { handleIOException(m_conn); closeChunk(); throw exception; } }
/** * Tests that we cannot read partial content if the server returns a complete body. */ @Test(expectedExceptions = IOException.class) public void testReadPartialContentWithCompleteBodyFail() throws Exception { ConnectionHandler handler = new TestConnectionHandler(new FailingContentConnection(m_content, Failure.PARTIAL_COMPLETE_BODY)); ContentRangeInputStream is = null; try { is = new ContentRangeInputStream(handler, m_testURL); is.read(new byte[1024]); // should succeed. is.read(new byte[1024]); // should fail! } finally { if (is != null) { is.close(); } } }
/** * Tests that we can read non-partial content and return the expected contents. */ @Test public void testReadNonPartialFileContentByteForByteOk() throws Exception { File file = File.createTempFile("cris", ".tmp"); file.deleteOnExit(); FileOutputStream fos = new FileOutputStream(file); fos.write(m_content.getBytes()); fos.close(); ConnectionHandler handler = new TestConnectionHandler(file.toURI().toURL().openConnection()); ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL); assertEquals(slurpAsStringByteForByte(is), m_content); // try several additional reads, which should all return -1 (= EOF)... int tries = 5; while (--tries > 0) { assertEquals(is.read(), -1); } }
/** * Tests that we can read non-partial content and return the expected contents. */ @Test public void testReadPartialContentWithOffsetAndChunkSizeOk() throws Exception { String content = m_content; PartialContentConnection conn = new PartialContentConnection(content, false); ConnectionHandler handler = new TestConnectionHandler(conn); // 4752 + 48 = 4800, causing only one chunk to be returned... ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL, 48, 4752); assertEquals(slurpAsStringWithBuffer(is), content.substring(48)); }
/** * {@inheritDoc} * <p> * Overridden for efficiency reasons. * </p> */ @Override public int read(byte[] b, int off, int len) throws IOException { assertOpen(); if (!prepareNextChunk()) { return -1; } InputStream is = getInputStream(); int read = is.read(b, off, len); if (read >= 0) { m_readChunk += read; m_readTotal += read; } // End of chunk?! if ((m_contentInfo[0] > 0) && (m_readChunk >= m_contentInfo[0])) { closeChunk(); } return read; }
/** * Tests that we cannot read partial content if the content is not available. */ @Test(expectedExceptions = IOException.class) public void testReadPartialContentNotFoundFail() throws Exception { ConnectionHandler handler = new TestConnectionHandler(new FailingContentConnection(m_content, Failure.CONTENT_NOT_FOUND)); ContentRangeInputStream is = null; try { is = new ContentRangeInputStream(handler, m_testURL); is.read(); // should fail! } finally { if (is != null) { is.close(); } } }
/** * Tests that we can read non-partial content and return the expected contents. */ @Test public void testReadNonPartialContentByteForByteOk() throws Exception { String content = m_content; ConnectionHandler handler = new TestConnectionHandler(new CompleteContentConnection(content, true)); ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL); assertEquals(slurpAsStringByteForByte(is), content); // try several additional reads, which should all return -1 (= EOF)... int tries = 5; while (--tries > 0) { assertEquals(is.read(), -1); } }
/** * Tests that we can read partial content and return the expected contents. */ @Test public void testReadPartialContentWithUnknownChunkSizeOk() throws Exception { String content = m_content; ConnectionHandler handler = new TestConnectionHandler(new FailingContentConnection(content, Failure.PARTIAL_UNKNOWN_CHUNK_SIZE)); ContentRangeInputStream is = new ContentRangeInputStream(handler, m_testURL); assertEquals(slurpAsStringWithBuffer(is), content.substring(48)); }
/** * Tests that we cannot read partial content without a Content-Range header. */ @Test(expectedExceptions = IOException.class) public void testReadPartialContentWithoutContentRangeHeaderFail() throws Exception { ConnectionHandler handler = new TestConnectionHandler(new FailingContentConnection(m_content, Failure.PARTIAL_NO_CONTENT_RANGE)); ContentRangeInputStream is = null; try { is = new ContentRangeInputStream(handler, m_testURL); is.read(); // should fail! } finally { if (is != null) { is.close(); } } }