memoメモ

最近はGo言語関連で。φ(..)メモメモ

JettyのwebsocketクライアントからGo言語のwebsocketサーバへ接続する #golang

JettyのwebsocketとGo言語のwebsocketは共にversion 13です*1(2013/03/02現在)

というわけで、Java(Jetty)からGoへ接続してみましょう!

Jettyは jetty-websocket-8.1.9.v20130131 を利用しました。

Jettyのwebsocketクライアントは WebSocketClientのドキュメント から素直に実装してみます。

が、ひとつここで注意。

CORS(Cross-Origin Resource Sharing) をクライアントへ設定しておく必要があります。 CORSについてはここ CORS(Cross-Origin Resource Sharing)って何? が参考になりました。

originを設定しないで接続を試みた場合:

Exception in thread "main" java.util.concurrent.ExecutionException: java.net.ProtocolException: Bad response status 400 Bad Request
    at org.eclipse.jetty.websocket.WebSocketClient$WebSocketFuture.get(WebSocketClient.java:569)
    at org.eclipse.jetty.websocket.WebSocketClient$WebSocketFuture.get(WebSocketClient.java:372)
    at WebsocketClientSample.main(WebsocketClientSample.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.net.ProtocolException: Bad response status 400 Bad Request
    at org.eclipse.jetty.websocket.WebSocketClientFactory$HandshakeConnection.onClose(WebSocketClientFactory.java:551)
    at org.eclipse.jetty.websocket.WebSocketClientFactory$WebSocketClientSelector.endPointClosed(WebSocketClientFactory.java:330)
    at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.destroyEndPoint(SelectorManager.java:853)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.doUpdateKey(SelectChannelEndPoint.java:607)
    at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:468)
    at org.eclipse.jetty.io.nio.SelectorManager$1.run(SelectorManager.java:290)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:722)

となってしまいます。

WebSocketClientのドキュメント をみると setOrigin() メソッドを利用すればいいことがわかりました。

以下、setOriginを施したクライアントのソースコード全文です:

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketClient;
import org.eclipse.jetty.websocket.WebSocketClientFactory;

import java.net.URI;
import java.util.concurrent.TimeUnit;

public class WebsocketClientSample {

    public static void main (String [] args) throws Exception {
        WebSocketClientFactory factory = new WebSocketClientFactory();
        factory.start();

        WebSocketClient client = factory.newWebSocketClient();
        // Configure the client
        client.setOrigin("http://192.168.56.101/");

        WebSocket.Connection connection = client.open(new URI("ws://192.168.56.101:8823/echo"), new WebSocket.OnTextMessage()
        {
            public void onOpen(Connection connection)
            {
                // open notification
            }

            public void onClose(int closeCode, String message)
            {
                // close notification
            }

            public void onMessage(String data)
            {
                // handle incoming message
                System.out.println(data);
            }
        }).get(5, TimeUnit.SECONDS);

        connection.sendMessage("Hello World");
    }
}

websocket echoサーバ(echo.go)は:

package main

import (
    "code.google.com/p/go.net/websocket"
    "io"
    "net/http"
)

func EchoServer(ws *websocket.Conn) {
    io.Copy(ws, ws)
}

func main() {
    http.Handle("/echo", websocket.Handler(EchoServer))
    err := http.ListenAndServe(":8823", nil)
    if err != nil {
        panic("ListenAndServe: " + err.Error())
    }
}

ただ応答するだけのプログラムです。

これを go run echo.go で走らせ、Javaのクライアントから接続すると無事に "Hello World" がJavaクライアントに表示されました。

逆に、Goのwebsocketクライアントから Jettyのwebsocketサーバへアクセスすることもできます。

ポイントは、CORSを考えてoriginの設定をちゃんとするということですね。私はここにハマってしまいました。。

*1:たぶん ここ を見るかぎりはそう。