diff --git a/spring-web-modules/spring-rest-http-2/pom.xml b/spring-web-modules/spring-rest-http-2/pom.xml
index 6aa8be365c..0ea3fd6840 100644
--- a/spring-web-modules/spring-rest-http-2/pom.xml
+++ b/spring-web-modules/spring-rest-http-2/pom.xml
@@ -19,6 +19,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
io.springfox
springfox-swagger2
@@ -29,6 +33,19 @@
springfox-swagger-ui
${swagger2.version}
+
+ com.h2database
+ h2
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ io.github.resilience4j
+ resilience4j-timelimiter
+ 1.6.1
+
diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java
new file mode 100644
index 0000000000..d425737bab
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java
@@ -0,0 +1,61 @@
+package com.baeldung.requesttimeout;
+
+import com.baeldung.requesttimeout.domain.Book;
+import com.baeldung.requesttimeout.domain.BookRepository;
+import io.github.resilience4j.timelimiter.TimeLimiter;
+import io.github.resilience4j.timelimiter.TimeLimiterConfig;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.time.Duration;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+
+@RestController
+public class RequestTimeoutRestController {
+
+ private final BookRepository bookRepository;
+ private final WebClient webClient;
+
+ public RequestTimeoutRestController(BookRepository bookRepository, WebClient webClient) {
+ this.bookRepository = bookRepository;
+ this.webClient = webClient;
+ }
+
+ @GetMapping("/author/transactional")
+ @Transactional(timeout = 1)
+ public String getWithTransactionTimeout(@RequestParam String title) {
+ return getAuthor(title);
+ }
+
+ private final TimeLimiter ourTimeLimiter = TimeLimiter.of(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(500)).build());
+ @GetMapping("/author/resilience4j")
+ public Callable getWithResilience4jTimeLimiter(@RequestParam String title) {
+ return TimeLimiter.decorateFutureSupplier(ourTimeLimiter, () -> CompletableFuture.supplyAsync(() -> getAuthor(title)));
+ }
+
+ @GetMapping("/author/mvc-request-timeout")
+ public Callable getWithMvcRequestTimeout(@RequestParam String title) {
+ return () -> getAuthor(title);
+ }
+
+ @GetMapping("/author/webclient")
+ public String getWithWebClient(@RequestParam String title) {
+ return webClient.get()
+ .uri(uriBuilder -> uriBuilder
+ .path("/author/transactional")
+ .queryParam("title", title)
+ .build())
+ .retrieve()
+ .bodyToMono(String.class)
+ .block();
+ }
+
+ private String getAuthor(String title) {
+ bookRepository.wasteTime();
+ return bookRepository.findById(title).map(Book::getAuthor).orElse("No book found for this title.");
+ }
+}
diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java
new file mode 100644
index 0000000000..52b3573411
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java
@@ -0,0 +1,21 @@
+package com.baeldung.requesttimeout.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.netty.http.client.HttpClient;
+
+import java.time.Duration;
+
+@Configuration
+public class WebClientConfiguration {
+
+ @Bean
+ public WebClient webClient() {
+ return WebClient.builder()
+ .baseUrl("http://localhost:8080")
+ .clientConnector(new ReactorClientHttpConnector(HttpClient.create().responseTimeout(Duration.ofMillis(250))))
+ .build();
+ }
+}
diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java
new file mode 100644
index 0000000000..846bfb2cec
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java
@@ -0,0 +1,28 @@
+package com.baeldung.requesttimeout.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Book {
+
+ @Id
+ private String title;
+ private String author;
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+}
diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java
new file mode 100644
index 0000000000..8ecab0f1d2
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java
@@ -0,0 +1,14 @@
+package com.baeldung.requesttimeout.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface BookRepository extends JpaRepository {
+
+ default int wasteTime() {
+ int i = Integer.MIN_VALUE;
+ while(i < Integer.MAX_VALUE) {
+ i++;
+ }
+ return i;
+ }
+}
diff --git a/spring-web-modules/spring-rest-http-2/src/main/resources/application.properties b/spring-web-modules/spring-rest-http-2/src/main/resources/application.properties
new file mode 100644
index 0000000000..ff4af943ec
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.mvc.async.request-timeout=750
\ No newline at end of file
diff --git a/spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java b/spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java
new file mode 100644
index 0000000000..da7d40d53c
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.requesttimeout;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClientRequestException;
+
+@SpringBootTest
+@RunWith(SpringRunner.class)
+public class RequestTimeoutTests {
+
+ private static final WebClient WEB_CLIENT = WebClient.builder().baseUrl("http://localhost:8080").build();
+
+ @Test(expected = WebClientRequestException.class)
+ public void givenTransactionTimeout_whenTimeExpires_thenReceiveException() {
+ getAuthor("transactional");
+ }
+
+ @Test(expected = WebClientRequestException.class)
+ public void givenResilience4jTimeLimiter_whenTimeExpires_thenReceiveException() {
+ getAuthor("resilience4j");
+ }
+
+ @Test(expected = WebClientRequestException.class)
+ public void givenMvcRequestTimeout_whenTimeExpires_thenReceiveException() {
+ getAuthor("mvc-request-timeout");
+ }
+
+ @Test(expected = WebClientRequestException.class)
+ public void givenWebClientTimeout_whenTimeExpires_thenReceiveException() {
+ getAuthor("webclient");
+ }
+
+ private void getAuthor(String authorPath) {
+ WEB_CLIENT.get()
+ .uri(uriBuilder -> uriBuilder
+ .path("/author/" + authorPath)
+ .queryParam("title", "title")
+ .build())
+ .retrieve()
+ .bodyToMono(String.class)
+ .block();
+ }
+}
diff --git a/spring-web-modules/spring-rest-http-2/src/test/resources/application.properties b/spring-web-modules/spring-rest-http-2/src/test/resources/application.properties
new file mode 100644
index 0000000000..ff4af943ec
--- /dev/null
+++ b/spring-web-modules/spring-rest-http-2/src/test/resources/application.properties
@@ -0,0 +1 @@
+spring.mvc.async.request-timeout=750
\ No newline at end of file