Skip to content
This repository was archived by the owner on May 10, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* @see dev.katsute.simplehttpserver.handler.RootHandler
* @see dev.katsute.simplehttpserver.handler.SSEHandler
* @see dev.katsute.simplehttpserver.handler.TemporaryHandler
* @see dev.katsute.simplehttpserver.handler.TimeoutHandler
* @see dev.katsute.simplehttpserver.handler.throttler.ThrottledHandler
* @see dev.katsute.simplehttpserver.handler.file.FileHandler
* @since 5.0.0
Expand Down
110 changes: 110 additions & 0 deletions src/main/java/dev/katsute/simplehttpserver/handler/TimeoutHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 Katsute <https://github.com/Katsute>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

package dev.katsute.simplehttpserver.handler;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import dev.katsute.simplehttpserver.SimpleHttpExchange;
import dev.katsute.simplehttpserver.SimpleHttpHandler;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Objects;
import java.util.concurrent.*;

/**
* The timeout handler runs a handler and times out after a set time.
*
* @since 5.0.0
* @version 5.0.0
* @author Katsute
*/
public class TimeoutHandler implements SimpleHttpHandler {

private final HttpHandler handler;

private final TimeUnit unit;
private final long timeout;

private final ExecutorService service = Executors.newCachedThreadPool();

/**
* Creates a timeout handler.
*
* @param handler handler
* @param timeout how long until timeout in seconds
*
* @since 5.0.0
*/
public TimeoutHandler(final HttpHandler handler, final double timeout){
this(handler, timeout, TimeUnit.SECONDS);
}

/**
* Creates a timeout handler.
*
* @param handler handler
* @param timeout how long until timeout
* @param unit timeout units
*
* @see TimeUnit
* @since 5.0.0
*/
public TimeoutHandler(final HttpHandler handler, final double timeout, final TimeUnit unit){
this.handler = Objects.requireNonNull(handler);
this.timeout = (long) timeout;
this.unit = Objects.requireNonNull(unit);
}

@Override
public final void handle(final HttpExchange exchange) throws IOException{

Check notice

Code scanning / CodeQL

Confusing overloading of methods

Method TimeoutHandler.handle(..) could be confused with overloaded method [handle](1), since dispatch depends on static types.
SimpleHttpHandler.super.handle(exchange);

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation

Invoking [SimpleHttpHandler.handle](1) should be avoided because it has been deprecated.
}

@Override
public final void handle(final SimpleHttpExchange exchange) throws IOException{
final Future<?> future = service.submit(() -> {
try{
handler.handle(exchange);
}catch(final IOException e){
throw new RuntimeException(e);
}
});
try{
future.get(timeout, unit);
}catch(final Throwable e){
future.cancel(true);
exchange.send(HttpURLConnection.HTTP_CLIENT_TIMEOUT);
if(!(e instanceof TimeoutException))
throw new RuntimeException(e);
}finally{
exchange.close();
}
}

@Override
public String toString(){
return "TimeoutHandler{" +
"handler=" + handler +
", timeout=" + timeout +
", unit=" + unit +
'}';
}

}
1 change: 1 addition & 0 deletions src/main/java9/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* <li>{@link dev.katsute.simplehttpserver.handler.RootHandler}</li>
* <li>{@link dev.katsute.simplehttpserver.handler.SSEHandler}</li>
* <li>{@link dev.katsute.simplehttpserver.handler.TemporaryHandler}</li>
* <li>{@link dev.katsute.simplehttpserver.handler.TimeoutHandler}</li>
* <li>
* {@link dev.katsute.simplehttpserver.handler.file.FileHandler}
* <ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package dev.katsute.simplehttpserver.handler;

import dev.katsute.simplehttpserver.*;
import org.junit.jupiter.api.*;

import java.io.IOException;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

final class TimeoutTests {

private static SimpleHttpServer server;

@BeforeAll
static void beforeAll() throws IOException{
server = SimpleHttpServer.create(8080);
server.start();
}

@AfterAll
static void afterAll(){
server.stop();
}

@Test
final void testTimeout(){
server.createContext("timeout/timeout", new TimeoutHandler(
(SimpleHttpHandler) exchange -> {
try{
Thread.sleep(4000);
exchange.send(200);
}catch(InterruptedException ignored){}
},
2,
TimeUnit.SECONDS
));

final AtomicInteger code = new AtomicInteger();

Assertions.assertTimeout(
Duration.of(3, ChronoUnit.SECONDS),
() -> code.set(Requests.getCode("http://localhost:8080/timeout/timeout"))
);

Assertions.assertEquals(408, code.get());
}

@Test
final void testTimeoutSeconds(){
server.createContext("timeout/nou", new TimeoutHandler(
(SimpleHttpHandler) exchange -> {
try{
Thread.sleep(4000);
exchange.send(200);
}catch(InterruptedException ignored){}
},
2
));

final AtomicInteger code = new AtomicInteger();

Assertions.assertTimeout(
Duration.of(3, ChronoUnit.SECONDS),
() -> code.set(Requests.getCode("http://localhost:8080/timeout/nou"))
);

Assertions.assertEquals(408, code.get());
}

@Test
final void testTimeoutPass(){
server.createContext("timeout/pass", new TimeoutHandler(
(SimpleHttpHandler) exchange -> {
try{
Thread.sleep(4000);
exchange.send(200);
}catch(InterruptedException ignored){}
},
5,
TimeUnit.SECONDS
));

Assertions.assertEquals(200, Requests.getCode("http://localhost:8080/timeout/pass"));
}

}