I am running a Spring Webflux application, as such I am using flatMap to convert my request body. I have a Router that looks like this,
#Configuration
public class TallyRouter {
private static final String TALLY_BASE_URL = "/admin/tally";
private static final String TALLY_ID_URL = "/{tallyId}";
#Bean
RouterFunction<ServerResponse> tallyRoutes(tallyHandler handler) {
return RouterFunctions.route(GET(TALLY_CONFIG_BASE_URL + "/active"), handler::getActiveTally)
.andRoute(POST(TALLY_BASE_URL).and(accept(MediaType.APPLICATION_JSON)), handler::createTally);
}
My handler class looks like this,
#Component
#RequiredArgsConstructor
public class TallyHandler {
#Autowired
TallyService tallyService;
private static final Logger logger = LogManager.getLogger(TallyHandler.class);
public Mono<ServerResponse> createTally(ServerRequest request) {
Mono<NewTallyRequest> req = request.bodyToMono(NewTallyRequest.class);
return req
.doOnNext(result -> logger.info(result))
.flatMap(a -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(tallyService.createTally(a, request.exchange()), String.class)); // the line of the code that is null
}
}
This is how my test looks like,
#ExtendWith(MockitoExtension.class)
#WebFluxTest
#ContextConfiguration(classes = { TallyHandler.class, TallyRouter.class })
#ActiveProfiles("test")
class TallyHandlerTest {
#MockBean
WebClient webClient;
#MockBean
TallyService tallyService;
#Autowired
private ApplicationContext context;
WebTestClient webTestClient;
MockWebServer mockWebServer;
String res = "testRes";
#BeforeEach
void before() throws IOException {
webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient().build();
mockWebServer = new MockWebServer();
mockWebServer.start();
}
#AfterEach
void tearDown() throws IOException {
mockWebServer.close();
}
#Test
#WithMockUser(roles = "ADMIN")
void createTallyTest_200() {
String url = "/admin/tally";
MockResponse mockedResponse = new MockResponse().setStatus("200").setBody(res);
mockWebServer.enqueue(mockedResponse);
NewTallyRequest req = new NewTallyRequest();
req.setIsActive(isActive);
when(tallyService.createTally(req, exchange)).thenReturn(Mono.just(res));
webTestClient.mutateWith(csrf()).post().uri(uriBuilder -> uriBuilder.path(url).build())
.body(BodyInserters.fromValue(req)).exchange().expectStatus().is2xxSuccessful();
}
}
Error:
java.lang.IllegalArgumentException: 'publisher' must not be null
at org.springframework.util.Assert.notNull(Assert.java:201)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.ui.LogoutPageGeneratingWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.ui.LoginPageGeneratingWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP POST "/admin/tally" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.springframework.util.Assert.notNull(Assert.java:201)
at org.springframework.web.reactive.function.BodyInserters.fromPublisher(BodyInserters.java:182)
at org.springframework.web.reactive.function.server.DefaultServerResponseBuilder.body(DefaultServerResponseBuilder.java:233)
at my.company.app.handler.TallyHandler.lambda$1(TallyHandler.java:58)
I have tried to mock the return value of the tallyService but the test is not picking it up in the body of ServerResponse. What should I do to mock that part of the code to return a correct ServerResponse value?
I finally found the answer to this problem, I didn't mock the body properly.
I was doing this,
when(tallyService.createTally(req, exchange)).thenReturn(Mono.just(res));
when I should be doing this,
when(tallyService.createTally(any(NewTallyRequest.class), any(ServerWebExchange.class)).thenReturn(Mono.just(res));
The body of the ServerResponse entity wasn't mocked hence I was faced with the error of returning an empty body.
Related
I'm new to junit and was writing some test cases to test my springboot controller. Inside my controller there is a helper class using which I'm trying to call a validateToken method and I'm expecting a custom exception from this method. But for some reason, it is just skipping that line and returning me 200 Ok response as in case of a successful execution. I'm expecting a 400 bad request but getting a 200 Ok response.
The validateToken is a void method.
Test class
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
#AutoConfigureMockMvc
class GetUserAddressControllerTest {
#Mock
private CustomerAddressService customerAddressService;
#Mock
private TokenValidator tokenValidator;
#InjectMocks
private GetUserAddressController getUserAddressController;
#Autowired
private MockMvc mockMvc;
#BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(getUserAddressController).build();
}
#Test
void getAllAddressesFailureTest() throws Exception {
doThrow(new AddressBookException(RestResultCode.BAD_REQUEST, ErrorType.UNAUTHORIZED_ERROR.getStatus(), "Access is denied"))
.when(tokenValidator)
.validateUser(header, userId);
MvcResult mvcResult = mockMvc.perform(get(getAddressUrl)
.param("userId", userId)
.header("Authorization", header))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().is4xxClientError())
.andReturn();
verify(customerAddressService).getAllAddresses(any());
}
Controller class
#ApiOperation(value = "This API will fetch all address for a user")
#GetMapping(value = "/addresses", produces = MediaType.APPLICATION_JSON_VALUE)
CustomerAddressResponse getAllAddresses(#RequestHeader(value = "Authorization", required = true) String authToken,
#RequestParam("userId") String userId) {
tokenValidator.validateUser(authToken, userId); //----> want exception
return customerAddressService.getAllAddresses(userId);
}
TokenValidator class
public void validateUser(String authToken, String uuid) {
if (StringUtils.isEmpty(authToken)) {
throw new AddressBookException(RestResultCode.UNAUTHORIZED, ErrorType.ACCESS_DENIED_ERROR.getStatus(), "Access is denied");
}
// some logic here
}
}
Earlier I was using the when().thenThrow() syntax, but came to know for void methods we have to use a different syntax. So I changed it. Then I tried somethings with different annotations, but could not get it working.
I'm expecting the exception to be thrown, but getting 200.
stacktrace
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.1.RELEASE)
Hibernate:
select
applicatio0_.id as id1_2_,
applicatio0_.active as active2_2_,
applicatio0_.created_ts as created_3_2_,
applicatio0_.updated_by as updated_4_2_,
applicatio0_.updated_ts as updated_5_2_,
applicatio0_.config_key as config_k6_2_,
applicatio0_.config_value as config_v7_2_
from
application_configuration applicatio0_
where
(
applicatio0_.active= true
)
MockHttpServletRequest:
HTTP Method = GET
Request URI = /addressservice/v1/addresses
Parameters = {userId=[be6cefe2-04ed-4d89-bd25-7342b400272c]}
Headers = {Authorization=[Bearer 279db47b-a419-40f7-81f3-a8bf755a9dcb]}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.addressbook.service.controller.GetUserAddressController
Method = com.addressbook.service.response.CustomerAddressResponse
com.addressbook.service.controller.GetUserAddressController.getAllAddresses(java.lang.String,java.lang.String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Range for response status value 200 expected:<CLIENT_ERROR> but was:<SUCCESSFUL>
Expected :CLIENT_ERROR
Actual :SUCCESSFUL
<Click to see difference>
java.lang.AssertionError: Range for response status value 200 expected:<CLIENT_ERROR> but was:<SUCCESSFUL>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:55)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:82)
at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$is4xxClientError$5(StatusResultMatchers.java:94)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:195)
at com.addressbook.service.controller.GetUserAddressControllerTest.getAllAddressesFailureTest(GetUserAddressControllerTest.java:150)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:170)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:102)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:82)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:78)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy5.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:132)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:829)
com.addressbook.service.controller.GetUserAddressControllerTest > getAllAddressesFailureTest() FAILED
java.lang.AssertionError at GetUserAddressControllerTest.java:150
Disconnected from the target VM, address: 'localhost:53204', transport: 'socket'
#Autowired If you want TokenValidator work as a service. e.g.
#Autowired
private TokenValidator tokenValidator;
if you want to mock TokenValidator as a bean, to throw exception manually, mock it using #Mockbean. e.g.
#MockBean
private TokenValidator tokenValidator;
...
doThrow(new Exception()).when(tokenValidator).validateUser(authToken, userId);
WebClient configuration (minimized reproducible use case, tested with different durations, no effect):
public WebClient createWebClient() {
ConnectionProvider provider = ConnectionProvider.builder("WebClientProvider")
.maxConnections(2)
.maxIdleTime(Duration.ofSeconds(10))
.maxLifeTime(Duration.ofSeconds(20))
.pendingAcquireTimeout(Duration.ofSeconds(10))
.evictInBackground(Duration.ofSeconds(10))
.build();
HttpClient httpClient = HttpClient.create(provider).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
ReactorClientHttpConnector reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);
return WebClient.builder()
.clientConnector(reactorClientHttpConnector)
.build();
}
Usage of the client, as a GET to a public cloud service provider:
public Flux<SomeClass> doGetSomething() {
return webClient.get()
.uri(buildUri())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(byte[].class)
.flatMapMany(byteArray -> Flux.fromArray(byteArrayToModel(byteArray, SomeClass[].class)))
.onErrorResume(e -> {
return Flux.error(new RuntimeException("Failed to call get", e));
});
}
Exception:
org.springframework.web.reactive.function.client.WebClientRequestException: Connection prematurely closed BEFORE response; nested exception is reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Assembly trace from producer [reactor.core.publisher.MonoLift] :
reactor.core.publisher.Mono.error(Mono.java:330)
org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.wrapException(ExchangeFunctions.java:141)
Error has been observed at the following site(s):
*__________Mono.error ⇢ at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.wrapException(ExchangeFunctions.java:141)
*__Mono.onErrorResume ⇢ at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.exchange(ExchangeFunctions.java:106)
|_ Mono.map ⇢ at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.exchange(ExchangeFunctions.java:107)
*________Mono.flatMap ⇢ at org.springframework.web.reactive.function.client.ExchangeFilterFunction.lambda$ofResponseProcessor$4(ExchangeFilterFunction.java:96)
*________Mono.flatMap ⇢ at org.springframework.web.reactive.function.client.ExchangeFilterFunction.lambda$ofRequestProcessor$3(ExchangeFilterFunction.java:85)
|_ checkpoint ⇢ Request to GET https://***********************.com/someendpoint*********************** [DefaultWebClient]
|_ Mono.switchIfEmpty ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$7(DefaultWebClient.java:433)
*__________Mono.defer ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.exchange(DefaultWebClient.java:430)
|_ Mono.flatMap ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.bodyToMono(DefaultWebClient.java:540)
|_ Mono.flatMapMany ⇢ at com.mycode(SomeClass.java:57)
Original Stack Trace:
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141)
at reactor.core.publisher.MonoErrorSupplied.subscribe(MonoErrorSupplied.java:55)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:234)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:234)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:234)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onError(MonoFlatMapMany.java:204)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.SerializedSubscriber.onError(SerializedSubscriber.java:124)
at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.whenError(FluxRetryWhen.java:225)
at reactor.core.publisher.FluxRetryWhen$RetryWhenOtherSubscriber.onError(FluxRetryWhen.java:274)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerError(FluxConcatMap.java:309)
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onError(FluxConcatMap.java:875)
at reactor.core.publisher.Operators.error(Operators.java:198)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:53)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:451)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:251)
at reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:491)
at reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:299)
at reactor.core.publisher.SinkManySerialized.tryEmitNext(SinkManySerialized.java:100)
at reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27)
at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onError(FluxRetryWhen.java:190)
at io.opentracing.contrib.reactor.TracedSubscriber.lambda$onError$4(TracedSubscriber.java:79)
at io.opentracing.contrib.reactor.TracedSubscriber.withActiveSpan(TracedSubscriber.java:95)
at io.opentracing.contrib.reactor.TracedSubscriber.onError(TracedSubscriber.java:79)
at reactor.core.publisher.MonoCreate$DefaultMonoSink.error(MonoCreate.java:194)
at reactor.netty.http.client.HttpClientConnect$HttpObserver.onUncaughtException(HttpClientConnect.java:384)
at reactor.netty.ReactorNetty$CompositeConnectionObserver.onUncaughtException(ReactorNetty.java:670)
at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.onUncaughtException(DefaultPooledConnectionProvider.java:202)
at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnection.onUncaughtException(DefaultPooledConnectionProvider.java:450)
at reactor.netty.http.client.HttpClientOperations.onInboundClose(HttpClientOperations.java:294)
at reactor.netty.channel.ChannelOperationsHandler.channelInactive(ChannelOperationsHandler.java:73)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:241)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelInactive(CombinedChannelDuplexHandler.java:418)
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:392)
at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:357)
at io.netty.handler.codec.http.HttpClientCodec$Decoder.channelInactive(HttpClientCodec.java:326)
at io.netty.channel.CombinedChannelDuplexHandler.channelInactive(CombinedChannelDuplexHandler.java:221)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:241)
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:392)
at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:357)
at io.netty.handler.ssl.SslHandler.channelInactive(SslHandler.java:1074)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:241)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelInactive(DefaultChannelPipeline.java:1405)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248)
at io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:901)
at io.netty.channel.AbstractChannel$AbstractUnsafe$7.run(AbstractChannel.java:813)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Test mechanism:
GET requests through insomnia to the reactive web app (clicking with the mouse on Send button continuously until it fails), requests failing intermittently, at random intervals with no obvious (for me), causes.
Any ideas how to solve this?
Versions:
org.springframework.boot:spring-boot-starter-reactor-netty:jar:2.6.6
io.projectreactor.netty:reactor-netty-http:jar:1.0.17
io.projectreactor.netty:reactor-netty-core:jar:1.0.17
io.projectreactor:reactor-core:jar:3.4.16
io.netty:netty-codec:jar:4.1.75
org.springframework:spring-webflux:jar:5.3.18
P.S.
Debugged internal libraries workflow, and got here, in reactor netty http:
#Bean
public SecurityWebFilterChain securityWebFilterChain(
ServerHttpSecurity http,
ReactiveAuthenticationManager jwtAuthenticationManager,
ServerAuthenticationConverter jwtAuthenticationConverter
) {
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(jwtAuthenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(jwtAuthenticationConverter);
return http
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
.securityMatcher(
new NegatedServerWebExchangeMatcher(
new OrServerWebExchangeMatcher(
ServerWebExchangeMatchers.pathMatchers(ALLOWED_URLS.toArray(new String[0])),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.OPTIONS, "/**")
)
)
)
.httpBasic()
.disable()
.csrf()
.disable()
.formLogin()
.disable()
.logout()
.disable()
.exceptionHandling()
.authenticationEntryPoint((exchange, e) -> {
if (e.getCause() instanceof AccessDeniedException) {
throw new ForbiddenException(e);
}
throw new UnauthorizedException(e);
})
.and()
.authorizeExchange()
.pathMatchers("/api/auth/password").hasAnyRole(RolesList.SUPER_ADMIN.name(), RolesList.ADMIN.name())
.pathMatchers("/api/client/**").hasRole(RolesList.SUPER_ADMIN.name())
.pathMatchers("/api/user/**").hasRole(RolesList.SUPER_ADMIN.name())
.pathMatchers("/api/**").authenticated() //entire server side app will be kept here
.anyExchange().authenticated()
.and()
.build();
}
#Configuration
#EnableWebFlux
public class CorsGlobalConfiguration implements WebFluxConfigurer {
private final NetworkProperties networkProperties;
public CorsGlobalConfiguration(NetworkProperties networkProperties) {
this.networkProperties = networkProperties;
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/client/search")
.allowedOrigins(networkProperties.getAllowedOrigins())
.allowedMethods("POST", "OPTIONS");
}
}
Above are my spring security config and cors config.
When super admin accessing /api/client/search he is getting success response.
But when admin is trying to access he is getting Cors error instead of access denied.
What am i missing here?
Below is the exception stack trace which very strange according to exception my exception handler is creating response but browser displaying cors error instead of Access denined.
Again same thing is working fine for super admin
com.adminpanel.api.common.exceptions.ForbiddenException: Forbidden
at com.adminpanel.api.auth.config.SecurityConfig.lambda$securityWebFilterChain$0(SecurityConfig.java:81)
Suppressed: org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.authorization.ReactiveAuthorizationManager.lambda$verify$1(ReactiveAuthorizationManager.java:53)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
Stack trace:
at org.springframework.security.authorization.ReactiveAuthorizationManager.lambda$verify$1(ReactiveAuthorizationManager.java:53)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)
at reactor.core.publisher.Mono.subscribe(Mono.java:4252)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:99)
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:77)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:274)
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:851)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:93)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.MonoAny$AnySubscriber.onComplete(MonoAny.java:130)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:144)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainAsync(FluxFlattenIterable.java:334)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:679)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onComplete(FluxFlattenIterable.java:260)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:165)
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:336)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:384)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1795)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:144)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:112)
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2393)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:184)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:81)
at reactor.core.publisher.MonoCurrentContext.subscribe(MonoCurrentContext.java:35)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:107)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:107)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:481)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:414)
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:267)
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:225)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:364)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)
at reactor.core.publisher.Mono.subscribe(Mono.java:4252)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:441)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:101)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:262)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1813)
at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:320)
at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:337)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:249)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP POST "/api/client/search" [ExceptionHandlingWebHandler]
Stack trace:
at com.adminpanel.api.auth.config.SecurityConfig.lambda$securityWebFilterChain$0(SecurityConfig.java:81)
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:72)
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.lambda$filter$1(ExceptionTranslationWebFilter.java:44)
at reactor.core.publisher.Mono.lambda$onErrorResume$31(Mono.java:3401)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:88)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2059)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:165)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2059)
at reactor.core.publisher.Operators.error(Operators.java:196)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:52)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.Mono.subscribe(Mono.java:4252)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:99)
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:77)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:274)
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:851)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:93)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.MonoAny$AnySubscriber.onComplete(MonoAny.java:130)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:144)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainAsync(FluxFlattenIterable.java:334)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:679)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onComplete(FluxFlattenIterable.java:260)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:165)
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:336)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:384)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1795)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:144)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:112)
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2393)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:184)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:81)
at reactor.core.publisher.MonoCurrentContext.subscribe(MonoCurrentContext.java:35)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:107)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:107)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:481)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:414)
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:267)
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:225)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:364)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)
at reactor.core.publisher.Mono.subscribe(Mono.java:4252)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:441)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:101)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:262)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1813)
at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:320)
at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:337)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:249)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: Not Authenticated
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:72)
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.lambda$filter$1(ExceptionTranslationWebFilter.java:44)
at reactor.core.publisher.Mono.lambda$onErrorResume$31(Mono.java:3401)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:88)
at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onError(FluxOnAssembly.java:390)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2059)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:165)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2059)
at reactor.core.publisher.Operators.error(Operators.java:196)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:52)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.Mono.subscribe(Mono.java:4252)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:99)
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:77)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:274)
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:851)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:93)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.MonoAny$AnySubscriber.onComplete(MonoAny.java:130)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:144)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainAsync(FluxFlattenIterable.java:334)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:679)
at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onComplete(FluxFlattenIterable.java:260)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:165)
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:336)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:384)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1795)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:144)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:112)
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2393)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:184)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:81)
at reactor.core.publisher.MonoCurrentContext.subscribe(MonoCurrentContext.java:35)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:107)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:107)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:481)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:414)
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:267)
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:225)
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:364)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)
at reactor.core.publisher.Mono.subscribe(Mono.java:4252)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:441)
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1812)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:101)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:262)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1813)
at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:320)
at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:337)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:249)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
[CIRCULAR REFERENCE:org.springframework.security.access.AccessDeniedException: Access Denied]
Probably the answer to my question is easy and I am missing something simple, but I have searched all around the internet and tried to debug this error for weeks, without any progress.
I have registered a filter in the reactive spring security config, this filter is executed at the authentication level, and its purpose is to validate a JWT token present in the authentication header. So it's the typical JWT authentication pattern.
The JWT validation, by itself, works properly. It is able to validate a JWT token, check if it is expired, wrong, valid, and so on. This is visible by printing the SecurityContext object:
SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=UserDetailsDTO(id=2, username=peppe2, password=null, email=peppe2#yopmail.com, roles=[ADMIN], isEnabled=true, isLocked=false), Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ADMIN]]]
And also the Authentication object:
UsernamePasswordAuthenticationToken [Principal=UserDetailsDTO(id=2, username=peppe2, password=null, email=peppe2#yopmail.com, roles=[ADMIN], isEnabled=true, isLocked=false), Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ADMIN]]
But, no matter what I do after the authentication filter, I receive a AuthenticationCredentialsNotFoundException in the ServerHttpSecurity error callback. Here is the full stack trace:
catalog-svc | 2021-09-13 13:52:41.753 ERROR 1 --- [ parallel-3]
a.w.r.e.AbstractErrorWebExceptionHandler : [8415b576-2] 500 Server Error for HTTP PATCH "/users/1"
catalog-svc |
catalog-svc | org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: Not Authenticated
catalog-svc | at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:70) ~[spring-security-web-5.5.1.jar!/:5.5.1]
catalog-svc | Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
catalog-svc | Error has been observed at the following site(s):
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ it.polito.ecommerce.catalogservice.security.JwtAuthenticationTokenFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
catalog-svc | |_ checkpoint ⇢ HTTP PATCH "/api/v1/users/1" [ExceptionHandlingWebHandler]
catalog-svc | Stack trace:
catalog-svc | at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:70) ~[spring-security-web-5.5.1.jar!/:5.5.1]
catalog-svc | at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.lambda$filter$1(ExceptionTranslationWebFilter.java:45) ~[spring-security-web-5.5.1.jar!/:5.5.1]
catalog-svc | at reactor.core.publisher.Mono.lambda$onErrorResume$32(Mono.java:3564) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2062) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onError(MonoPeekTerminal.java:258) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onError(MonoPeekTerminal.java:258) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2062) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators.error(Operators.java:197) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoError.subscribe(MonoError.java:52) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:81) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:106) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:83) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:281) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:860) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:108) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:171) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:344) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:391) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:148) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2397) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:110) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoCurrentContext.subscribe(MonoCurrentContext.java:36) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2397) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:169) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:110) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:448) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:218) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:164) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:255) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:81) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:84) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2399) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2193) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2067) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:81) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:846) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:608) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:588) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:465) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onComplete(FluxPeekFuseable.java:277) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:292) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:228) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:164) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:108) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onComplete(FluxMap.java:269) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:337) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:354) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:73) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoPublishOn$PublishOnSubscriber.run(MonoPublishOn.java:181) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at java.base/java.util.concurrent.FutureTask.run(Unknown Source) ~[na:na]
catalog-svc | at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[na:na]
catalog-svc | at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:na]
catalog-svc | at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:na]
catalog-svc | at java.base/java.lang.Thread.run(Unknown Source) ~[na:na]
catalog-svc | Caused by: org.springframework.security.access.AccessDeniedException: Access Denied
catalog-svc | at org.springframework.security.authorization.ReactiveAuthorizationManager.lambda$verify$0(ReactiveAuthorizationManager.java:53) ~[spring-security-core-5.5.1.jar!/:5.5.1]
catalog-svc | Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
catalog-svc | Error has been observed at the following site(s):
catalog-svc | |_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
catalog-svc | Stack trace:
catalog-svc | at org.springframework.security.authorization.ReactiveAuthorizationManager.lambda$verify$0(ReactiveAuthorizationManager.java:53) ~[spring-security-core-5.5.1.jar!/:5.5.1]
catalog-svc | at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:81) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:166) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:106) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:83) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:281) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:860) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:108) ~[reactor-core-3.4.7.jar!/:3.4.7]
catalog-svc | at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:150) ~[reactor-core-3.4.7.jar!/:3.4.7]
While here is the SecurityConfig:
#Bean
fun springSecurityFilterChain(
http: ServerHttpSecurity,
authManager: ReactiveAuthenticationManager?
): SecurityWebFilterChain {
return http
.exceptionHandling()
.authenticationEntryPoint { swe, e ->
// The error is caught here
Mono.fromRunnable {
swe.response.statusCode = HttpStatus.UNAUTHORIZED
throw e
}
}.accessDeniedHandler { swe, e ->
Mono.fromRunnable {
swe.response.statusCode = HttpStatus.FORBIDDEN
throw e
}
}.and()
.addFilterBefore(
jwtAuthenticationTokenFilter,
SecurityWebFiltersOrder.AUTHENTICATION)
.cors()
.and()
.csrf().disable()
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
.authorizeExchange()
.pathMatchers("/auth/**").permitAll()
.anyExchange().authenticated()
.and().build()
}
and, in the end, the jwt filter:
#Component
class JwtAuthenticationTokenFilter(
private val jwtUtils: JwtUtils,
#Value("\${application.jwt.jwtHeader}") private val jwtHeader: String,
#Value("\${application.jwt.jwtHeaderStart}") private val jwtHeaderStart: String
) : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
val authorizationHeader= exchange.request.headers[jwtHeader]?.get(0)
if (authorizationHeader != null) {
val jwt = authorizationHeader.removePrefix("$jwtHeaderStart ")
if (jwtUtils.validateJwtToken(jwt)) {
val detailsFromJwtToken = jwtUtils.getDetailsFromJwtToken(jwt)
val authentication = UsernamePasswordAuthenticationToken(
detailsFromJwtToken,
null,
detailsFromJwtToken.authorities
)
ReactiveSecurityContextHolder.withAuthentication(authentication)
}
}
return chain.filter(exchange)
}
}
My questions are: how do I solve this problem? If you can't help me solving this, do you know any way to improve the stacktrace in Spring Reactive? Because I am not getting any hint for debugging.
Thank you
Ok first of all code (its mega simple):
#Controller
#RequestMapping("/")
public class HelloController {
private final static Logger logger = Logger.getLogger(HelloController.class);
#RequestMapping(method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
logger.info("ELO ELO");
model.addAttribute("message", "Hello world!");
RestTemplate restTemplate = new RestTemplate();
String url = "http://192.168.0.200:8000/GPIO/11/function/in";
//String url = "http://192.168.0.200:8000/GPIO/11/function";
//restTemplate.getForObject(url, String.class);
String test = "";
restTemplate.postForObject(url, null, String.class);
logger.info(test);
return "hello";
}
Next example that I'm not a crazy man here is response from postman (chrome):
And at the end full error log:
type Exception report
message Request processing failed; nested exception is
java.lang.IllegalArgumentException: "None" does not contain '/'
description The server encountered an internal error that prevented it
from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
java.lang.IllegalArgumentException: "None" does not contain '/'
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:927)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.lang.IllegalArgumentException: "None" does not contain '/'
org.springframework.http.MediaType.parseMediaType(MediaType.java:697)
org.springframework.http.HttpHeaders.getContentType(HttpHeaders.java:305)
org.springframework.web.client.HttpMessageConverterExtractor.getContentType(HttpMessageConverterExtractor.java:113)
org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:84)
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:492)
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:447)
org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:295)
pl.piquarium.mvc.HelloController.printWelcome(HelloController.java:35)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:439)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:427)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
note The full stack trace of the root cause is available in the Apache
Tomcat/8.0.3 logs.
Request headers:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Cookie:__utma=212787668.2094541430.1400264829.1400264829.1400268775.2; __utmz=212787668.1400264829.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Host:192.168.0.200:8000
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36
Response Headers:
Cache-Control:no-cache
Content-Length:2
Content-Type:None
Date:Fri, 16 May 2014 22:37:16 GMT
Server:WebIOPi/0.7.0/Python3.2
Did you try setting headers like below,
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);//or any other required
HttpEntity request = new HttpEntity(null, headers);
RestTemplate restTemplate = new RestTemplate();
String url = "http://192.168.0.200:8000/GPIO/11/function/in";
String response = restTemplate.postForObject(url,request,String.class);
The problem is that the server is returning an invalid content type of None instead of something like text/plain, and Spring REST is choking on it. You will need to add a custom message converter for the None type, not use a typed query and parse the response object yourself, or get the Pi people to fix their broken Web server.