How to Complete a WebSocket Handshake? - javascript

I'm making a website with a game on it. For the game i need to send data trought sockets. Everything is working fine with loading the page but I can't get the handshaking to work.
class ServerClient {
public ServerClient() {
handshake();
}
private void handshake() {
try {
String line;
String key = "";
boolean socketReq = false;
while (true) {
line = input.readLine();
if (line.startsWith("Upgrade: websocket"))
socketReq = true;
if (line.startsWith("Sec-WebSocket-Key: "))
key = line;
if (line.isEmpty())
break;
}
if (!socketReq)
socket.close();
String encodedKey = DatatypeConverter.printBase64Binary(
MessageDigest.getInstance("SHA-1")
.digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()));
System.out.println(encodedKey);
output.println("HTTP/1.1 101 Switching Protocols");
output.println("Upgrade: websocket");
output.println("Connection: Upgrade");
output.println("Sec-WebSocket-Accept: " + encodedKey);
output.flush();
output.close(); // output = new PrintWriter(
// Socket.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The socketReq variable is there because I don't want anyone to connect to localhost:25580 straight from their browser.
My send and receive functions are in different Threads and they will be started after the handshake.
The result of new WebSocket("ws://localhost:25580") in JS is
WebSocket connection to 'ws://localhost:25580/' failed: Error during WebSocket handshake: net::ERR_CONNECTION_CLOSED
I was having
Error during WebSocket handshake: Incorrect 'Sec-WebSocket-Accept' header value
but I guess I changed something in the code.
I searched for hours trought Article 1 and Article 2 and from other sites. Just couldn't get the whole thing to work properly.
I don't get the point of the keys and why we have to encode them.
The socket is connected to the browser and I am getting the headers from it.
Host: localhost:25580
Sec-WebSocket-Key: iWLnXKrA3fLD6h6UGMIigg==
How does the handshaking work?

You are getting a net::ERR_CONNECTION_CLOSED error, because you are closing the connection with output.close().
If you want to keep the connection open, obviously don't close it during a handshake.

Done!
Found the answers from http://blog.honeybadger.io/building-a-simple-websockets-server-from-scratch-in-ruby/ and It's working perfectly!
The code:
public ClientSocket(Socket socket) {
try {
this.socket = socket;
this.input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.output = new PrintWriter(socket.getOutputStream());
handshake();
}
private void handshake() {
try {
String line;
String key = "";
while (true) {
line = input.readLine();
if (line.startsWith("Sec-WebSocket-Key: ")) {
key = line.split(" ")[1];
System.out.println("'" + key + "'");
}
if (line == null || line.isEmpty())
break;
}
output.println("HTTP/1.1 101 Switching Protocols");
output.println("Upgrade: websocket");
output.println("Connection: Upgrade");
output.println("Sec-WebSocket-Accept: " + encode(key));
output.println();
output.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
private String encode(String key) throws Exception {
key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] bytes = MessageDigest.getInstance("SHA-1").digest(key.getBytes());
return DatatypeConverter.printBase64Binary(bytes);
}
Now I just have to decode the messages.

Related

Why does netcat work just fine when using my listener but it doesnt work when I use JS

I wrote a C# listener that listens on a port and prints everything it recieves, and it works perfectly but when i used a JS client to send that data something get recieved but nothing gets written to the console
My C# code:
while (true)
{
TcpClient client = null;
NetworkStream stream = null;
try
{
client = listener.AcceptTcpClient();
stream = client.GetStream();
using (StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = false })
{
using (StreamReader reader = new StreamReader(stream, Encoding.ASCII))
{
while (true)
{
string inputLine = "";
while (inputLine != null)
{
inputLine = reader.ReadLine();
Console.WriteLine(inputLine);
Console.WriteLine(Encoding.UTF8.GetString(Encoding.ASCII.GetBytes(inputLine)));
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (stream != null)
{
stream.Close();
}
if (client != null)
{
client.Close();
}
}
Console.WriteLine("Verbinding met client verbroken");
}
const net = require('net');
const client = new net.Socket();
client.connect(1234, '192.168.2.13', () => {
console.log('Connected to server');
client.write("Hello server\r");
});
I tried running a netcat listener and that worked with my JS program, I also set breakpoints in my code and concluded that when i send something with my JS code it indeed gets recieved by my server but not processed.
Your server/listener tries to read a complete line ('reader.ReadLine()').
This means, your JS client must send some "line end" marker. "Line end" is newline '\n' (although documentation specifies apparently non-working alternatives).
So socket.write("Hello server\n") should work.

Can't send data from Java Socket Server to NodeJS client

I'm making a live connection between a Socket Server (Java) and a Socket Client (NodeJS). This is for a webinterface.
I can send data from NodeJS to Java, but not the other way around. I commented in the code, which positions I mean. I tried it already like you see with out.write("Hello World\n"); (with flush, of course). I tried also with out.println("Hello World"); (with flush, of course).
public class WebHandler {
private ServerSocket server;
private static Socket sock;
public void listen(int port) {
try {
server = new ServerSocket(port);
} catch (IOException e) {
System.out.println("Could not listen on port " + port);
System.exit(-1);
}
Bukkit.getScheduler().scheduleSyncRepeatingTask(Main.getPlugin(), new BukkitRunnable() {
#Override
public void run() {
try {
System.out.println("Waiting for connection");
final Socket socket = server.accept();
sock = socket;
final InputStream inputStream = socket.getInputStream();
final InputStreamReader streamReader = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(streamReader);
// readLine blocks until line arrives or socket closes, upon which it returns null
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
WebHandler.sendMessage();
} catch (IOException e) {
System.out.println("Accept failed: " + port);
System.exit(-1);
}
}
}, 0, 100);
}
// CRITICAL
public static void sendMessage() {
try {
PrintWriter out = new PrintWriter(sock.getOutputStream());
out.write("Hello World from Java!" + "\n");
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
var net = require('net');
var client = net.connect(9090, 'localhost');
client.setEncoding('utf8');
setInterval(function() {
console.log("Writing....")
var ret = client.write('Hello from node.js\n');
console.log("Wrote", ret)
}, 5000);
// CRITICAL
client.on('data', function(data) {
console.log('Received: ' + data);
});
Please don't let you distract because of the Bukkit.getScheduler()... It's only a Task Manager. Thanks in advance :D
You don't receive a message from java because you have set an interval which will always send messages to the server and the server will be stuck in the while loop.
I would suggest to stop the interval at some point so that sendMessage() will be called.

Error when communicating with the native messaging host from extension

I need to pass phonenumber from web page to desktop dialer. I have host, that catches phone number from page that passed from background.js:
var responseMessage = "";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(request.phonenumber);
chrome.runtime.sendNativeMessage('com.avvd.microsipcallfield', {text: request.phonenumber},function(response) {
if (chrome.runtime.lastError) {
responseMessage = chrome.runtime.lastError.message
console.log(responseMessage);
} else {
responseMessage = response;
}} )
sendResponse(responseMessage);
});
and sending it to host:
{
"name": "com.avvd.microsipcallfield",
"description": "Call Field Microsip host",
"path": "C:\\Users\\All Users\\callFieldHost\\host.bat",
"type": "stdio",
"allowed_origins": [
"chrome-extension://lbhgehbjeemkjmooaeopmfljahdoombd/"
]
}
Host.bat
java -jar HostDecoder.jar %*
in general, for the time being there is no host, there will be an application that opens an object from JSON and sends it to the dialer. However
I wrote simple recorder and this is an output:
1argument: chrome-extension://lbhgehbjeemkjmooaeopmfljahdoombd/
2argument: --parent-window=0
While i try to send number i get in console
"Error when communicating with the native messaging host." and how we can see there is no number passed to host listener.
Can somebody advice me to look for to repair this?
I resolve issue. Trouble was in absent #echo off in launcher.
Ok. There is another question. I try to read incoming messages but get stability zero size.
private String read(InputStream in) throws IOException {
byte[] length = new byte[4];
in.read(length);
int size = getInt(length);
if (size == 0) {
throw new InterruptedIOException("Zero size message");
}
byte[] message = new byte[size];
in.read(b);
return new String(message, "UTF-8");
}
private int getInt(byte[] bytes) {
return (bytes[3]<<24) & 0xff000000|
(bytes[2]<<16) & 0x00ff0000|
(bytes[1]<< 8) & 0x0000ff00|
(bytes[0]<< 0) & 0x000000ff;
}
I send with native message an object
{text: 4325345423}
What i miss in this case?

How to send message from node.js server to Android app?

Im trying to make a simple application. That is When I write a word at edittext in android app such as "Hi", Then android app send message "Hi" to node.js server and node.js server send message "Hi has sent successflly" to android app. This is just a example, actually my object is android send a data(message) to server, and receive another data(message) from server.
The problem is this. When I write a word at android app and press button, the message transmitted successfully(I can confirm by console at node.js). But I cant send message to android from node.js .. When I press send button, My android app shut down..
What android says is "java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Activity.runOnUiThread(java.lang.Runnable)' on a null object reference" ..
Yesterday, this error didn't happened and another error occured. "cannot cast string to JSONObject."
I will show you my code.
Server Side(Node.js)
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 12000;
app.get('/', function(req, res) {
res.sendFile('index.html');
})
io.on('connection', function(socket) {
console.log('Android device has been connected');
socket.on('message', function(data) {
console.log('message from Android : ' + data);
Object.keys(io.sockets.sockets);
Object.keys(io.sockets.sockets).forEach(function (id) {
console.log("ID : ", id );
io.to(id).emit('message', data);
console.log(data + ' has sent successfully');
})
/*if (data != null) {
io.emit('message', {message : data + ' has received successfully'});
}*/
})
socket.on('disconnect', function() {
console.log('Android device has been disconnected');
})
})
http.listen(port, function() {
console.log('Server Start at port number ' + port);
})
Client Side (Android)
private Emitter.Listener handleIncomingMessages = new Emitter.Listener(){
#Override
public void call(final Object... args){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String message;
try {
message = data.getString("text").toString();
Log.i("result", message);
addMessage(message);
} catch (JSONException e) {
Log.e("result", "Error : JSONException");
return;
} catch (ClassCastException e) {
Log.e("result", "Error : ClassCastException");
e.printStackTrace();
return;
}
}
});
}
};
private void sendMessage(){
String message = mInputMessageView.getText().toString().trim();
mInputMessageView.setText("");
addMessage(message);
JSONObject sendText = new JSONObject();
try{
sendText.put("text", message);
socket.emit("message", message);
}catch(JSONException e){
}
}
private void addMessage(String message) {
mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
.message(message).build());
// mAdapter = new MessageAdapter(mMessages);
mAdapter = new MessageAdapter( mMessages);
mAdapter.notifyItemInserted(0);
scrollToBottom();
}
private void scrollToBottom() {
mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
}
I already searched similar problems that other people asked, but It didn't give me solution. Please help me. Thank you for reading long question.
p.s Because Im not English speaker, Im not good at English .. There will be many problems at grammar and writing skills. Thanks for understanding...
Reason this happens is because method getActivity() returns null. This might happen if you run this on a fragment after it is detached from an activity or activity is no longer visible. I would do a normal null check before like:
Activity activity = getActivity();
if(activity != null) {
activity.runOnUiThread(new Runnable() {...}
}
I'm not familiar with socket.emit() method but it might throw network exception since it's running on UI thread and you are not allowed to do that. I recommend using RxJava/RxAndroid if you want to do this on another thread.
If you want to do network operation just use it like this:
Observable
.fromRunnable(new Runnable {
void run() {
// here do your work
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
// not really needed here
}
#Override
public void onError(Throwable e) {
// handle errors on UI thread
}
#Override
public void onNext(Void void) {
// do something on UI thread after run is done
}
});
Basically what it does it calls method call from Callable you just made on separate thread and when it's over it invokes onNext method if no exception was thrown or onError method if exception was thrown from Subscriber class.
Note that Response class isn't part of the RxJava/RxAndroid API and you can make it if you want. You can make it a simple POJO class or anything else you need it to be. If you don't need to have response you can use Runnable instead of Callable and it will work just fine.
In order for this to work you need to add this dependencies to your modules Gradle file:
dependencies {
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
}

WebSocket - Invalid status line javascript

I'm running a spring 3.1.2 backend on a weblogic 12.1.3 server.
In order to accept websocket connections, my configurator as follows:
public class SpringConfigurator extends Configurator {
private static final Logger LOGGER = LoggerFactory.make();
private static final Map<String, Map<Class<?>, String>> cache = new ConcurrentHashMap<String, Map<Class<?>, String>>();
private static final String MAGIC_STR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private static final String NO_VALUE = ObjectUtils.identityToString(new Object());
#SuppressWarnings("unchecked")
#Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
if (wac == null) {
String message = "Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?";
LOGGER.error(message);
throw new IllegalStateException(message);
}
String beanName = ClassUtils.getShortNameAsProperty(endpointClass);
if (wac.containsBean(beanName)) {
T endpoint = wac.getBean(beanName, endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using #ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
Component annot = AnnotationUtils.findAnnotation(endpointClass, Component.class);
if ((annot != null) && wac.containsBean(annot.value())) {
T endpoint = wac.getBean(annot.value(), endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using #ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
beanName = getBeanNameByType(wac, endpointClass);
if (beanName != null) {
return (T) wac.getBean(beanName);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Creating new #ServerEndpoint instance of type " + endpointClass);
}
return wac.getAutowireCapableBeanFactory().createBean(endpointClass);
}
// modifyHandshake() is called before getEndpointInstance()
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
private String getBeanNameByType(WebApplicationContext wac, Class<?> endpointClass) {
String wacId = wac.getId();
Map<Class<?>, String> beanNamesByType = cache.get(wacId);
if (beanNamesByType == null) {
beanNamesByType = new ConcurrentHashMap<Class<?>, String>();
cache.put(wacId, beanNamesByType);
}
if (!beanNamesByType.containsKey(endpointClass)) {
String[] names = wac.getBeanNamesForType(endpointClass);
if (names.length == 1) {
beanNamesByType.put(endpointClass, names[0]);
} else {
beanNamesByType.put(endpointClass, NO_VALUE);
if (names.length > 1) {
String message = "Found multiple #ServerEndpoint's of type " + endpointClass + ", names=" + names;
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
}
String beanName = beanNamesByType.get(endpointClass);
return NO_VALUE.equals(beanName) ? null : beanName;
}
}
The problem is when I try to open websocket connection via a javascript client, it correctly generates response headers as I debugged this location:
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
But in client side it gives following error:
WebSocket connection to 'ws://localhost:7001/websocket' failed: Error during >WebSocket handshake: Invalid status line
In chrome developer tools the response seems as follows:
HTTP/0.9 200 OK
I think somehow http request does not upgrade to websocket connection.
I really appreciate any help regarding this issue.
I encountered exactly this issue today when testing http://showcase.omnifaces.org/push/socket on WebLogic 12.2.1.
Already at the first test attempt of the webapp, WebLogic throws the below exception when making a websocket connection:
java.lang.IllegalStateException: The async-support is disabled on this request: weblogic.servlet.internal.ServletRequest
Impl#6682044b[GET /omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb HTTP/1.1]
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1949)
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1925)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
at weblogic.websocket.tyrus.TyrusServletFilter.doFilter(TyrusServletFilter.java:234)
...
It turns out that, on contrary to all other servers I tested, WebLogic's own TyrusServletFilter, which is responsible for handling websocket handshake requests, is internally installed after the filters provided via web.xml of the deployed webapp. The webapp shipped with a character encoding filter and a GZIP filter mapped on /*, so they were invoked before the websocket filter. This was strange at first sight, but I thought it is what it is, so I just added <async-supported>true</async-supported> to those webapp-provided filters so that the TyrusServletFilter can do its job.
However, when making a websocket connection, a JavaScript error in the client side occurred when a push message was being sent, exactly the one you faced:
WebSocket connection to 'ws://localhost:7001/omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb' failed: Error during WebSocket handshake: Invalid status line
It turns out that WebSockets just can't deal with GZIP responses. After disabling the GZIP filter, everything continued to work flawlessly.
The root problem is however that WebLogic should have installed its TyrusServletFilter before all webapp-provided filters. All other Java EE servers I ever have tested do this correctly. Your workaround of immediately dispatching and forwarding all websocket handshake requests to their target URL pattern, as mentioned in your comment on the question, is a good one. The alternative would be to reconfigure the web.xml-provided filters to not match websocket handshake requests anymore, e.g. by using a more specific URL pattern, or mapping to a specific servlet instead.

Categories