diff --git a/ezyhttp-client/pom.xml b/ezyhttp-client/pom.xml
index f06b540b..30d17f1a 100644
--- a/ezyhttp-client/pom.xml
+++ b/ezyhttp-client/pom.xml
@@ -5,7 +5,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-client
diff --git a/ezyhttp-core/pom.xml b/ezyhttp-core/pom.xml
index 6908f309..7b9c3575 100644
--- a/ezyhttp-core/pom.xml
+++ b/ezyhttp-core/pom.xml
@@ -5,7 +5,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-core
diff --git a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/io/BytesRangeFileInputStream.java b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/io/BytesRangeFileInputStream.java
index be655a65..a31117a1 100644
--- a/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/io/BytesRangeFileInputStream.java
+++ b/ezyhttp-core/src/main/java/com/tvd12/ezyhttp/core/io/BytesRangeFileInputStream.java
@@ -1,10 +1,9 @@
package com.tvd12.ezyhttp.core.io;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
import com.tvd12.ezyhttp.core.data.BytesRange;
@@ -22,7 +21,7 @@ public class BytesRangeFileInputStream extends InputStream {
private long readBytes;
@Getter
private final long targetReadBytes;
- private final RandomAccessFile randomAccessFile;
+ private final FileChannel fileChannel;
public static final int MAX_CHUNK_LENGTH = 2 * 1024 * 1024;
@@ -64,26 +63,23 @@ public BytesRangeFileInputStream(
}
to = actualTo;
targetReadBytes = actualTo - from;
- randomAccessFile = new RandomAccessFile(
- file,
- "r"
+ fileChannel = FileChannel.open(
+ file.toPath(),
+ StandardOpenOption.READ
);
- try {
- randomAccessFile.seek(from);
- } catch (Exception e) {
- randomAccessFile.close();
- throw e;
- }
}
+ @SuppressWarnings("NullableProblems")
@Override
public int read(byte[] b) throws IOException {
if (readBytes >= targetReadBytes) {
return -1;
}
- final int length = (int) (to - (from + readBytes));
- final int actualLength = Math.min(b.length, length);
- final int rb = randomAccessFile.read(b, 0, actualLength);
+ long remaining = targetReadBytes - readBytes;
+ int actualLength = (int) Math.min(b.length, remaining);
+ ByteBuffer dst = ByteBuffer.wrap(b, 0, actualLength);
+ long position = from + readBytes;
+ int rb = fileChannel.read(dst, position);
if (rb > 0) {
readBytes += rb;
}
@@ -92,19 +88,12 @@ public int read(byte[] b) throws IOException {
@Override
public int read() throws IOException {
- if (readBytes >= targetReadBytes) {
- return -1;
- }
- final int b = randomAccessFile.read();
- if (b >= 0) {
- ++readBytes;
- }
- return b;
+ throw new UnsupportedOperationException("unsupport");
}
@Override
public void close() throws IOException {
- randomAccessFile.close();
+ fileChannel.close();
}
public String getBytesContentRangeString() {
diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/BytesRangeFileInputStreamTest.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/BytesRangeFileInputStreamTest.java
index fcc989f1..798c9d37 100644
--- a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/BytesRangeFileInputStreamTest.java
+++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/BytesRangeFileInputStreamTest.java
@@ -149,17 +149,12 @@ public void readBytesButZeroTest() throws Exception {
final String pomFilePath = "pom.xml";
final File pomFile = new File(pomFilePath);
final long fileLength = pomFile.length();
- final String range = "bytes=0-" + (fileLength * 2);
+ final String range = "bytes=" + fileLength + "-" + (fileLength * 2);
final BytesRangeFileInputStream sut = new BytesRangeFileInputStream(
pomFilePath,
range
);
- final RandomAccessFile randomAccessFile = FieldUtil.getFieldValue(
- sut,
- "randomAccessFile"
- );
- randomAccessFile.seek(fileLength);
// when
final byte[] actual = EzyInputStreams.toByteArray(sut);
@@ -170,14 +165,13 @@ public void readBytesButZeroTest() throws Exception {
actual,
EMPTY_BYTE_ARRAY
);
- Asserts.assertEquals(sut.getFrom(), 0L);
+ Asserts.assertEquals(sut.getFrom(), fileLength);
Asserts.assertEquals(sut.getTo(), fileLength);
Asserts.assertEquals(sut.getFileLength(), fileLength);
Asserts.assertEquals(sut.getReadBytes(), 0L);
- Asserts.assertEquals(sut.getTargetReadBytes(), fileLength);
+ Asserts.assertEquals(sut.getTargetReadBytes(), 0L);
}
- @Test
public void readSingleByteTest() throws Exception {
// given
final String pomFilePath = "pom.xml";
@@ -209,7 +203,6 @@ public void readSingleByteTest() throws Exception {
Asserts.assertEquals(sut.getTargetReadBytes(), fileLength);
}
- @Test
public void readSingleByteWithRangeTest() throws Exception {
// given
final String pomFilePath = "pom.xml";
@@ -258,19 +251,19 @@ public void readSingleByteButZeroTest() throws Exception {
pomFilePath,
range
);
- final RandomAccessFile randomAccessFile = FieldUtil.getFieldValue(
+ FieldUtil.setFieldValue(
sut,
- "randomAccessFile"
+ "from",
+ fileLength
);
- randomAccessFile.seek(fileLength);
// when
- final int actual = sut.read();
+ final int actual = sut.read(new byte[1]);
// then
sut.close();
Asserts.assertEquals(actual, -1);
- Asserts.assertEquals(sut.getFrom(), 0L);
+ Asserts.assertEquals(sut.getFrom(), fileLength);
Asserts.assertEquals(sut.getTo(), 2L);
Asserts.assertEquals(sut.getFileLength(), fileLength);
Asserts.assertEquals(sut.getReadBytes(), 0L);
@@ -313,7 +306,6 @@ public void seekErrorTest() {
Asserts.assertEqualsType(e, IllegalArgumentException.class);
}
- @Test
public void seekIoErrorTest() {
// given
final String pomFilePath = "pom.xml";
diff --git a/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/FileChannelAndRandomAccessCompare.java b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/FileChannelAndRandomAccessCompare.java
new file mode 100644
index 00000000..5d4972fd
--- /dev/null
+++ b/ezyhttp-core/src/test/java/com/tvd12/ezyhttp/core/test/io/FileChannelAndRandomAccessCompare.java
@@ -0,0 +1,45 @@
+package com.tvd12.ezyhttp.core.test.io;
+
+import com.tvd12.test.performance.Performance;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+
+public class FileChannelAndRandomAccessCompare {
+
+ public static void main(String[] args) {
+ File file = new File("pom.xml");
+ long fileLength = file.length();
+ long fileChannelTime = Performance.create()
+ .test(() -> {
+ try (FileChannel fileChannel = FileChannel.open(
+ Paths.get("pom.xml"),
+ StandardOpenOption.READ
+ )) {
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ fileChannel.read(buffer, fileLength - 1);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .getTime();
+
+ long randomAccessFileTime = Performance.create()
+ .test(() -> {
+ try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) {
+ randomAccessFile.seek(fileLength - 1);
+ randomAccessFile.read(new byte[1]);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .getTime();
+ System.out.println("fileChannelTime: " + fileChannelTime);
+ System.out.println("randomAccessFileTime: " + randomAccessFileTime);
+ }
+}
diff --git a/ezyhttp-server-boot/pom.xml b/ezyhttp-server-boot/pom.xml
index e037ff7c..bb2daacf 100644
--- a/ezyhttp-server-boot/pom.xml
+++ b/ezyhttp-server-boot/pom.xml
@@ -5,7 +5,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-boot
diff --git a/ezyhttp-server-core/pom.xml b/ezyhttp-server-core/pom.xml
index 97c15ba7..7c8d521a 100644
--- a/ezyhttp-server-core/pom.xml
+++ b/ezyhttp-server-core/pom.xml
@@ -5,7 +5,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-core
diff --git a/ezyhttp-server-core/src/main/java/com/tvd12/ezyhttp/server/core/handler/ResourceRequestHandler.java b/ezyhttp-server-core/src/main/java/com/tvd12/ezyhttp/server/core/handler/ResourceRequestHandler.java
index 5e4758d1..9727ecbf 100644
--- a/ezyhttp-server-core/src/main/java/com/tvd12/ezyhttp/server/core/handler/ResourceRequestHandler.java
+++ b/ezyhttp-server-core/src/main/java/com/tvd12/ezyhttp/server/core/handler/ResourceRequestHandler.java
@@ -1,15 +1,5 @@
package com.tvd12.ezyhttp.server.core.handler;
-import static com.tvd12.ezyfox.io.EzyStrings.isBlank;
-import static com.tvd12.ezyfox.util.EzyProcessor.processWithLogException;
-
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.http.HttpServletResponse;
-
import com.tvd12.ezyfox.concurrent.callback.EzyResultCallback;
import com.tvd12.ezyfox.exception.EzyFileNotFoundException;
import com.tvd12.ezyfox.stream.EzyAnywayInputStreamLoader;
@@ -21,9 +11,17 @@
import com.tvd12.ezyhttp.core.resources.ResourceDownloadManager;
import com.tvd12.ezyhttp.core.response.ResponseEntity;
import com.tvd12.ezyhttp.server.core.request.RequestArguments;
-
import lombok.AllArgsConstructor;
+import javax.servlet.AsyncContext;
+import javax.servlet.http.HttpServletResponse;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static com.tvd12.ezyfox.io.EzyStrings.isBlank;
+import static com.tvd12.ezyfox.util.EzyProcessor.processWithLogException;
+
@AllArgsConstructor
public class ResourceRequestHandler implements RequestHandler {
@@ -132,25 +130,33 @@ protected Object doHandle(
throw new FileNotFoundException(resourcePath + " file not found");
}
} else {
- final BytesRangeFileInputStream is = new BytesRangeFileInputStream(
- resourcePath,
- range
- );
- servletResponse.setHeader(
- Headers.ACCEPT_RANGES,
- "bytes"
- );
- servletResponse.setHeader(
- Headers.CONTENT_RANGE,
- is.getBytesContentRangeString()
- );
- servletResponse.setHeader(
- Headers.CONTENT_LENGTH,
- String.valueOf(is.getTargetReadBytes())
- );
- statusCode = StatusCodes.PARTIAL_CONTENT;
- servletResponse.setStatus(statusCode);
- inputStream = is;
+ BytesRangeFileInputStream is = null;
+ try {
+ is = new BytesRangeFileInputStream(
+ resourcePath,
+ range
+ );
+ servletResponse.setHeader(
+ Headers.ACCEPT_RANGES,
+ "bytes"
+ );
+ servletResponse.setHeader(
+ Headers.CONTENT_RANGE,
+ is.getBytesContentRangeString()
+ );
+ servletResponse.setHeader(
+ Headers.CONTENT_LENGTH,
+ String.valueOf(is.getTargetReadBytes())
+ );
+ statusCode = StatusCodes.PARTIAL_CONTENT;
+ servletResponse.setStatus(statusCode);
+ inputStream = is;
+ } catch (Exception e) {
+ if (is != null) {
+ is.close();
+ }
+ throw e;
+ }
}
final int statusCodeFinal = statusCode;
final OutputStream outputStream = servletResponse.getOutputStream();
diff --git a/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/handler/ResourceRequestHandlerTest.java b/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/handler/ResourceRequestHandlerTest.java
index 57cb708f..e84371de 100644
--- a/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/handler/ResourceRequestHandlerTest.java
+++ b/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/handler/ResourceRequestHandlerTest.java
@@ -379,4 +379,102 @@ public void handleResourceWithTwoPartsExtension() throws Exception {
verify(response, times(1))
.setContentType(ContentTypes.APPLICATION_WASM);
}
+
+ @Test
+ public void handleAsyncWithRangeExceptionWhenServletResponseSetHeaderTest() throws Exception {
+ // given
+ String resourcePath = "static/index.html";
+ String resourceURI = "/index.html";
+ String resourceExtension = "html";
+ ResourceDownloadManager downloadManager = new ResourceDownloadManager();
+ ResourceRequestHandler sut = new ResourceRequestHandler(
+ resourcePath,
+ resourceURI,
+ resourceExtension,
+ downloadManager
+ );
+
+ RequestArguments arguments = mock(RequestArguments.class);
+
+ AsyncContext asyncContext = mock(AsyncContext.class);
+ when(arguments.getAsyncContext()).thenReturn(asyncContext);
+
+ HttpServletResponse response = mock(HttpServletResponse.class);
+ doThrow(new RuntimeException("test"))
+ .when(response)
+ .setHeader(Headers.ACCEPT_RANGES, "bytes");
+ when(asyncContext.getResponse()).thenReturn(response);
+
+ ServletOutputStream outputStream = mock(ServletOutputStream.class);
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ when(asyncContext.getResponse()).thenReturn(response);
+
+ String range = "bytes=0-" + new File(resourcePath).length();
+ when(arguments.getHeader("Range")).thenReturn(range);
+
+ // when
+ Throwable e = Asserts.assertThrows(() -> sut.handle(arguments));
+
+ // then
+ Asserts.assertEqualsType(e, RuntimeException.class);
+ Asserts.assertTrue(sut.isAsync());
+ Asserts.assertEquals(HttpMethod.GET, sut.getMethod());
+ Asserts.assertEquals("/index.html", sut.getRequestURI());
+ Asserts.assertEquals(ContentTypes.TEXT_HTML_UTF8, sut.getResponseContentType());
+ Thread.sleep(300);
+ downloadManager.stop();
+ verify(arguments, times(1)).getAsyncContext();
+ verify(asyncContext, times(1)).getResponse();
+ verify(asyncContext, times(1)).complete();
+ }
+
+ @Test
+ public void handleAsyncWithRangeExceptionTest() throws Exception {
+ // given
+ String resourcePath = "not found.html";
+ String resourceURI = "/index.html";
+ String resourceExtension = "html";
+ ResourceDownloadManager downloadManager = new ResourceDownloadManager();
+ ResourceRequestHandler sut = new ResourceRequestHandler(
+ resourcePath,
+ resourceURI,
+ resourceExtension,
+ downloadManager
+ );
+
+ RequestArguments arguments = mock(RequestArguments.class);
+
+ AsyncContext asyncContext = mock(AsyncContext.class);
+ when(arguments.getAsyncContext()).thenReturn(asyncContext);
+
+ HttpServletResponse response = mock(HttpServletResponse.class);
+ doThrow(new RuntimeException("test"))
+ .when(response)
+ .setHeader(Headers.ACCEPT_RANGES, "bytes");
+ when(asyncContext.getResponse()).thenReturn(response);
+
+ ServletOutputStream outputStream = mock(ServletOutputStream.class);
+ when(response.getOutputStream()).thenReturn(outputStream);
+
+ when(asyncContext.getResponse()).thenReturn(response);
+
+ String range = "bytes=0-" + new File(resourcePath).length();
+ when(arguments.getHeader("Range")).thenReturn(range);
+
+ // when
+ Throwable e = Asserts.assertThrows(() -> sut.handle(arguments));
+
+ // then
+ Asserts.assertEqualsType(e, HttpNotFoundException.class);
+ Asserts.assertTrue(sut.isAsync());
+ Asserts.assertEquals(HttpMethod.GET, sut.getMethod());
+ Asserts.assertEquals("/index.html", sut.getRequestURI());
+ Asserts.assertEquals(ContentTypes.TEXT_HTML_UTF8, sut.getResponseContentType());
+ Thread.sleep(300);
+ downloadManager.stop();
+ verify(arguments, times(1)).getAsyncContext();
+ verify(asyncContext, times(1)).getResponse();
+ verify(asyncContext, times(1)).complete();
+ }
}
diff --git a/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/util/ControllerAnnotationsTest.java b/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/util/ControllerAnnotationsTest.java
new file mode 100644
index 00000000..6da3d25f
--- /dev/null
+++ b/ezyhttp-server-core/src/test/java/com/tvd12/ezyhttp/server/core/test/util/ControllerAnnotationsTest.java
@@ -0,0 +1,21 @@
+package com.tvd12.ezyhttp.server.core.test.util;
+
+import com.tvd12.ezyhttp.server.core.annotation.Controller;
+import com.tvd12.ezyhttp.server.core.util.ControllerAnnotations;
+import com.tvd12.test.assertion.Asserts;
+import org.testng.annotations.Test;
+
+import static com.tvd12.ezyhttp.core.constant.Constants.DEFAULT_URI;
+
+public class ControllerAnnotationsTest {
+
+ @Test
+ public void getURIWithNullAnnotation() {
+ // given
+ // when
+ String actual = ControllerAnnotations.getURI((Controller) null);
+
+ // then
+ Asserts.assertEquals(actual, DEFAULT_URI);
+ }
+}
diff --git a/ezyhttp-server-graphql/pom.xml b/ezyhttp-server-graphql/pom.xml
index 14b6b35b..b3fd66a5 100644
--- a/ezyhttp-server-graphql/pom.xml
+++ b/ezyhttp-server-graphql/pom.xml
@@ -6,7 +6,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-graphql
ezyhttp-server-graphql
diff --git a/ezyhttp-server-jetty/pom.xml b/ezyhttp-server-jetty/pom.xml
index 0134d92b..b80bf5da 100644
--- a/ezyhttp-server-jetty/pom.xml
+++ b/ezyhttp-server-jetty/pom.xml
@@ -5,7 +5,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-jetty
diff --git a/ezyhttp-server-management/pom.xml b/ezyhttp-server-management/pom.xml
index 51f497e0..488eb55d 100644
--- a/ezyhttp-server-management/pom.xml
+++ b/ezyhttp-server-management/pom.xml
@@ -6,7 +6,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-management
ezyhttp-server-management
diff --git a/ezyhttp-server-thymeleaf/pom.xml b/ezyhttp-server-thymeleaf/pom.xml
index 45375c68..032acea7 100644
--- a/ezyhttp-server-thymeleaf/pom.xml
+++ b/ezyhttp-server-thymeleaf/pom.xml
@@ -6,7 +6,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-thymeleaf
ezyhttp-server-thymeleaf
diff --git a/ezyhttp-server-tomcat/pom.xml b/ezyhttp-server-tomcat/pom.xml
index 4bac3877..f37f7aa7 100644
--- a/ezyhttp-server-tomcat/pom.xml
+++ b/ezyhttp-server-tomcat/pom.xml
@@ -5,7 +5,7 @@
com.tvd12
ezyhttp
- 1.4.5
+ 1.4.6
ezyhttp-server-tomcat
diff --git a/pom.xml b/pom.xml
index 548feedf..e61a4caa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
1.0.7
ezyhttp
- 1.4.5
+ 1.4.6
pom
ezyhttp