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の設定をちゃんとするということですね。私はここにハマってしまいました。。