main
Raw Download raw file
  1package transport
  2
  3import (
  4	"context"
  5	"fmt"
  6	"net/http"
  7	"net/url"
  8
  9	"github.com/coder/websocket"
 10)
 11
 12// WebSocketTransport implements Transport over WebSocket connections.
 13// This is used for noVNC-style VNC connections where each RFB message
 14// is sent as a discrete WebSocket binary message.
 15type WebSocketTransport struct {
 16	conn *websocket.Conn
 17}
 18
 19// WebSocketDialer implements Dialer for WebSocket connections.
 20type WebSocketDialer struct{}
 21
 22// NewWebSocketDialer creates a new WebSocket dialer.
 23func NewWebSocketDialer() *WebSocketDialer {
 24	return &WebSocketDialer{}
 25}
 26
 27// Dial establishes a WebSocket connection to the target URL.
 28func (d *WebSocketDialer) Dial(ctx context.Context, target string, opts *DialOptions) (Transport, error) {
 29	wsURL, err := url.Parse(target)
 30	if err != nil {
 31		return nil, fmt.Errorf("parsing url=%q: %w", target, err)
 32	}
 33
 34	dialOpts := &websocket.DialOptions{}
 35
 36	if opts != nil {
 37		// Build HTTP headers
 38		if opts.Headers != nil {
 39			dialOpts.HTTPHeader = opts.Headers.Clone()
 40		} else {
 41			dialOpts.HTTPHeader = make(http.Header)
 42		}
 43
 44		// Add User-Agent if specified
 45		if opts.UserAgent != "" {
 46			dialOpts.HTTPHeader.Set("User-Agent", opts.UserAgent)
 47		}
 48
 49		// Add subprotocols if specified
 50		if len(opts.Subprotocols) > 0 {
 51			dialOpts.Subprotocols = opts.Subprotocols
 52		}
 53
 54		// TODO: Support proxy via custom HTTP client
 55		// TODO: Support custom TLS config
 56	}
 57
 58	// Apply timeout to context if specified
 59	if opts != nil && opts.Timeout > 0 {
 60		var cancel context.CancelFunc
 61		ctx, cancel = context.WithTimeout(ctx, opts.Timeout)
 62		defer cancel()
 63	}
 64
 65	conn, _, err := websocket.Dial(ctx, wsURL.String(), dialOpts)
 66	if err != nil {
 67		return nil, fmt.Errorf("connecting to url=%q: %w", wsURL.String(), err)
 68	}
 69
 70	return &WebSocketTransport{conn: conn}, nil
 71}
 72
 73// Read reads a single message from the WebSocket connection.
 74func (t *WebSocketTransport) Read(ctx context.Context) ([]byte, error) {
 75	_, data, err := t.conn.Read(ctx)
 76	if err != nil {
 77		return nil, err
 78	}
 79	return data, nil
 80}
 81
 82// Write writes a single message to the WebSocket connection.
 83func (t *WebSocketTransport) Write(ctx context.Context, data []byte) error {
 84	return t.conn.Write(ctx, websocket.MessageBinary, data)
 85}
 86
 87// Close closes the WebSocket connection.
 88func (t *WebSocketTransport) Close() error {
 89	return t.conn.CloseNow()
 90}
 91
 92// SetReadLimit sets the maximum message size for reads.
 93func (t *WebSocketTransport) SetReadLimit(limit int64) {
 94	t.conn.SetReadLimit(limit)
 95}
 96
 97// Conn returns the underlying WebSocket connection.
 98// This is useful for advanced operations not exposed by the Transport interface.
 99func (t *WebSocketTransport) Conn() *websocket.Conn {
100	return t.conn
101}
102
103// Compile-time interface checks
104var (
105	_ Transport = (*WebSocketTransport)(nil)
106	_ Dialer    = (*WebSocketDialer)(nil)
107)