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