1package terminal 2 3import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "net/http" 8 "net/url" 9 10 "github.com/gorilla/websocket" 11) 12 13type TerminalSettings struct { 14 // The terminal provider may require use of a particular subprotocol. If so, 15 // it must be specified here, and Workhorse must have a matching codec. 16 Subprotocols []string 17 18 // The websocket URL to connect to. 19 Url string 20 21 // Any headers (e.g., Authorization) to send with the websocket request 22 Header http.Header 23 24 // The CA roots to validate the remote endpoint with, for wss:// URLs. The 25 // system-provided CA pool will be used if this is blank. PEM-encoded data. 26 CAPem string 27 28 // The value is specified in seconds. It is converted to time.Duration 29 // later. 30 MaxSessionTime int 31} 32 33func (t *TerminalSettings) URL() (*url.URL, error) { 34 return url.Parse(t.Url) 35} 36 37func (t *TerminalSettings) Dialer() *websocket.Dialer { 38 dialer := &websocket.Dialer{ 39 Subprotocols: t.Subprotocols, 40 } 41 42 if len(t.CAPem) > 0 { 43 pool := x509.NewCertPool() 44 pool.AppendCertsFromPEM([]byte(t.CAPem)) 45 dialer.TLSClientConfig = &tls.Config{RootCAs: pool} 46 } 47 48 return dialer 49} 50 51func (t *TerminalSettings) Clone() *TerminalSettings { 52 // Doesn't clone the strings, but that's OK as strings are immutable in go 53 cloned := *t 54 cloned.Header = headerClone(t.Header) 55 return &cloned 56} 57 58func (t *TerminalSettings) Dial() (*websocket.Conn, *http.Response, error) { 59 return t.Dialer().Dial(t.Url, t.Header) 60} 61 62func (t *TerminalSettings) Validate() error { 63 if t == nil { 64 return fmt.Errorf("Terminal details not specified") 65 } 66 67 if len(t.Subprotocols) == 0 { 68 return fmt.Errorf("No subprotocol specified") 69 } 70 71 parsedURL, err := t.URL() 72 if err != nil { 73 return fmt.Errorf("Invalid URL") 74 } 75 76 if parsedURL.Scheme != "ws" && parsedURL.Scheme != "wss" { 77 return fmt.Errorf("Invalid websocket scheme: %q", parsedURL.Scheme) 78 } 79 80 return nil 81} 82 83func (t *TerminalSettings) IsEqual(other *TerminalSettings) bool { 84 if t == nil && other == nil { 85 return true 86 } 87 88 if t == nil || other == nil { 89 return false 90 } 91 92 if len(t.Subprotocols) != len(other.Subprotocols) { 93 return false 94 } 95 96 for i, subprotocol := range t.Subprotocols { 97 if other.Subprotocols[i] != subprotocol { 98 return false 99 } 100 } 101 102 if len(t.Header) != len(other.Header) { 103 return false 104 } 105 106 for header, values := range t.Header { 107 if len(values) != len(other.Header[header]) { 108 return false 109 } 110 for i, value := range values { 111 if other.Header[header][i] != value { 112 return false 113 } 114 } 115 } 116 117 return t.Url == other.Url && 118 t.CAPem == other.CAPem && 119 t.MaxSessionTime == other.MaxSessionTime 120} 121