mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 03:37:34 -08:00
Add basic support for thread interruption, and add protection for long running requests in web app
This commit is contained in:
parent
2369986a0d
commit
45df470227
|
@ -169,6 +169,9 @@ public final class MultiFormatReader implements Reader {
|
|||
private Result decodeInternal(BinaryBitmap image) throws NotFoundException {
|
||||
if (readers != null) {
|
||||
for (Reader reader : readers) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
try {
|
||||
return reader.decode(image, hints);
|
||||
} catch (ReaderException re) {
|
||||
|
@ -179,6 +182,9 @@ public final class MultiFormatReader implements Reader {
|
|||
// Calling all readers again with inverted image
|
||||
image.getBlackMatrix().flip();
|
||||
for (Reader reader : readers) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
try {
|
||||
return reader.decode(image, hints);
|
||||
} catch (ReaderException re) {
|
||||
|
|
|
@ -391,7 +391,7 @@ public final class DecodeServlet extends HttpServlet {
|
|||
savedException = re;
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
if (results.isEmpty() && !Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
// Look for pure barcode
|
||||
Result theResult = reader.decode(bitmap, HINTS_PURE);
|
||||
|
@ -403,7 +403,7 @@ public final class DecodeServlet extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
if (results.isEmpty() && !Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
// Look for normal barcode in photo
|
||||
Result theResult = reader.decode(bitmap, HINTS);
|
||||
|
@ -415,7 +415,7 @@ public final class DecodeServlet extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
if (results.isEmpty() && !Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
// Try again with other binarizer
|
||||
BinaryBitmap hybridBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2022 ZXing 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 com.google.zxing.web;
|
||||
|
||||
import com.google.common.util.concurrent.SimpleTimeLimiter;
|
||||
import com.google.common.util.concurrent.TimeLimiter;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.annotation.WebInitParam;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Protect the decode endpoint from long-running requests.
|
||||
*/
|
||||
@WebFilter(urlPatterns = {"/w/decode"}, initParams = {
|
||||
@WebInitParam(name = "timeoutSec", value = "10"),
|
||||
})
|
||||
public final class TimeoutFilter implements Filter {
|
||||
|
||||
private ExecutorService executorService;
|
||||
private TimeLimiter timeLimiter;
|
||||
private int timeoutSec;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
executorService = Executors.newCachedThreadPool();
|
||||
timeLimiter = SimpleTimeLimiter.create(executorService);
|
||||
timeoutSec = Integer.parseInt(filterConfig.getInitParameter("timeoutSec"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request,
|
||||
ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
try {
|
||||
timeLimiter.callWithTimeout(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
chain.doFilter(request, response);
|
||||
return null;
|
||||
}
|
||||
}, timeoutSec, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||
servletResponse.setStatus(HttpServletResponse.SC_REQUEST_TIMEOUT);
|
||||
servletResponse.getWriter().write("Request took too long");
|
||||
} catch (ExecutionException e) {
|
||||
if (e.getCause() instanceof ServletException) {
|
||||
throw (ServletException) e.getCause();
|
||||
}
|
||||
if (e.getCause() instanceof IOException) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
throw new ServletException(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (executorService != null) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2022 ZXing 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 com.google.zxing.web;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockFilterConfig;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.GenericServlet;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Tests {@link TimeoutFilter}.
|
||||
*/
|
||||
public final class TimeoutFilterTestCase extends Assert {
|
||||
|
||||
@Test
|
||||
public void testTimeout() throws Exception {
|
||||
MockFilterConfig config = new MockFilterConfig();
|
||||
config.addInitParameter("timeoutSec", "1");
|
||||
Filter filter = new TimeoutFilter();
|
||||
filter.init(config);
|
||||
|
||||
FilterChain chain = new MockFilterChain(new GenericServlet() {
|
||||
@Override
|
||||
public void service(ServletRequest req, ServletResponse res) {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
});
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
filter.doFilter(new MockHttpServletRequest(), response, chain);
|
||||
filter.destroy();
|
||||
assertEquals(HttpServletResponse.SC_REQUEST_TIMEOUT, response.getStatus());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue