This document describes the client application's command structure and dispatching mechanism located in cmd/client The client uses a multicall runner architecture that routes execution to protocol-specific handlers based on command-line arguments. This architecture enables a single binary to support multiple transport protocols (TCP, QUIC-go, net-QUIC) through a unified command interface.
For details on the individual protocol implementations, see QUIC Client Implementations and TCP Client. For server-side protocol handling, see Protocol Upgraders.
The client application employs the btwiuse/multicall library to implement a multicall pattern where a single binary can execute different functions based on the command name or first argument. This design allows the client to support multiple protocols without requiring separate binaries.
The command registry maps protocol names to their respective handler functions:
| Command Name | Handler Function | Protocol |
|---|---|---|
tcp | RunTcp | TCP transport over webteleport |
quic-go | RunQuicGo | QUIC using quic-go library |
net-quic | RunNetQuic | QUIC using golang.org/x/net/quic |
The registry is implemented as a multicall.RunnerFuncMap:
cmdRun = map[string]multicall.RunnerFunc{
"tcp": RunTcp,
"quic-go": RunQuicGo,
"net-quic": RunNetQuic,
}
Sources: cmd/client/main.go18-22
The following diagram illustrates how the client dispatches commands from the command line to protocol-specific handlers:
Sources: cmd/client/main.go24-38
The main() function serves as the entry point and performs minimal initialization before delegating to the dispatch mechanism:
Sources: cmd/client/main.go24-38
This diagram maps the command structure to concrete code entities in the codebase:
Sources: cmd/client/main.go1-39 cmd/go.mod8
The client provides a utility function for safe argument access with fallback:
The arg0 helper function safely retrieves the first argument from a slice with a fallback value if the slice is empty. This prevents index out-of-bounds errors during argument parsing.
While this function is defined in the codebase, it is not currently used in the main execution path. The multicall.RunnerFuncMap.Run() method handles argument parsing and command selection internally.
Sources: cmd/client/main.go11-16
The client implements a fail-fast error handling strategy:
| Error Condition | Handling Mechanism | Exit Behavior |
|---|---|---|
| Unknown command | multicall returns error | log.Fatalln(err) terminates with non-zero exit code |
| Handler execution failure | Propagated through Run() | log.Fatalln(err) terminates with non-zero exit code |
| Successful execution | nil error returned | Normal exit (code 0) |
The logging configuration includes:
log.Ldate - Date (YYYY/MM/DD format)log.Ltime - Time (HH:MM:SS format)log.Lshortfile - File name and line numberThis configuration ensures that any errors are logged with sufficient context for debugging.
Sources: cmd/client/main.go25-29
The client command structure relies on the following key dependencies:
| Dependency | Version | Purpose |
|---|---|---|
github.com/btwiuse/multicall | v0.0.5 | Multicall runner framework |
github.com/quic-go/quic-go | v0.56.0 | QUIC protocol implementation (quic-go handler) |
github.com/webteleport/webteleport | v0.5.40-alpha.9 | Transport library for TCP and net-quic handlers |
golang.org/x/net | v0.46.0 | Standard library extensions for net-quic |
The cmd/go.mod file uses a local replace directive for the relay library:
replace github.com/webteleport/relay => ..
This enables co-development of the client and relay server components.
Sources: cmd/go.mod1-13
The client command structure employs several design patterns:
The multicall pattern allows a single binary to serve multiple purposes based on invocation context. This is similar to BusyBox in Unix systems. Benefits include:
Each protocol handler (RunTcp, RunQuicGo, RunNetQuic) implements the multicall.RunnerFunc interface, allowing the dispatcher to treat them uniformly while each provides protocol-specific behavior.
The Run() function accepts arguments as a parameter rather than reading os.Args directly, enabling testability and flexibility in invocation.
Sources: cmd/client/main.go18-38
The client can be invoked in multiple ways depending on the desired transport protocol:
The dispatcher extracts the first argument (tcp, quic-go, or net-quic) to select the appropriate handler, then passes remaining arguments to that handler for protocol-specific processing.
Sources: cmd/client/main.go32-33
Refresh this wiki