package module
v0.2.4 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Dec 19, 2022 License: MIT Imports: 23 Imported by: 1



wsep is a high performance command execution protocol over WebSocket. It can be thought of as SSH over WebSockets without encryption.

The package offers the wsep.Execer interface so that local, SSH, and WebSocket execution can be interchanged. This is particular useful when testing.


Error handling is omitted for brevity.

conn, _, _ := websocket.Dial(ctx, "ws://remote.exec.addr", nil)
defer conn.Close(websocket.StatusNormalClosure, "normal closure")

execer := wsep.RemoteExecer(conn)
process, _ := execer.Start(ctx, wsep.Command{
  Command: "cat",
  Args:    []string{"go.mod"},
  Stdin:   false,

go io.Copy(os.Stderr, process.Stderr())
go io.Copy(os.Stdout, process.Stdout())

func (s server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  conn, _ := websocket.Accept(w, r, nil)
  defer conn.Close(websocket.StatusNormalClosure, "normal closure")

  wsep.Serve(r.Context(), conn, wsep.LocalExecer{})
Development / Testing

Start a local executor:

go run ./dev/server

Start a client:

go run ./dev/client tty --id 1 -- bash
go run ./dev/client notty -- ls -la

Local sh through a local wsep connection

$ head -c 100000000 /dev/urandom > /tmp/random; cat /tmp/random | pv | time ./bin/client notty -- sh -c "cat > /dev/null"

95.4MiB 0:00:00 [ 269MiB/s] [ <=>                                                                                  ]
./bin/client notty -- sh -c "cat > /dev/null"  0.32s user 0.31s system 31% cpu 2.019 total

Local sh directly

$ head -c 100000000 /dev/urandom > /tmp/random; cat /tmp/random | pv | time  sh -c "cat > /dev/null"

95.4MiB 0:00:00 [1.73GiB/s] [ <=>                                                                                  ]
sh -c "cat > /dev/null"  0.00s user 0.02s system 32% cpu 0.057 total




View Source
const (
	// StateStarting is the default/start state.
	StateStarting = iota
	// StateReady means the session is ready to be attached.
	// StateClosing means the session has begun closing.  The underlying process
	// may still be exiting.
	// StateDone means the session has completely shut down and the process has
	// exited.


This section is empty.


func Serve

func Serve(ctx context.Context, c *websocket.Conn, execer Execer, options *Options) error

Serve runs the server-side of wsep. Deprecated: Use Server.Serve() instead.


type Command

type Command struct {
	// ID allows reconnecting commands that have a TTY.
	ID      string
	Command string
	Args    []string
	// Commands with a TTY also require Rows and Cols.
	TTY        bool
	Rows       uint16
	Cols       uint16
	Stdin      bool
	UID        uint32
	GID        uint32
	Env        []string
	WorkingDir string

Command represents an external command to be run

type Execer

type Execer interface {
	Start(ctx context.Context, c Command) (Process, error)

Execer starts commands.

func RemoteExecer

func RemoteExecer(conn *websocket.Conn) Execer

RemoteExecer creates an execution interface from a WebSocket connection.

type ExitError

type ExitError struct {
	// contains filtered or unexported fields

ExitError is sent when the command terminates.

func (ExitError) Error

func (e ExitError) Error() string

Error returns a string describing why the process errored.

func (ExitError) ExitCode added in v0.2.3

func (e ExitError) ExitCode() int

ExitCode returns the exit code of the process.

type LocalExecer

type LocalExecer struct {
	// ChildProcessPriority overrides the default niceness of all child processes launch by LocalExecer.
	ChildProcessPriority *int

LocalExecer executes command on the local system.

func (LocalExecer) Start

func (l LocalExecer) Start(ctx context.Context, c Command) (Process, error)

Start executes the given command locally

type Options added in v0.2.0

type Options struct {
	SessionTimeout time.Duration

Options allows configuring the server.

type Process

type Process interface {
	// Pid is populated immediately during a successful start with the process ID.
	Pid() int
	// Stdout returns an io.WriteCloser that will pipe writes to the remote command.
	// Closure of stdin sends the corresponding close message.
	Stdin() io.WriteCloser
	// Stdout returns an io.Reader that is connected to the command's standard output.
	Stdout() io.Reader
	// Stderr returns an io.Reader that is connected to the command's standard error.
	Stderr() io.Reader
	// Resize resizes the TTY if a TTY is enabled.
	Resize(ctx context.Context, rows, cols uint16) error
	// Wait returns ExitError when the command terminates with a non-zero exit code.
	Wait() error
	// Close sends a SIGTERM to the process.  To force a shutdown cancel the
	// context passed into the execer.
	Close() error

Process represents a started command.

type Server added in v0.2.3

type Server struct {
	// contains filtered or unexported fields

Server runs the server-side of wsep. The execer may be another wsep connection for chaining. Use LocalExecer for local command execution.

func NewServer added in v0.2.3

func NewServer() *Server

NewServer returns as new wsep server.

func (*Server) Close added in v0.2.3

func (srv *Server) Close()

Close closes all sessions.

func (*Server) Serve added in v0.2.3

func (srv *Server) Serve(ctx context.Context, c *websocket.Conn, execer Execer, options *Options) error

Serve runs the server-side of wsep. The execer may be another wsep connection for chaining. Use LocalExecer for local command execution. The web socket will not be closed automatically; the caller must call Close() on the web socket (ideally with a reason) once Serve yields.

func (*Server) SessionCount added in v0.2.3

func (srv *Server) SessionCount() int

SessionCount returns the number of sessions.

type Session added in v0.2.3

type Session struct {
	// contains filtered or unexported fields

Session represents a `screen` session.

func NewSession added in v0.2.3

func NewSession(command *Command, execer Execer, options *Options) *Session

NewSession sets up a new session. Any errors with starting are returned on Attach(). The session will close itself if nothing is attached for the duration of the session timeout.

func (*Session) Attach added in v0.2.3

func (s *Session) Attach(ctx context.Context) (Process, error)

Attach attaches to the session, waits for the attach to complete, then returns the attached process.

func (*Session) Close added in v0.2.3

func (s *Session) Close(reason string)

Close attempts to gracefully kill the session's underlying process then waits for the process to exit. If the session does not exit in a timely manner it forcefully kills the process.

func (*Session) Wait added in v0.2.3

func (s *Session) Wait()

Wait waits for the session to close. The underlying process might still be exiting.

func (*Session) WaitForState added in v0.2.4

func (s *Session) WaitForState(state State) (State, error)

WaitForState blocks until the state or a greater one is reached.

type State added in v0.2.3

type State int

State represents the current state of the session. States are sequential and will only move forward.


Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL