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  }