github.com/openziti/transport@v0.1.5/wss/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 wss 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 wssListener struct { 34 log *logrus.Entry 35 incoming chan transport.Connection 36 cfg *WSSConfig 37 } 38 39 /** 40 * Accept incoming HTTP connection, and upgrade it to a websocket suitable for comms between browZer and Ziti Edge Router 41 */ 42 func (listener *wssListener) 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: "wss:" + c.UnderlyingConn().RemoteAddr().String(), 55 InBound: true, 56 Name: "wss", 57 }, 58 ws: c, 59 log: log, 60 rxbuf: newSafeBuffer(log), 61 txbuf: newSafeBuffer(log), 62 done: make(chan struct{}), 63 cfg: listener.cfg, 64 } 65 66 go connection.pinger() 67 68 listener.incoming <- connection // pass the Websocket to the goroutine that will validate the HELLO handshake 69 } 70 } 71 72 func Listen(bindAddress string, name string, incoming chan transport.Connection, tcfg transport.Configuration) (io.Closer, error) { 73 log := pfxlog.ContextLogger(name + "/wss:" + bindAddress) 74 75 cfg := NewDefaultWSSConfig() 76 if tcfg != nil { 77 if err := cfg.Load(tcfg); err != nil { 78 return nil, errors.Wrap(err, "load configuration") 79 } 80 } 81 logrus.Infof(cfg.Dump()) 82 83 go wsslistener(log.Entry, bindAddress, cfg, name, incoming) 84 85 return nil, nil 86 } 87 88 /** 89 * The TLS-based listener that accepts incoming HTTP connections that we will upgrade to Websocket connections 90 */ 91 func wsslistener(log *logrus.Entry, bindAddress string, cfg *WSSConfig, name string, incoming chan transport.Connection) { 92 93 log.Infof("starting HTTP (websocket) server at bindAddress [%s]", bindAddress) 94 95 listener := &wssListener{ 96 log: log, 97 incoming: incoming, 98 cfg: cfg, 99 } 100 101 // Set up the HTTP -> Websocket upgrader options (once, before we start listening) 102 upgrader.HandshakeTimeout = cfg.handshakeTimeout 103 upgrader.ReadBufferSize = cfg.readBufferSize 104 upgrader.WriteBufferSize = cfg.writeBufferSize 105 upgrader.EnableCompression = cfg.enableCompression 106 upgrader.CheckOrigin = func(r *http.Request) bool { return true } // Allow all origins 107 108 router := mux.NewRouter() 109 110 router.HandleFunc("/wss", listener.handleWebsocket).Methods("GET") 111 112 httpServer := &http.Server{ 113 Addr: bindAddress, 114 WriteTimeout: cfg.writeTimeout, 115 ReadTimeout: cfg.readTimeout, 116 IdleTimeout: cfg.idleTimeout, 117 Handler: router, 118 TLSConfig: &tls.Config{ 119 ClientAuth: tls.RequestClientCert, 120 }, 121 } 122 123 if err := httpServer.ListenAndServeTLS(cfg.serverCert, cfg.key); err != nil { 124 panic(err) 125 } 126 }