Skip to content
This repository was archived by the owner on May 10, 2024. It is now read-only.

Commit 0e4c1cb

Browse files
authored
Add timeout handler (#17)
1 parent d65841d commit 0e4c1cb

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed

src/main/java/dev/katsute/simplehttpserver/SimpleHttpHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* @see dev.katsute.simplehttpserver.handler.RootHandler
3838
* @see dev.katsute.simplehttpserver.handler.SSEHandler
3939
* @see dev.katsute.simplehttpserver.handler.TemporaryHandler
40+
* @see dev.katsute.simplehttpserver.handler.TimeoutHandler
4041
* @see dev.katsute.simplehttpserver.handler.throttler.ThrottledHandler
4142
* @see dev.katsute.simplehttpserver.handler.file.FileHandler
4243
* @since 5.0.0
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (C) 2022 Katsute <https://github.com/Katsute>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
*/
18+
19+
package dev.katsute.simplehttpserver.handler;
20+
21+
import com.sun.net.httpserver.HttpExchange;
22+
import com.sun.net.httpserver.HttpHandler;
23+
import dev.katsute.simplehttpserver.SimpleHttpExchange;
24+
import dev.katsute.simplehttpserver.SimpleHttpHandler;
25+
26+
import java.io.IOException;
27+
import java.net.HttpURLConnection;
28+
import java.util.Objects;
29+
import java.util.concurrent.*;
30+
31+
/**
32+
* The timeout handler runs a handler and times out after a set time.
33+
*
34+
* @since 5.0.0
35+
* @version 5.0.0
36+
* @author Katsute
37+
*/
38+
public class TimeoutHandler implements SimpleHttpHandler {
39+
40+
private final HttpHandler handler;
41+
42+
private final TimeUnit unit;
43+
private final long timeout;
44+
45+
private final ExecutorService service = Executors.newCachedThreadPool();
46+
47+
/**
48+
* Creates a timeout handler.
49+
*
50+
* @param handler handler
51+
* @param timeout how long until timeout in seconds
52+
*
53+
* @since 5.0.0
54+
*/
55+
public TimeoutHandler(final HttpHandler handler, final double timeout){
56+
this(handler, timeout, TimeUnit.SECONDS);
57+
}
58+
59+
/**
60+
* Creates a timeout handler.
61+
*
62+
* @param handler handler
63+
* @param timeout how long until timeout
64+
* @param unit timeout units
65+
*
66+
* @see TimeUnit
67+
* @since 5.0.0
68+
*/
69+
public TimeoutHandler(final HttpHandler handler, final double timeout, final TimeUnit unit){
70+
this.handler = Objects.requireNonNull(handler);
71+
this.timeout = (long) timeout;
72+
this.unit = Objects.requireNonNull(unit);
73+
}
74+
75+
@Override
76+
public final void handle(final HttpExchange exchange) throws IOException{
77+
SimpleHttpHandler.super.handle(exchange);
78+
}
79+
80+
@Override
81+
public final void handle(final SimpleHttpExchange exchange) throws IOException{
82+
final Future<?> future = service.submit(() -> {
83+
try{
84+
handler.handle(exchange);
85+
}catch(final IOException e){
86+
throw new RuntimeException(e);
87+
}
88+
});
89+
try{
90+
future.get(timeout, unit);
91+
}catch(final Throwable e){
92+
future.cancel(true);
93+
exchange.send(HttpURLConnection.HTTP_CLIENT_TIMEOUT);
94+
if(!(e instanceof TimeoutException))
95+
throw new RuntimeException(e);
96+
}finally{
97+
exchange.close();
98+
}
99+
}
100+
101+
@Override
102+
public String toString(){
103+
return "TimeoutHandler{" +
104+
"handler=" + handler +
105+
", timeout=" + timeout +
106+
", unit=" + unit +
107+
'}';
108+
}
109+
110+
}

src/main/java9/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* <li>{@link dev.katsute.simplehttpserver.handler.RootHandler}</li>
4040
* <li>{@link dev.katsute.simplehttpserver.handler.SSEHandler}</li>
4141
* <li>{@link dev.katsute.simplehttpserver.handler.TemporaryHandler}</li>
42+
* <li>{@link dev.katsute.simplehttpserver.handler.TimeoutHandler}</li>
4243
* <li>
4344
* {@link dev.katsute.simplehttpserver.handler.file.FileHandler}
4445
* <ul>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package dev.katsute.simplehttpserver.handler;
2+
3+
import dev.katsute.simplehttpserver.*;
4+
import org.junit.jupiter.api.*;
5+
6+
import java.io.IOException;
7+
import java.time.Duration;
8+
import java.time.temporal.ChronoUnit;
9+
import java.util.concurrent.TimeUnit;
10+
import java.util.concurrent.atomic.AtomicInteger;
11+
12+
final class TimeoutTests {
13+
14+
private static SimpleHttpServer server;
15+
16+
@BeforeAll
17+
static void beforeAll() throws IOException{
18+
server = SimpleHttpServer.create(8080);
19+
server.start();
20+
}
21+
22+
@AfterAll
23+
static void afterAll(){
24+
server.stop();
25+
}
26+
27+
@Test
28+
final void testTimeout(){
29+
server.createContext("timeout/timeout", new TimeoutHandler(
30+
(SimpleHttpHandler) exchange -> {
31+
try{
32+
Thread.sleep(4000);
33+
exchange.send(200);
34+
}catch(InterruptedException ignored){}
35+
},
36+
2,
37+
TimeUnit.SECONDS
38+
));
39+
40+
final AtomicInteger code = new AtomicInteger();
41+
42+
Assertions.assertTimeout(
43+
Duration.of(3, ChronoUnit.SECONDS),
44+
() -> code.set(Requests.getCode("http://localhost:8080/timeout/timeout"))
45+
);
46+
47+
Assertions.assertEquals(408, code.get());
48+
}
49+
50+
@Test
51+
final void testTimeoutSeconds(){
52+
server.createContext("timeout/nou", new TimeoutHandler(
53+
(SimpleHttpHandler) exchange -> {
54+
try{
55+
Thread.sleep(4000);
56+
exchange.send(200);
57+
}catch(InterruptedException ignored){}
58+
},
59+
2
60+
));
61+
62+
final AtomicInteger code = new AtomicInteger();
63+
64+
Assertions.assertTimeout(
65+
Duration.of(3, ChronoUnit.SECONDS),
66+
() -> code.set(Requests.getCode("http://localhost:8080/timeout/nou"))
67+
);
68+
69+
Assertions.assertEquals(408, code.get());
70+
}
71+
72+
@Test
73+
final void testTimeoutPass(){
74+
server.createContext("timeout/pass", new TimeoutHandler(
75+
(SimpleHttpHandler) exchange -> {
76+
try{
77+
Thread.sleep(4000);
78+
exchange.send(200);
79+
}catch(InterruptedException ignored){}
80+
},
81+
5,
82+
TimeUnit.SECONDS
83+
));
84+
85+
Assertions.assertEquals(200, Requests.getCode("http://localhost:8080/timeout/pass"));
86+
}
87+
88+
}

0 commit comments

Comments
 (0)