I am using Spring Integration's TcpNetServerConnectionFactory and TcpInboundGateway to receive TCP messages. Everything is working as expected, but I was wondering if there is any way to implement address whitelisting? (Basically I want to allow a specified address and reject connections from others.) Maybe there is a way to add a callback to accept/reject when a connection is made, I couldn't find any mention in the docs or samples.
Create a custom TcpNetConnectionSupport (subclass DefaultTcpNetConnectionSupport and override createNewConnection()).
I think you should be able to close the socket there.
Inject it into the server connection factory.
See Advanced Techniques.
EDIT
It was added in Spring Integration 5...
#SpringBootApplication
public class So48951046Application {
public static void main(String[] args) {
SpringApplication.run(So48951046Application.class, args).close();
}
#Bean
public ApplicationRunner runner() {
return args -> {
Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234);
Thread.sleep(10_000);
socket = SocketFactory.getDefault().createSocket("localhost", 1234);
Thread.sleep(10_000);
};
}
#Bean
public TcpNetServerConnectionFactory server() {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(1234);
server.setTcpNetConnectionSupport(new DefaultTcpNetConnectionSupport() {
#Override
public TcpNetConnection createNewConnection(Socket socket, boolean server, boolean lookupHost,
ApplicationEventPublisher applicationEventPublisher, String connectionFactoryName)
throws Exception {
TcpNetConnection conn = super.createNewConnection(socket, server, lookupHost, applicationEventPublisher, connectionFactoryName);
if (conn.getHostAddress().contains("127")) {
conn.close();
}
return conn;
}
});
return server;
}
#Bean
public TcpReceivingChannelAdapter adapter() {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(server());
adapter.setOutputChannel(new NullChannel());
return adapter;
}
}
and
: server, port=1234 Listening
: Started So48951046Application in 0.907 seconds (JVM running for 1.354)
: Accepted connection from 127.0.0.1
: New connection localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478
: server: Added new connection: localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478
: localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478 Reading...
: server: Removed closed connection: localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478
: Read exception localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478 SocketException:Socket is closed
: Accepted connection from 127.0.0.1
: New connection localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33
: server: Added new connection: localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33
: server: Removed closed connection: localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33
: localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33 Reading...
: Read exception localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33 SocketException:Socket is closed
Related
I'm writing a simple rabbitmq producer with spring boot 2.2.7.
On the broker side I've setup a direct exchange samples , a queue named samples.default and binded them together adding a samples.default bindkey key.
when running the application I get the following error
Attempting to connect to: [127.0.0.1:5672]
2020-05-14 15:13:39.232 INFO 28393 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#2f860823:0/SimpleConnection#3946e760 [delegate=amqp://open-si#127.0.0.1:5672/, localPort= 34710]
2020-05-14 15:13:39.267 ERROR 28393 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange '"samples"' in vhost '/', class-id=60, method-id=40)
The rabbitmq server configuration is correct as I've a python producer that already puts messages succesfully in the "samples.default" queue.
in Spring boot I'm using jackson serialization, but that's not the prolem here I think as I've tested the code without the Jakson serialization configuration and the problem is still the same.
My broker configuration is set both in the application.properties :
#spring.rabbitmq.host=localhost
spring.rabbitmq.addresses=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=xxxx
spring.rabbitmq.password=xxxx
broker.exchange = "samples"
broker.routingKey = "samples.default"
note that using spring.rabbitmq.host doesn't work as it results in using my internet provider address !
and in a BrokerConf configuration class :
#Configuration
public class BrokerConf {
#Bean("publisher")
MessagePublisher<BaseSample> baseSamplePublisher(RabbitTemplate rabbitTemplate) {
return new MessagePublisher<BaseSample>(rabbitTemplate);
}
#Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final var rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
return rabbitTemplate;
}
#Bean
public MessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
The publisher base class is as :
#Component
public class MessagePublisher<T> {
private static final Logger log = LoggerFactory.getLogger(MessagePublisher.class);
private final RabbitTemplate rabbitTemplate;
public MessagePublisher(RabbitTemplate r) {
rabbitTemplate = r;
}
public void publish(List<BaseSample> messages, String exchange, String routingKey) {
for (BaseSample message: messages) {
rabbitTemplate.convertAndSend(exchange, routingKey, message);
}
}
}
that I use in a rest controller
private static final Logger logger = LoggerFactory.getLogger(SamplesController.class);
#Autowired
private MessagePublisher<BaseSample> publisher;
#Value("${broker.exchange}")
private String exchange;
#Value("${broker.routingKey}")
private String routingKey;
#PutMapping(value = "/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SampleAck> add(#RequestBody List<BaseSample> samples) {
publisher.publish(samples, exchange, routingKey);
return ResponseEntity.ok(new SampleAck(samples.size(), new Date()));
}
So the broker connection is OK but the exchange is not found
and rabbitmq resources exists
xxxxxx#xxxxxxx:~/factory/udc-collector$ sudo rabbitmqctl list_exchanges
Listing exchanges for vhost / ...
name type
amq.topic topic
amq.rabbitmq.trace topic
amq.match headers
amq.direct direct
amq.fanout fanout
direct
amq.rabbitmq.log topic
amq.headers headers
samples direct
xxxx#xxxxx:~/factory/udc-collector$ sudo rabbitmqctl list_queues
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
name messages
samples.default 2
Any idea ?
thanks in advance.
The error seems quite obvious:
no exchange '"samples"' in vhost
broker.exchange = "samples"
broker.routingKey = "samples.default"
Remove the quotes
broker.exchange=samples
broker.routingKey=samples.default
When I use TCP client with fixed ConnectionProvider, I can't reuse connection.
They made connection exceed maxConnection variable.
This is what i wrote.
Even if I user 10 maxConnection like 'ConnectionProvider.fixed("TEST", 10)', there are 22 idle connection. omg
//NettyClient.java
#AllArgsConstructor
public class NettyClient {
private final int port;
public final ConnectionProvider connectionProvider = ConnectionProvider.fixed("TEST", 10);
public void sendTest() {
TcpClient c = TcpClient
.create(connectionProvider)
.port(port)
.handle((in, out) -> {
return out
.sendString(Mono.just("string"))
.then(in
.receive()
.asString()
.flatMap(ss -> {
out.withConnection(connection -> {
connection.disposeNow();
});
return Mono.empty();
}));
})
.option(ChannelOption.SO_KEEPALIVE, true)
.wiretap(true);
c.connect().subscribe();
}
}
//NettyClientTest.java
public class NettyClientTest {
#Test
public void send() throws InterruptedException {
NettyClient nettyClient = new NettyClient(11);
for (int i = 0; i < 20; i++) {
nettyClient.sendTest();
}
nettyClient.sendTest();
nettyClient.sendTest();
}
}
14:55:27.397 [reactor-tcp-nio-5] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x3444910e, L:/0:0:0:0:0:0:0:1:53928 ! R:/0:0:0:0:0:0:0:1:33333] Channel cleaned, now 0 active connections and 22 inactive connections
In the example that you posted you do the following:
out.withConnection(connection -> {
connection.disposeNow();
});
So every time when you receive a response from the server you close the connection.
As there are 22 requests to the server, you will use 22 connections for executing the test. Every connection first is returned to the pool and then immediately closed because you requested that.
What was missing in Reactor Netty logs was exactly a log when the connection was closed, so I added such log. If you try 0.8.6.BUILD-SNAPSHOT version you will be able to see at the end of the test:
10:36:42.341 [reactor-tcp-nio-3] DEBUG r.n.r.PooledConnectionProvider - [id: 0x0dad123c, L:/0:0:0:0:0:0:0:1:51530 ! R:/0:0:0:0:0:0:0:1:8080] Channel closed, now 0 active connections and 0 inactive connections
Using Spring FTP Integration and Annotation configuration, I downloaded files from the FTP server. After downloaded still our application is trigger to connect the server and find the any newly added files, if any files added it will download from the server. But I don't need to maintain the FTP server session alive and disconnect the server after first connection or first time downloaded.
Code :
public class FtpServices {
#Bean(name="ftpSessionFactory")
public DefaultFtpSessionFactory ftpSessionFactory() {
System.out.println("session");
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("localhost");
sf.setPort(21);
sf.setUsername("user");
sf.setPassword("password");
return sf;
}
#Bean
public FtpInboundFileSynchronizer ftpInboundFileSynchronizer() {
System.out.println("2");
FtpInboundFileSynchronizer fileSynchronizer = new FtpInboundFileSynchronizer(ftpSessionFactory());
fileSynchronizer.setDeleteRemoteFiles(false);
fileSynchronizer.afterPropertiesSet();
fileSynchronizer.setRemoteDirectory("/test/");
// fileSynchronizer.setFilter(new FtpSimplePatternFileListFilter("*.docx"));
fileSynchronizer.setFilter(filter);
return fileSynchronizer;
}
#Bean()
#InboundChannelAdapter(value="ftpChannel", poller = #Poller(fixedDelay = "50", maxMessagesPerPoll = "1"))
public FtpInboundFileSynchronizingMessageSource ftpMessageSource() {
System.out.println(3);
FtpInboundFileSynchronizingMessageSource source =
new FtpInboundFileSynchronizingMessageSource(ftpInboundFileSynchronizer());
source.setLocalDirectory(new File("D:/Test-downloaded/"));
//source.stop();
return source;
}
#Bean
#ServiceActivator(inputChannel = "ftpChannel", requiresReply="false")
public MessageHandler handler() {
System.out.println(4);
MessageHandler handler = new MessageHandler() {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println(message.getPayload()+" #ServiceActivator");
System.out.println(" Message Header :"+message.getHeaders());
}
};
return handler;
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(triggerOnlyOnce());
return pollerMetadata;
}
}
and also I override the AbtractFTPSessionFactory.java to test FTP server connection and disconnection process.
protected void postProcessClientAfterConnect(T t) throws IOException {
System.out.println("After connect");
}
protected void postProcessClientBeforeConnect(T client) throws IOException {
System.out.println("Before connect");
}
Console :
INFO : org.springframework.context.support.DefaultLifecycleProcessor - Starting beans in phase -2147483648
INFO : org.springframework.context.support.DefaultLifecycleProcessor - Starting beans in phase 0
Before connect
After connect
D:\Test-downloaded\demo 1.txt #ServiceActivator
Message Header :{id=e4a1fd7f-0bbf-9692-f70f-b0ac68b4dec4, timestamp=1477317086272}
D:\Test-downloaded\demo.txt #ServiceActivator
Message Header :{id=9115ee92-12b4-bf1f-d592-9c13bf7a27fa, timestamp=1477317086324}
Before connect
After connect
Before connect
After connect
Before connect
After connect
Before connect
After connect
Before connect
After connect
Before connect
After connect
Thanks.
That is really a purpose of any #InboundChannelAdapter: poll the target system for new data periodically.
To do that once we sometimes suggest OnlyOnceTrigger:
public class OnlyOnceTrigger implements Trigger {
private final AtomicBoolean done = new AtomicBoolean();
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
return !this.done.getAndSet(true) ? new Date() : null;
}
}
But this might not work for your case, because there might not be desired files in the source FTP directory yet.
Therefore we have to poll until you will receive required files and .stop() an adapter when that condition is met.
For this purpose you can use any downstream logic to determine the state or consider to implement AbstractMessageSourceAdvice to be injected to the PollerMetadata of the #Poller: http://docs.spring.io/spring-integration/reference/html/messaging-channels-section.html#conditional-pollers
Library in use:
AsyncHtpClient Library:
Version : 1.9.32
Location: https://github.com/AsyncHttpClient/async-http-client
Netty Version : 3.10.3.Final
Proxy: Squid Proxy
I am trying to create a websocket connection using AsyncHttpClinet library. It works fine when using without the proxy.
But when I start a proxy and pass in the Host, port, username and password , I am unable to create a websocket connection.
It get a stack trace which says Invalid Status Code 400:
Caused by: java.lang.IllegalStateException: Invalid Status Code 400
at com.ning.http.client.ws.WebSocketUpgradeHandler.onCompleted(WebSocketUpgradeHandler.java:76)
at com.ning.http.client.ws.WebSocketUpgradeHandler.onCompleted(WebSocketUpgradeHandler.java:29)
at com.ning.http.client.providers.netty.future.NettyResponseFuture.getContent(NettyResponseFuture.java:177)
at com.ning.http.client.providers.netty.future.NettyResponseFuture.done(NettyResponseFuture.java:214)
... 35 more
I am setting the proxy object like this:
ProxyServer ps = new ProxyServer("host-name",portNo,"user_name","password");
AsyncHttpClientConfig cf = new AsyncHttpClientConfig.Builder().setProxyServer(ps).build();
WebSocket websocket = c.prepareGet(url)
.execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(
new WebSocketTextListener() {
#Override
public void onMessage(String message) {
}
#Override
public void onFragment(String s, boolean b) {
}
#Override
public void onOpen(WebSocket websocket) {
}
#Override
public void onClose(WebSocket websocket) {
}
#Override
public void onError(Throwable t) {
}
}
).build()
).get();
Are there any other steps to configure a proxy for websocket connections?
I have also tried configuring the ProxyServer object like this:
ProxyServer ps = new ProxyServer(ProxyServer.Protocol.HTTPS,"host-name",portNo,"user_name","password");
I am using spring boot as per examples for TcpInboundGateway,so different devices send data to this Gateways,things works fine but in between in logs it showing following exception:
2015-12-29 18:42:19.455 ERROR 3465 --- [ool-3-thread-47] o.s.i.i.tcp.connection.TcpNetConnection : Read exception 106.221.159.216:38170:8765:934c050d-c4b5-4466-98ab-ee87714c3d00 SocketException:Connection reset
If this exception is resetting connection then how to avoid this reset?What is the cause of this error?
My code as follows
#SpringBootApplication
#IntegrationComponentScan
public class SpringIntegrationApplication extends SpringBootServletInitializer{
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringIntegrationApplication.class, args);
System.in.read();
ctx.close();
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringIntegrationApplication.class);
}
private static Class<SpringIntegrationApplication> applicationClass = SpringIntegrationApplication.class;
#Bean
TcpNetServerConnectionFactory cf(){
TcpNetServerConnectionFactory connectionFactory=new TcpNetServerConnectionFactory(8765);
return connectionFactory;
}
#Bean
TcpInboundGateway tcpGate(){
TcpInboundGateway gateway=new TcpInboundGateway();
gateway.setConnectionFactory(cf());
gateway.setRequestChannel(requestChannel());
return gateway;
}
#Bean
public MessageChannel requestChannel(){
return new DirectChannel();
}
#MessageEndpoint
public class Echo {
#ServiceActivator(inputChannel="requestChannel")
public byte[] echo(byte[] in,#SuppressWarnings("deprecation") #Header("ip_address") String ip){
byte[] rawbytes = gosDataSerivce.byteArrayToHex(in,ip);//Process bytes and returns result
return rawbytes;
}
}
}
After setting singleUse to true now exception message is changed slightly.
2015-12-31 06:09:00.481 ERROR 16450 --- [ool-3-thread-10] o.s.i.i.tcp.connection.TcpNetConnection : Read exception 106.221.146.40:9195:8765:1b4755e8-5b0c-44b9-b4e6-b3aacc25e228 SocketException:Connection reset
Use Case:
I have several clients that established GPRS connection to TcpInboundGateWay and sends login packet,our server will reply to this login packet.If client receives server reply to login packet then it will send data packets at regular interval. Server needs to reply to these packet also if server fails to send reply to those data packets then client GPRS connection is terminated and client will try to establish connections again.Let me know if this use case can be handle with TcpInboundGateWay
Network Trace Analysis
General flow of communication between client and server is as follows:Client sends login packet from ip say 106.221.148.165 so at server connection named 106.221.148.165:63430:8765:cc105da2-dae4-494b-af9c-d1ba268f34f1 is created, that client sends subsequent packets from that ip only.So everything works fine,but after some time same client sends its login packet from another ip say 106.221.142.204.And subsequent packets from new ip.But in logs following error comes that for previous connection exception occurred.
2016-01-05 05:16:14.871 ERROR 6819 --- [pool-3-thread-5] o.s.i.i.tcp.connection.TcpNetConnection : Read exception 106.221.148.165:63430:8765:cc105da2-dae4-494b-af9c-d1ba268f34f1 SocketException:Connection reset
I have set singleUse true and I am using spring integration 4.2.1
This message is emitted when the client closes the socket - if your client only sends one message then closes the socket, you can set singleUse to true and it will suppress this message (as long as the socket is closed normally - between messages).
With Spring Integration version 4.2 and later, the message is not emitted on a normal close, even if singleUse is false.