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  }