github.com/openziti/transport@v0.1.5/ws/listener.go (about) 1 /* 2 Copyright NetFoundry, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 https://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package ws 18 19 import ( 20 "crypto/tls" 21 "github.com/gorilla/mux" 22 "github.com/gorilla/websocket" 23 "github.com/michaelquigley/pfxlog" 24 "github.com/openziti/transport" 25 "github.com/pkg/errors" 26 "github.com/sirupsen/logrus" 27 "io" 28 "net/http" 29 ) 30 31 var upgrader = websocket.Upgrader{} 32 33 type wsListener struct { 34 log *logrus.Entry 35 incoming chan transport.Connection 36 cfg *WSConfig 37 } 38 39 /** 40 * Accept incoming HTTP connection, and upgrade it to a websocket suitable for comms between ziti-sdk-js and Ziti Edge Router 41 */ 42 func (listener *wsListener) handleWebsocket(w http.ResponseWriter, r *http.Request) { 43 log := listener.log 44 log.Info("entered") 45 46 c, err := upgrader.Upgrade(w, r, nil) // upgrade from HTTP to binary socket 47 48 if err != nil { 49 log.WithField("err", err).Error("websocket upgrade failed. Failure not recoverable.") 50 } else { 51 52 connection := &Connection{ 53 detail: &transport.ConnectionDetail{ 54 Address: "ws:" + c.UnderlyingConn().RemoteAddr().String(), 55 InBound: true, 56 Name: "ws", 57 }, 58 ws: c, 59 log: log, 60 rxbuf: newSafeBuffer(log), 61 txbuf: newSafeBuffer(log), 62 tlsrxbuf: newSafeBuffer(log), 63 tlstxbuf: newSafeBuffer(log), 64 done: make(chan struct{}), 65 cfg: listener.cfg, 66 incoming: listener.incoming, 67 } 68 69 err := connection.tlsHandshake() // Do not proceed until the JS client can successfully complete a TLS handshake 70 if err == nil { 71 go connection.pinger() 72 listener.incoming <- connection // pass the Websocket to the goroutine that will validate the HELLO handshake 73 } 74 } 75 } 76 77 func Listen(bindAddress string, name string, incoming chan transport.Connection, tcfg transport.Configuration) (io.Closer, error) { 78 log := pfxlog.ContextLogger(name + "/ws:" + bindAddress) 79 80 cfg := NewDefaultWSConfig() 81 if tcfg != nil { 82 if err := cfg.Load(tcfg); err != nil { 83 return nil, errors.Wrap(err, "load configuration") 84 } 85 } 86 logrus.Infof(cfg.Dump()) 87 88 go wslistener(log.Entry, bindAddress, cfg, name, incoming) 89 90 return nil, nil 91 } 92 93 /** 94 * The TCP-based listener that accepts incoming HTTP connections that we will upgrade to Websocket connections. 95 */ 96 func wslistener(log *logrus.Entry, bindAddress string, cfg *WSConfig, name string, incoming chan transport.Connection) { 97 98 log.Infof("starting HTTP (websocket) server at bindAddress [%s]", bindAddress) 99 100 listener := &wsListener{ 101 log: log, 102 incoming: incoming, 103 cfg: cfg, 104 } 105 106 // Set up the HTTP -> Websocket upgrader options (once, before we start listening) 107 upgrader.HandshakeTimeout = cfg.handshakeTimeout 108 upgrader.ReadBufferSize = cfg.readBufferSize 109 upgrader.WriteBufferSize = cfg.writeBufferSize 110 upgrader.EnableCompression = cfg.enableCompression 111 upgrader.CheckOrigin = func(r *http.Request) bool { return true } // Allow all origins 112 113 router := mux.NewRouter() 114 115 router.HandleFunc("/ws", listener.handleWebsocket).Methods("GET") 116 117 cert, _ := tls.LoadX509KeyPair(cfg.serverCert, cfg.key) 118 119 httpServer := &http.Server{ 120 Addr: bindAddress, 121 WriteTimeout: cfg.writeTimeout, 122 ReadTimeout: cfg.readTimeout, 123 IdleTimeout: cfg.idleTimeout, 124 Handler: router, 125 TLSConfig: &tls.Config{ 126 Certificates: []tls.Certificate{cert}, 127 }, 128 } 129 130 if err := httpServer.ListenAndServeTLS("", ""); err != nil { 131 panic(err) 132 } 133 }