Spring Boot Websocket Throws "Connection Refused" in SockJS Client Test - spring-boot

I am working on a Spring Boot Server Project which offered simple REST resources until now. In order to push notifications to the client I want to add a websocket connection. To test this connection I have written a Integration Test using a SockJS Client based on this tutorial :
http://rafaelhz.github.io/testing-websockets/
Problem is that the Connection is refused with the following error:
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:9090/websocket/info": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
My Websocket Configuration is as follows:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
}
I can see in the that the socket endpoint is mapped int the log:
2017-07-14 15:22:59.561 INFO 13765 --- [ main] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped URL path [/websocket/**] onto handler of type [class org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler]
The Server Port is set to 9090 in the application.yml file:
server:
port: 9090
The following unit test is not able to connect to the socket:
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import java.lang.reflect.Type;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.SECONDS;
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
//#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class WebSocketConnectionTest {
static final String WEBSOCKET_URI = "ws://localhost:9090/websocket";
static final String WEBSOCKET_TOPIC = "/topic";
BlockingQueue<String> blockingQueue;
WebSocketStompClient stompClient;
#Before
public void setup() {
blockingQueue = new LinkedBlockingDeque<>();
stompClient = new WebSocketStompClient(new SockJsClient(
asList(new WebSocketTransport(new StandardWebSocketClient()))));
System.out.println(WEBSOCKET_URI);
}
#Test
public void shouldReceiveAMessageFromTheServer() throws Exception {
StompSession session = stompClient
.connect(WEBSOCKET_URI, new StompSessionHandlerAdapter() {})
.get(1, SECONDS);
session.subscribe(WEBSOCKET_TOPIC, new DefaultStompFrameHandler());
String message = "MESSAGE TEST";
session.send(WEBSOCKET_TOPIC, message.getBytes());
Assert.assertEquals(message, blockingQueue.poll(1, SECONDS));
}
class DefaultStompFrameHandler implements StompFrameHandler {
#Override
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
#Override
public void handleFrame(StompHeaders stompHeaders, Object o) {
blockingQueue.offer(new String((byte[]) o));
}
}
}
The connection is refused. Im fairly certain that this happens because the URI endpoint does not exist, but I don't know why. Does somebody know if there is a error in the URI or if something else leads to the refused connection ?

I found out the cause of the problem. The endpoint did not exist on PORT 9090. That is because the #SpringBootTest Annotation sets the WebEnvironment to WebEnvironment.MOCK by default. In this configuration No Embedded Servlet is started and therefor and no port exists, only MockMvc-based testing is possible. In order to start an Embedded servlet
the Environment has to be set to WebEnvironment.RANDOM_PORT or WebEnvironment.DEFINED_PORT. I set it to DEFINED_PORT so that the port 9090 from my application.yml is used. By Setting the Environment the test runs correctly.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)//!!!!!
#ActiveProfiles("test")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class WebSocketConnectionTest {
String WEBSOCKET_URI = "ws://localhost:9090/websocket";
String WEBSOCKET_TOPIC = "/topic";
.
.
.

Related

Consul discoveryClient in test

I am using trying to setup project with Consul. I can start my SpringBoot with Consul running. But how can I do test in Maven build? The build server does not have Consul install and running
package com.example.demo2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryClient;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
#SpringBootTest
class Demo2ApplicationTests {
#Autowired
private ConsulDiscoveryClient discoveryClient;
#Test
void contextLoads() {
}
#Test
public void testClient() {
List<ServiceInstance> instances = this.discoveryClient.getInstances("testConsulDiscovery");
assertThat(instances).as("instances was null").isNotNull();
assertThat(instances.isEmpty()).as("instances was empty").isFalse();
ServiceInstance instance = instances.get(0);
assertThat(instance.isSecure()).as("instance was secure (https)").isFalse();
assertIpAddress(instance);
assertThat(instance.getMetadata()).containsEntry("foo", "bar");
}
}
The following error was received
com.ecwid.consul.transport.TransportException: org.apache.http.conn.HttpHostConnectException: Connect to localhost:8500 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: no further information

In CustomAbstracthandler, Modify Httpfield is not working in jetty server 11 version

Getting header Connection: close, upgrade from Client, so I am trying to change connection header from connection: close, upgrade to connection: Upgrade.
I modified httpfield in CustomAbstractHandler, but not working in Jetty 11 version.
CustomAbstractHandler.Java :
import java.io.IOException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class CustomAbstractHandler extends AbstractHandler {
private static final Logger LOG = LoggerFactory.getLogger(CustomAbstractHandler.class);
#Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
HttpFields.Mutable replacement = HttpFields.build(baseRequest.getHttpFields()).put("Connection", "Upgrade");
baseRequest.setHttpFields(replacement);
LOG.info("" + baseRequest.getHttpFields());
baseRequest.setHandled(true);
}
}
In Main Class configure Handler in server.setHandler()
import java.util.EnumSet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import jakarta.servlet.DispatcherType;
public class WebSocketServer {
private static Server server;
public static void main(String args[]) throws Exception {
server = new Server();
server.setStopTimeout(100);
ServerConnector connector = new ServerConnector(server);
connector.setIdleTimeout(1000 * 60 * 60);
connector.setPort(56667);
connector.setAcceptedTcpNoDelay(true);
connector.setAccepting(true);
connector.join(100);
connector.setAcceptedReceiveBufferSize(1234567889);
connector.getConnectedEndPoints();
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/shop");
FilterHolder filterHolder = contextHandler.addFilter(websocketcreator.class, "/*",
EnumSet.of(DispatcherType.REQUEST));
filterHolder.setAsyncSupported(true);
contextHandler.getSessionHandler().setMaxInactiveInterval(5);
server.setHandler(new CustomAbstractHandler());
contextHandler.addServlet(WebSocketServerServlet.class, "/web-socket/");
JettyWebSocketServletContainerInitializer.configure(contextHandler, null);
server.setHandler(contextHandler);
server.start();
server.dump(System.err);
server.join();
}
Anything wrong from the code?
Your code is undoing your efforts.
server.setHandler(new CustomAbstractHandler());
...(snip)...
server.setHandler(contextHandler);
That replaces CustomAbstractHandler with contextHandler
The server.setHandler(Handler) is a single Handler, it looks like you want multiple Handlers.
Use a HandlerList instead.
// this is a org.eclipse.jetty.server.handler.HandlerList
HandlerList handlers = new HandlerList();
handlers.add(new CustomAbstractHandler());
handlers.add(contextHandler);
server.setHandler(handlers);
See past answers for more details on Handler trees in Jetty.
https://stackoverflow.com/a/39217143/775715
https://stackoverflow.com/a/57716374/775715

'url' should start with a path or be a complete HTTP URL:

I did write an application that pulls customer info.
When I run the application in postman it works fine.
But when trying to run some initial tests but it gives bean error ,
The exact same configuration with the same annotations works fine in another component .
Thanks in advance
'url' should start with a path or be a complete HTTP URL: v1/customers/2503427
java.lang.IllegalArgumentException: 'url' should start with a path or be a complete HTTP URL: v1/customers/2503427
package az.iba.ms.customer.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
#SpringBootTest
public class CustomerControllerTest {
String endpoint = "v1/customers/";
String cifs = "2503427";
#Autowired
private MockMvc mockMvc;
#Autowired
private CustomerController customerController;
#Test
public void controllerInitializedCorrectly() {
Assertions.assertThat(customerController).isNotNull();
}
#Test
public void whenValidInput_providedToCustomerQueryThenReturns200() throws Exception {
mockMvc.perform(get(endpoint + cifs)
.contentType("application/json"))
.andExpect(MockMvcResultMatchers.status().is(HttpStatus.OK.value()));
}
#Test
void whenValidNotInput_providedToCustomerQueryThenReturns400() throws Exception {
mockMvc.perform(get(endpoint)
.contentType("application/json"))
.andExpect(MockMvcResultMatchers.status().is(HttpStatus.BAD_REQUEST.value()));
}
#Test
void whenValidNotMethod_providedToCustomerQueryThenReturns405() throws Exception {
mockMvc.perform(post(endpoint + cifs)
.contentType("application/json"))
.andExpect(MockMvcResultMatchers.status().is(HttpStatus.METHOD_NOT_ALLOWED.value()));
}
}
I was fixing the same error now... Error arise because endpoint must start with /.
Change your's variable endpoint from v1/customers/ to /v1/customers/.

Unable to fetch random port when server.port is 0

I need to fetch port number on which undertow was Started by my Sprint boot app. I have defined server.port=0 in application.properties. I cannot use fix port numbers like 8080.
package com.aggregate.application;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
#Configuration
#ComponentScan(basePackages = {"com.aggregate"})
#EnableAutoConfiguration
public class GServiceApplication extends SpringBootServletInitializer
implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
private ApplicationContext applicationContext;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
try {
String ip = InetAddress.getLocalHost().getHostAddress();
String port = applicationContext.getBean(Environment.class).getProperty("server.port");
System.out.printf("ip:port=" +ip+ ":"+port);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws UnknownHostException
{
SpringApplication application = new SpringApplication(GServiceApplication.class);
Properties properties = new Properties();
properties.put("server.port", 0);
properties.put("server.address", InetAddress.getLocalHost().getHostAddress());
application.setDefaultProperties(properties);
application.run(args);
}
}
Undertow started:- o.s.b.w.e.u.UndertowServletWebServer : Undertow started on port(s) 55646 (http) with context path '' as printed in console
Expected result:- ip:port=xx.xx.x.1x1:55646
Actual result:- ip:port=xx.xx.x.1x1:0
Passing in the port number 0 is a trick that the Java core ServerSocket class can do. Undertow isn't aware of this; it just assumes that the port is always fixed. So there's no official API to read the port number that is actually used; but if you find the Undertow object, you can do:
// assuming you only have one:
ListenerInfo listenerInfo = undertow.getListenerInfo().iterator().next();
InetSocketAddress socketAddress = (InetSocketAddress) listenerInfo.getAddress();
URI uri = URI.create(listenerInfo.getProtcol() + "://" + socketAddress.getHostString() + ":" + socketAddress.getPort());
HTH

How to provide custom SSLContext to Netty server on spring boot

How can we configure a custom SSLContext to a spring boot application with Netty server?
From the source code, I see 'reactor.ipc.netty.http.server.HttpServerOptions' which are some server startup options, but I don't find a way to configure them.
Is there any handler through which we can inject our custom SSLContext?
I am looking something similar to this (Spring 5 WebClient using ssl) where WebClient is configured with a custom SSLContext through 'reactor.ipc.netty.http.client.HttpClientOptions'.
Netty can be customized like blow example in spring-boot 2.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
* author : Mohammad Ghoreishi
*/
#Configuration
#ImportResource({"classpath:convert-iban-service.xml", "classpath:config-loader-context.xml", "classpath*:error-resolver.xml"})
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer(){
return new WebServerFactoryCustomizer<NettyReactiveWebServerFactory>() {
#Override
public void customize(NettyReactiveWebServerFactory factory) {
Ssl ssl = new Ssl();
// Your SSL Cusomizations
ssl.setEnabled(true);
ssl.setKeyStore("/path/to/keystore/keystore.jks");
ssl.setKeyAlias("alias");
ssl.setKeyPassword("password");
factory.setSsl(ssl);
factory.addErrorPages(new ErrorPage("/errorPage"));
}
};
}
}

Resources