{"id":1937,"date":"2015-01-15T04:49:00","date_gmt":"2015-01-15T04:49:00","guid":{"rendered":"http:\/\/41j.com\/blog\/?p=1937"},"modified":"2015-01-17T03:02:21","modified_gmt":"2015-01-17T03:02:21","slug":"golang-websocket-tcp-forwarder","status":"publish","type":"post","link":"https:\/\/41j.com\/blog\/2015\/01\/golang-websocket-tcp-forwarder\/","title":{"rendered":"golang websocket to tcp forwarder"},"content":{"rendered":"<p>I&#8217;ve been writing a simple websocket to tcp forwarder in golang for a project I&#8217;m working on.<\/p>\n<p>I previously showed a <a href=\"http:\/\/41j.com\/blog\/2014\/12\/gorilla-websockets-golang-simple-websockets-example\/\">simple echo example<\/a> using Gorilla websockets. The echoing example blocks on the sockets until data is available, however that isn&#8217;t really going to work here. At first I tried to use the SetReadDeadLine, mirroring non-blocking IO in C. While this worked, it wasn&#8217;t really an ideal solution. What I ended up with was my first use of go routines! Basically breaking the code into two threads each of which blocks on reads from the 2 sockets. The code is below for reference (it currently forwards traffic to\/from localhost, but the first 12bytes sent from the client should be the destination host port, I&#8217;ll update this post then that codes in place):<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\npackage main\r\n \r\nimport (\r\n    &quot;github.com\/gorilla\/websocket&quot;\r\n    &quot;net\/http&quot;\r\n    &quot;net&quot;\r\n    &quot;fmt&quot;\r\n    &quot;io&quot;\r\n)\r\n\r\nvar upgrader = websocket.Upgrader{\r\n    ReadBufferSize:  1024,\r\n    WriteBufferSize: 1024,\r\n}\r\n\r\n\r\n      \r\nfunc print_binary(s &#x5B;]byte) {\r\n  fmt.Printf(&quot;print b:&quot;);\r\n  for n := 0;n &lt; len(s);n++ {\r\n    fmt.Printf(&quot;%d,&quot;,s&#x5B;n]);\r\n  }\r\n  fmt.Printf(&quot;\\n&quot;);\r\n}\r\n\r\nfunc address_decode(address_bin &#x5B;]byte) (string,string) {\r\n  \r\n  var host string = &quot;127.0.0.1&quot;\r\n  var port string = &quot;22&quot;;\r\n\r\n  return host,port\r\n}\r\n \r\n\r\nfunc forwardtcp(wsconn *websocket.Conn,conn net.Conn) {\r\n\r\n  for {\r\n    \/\/ Receive and forward pending data from tcp socket to web socket\r\n    tcpbuffer := make(&#x5B;]byte, 1024)\r\n\r\n    n,err := conn.Read(tcpbuffer)\r\n    if err == io.EOF { fmt.Printf(&quot;TCP Read failed&quot;); break; }\r\n    if err == nil {\r\n      fmt.Printf(&quot;Forwarding from tcp to ws: %d bytes: %s\\n&quot;,n,tcpbuffer)\r\n      print_binary(tcpbuffer)\r\n      wsconn.WriteMessage(websocket.BinaryMessage,tcpbuffer&#x5B;:n])\r\n    }\r\n  }\r\n}\r\n\r\nfunc forwardws (wsconn *websocket.Conn,conn net.Conn) {\r\n\r\n for {\r\n    \/\/ Send pending data to tcp socket\r\n    n,buffer,err := wsconn.ReadMessage()\r\n    if err == io.EOF { fmt.Printf(&quot;WS Read Failed&quot;); break; }\r\n    if err == nil {\r\n      s := string(buffer&#x5B;:len(buffer)])\r\n      fmt.Printf(&quot;Received (from ws) forwarding to tcp: %d bytes: %s %d\\n&quot;,len(buffer),s,n)\r\n      print_binary(buffer)\r\n      conn.Write(buffer)\r\n    }\r\n  }\r\n}\r\n\r\nfunc wsProxyHandler(w http.ResponseWriter, r *http.Request) {\r\n\r\n  wsconn, err := upgrader.Upgrade(w, r, nil)\r\n\r\n  if err != nil {\r\n    \/\/log.Println(err)\r\n    return\r\n  }\r\n\r\n  \/\/ get connection address and port\r\n  address := make(&#x5B;]byte, 16)\r\n\r\n  n,address,err := wsconn.ReadMessage()\r\n  if err != nil {\r\n    fmt.Printf(&quot;address read error&quot;);\r\n    fmt.Printf(&quot;read %d bytes&quot;,n);  \r\n  }\r\n\r\n  print_binary(address)\r\n\r\n  host, port := address_decode(address)\r\n\r\n  conn, err := net.Dial(&quot;tcp&quot;, host + &quot;:&quot; + port)\r\n  if err != nil {\r\n\t\/\/ handle error\r\n  }\r\n\r\n  go forwardtcp(wsconn,conn)\r\n  go forwardws(wsconn,conn)\r\n\r\n  fmt.Printf(&quot;websocket closed&quot;);\r\n}\r\n \r\nfunc main() {\r\n  http.HandleFunc(&quot;\/echo&quot;, wsProxyHandler)\r\n  http.Handle(&quot;\/&quot;, http.FileServer(http.Dir(&quot;.&quot;)))\r\n  err := http.ListenAndServe(&quot;:8080&quot;, nil)\r\n  if err != nil {\r\n    panic(&quot;Error: &quot; + err.Error())\r\n  }\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been writing a simple websocket to tcp forwarder in golang for a project I&#8217;m working on. I previously showed a simple echo example using Gorilla websockets. The echoing example blocks on the sockets until data is available, however that isn&#8217;t really going to work here. At first I tried to use the SetReadDeadLine, mirroring [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-1937","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1RRoU-vf","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1937","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/comments?post=1937"}],"version-history":[{"count":1,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1937\/revisions"}],"predecessor-version":[{"id":1938,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1937\/revisions\/1938"}],"wp:attachment":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/media?parent=1937"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/categories?post=1937"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/tags?post=1937"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}