How to get all established connections to vertx based http server? - vert.x

I have got https server run by vertx http stack. Is there a way to get client IP addresses for all established connections (i.e. for audit, security, monitoring, QoS and other purposes). Ideally, I would like to have event (callback) driven API notifying about established and closed connections. How can I achieve it?
Current workaround is to poll a tool similar to netstat, but it is very inconvenient and not really real-time (i.e. short connections can be missed).

Github community responded with the answer: https://github.com/vert-x3/vertx-web/issues/685
You can use the HttpServer connection handler and manage it yourself easily:
server.connectionHandler(conn -> {
// Track conn.remoteAddress()
conn.closeHandler(v -> {
// Cleanup track of conn.remoteAddress()
});
});

In my example I'll write all IP addresses to a console. But of course you could write it to a log file, DB, or some other place of your choice.
Vertx vertx = Vertx.vertx();
// This your regular router
Router router = Router.router(vertx);
// We'll have two routes, just for fun
router.route("/a").handler((ctx) -> {
ctx.response().end("A");
});
router.route("/b").handler((ctx) -> {
ctx.response().end("B");
});
Router filterRouter = Router.router(vertx);
filterRouter.get().handler((ctx)->{
System.out.println("IP is " + ctx.request().remoteAddress());
// Forward to your actual router
ctx.next();
});
filterRouter.mountSubRouter("/", router);
vertx.createHttpServer().requestHandler(filterRouter::accept).listen(8080);

Related

Vertx - This Route is exclusive for already mounted sub router issue

I am trying to create 2 Verticles one for the http server and another for my sockjs eventbus bridge. Then I am trying to deplowy the two Verticles in the Main app. For for some reason I get this error in my Sockjs Verticle.
java.lang.IllegalStateException: This Route is exclusive for already mounted sub router.
Router router = Router.router(vertx);
vertx.deployVerticle(new RestApiVerticle(router))
.onFailure(startPromise::fail)
.onSuccess(id -> {
LOG.info("Deployed {} with id {}", RestApiVerticle.class.getSimpleName(), id);
startPromise.complete();
});
vertx.deployVerticle(new com.ali.websockjs.WebSocketVerticle(router))
.onFailure(startPromise::fail)
.onSuccess(id -> {
LOG.info("Deployed {} with id {}", com.ali.websockjs.WebSocketVerticle.class.getSimpleName(), id);
startPromise.complete();
});
In my RestApiVerticle
private Router router;
public RestApiVerticle(Router router) {
this.router = router;
}
router.route().handler(bodyHandler).failureHandler(FailureResponse.handleFailure());
router.route("/").handler(req -> req.response().end("Hello there!"))
.failureHandler(error -> LOG.error("Something is wrong with server!!"));
and in my WebSocketVerticle, where I get error
private Router router;
public WebSocketVerticle(Router router) {
this.router = router;
}
Below code I get error
// mount the bridge on the router
router.mountSubRouter("/eventbus", subRouter).failureHandler(error -> LOG.error("error {}", error));
Please help!!!!!!
UPDATE
Doing your approach is giving my CORS error in the browser
Access to XMLHttpRequest at 'http://localhost:1234/eventbus/info?t=1653982669356' from origin 'http://localhost:4200' has been blocked by CORS policy:
vertx.deployVerticle(new RestApiVerticle(router))
.compose(var ->
vertx.deployVerticle(new WebSocketVerticle(router)))
.onFailure(startPromise::fail)
.onSuccess(server -> {
LOG.debug("Server and Sockjs started !!");
startPromise.complete();
});
any suggestion??
The error is telling you that there is a configuration problem with your setup. In a way this is what is happening:
You have a router and in this router you have a route /eventbus that transfers the route evaluation from the 1st router to the 2nd (the sockjs router).
It seems that your code is trying to call that setup twice. This means that there's something wrong. The problem is that on that given route, if you transfer the execution to the 2nd router, the 3rd one (causing the problem) will never be called because the processing is now happening on the 2nd router.
I'd say that probably you need to debug your application startup and see why is the setup being called more than once.
My assumption here is that the problem you're seeing is coming from a race condition on your deployment. You're calling:
vertx.deployVerticle(new RestApiVerticle(router))
vertx.deployVerticle(new WebSocketVerticle(router))
These are asynchronous operations, and they will race to add their handlers to the shared router, now it could be that the WebSocketVerticle runs first and adds the eventbus handler, and then the RestApiVerticle runs and tries to add more handlers and this is problematic (like described above).
I believe the solution is to use composition to ensure your handlers are added in the right order:
vertx.deployVerticle(new RestApiVerticle(router))
.compose(1stDeploymentId ->
vertx.deployVerticle(new WebSocketVerticle(router)))
.onSuccess(2ndDeploymentId -> startPromise.complete();
.onFailure(startPromise::fail);
You may want to re-arrange the composition if you need to log the deployment ids.

SockJS eventbus client in Java for Vert.x

is there a Java SockJS client for Vert.x available?
Similar to the TCP/IP bridge, but based on SockJS.
Reason is that we want a unified protocol stack, connecting clients
to Vert.x. For JavaScript we can use vertx3-eventbus-client, which work great.
We are looking now for a similar solution for Java.
There isn't yet (work-in-progress). However you can write a basic client yourself using the Vert.x HttpClient:
open a websocket
send pings periodically to prevent the connection from being closed
register a handler
listen for messages
Here's an example:
client.websocket(HTTP_PORT, HTTP_HOST, "/eventbus/websocket", ws -> {
JsonObject msg = new JsonObject().put("type", "ping");
ws.writeFrame(io.vertx.core.http.WebSocketFrame.textFrame(msg.encode(), true));
// Send pings periodically to avoid the websocket connection being closed
vertx.setPeriodic(5000, id -> {
JsonObject msg = new JsonObject().put("type", "ping");
ws.writeFrame(io.vertx.core.http.WebSocketFrame.textFrame(msg.encode(), true));
});
// Register
JsonObject msg = new JsonObject().put("type", "register").put("address", "my-address");
ws.writeFrame(io.vertx.core.http.WebSocketFrame.textFrame(msg.encode(), true));
ws.handler(buff -> {
JsonObject json = new JsonObject(buff.toString()).getJsonObject("body");
// Do stuff with the body
});
});
If you need to work with different addresses then your handler will have to inspect the JSON object, not just get the body.

Milo: get IP of client

Is there a way to get a Clients IP in Context of a write?
I want to get the IP of an Client that writes to my Milo-OPCUA-Server, so I can handle these writes differently based on the Clients IP (local Clients should be able to write directly on the Server, whilst other writes should get forwarded to another Server)
Okay, this is not part of any official API right now, so it almost certainly will break in the future, but:
With the OperationContext you get when implementing AttributeManager#write(WriteContext, List<WriteValue>):
context.getSession().ifPresent(session -> {
UaStackServer stackServer = context.getServer().getServer();
if (stackServer instanceof UaTcpStackServer) {
ServerSecureChannel secureChannel = ((UaTcpStackServer) stackServer)
.getSecureChannel(session.getSecureChannelId());
Channel channel = secureChannel.attr(UaTcpStackServer.BoundChannelKey).get();
SocketAddress remoteAddress = channel.remoteAddress();
}
});
I'll have to add some official API to do this, probably something hanging off the Session object.

keep all connected clients' ip in netty

My TCP server uses netty.The situation is: When a client connects to the server,I will save the client's ip in a global variable(such as a Map); When the client is disconnected,I will remove the IP from the map.
I used channelConnected() and channelDisconnected() method in SimpleChannelHandler.But my problem is ,some times the channelDisconnected() method cannot catch the event when I think the client is disconnected(maybe the computer closed,or the client process closed,or some other situations...) Can you give me some suggestions.
Just use DefaultChannelGroup which will automatically remove the Channel from it when it was closed.
Alternative you can register a ChannelFutureListener to the Channels close future to do the removal from your map.
Something like this:
channel.getCloseFuture().addListener(new ChannelFutureListener() {
public void operationCompleted(ChannelFuture f) {
map.remove(f.getChannel());
}
});

Any off the shelf app to rebroadcast tcp packets?

I am working with a 3rd party device which opens a tcp port that only allows one connection at a time. If my app connects to the port, all other connections are denied.
I'd like to find an app that basically connects to this port, then allows others to connect to it on a different port.
Any data sent out of the device's port is then rebroadcast to any connected client.
I know how to write such an app, but it seems like it would be something someone else has already thought off and written it & shared, and I could avoid taking the time to write it.
basicaly code would be:
1) start a tcp socket server, binding to TO_PORT (clients connect to this)
2) connect as a client to DEVICE_IP:DEVICE_PORT
3) when data is read into a buffer from DEVICE_IP:DEVICE_PORT, the buffer content is resent to each connected client.
4) everything else that makes it a stable, working program.
This is for windows, and I'd prefer it not require a java install.
My google skills have failed me.
Anyone know of such an app?
Not a complete solution for you, but might be interesting, though
http://www.codeproject.com/KB/IP/serversocket.aspx
http://www.codeproject.com/KB/IP/UniversalTCPSocketClass.aspx
Guess I'll answer my own question.
I implemented the solution my self.
Key points to my solution:
A class named IPClient which wraps up a TcpClient instance, uses async model of calling TcpClient.BeginConnect, BeginRead, etc. It has a Timer used for reconnecting if it loses connection.
This is the class that connects to the device.
It's public interface would look something like this:
public class IPClient{
public event EventHandler<MyConnectedArgs> Connected;
public event EventHandler<MyDisconnectedArgs>Disconnected;
public event EventHandler<MyDataReceivedArgs> DataReceived;
public bool Connect(string address, int port){...}
public bool Disconnect() {...}
}
To open the port that would allow other clients to connect, I used this library: http://codeproject.com/KB/IP/BasicTcpServer.aspx and modified it a bit.
It's job was to open a port, accept connections, and do the following:
in the Connected handler, start the listening port
in the Disconnected handler, stop the listening port
in the DataReceived handler, broadcast the data to any connected clients.
I'll leave out the rest of the boring details, but say it wasn't "too hard", and eventually I just had to roll my own.
command line usage: myapp.exe remote_addr remote_port listen_port
psuedocode/main idea of my program main:
static int Main(string[] args){
//SetConsoleCtrlHandler(my callback re: ctrl+C,etc)
//get command line params
var ipClient = new IPClient();
var myprovider = MyTcpServiceProvider();
var server = new TcpServer(myProvider, listenPort);
ipClient.Connected += (sender, e) => server.Start();
ipClient.Disconnected += (sender,e) => server.Stop();
ipClient.DataReceived += (sender,e)=> provider.BroadcastToClients(e.Data);
ipClient.Connect(remoteAddress, remotePort);
//wait for Ctrl+C or program exit
//shutdown code,etc
return 0;
}