i have a MVC asp.net web app.
It is hosted on my local network on a Windows 8.1 32bit OS.
I have found that SignalR only supports 1 client connection.
So, I removed SignalR and switched to web sockets.
However, this did not solve the problem.
Am I accurate in stating that Web Sockets only supports 1 client connection on this OS and IIS 8 Client?
Is the solution to use IIS Express because I had problems getting this to work and in the end ended up rebuilding my PC.
Is there no workaround for this issue?
This is my code:
In my Global.cs file:
public static WebSocketCollection clients = new WebSocketCollection();
In my handler:
public class MicrosoftWebSockets : WebSocketHandler
{
private string name;
public override void OnOpen()
{
this.name = this.WebSocketContext.QueryString["chatName"];
MvcApplication.clients.Add(this);
//clients.Broadcast(name + " has connected.");
}
public void SendImage(byte[] jpeg)
{
this.Send(jpeg);
}
public override void OnMessage(string message)
{
MvcApplication.clients.Broadcast(string.Format("{0} said: {1}", name, message));
}
public override void OnClose()
{
MvcApplication.clients.Remove(this);
MvcApplication.clients.Broadcast(string.Format("{0} has gone away.", name));
}
}
In my HTML page:
var conversation = $('conversation');
var url = 'ws://192.168.0.13:80/SocketHandler.ashx?name=John Doe';
ws = new WebSocket(url);
ws.binaryType = 'arraybuffer';
ws.onerror = function (e) {
$('#divSystemMessage').html('Problem with connection: ' + e.message);
};
ws.onopen = function () {
$('#divSystemMessage').html('Client connected');
};
ws.onmessage = function (e) {
liveViewIndex = 0;
desktopImage.src = 'data:image/jpeg;base64,' + base64ArrayBuffer(e.data);
};
ws.onclose = function () {
$('#divSystemMessage').html('Closed connection!');
};
public class SocketHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
context.AcceptWebSocketRequest(new MicrosoftWebSockets());
}
public bool IsReusable
{
get
{
return false;
}
}
}
Related
I want to filter the range of client IPs who can route to Prometheus metrics.
So in startup I have
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.UsePrometheusServer(q =>
{
q.MapPath = "/metrics";
});
app.UseWebApi(config);
}
And this is my custom actionFilter class
public class IpFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
string clinetIP = GetClientIpAddress(actionContext.HttpContext.Items["MS_HttpRequestMessage"] as HttpRequestMessage);
if (IpAllowed(clinetIP))
base.OnActionExecuting(actionContext);
}
But I have no idea how to use IpFilter since it cannot be use as an attribute on a controller action.
I tried to use it by adding a middleware using owin but the next.Invoke doesn't work properly
public void Configuration(IAppBuilder app)
{
app.Map("/metrics", metricsApp =>
{
metricsApp.Use<TestIpMid>(deniedIps);
metricsApp.UsePrometheusServer(q => q.MapPath = "/metrics");
});
app.UsePrometheusServer(q =>
{
q.MapPath = "/metrics";
});
app.UseWebApi(config);
}
and this is the middleware:
public class TestIpMid : OwinMiddleware
{
private readonly HashSet<string> _deniedIps;
public TestIpMid(OwinMiddleware next, HashSet<string> deniedIps) : base(next)
{
_deniedIps = deniedIps;
}
public override async Task Invoke(IOwinContext context)
{
var ipAddress = context.Request.RemoteIpAddress;
if (_deniedIps.Contains(ipAddress))
{
context.Response.StatusCode = 403;
return;
}
await Next.Invoke(context);
}
}
please help me :'(
this solution worked for me but other ways I was thinking of didn't work
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var config = new HttpConfiguration();
var allowedIps = ProtectedSettings.Read(ProtectedSettings.protheusIpWhitelist).Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
app.Use(async (Context, next) =>
{
var ipAddress = Context.Request.RemoteIpAddress;
if ((!allowedIps.Contains(ipAddress)) && Context.Request.Path.Value == "/metrics")
{
Context.Response.StatusCode = 403;
return;
}
await next.Invoke();
});
app.UsePrometheusServer(q =>
{
q.MapPath = "/metrics";
});
app.UseWebApi(config);
}
I want to build an API based on Futures (from java.util.concurrent) that is powered by a custom protocol on top of Netty (version 4). Basic idea is to write a simple library that would abstract the underlying Netty implementation and make it easier to make requests.
Using this library, one should be able to write something like this:
Request req = new Request(...);
Future<Response> responseFuture = new ServerIFace(host, port).call(req);
// For example, let's block until this future is resolved
Reponse res = responseFuture.get().getResult();
Underneath this code, a Netty client is connected
public class ServerIFace {
private Bootstrap bootstrap;
private EventLoopGroup workerGroup;
private String host;
private int port;
public ServerIFace(String host, int port) {
this.host = host;
this.port = port;
this.workerGroup = new NioEventLoopGroup();
bootstrap();
}
private void bootstrap() {
bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Response.class.getClassLoader())));
ch.pipeline().addLast("response", new ResponseReceiverChannelHandler());
}
});
}
public Future<Response> call(final Request request) throws InterruptedException {
CompletableFuture<Response> responseFuture = new CompletableFuture<>();
Channel ch = bootstrap.connect(host, port).sync().channel();
ch.writeAndFlush(request).addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Wrote successfully");
} else {
f.cause().printStackTrace();
}
});
ChannelFuture closeFuture = ch.closeFuture();
// Have to 'convert' ChannelFuture to java.util.concurrent.Future
closeFuture.addListener((f) -> {
if (f.isSuccess()) {
// How to get this response?
Response response = ((ResponseReceiverChannelHandler) ch.pipeline().get("response")).getResponse();
responseFuture.complete(response);
} else {
f.cause().printStackTrace();
responseFuture.cancel(true);
}
ch.close();
}).sync();
return responseFuture;
}
}
Now, as you can see, in order to abstract Netty's inner ChannelFuture, I have to 'convert' it to Java's Future (I'm aware that ChannelFuture is derived from Future, but that information doesn't seem useful at this point).
Right now, I'm capturing this Response object in the last handler of my inbound part of the client pipeline, the ResponseReceiverChannelHandler.
public class ResponseReceiverChannelHandler extends ChannelInboundHandlerAdapter {
private Response response = null;
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
this.response = (Response)msg;
ctx.close();
}
public Response getResponse() {
return response;
}
}
Since I'm new to Netty and these things in general, I'm looking for a cleaner, thread-safe way of delivering this object to the API user.
Correct me if I'm wrong, but none of the Netty examples show how to achieve this, and most of the Client examples just print out whatever they get from Server.
Please note that my main goal here is to learn more about Netty, and that this code has no production purposes.
For the reference (although I don't think it's that relevant) here's the Server code.
public class Server {
public static class RequestProcessorHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ChannelFuture future;
if (msg instanceof Request) {
Request req = (Request)msg;
Response res = some function of req
future = ctx.writeAndFlush(res);
} else {
future = ctx.writeAndFlush("Error, not a request!");
}
future.addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Response sent!");
} else {
System.out.println("Response not sent!");
f.cause().printStackTrace();
}
});
}
}
public int port;
public Server(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Request.class.getClassLoader())));
ch.pipeline().addLast(new ObjectEncoder());
// Not really shutting down this threadpool but it's ok for now
ch.pipeline().addLast(new DefaultEventExecutorGroup(2), new RequestProcessorHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new Server(port).run();
}
}
Im trying spring websockets and for some reason i dont understand, i can establish connection with the server but when i send data nothing happens.
Here is my Config class (exatcly equal to other spring websocket examples):
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketsConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
My controller, in a package where i ensure that spring inicialites it, as i shee the init() message with the #PostConstruct annotation. As you see i wrote System.out.println to see in the console if the method triggers but this never happens, so data never gets to the controller:
#Controller
public class MonitorSpring {
#PostConstruct
protected void init() {
System.out.println("init()");
}
#MessageMapping("/sendmessage")
#SendTo("/topic/message")
public ChatMessage sendMessage(#Payload ChatMessage chatMessage) {
System.out.println("sendMessage here");
return chatMessage;
}
#MessageMapping("/adduser")
#SendTo("/topic/user")
public ChatMessage addUser(#Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {
System.out.println("addUser here");
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
return chatMessage;
}
}
And finaly my JavaScript client, i will abbreviate putting only the important parts here:
function connect(event) {
username = document.querySelector('#name').value.trim();
if(username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
var socket = new SockJS('https://' + document.location.host + '/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError);
}
event.preventDefault();
}
function onConnected() {
stompClient.subscribe('/topic/message', onMessageReceived);
stompClient.subscribe('/topic/user', onMessageReceived);
stompClient.send("/app/adduser", {}, JSON.stringify({sender: username, type: 'JOIN'}))
connectingElement.classList.add('hidden');
}
function sendMessage(event) {
var messageContent = messageInput.value.trim();
if (messageContent && stompClient) {
var chatMessage = {
sender: username,
content: messageInput.value,
type: 'CHAT'
};
stompClient.send("/app/sendmessage", {}, JSON.stringify(chatMessage));
messageInput.value = '';
}
event.preventDefault();
}
I do connect and create the websocket without any problem, but when i send something to the server
i see this message in the chrome console but nothing happens on the server:
>>> SEND
destination:/app/sendmessage
content-length:53
{"sender":"user1","content":"message1","type":"CHAT"}
What could i be doing wrong?
First of all, you should implements WebSocketMessageBrokerConfigurer instead of AbstractWebSocketMessageBrokerConfigurer because is deprecated.
When you connect to the websocket
var socket = new SockJS('https://' + document.location.host + '/ws');
you should connect to the http endpoint
var socket = new SockJS('http://' + document.location.host + '/ws');
If you trying to send message to the https and dont have the certificates configured it might reject your messages.
Not sure if someone still facing these issues.
For anyone in the future
In addition to TOvidiu's answer, you should allow cross-origin header
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
I will try to help you, I have a chat application, where user can not just chat also can upload images, even bookings using the spring and stomp, as I can check your code, I cant finally get the right issue with it but if you accept from me I can suggest some small changes wish that can be helpful to solve your issue.
for
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
can you change it to something like this?
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").setAllowedOrigins("*").withSockJS();
}
and edition with the connection to stomp in JS
var socket = new SockJS('https://' + document.location.host + '/ws');
stompClient = Stomp.over(socket);
can you use this code?
function connectToChat(userName) {
let socket = new SockJS(url + '/chat');
console.log("socket.name", socket.name)
console.log("WebSocket.name", WebSocket.name)
stompClient = Stomp.over(socket);
console.log("stompClient = ", stompClient)
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
successCallback(userName);
}, () => {
reconnect(url, successCallback, userName);
});
}
function successCallback(userName) {
stompClient.subscribe("/topic/messages/" + userName, function (response) {
let data = JSON.parse(response.body);
console.log("data ", data);
if (selectedUser === data.to) {
render(data.message, data.from);
}
});
}
function reconnect(socketUrl, successCallback, userName) {
let connected = false;
let reconInv = setInterval(() => {
//let socket = new WebSocket(url +'/chat')
let socket = new SockJS(url + '/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, (frame) => {
clearInterval(reconInv);
connected = true;
successCallback(userName);
}, () => {
if (connected) {
reconnect(socketUrl, successCallback, userName);
}
});
}, 3000);
}
make sure that your link is the right one, for my code, I'm using
let socket = new SockJS(url + '/chat');
regarding HTTP or HTTPS if you are using stomp, it will understand if the link is with SSL or not.
finally, the first user must connect to the second user but in your case, you have 2 links
stompClient.subscribe('/topic/message', onMessageReceived);
stompClient.subscribe('/topic/user', onMessageReceived);
solve this issue so the socket can understand to which user to connect and send the message from the chat.
Wish I was helpful and wish that you can solve your issue.
finally, let me show you the exact configuration for the links to connect
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").setAllowedOrigins("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app").enableSimpleBroker("/topic");
}
}
try this
var socket = new SockJS('/ws');
this worked for me
My application is running behind a corporate firewall and I need to use http proxy(http://theclientproxy.net:8080) to connect to internet
I have used the Netty client as below,
https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/client
Code:
public final class WebSocketClient {
static final String URL = System.getProperty("url", "wss://127.0.0.1:8080/websocket");
public static void main(String[] args) throws Exception {
URI uri = new URI(URL);
String scheme = uri.getScheme() == null? "ws" : uri.getScheme();
final String host = uri.getHost() == null? "127.0.0.1" : uri.getHost();
final int port;
final boolean ssl = "wss".equalsIgnoreCase(scheme);
final SslContext sslCtx;
if (ssl) {
sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} else {
sslCtx = null;
}
EventLoopGroup group = new NioEventLoopGroup();
try {
final WebSocketClientHandler handler =
new WebSocketClientHandler(
WebSocketClientHandshakerFactory.newHandshaker(
uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addFirst(new HttpProxyHandler(new InetSocketAddress("theclientproxy.net", 8080) ) );
p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
}
p.addLast(
new HttpClientCodec(),
new HttpObjectAggregator(8192),
WebSocketClientCompressionHandler.INSTANCE,
handler);
}
});
Channel ch = b.connect(uri.getHost(), port).sync().channel();
handler.handshakeFuture().sync();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String msg = console.readLine(); //THIS IS NULL IN DATA CENTER LOGS
if (msg == null) {
break;
} else if ("bye".equals(msg.toLowerCase())) {
ch.writeAndFlush(new CloseWebSocketFrame());
ch.closeFuture().sync();
break;
} else if ("ping".equals(msg.toLowerCase())) {
WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[] { 8, 1, 8, 1 }));
ch.writeAndFlush(frame);
} else {
WebSocketFrame frame = new TextWebSocketFrame(msg);
ch.writeAndFlush(frame);
}
}
} finally {
group.shutdownGracefully();
}
Handler:
public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
private final WebSocketClientHandshaker handshaker;
private ChannelPromise handshakeFuture;
public WebSocketClientHandler(WebSocketClientHandshaker handshaker) {
this.handshaker = handshaker;
}
public ChannelFuture handshakeFuture() {
return handshakeFuture;
}
#Override
public void handlerAdded(ChannelHandlerContext ctx) {
handshakeFuture = ctx.newPromise();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
handshaker.handshake(ctx.channel());
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("WebSocket Client disconnected!");
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel ch = ctx.channel();
if (!handshaker.isHandshakeComplete()) {
try {
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
System.out.println("WebSocket Client connected!");
handshakeFuture.setSuccess();
} catch (WebSocketHandshakeException e) {
System.out.println("WebSocket Client failed to connect");
handshakeFuture.setFailure(e);
}
return;
}
The application is able to connect to the websocket server endpoint from my local machine successfully.
But in the company datacenter where my application is deployed, I see the msg value is null and the websocket client is disconnected
Does that mean my connection is blocked at firewall? If that is the case then why did the statement "WebSocket Client connected!" is printed at all?
Thanks
The httpproxyhandler you used is correct
Just remove the BufferredReader code as mentioned below when deploying in linux, docker, etc:
Netty WebSocket Client Channel always gets inactive on Linux Server
I'm developing a realtime notification system through WebSockets by using Spring 4.
The source code is as follows:
WebSocketConfig:
#Configuration
#EnableScheduling
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/lrt").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
LRTStatusListener:
#Service
public class LRTStatusListener implements ApplicationListener<BrokerAvailabilityEvent>{
private static final Logger LOG = LoggerFactory.getLogger(LRTStatusListener.class);
private final static long LRT_ID = 1234567890;
private final static String LRT_OWNER = "Walter White";
private final LRTStatusGenerator lrtStatusGenerator = new LRTStatusGenerator(LRT_ID, LRT_OWNER);
private final MessageSendingOperations<String> messagingTemplate;
private AtomicBoolean brokerAvailable = new AtomicBoolean();
#Autowired
public LRTStatusListener(MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#Override
public void onApplicationEvent(BrokerAvailabilityEvent event) {
this.brokerAvailable.set(event.isBrokerAvailable());
}
#Scheduled(fixedDelay=2000)
public void sendLRTStatus() {
LRTStatus lrtStatus = this.lrtStatusGenerator.generateLRTStatus();
if (LOG.isTraceEnabled())
LOG.trace("Sending LRT status");
if (this.brokerAvailable.get())
this.messagingTemplate
.convertAndSend("/topic/status" + lrtStatus.getLRTId(), lrtStatus);
}
// Random status generator
private static class LRTStatusGenerator {
private LRTStatus lrtStatus;
public LRTStatusGenerator(long lrtId, String owner) {
lrtStatus = new LRTStatus(lrtId, owner, getCurrentTimestamp(), generateLRTStatusMessage());
}
public LRTStatus generateLRTStatus() {
lrtStatus.setMessage(generateLRTStatusMessage());
return lrtStatus;
}
private String getCurrentTimestamp() {
Date date = new Date();
Timestamp timestamp = new Timestamp(date.getTime());
return timestamp.toString();
}
private String generateLRTStatusMessage() {
String statusMessage;
switch ((int) Math.random() * 2) {
case 1:
statusMessage =
"HANK: What? You want me to beg? You're the smartest guy I ever met. " +
"And you're too stupid to see... he made up his mind ten minutes ago.";
break;
case 2:
statusMessage =
"WALTER: That's right. Now say my name. " +
"- DECLAN: ...You're Heisenberg. - WALTER: You're goddamn right.";
break;
default:
statusMessage =
"WALTER: I am not in danger, Skyler. I am the danger! " +
"A guy opens his door and gets shot and you think that of me? " +
"No. I am the one who knocks!";
break;
}
return statusMessage;
}
}
}
CheckLRTStatusController
#Controller
public class CheckLRTStatusController {
#MessageExceptionHandler
#SendToUser("/topic/errors")
public String handleException(Throwable exception) {
return exception.getMessage();
}
}
The application simulates the status of a long running transaction (LRT), by changing its info every 2000ms.
Now, I'm testing the WebSocket by defining a client via SockJS:
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
<script>
var sock = new SockJS('/lrt');
sock.onopen = function() {
console.log('open');
};
sock.onmessage = function(e) {
console.log('message', e.data);
};
sock.onclose = function() {
console.log('close');
};
</script>
The connection works fine, but I'm unable to see the data stream.
How can I properly configure my application in order to produce and then route on my client's console the messages sent by the WebSocket Server?
Note that I'm also using a build-in Message Broker with the aim to manage the message queue.
Is this the only JavaScript code you currently have?:
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
<script>
var sock = new SockJS('/lrt');
sock.onopen = function() {
console.log('open');
};
sock.onmessage = function(e) {
console.log('message', e.data);
};
sock.onclose = function() {
console.log('close');
};
</script>
That only sets up the connection with fallback on SockJS but you are not subscribing to the message broker. You need to do that too.
In your current setup you have:
registry.enableSimpleBroker("/queue/", "/topic/");
You need to create a JavaScript STOMP client (over SockJS) that subscribes for those, something like:
stompClient.subscribe("/topic/status*", function(message) {
...
});
stompClient.subscribe("/queue/whatever", function(message) {
...
});
Have a look at the spring-websocket-portfolio application for a complete working example.