Skip to content

Commit bef2997

Browse files
committed
feat: add wasm support
1 parent 548b274 commit bef2997

12 files changed

Lines changed: 632 additions & 62 deletions

.golangci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,6 @@ linters:
9595
- contextcheck
9696
- tparallel
9797
- dupword
98-
98+
exclusions:
99+
paths:
100+
- webdemo

client_config.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -472,67 +472,6 @@ func (c *Client) GetHandshakeTimeout() time.Duration {
472472

473473
// GetWsDialer builds the WebSocket dialer options for SOCKS over WS.
474474
//
475-
// GetWsDialer creates coder/websocket.DialOptions configured from WebSocketConfig,
476-
// GetDialer, and GetTLSConfig. Returns nil if WebSocketURL is empty.
477-
//
478-
// # Configuration
479-
//
480-
// The dialer is configured with:
481-
// - HTTPClient: Uses custom Dialer via Transport for TCP connections
482-
// - Subprotocols: From WebSocketConfig
483-
// - CompressionMode: From WebSocketConfig.EnableCompression
484-
// - HTTPHeader: From WebSocketConfig.RequestHeader
485-
//
486-
// # Returns
487-
//
488-
// *websocket.DialOptions for WebSocket connections, or nil if WebSocketURL is empty.
489-
//
490-
// # See Also
491-
//
492-
// - WebSocketConfig: WebSocket configuration options
493-
// - github.com/coder/websocket: Underlying WebSocket library
494-
func (c *Client) GetWsDialer() *websocket.DialOptions {
495-
if c.WebSocketURL == "" {
496-
return nil
497-
}
498-
499-
httpClient := &http.Client{
500-
Jar: c.WebSocketConfig.jar(),
501-
}
502-
tlsConfig := c.GetTLSConfig()
503-
504-
if tlsConfig != nil || c.Dialer != nil {
505-
transport := &http.Transport{}
506-
if c.Dialer != nil {
507-
transport.DialContext = c.Dialer
508-
}
509-
if tlsConfig != nil {
510-
transport.TLSClientConfig = tlsConfig
511-
}
512-
httpClient.Transport = transport
513-
}
514-
515-
// Configure compression
516-
compressionMode := websocket.CompressionDisabled
517-
var httpHeader http.Header
518-
var subprotocols []string
519-
520-
if c.WebSocketConfig != nil {
521-
if c.WebSocketConfig.enableCompression() {
522-
compressionMode = websocket.CompressionContextTakeover
523-
}
524-
httpHeader = c.WebSocketConfig.RequestHeader
525-
subprotocols = c.WebSocketConfig.subprotocols()
526-
}
527-
528-
return &websocket.DialOptions{
529-
HTTPClient: httpClient,
530-
HTTPHeader: httpHeader,
531-
Subprotocols: subprotocols,
532-
CompressionMode: compressionMode,
533-
}
534-
}
535-
536475
// connectWebSocket establishes a WebSocket connection to the proxy.
537476
//
538477
// connectWebSocket dials the WebSocket URL and wraps the connection

client_config_js.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//go:build js
2+
3+
package socksgo
4+
5+
import (
6+
"github.com/coder/websocket"
7+
)
8+
9+
// GetWsDialer creates coder/websocket.DialOptions configured from WebSocketConfig,
10+
// GetDialer, and GetTLSConfig. Returns nil if WebSocketURL is empty.
11+
//
12+
// # Configuration
13+
//
14+
// The dialer is configured with:
15+
// - Subprotocols: From WebSocketConfig
16+
//
17+
// Note: HTTPClient, HTTPHeader, and CompressionMode are not supported in WASM builds
18+
// due to browser WebSocket API limitations.
19+
//
20+
// # Returns
21+
//
22+
// *websocket.DialOptions for WebSocket connections, or nil if WebSocketURL is empty.
23+
//
24+
// # See Also
25+
//
26+
// - WebSocketConfig: WebSocket configuration options
27+
// - github.com/coder/websocket: Underlying WebSocket library
28+
func (c *Client) GetWsDialer() *websocket.DialOptions {
29+
if c.WebSocketURL == "" {
30+
return nil
31+
}
32+
33+
var subprotocols []string
34+
if c.WebSocketConfig != nil {
35+
subprotocols = c.WebSocketConfig.subprotocols()
36+
}
37+
38+
return &websocket.DialOptions{
39+
Subprotocols: subprotocols,
40+
}
41+
}

client_config_notjs.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//go:build !js
2+
3+
package socksgo
4+
5+
import (
6+
"net/http"
7+
8+
"github.com/coder/websocket"
9+
)
10+
11+
// GetWsDialer creates coder/websocket.DialOptions configured from WebSocketConfig,
12+
// GetDialer, and GetTLSConfig. Returns nil if WebSocketURL is empty.
13+
//
14+
// # Configuration
15+
//
16+
// The dialer is configured with:
17+
// - HTTPClient: Uses custom Dialer via Transport for TCP connections
18+
// - Subprotocols: From WebSocketConfig
19+
// - CompressionMode: From WebSocketConfig.EnableCompression
20+
// - HTTPHeader: From WebSocketConfig.RequestHeader
21+
//
22+
// # Returns
23+
//
24+
// *websocket.DialOptions for WebSocket connections, or nil if WebSocketURL is empty.
25+
//
26+
// # See Also
27+
//
28+
// - WebSocketConfig: WebSocket configuration options
29+
// - github.com/coder/websocket: Underlying WebSocket library
30+
func (c *Client) GetWsDialer() *websocket.DialOptions {
31+
if c.WebSocketURL == "" {
32+
return nil
33+
}
34+
35+
httpClient := &http.Client{
36+
Jar: c.WebSocketConfig.jar(),
37+
}
38+
tlsConfig := c.GetTLSConfig()
39+
40+
if tlsConfig != nil || c.Dialer != nil {
41+
transport := &http.Transport{}
42+
if c.Dialer != nil {
43+
transport.DialContext = c.Dialer
44+
}
45+
if tlsConfig != nil {
46+
transport.TLSClientConfig = tlsConfig
47+
}
48+
httpClient.Transport = transport
49+
}
50+
51+
// Configure compression
52+
compressionMode := websocket.CompressionDisabled
53+
var httpHeader http.Header
54+
var subprotocols []string
55+
56+
if c.WebSocketConfig != nil {
57+
if c.WebSocketConfig.enableCompression() {
58+
compressionMode = websocket.CompressionContextTakeover
59+
}
60+
httpHeader = c.WebSocketConfig.RequestHeader
61+
subprotocols = c.WebSocketConfig.subprotocols()
62+
}
63+
64+
return &websocket.DialOptions{
65+
HTTPClient: httpClient,
66+
HTTPHeader: httpHeader,
67+
Subprotocols: subprotocols,
68+
CompressionMode: compressionMode,
69+
}
70+
}

flake.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
gopls
6363
pkgsite
6464

65+
#nodejs # for wasm builds testing
66+
python3 # for local dev http server
67+
just
68+
6569
gost
6670
curl
6771
tor

scripts/filter_coverage.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ trap 'rm -f "$TEMP_FILE"' EXIT
3434
grep -v "client_testhooks.go" "$INPUT_FILE" | \
3535
grep -v "client5_testhooks.go" | \
3636
grep -v "_internal_test.go" | \
37+
grep -v "webdemo/" | \
3738
grep -v "examples/" > "$TEMP_FILE"
3839

3940
mv "$TEMP_FILE" "$OUTPUT_FILE"

webdemo/.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Build artifacts
2+
main.wasm
3+
wasm_exec.js
4+
5+
# Editor files
6+
.vscode/
7+
.idea/
8+
*.swp
9+
*.swo
10+
*~
11+
12+
# OS files
13+
.DS_Store
14+
Thumbs.db

webdemo/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# SOCKS WebSocket WASM Demo
2+
3+
A WebAssembly demo that demonstrates using socksgo library to make HTTP requests through a SOCKS proxy over WebSocket connections.
4+
5+
## Features
6+
7+
- Runs entirely in browser using WebAssembly
8+
- Connects to SOCKS proxy servers over WebSocket (socks5+ws:// or socks5+wss://)
9+
- Makes HTTP requests through the proxy
10+
- Uses [coder/websocket](https://github.com/coder/websocket) library for WebSocket support
11+
12+
## Prerequisites
13+
14+
1. Go 1.25.5 or later
15+
2. [gost](https://github.com/ginuerzh/gost) proxy tool (for SOCKS-over-WebSocket server)
16+
3. Python 3 (for HTTP server)
17+
4. [Just](https://just.systems) (optional, for build automation)
18+
19+
## Quick Start
20+
21+
### With Just (Recommended)
22+
23+
```bash
24+
# Terminal 1: Start gost SOCKS-over-WebSocket server
25+
just start-proxy
26+
27+
# Terminal 2: Build and serve the demo
28+
just serve
29+
30+
# Open your browser to
31+
# http://localhost:8000
32+
```
33+
34+
### Without Just (Manual)
35+
36+
```bash
37+
# Terminal 1: Start gost SOCKS-over-WebSocket server
38+
gost -L socks5+ws://localhost:1080
39+
40+
# Terminal 2: Build WASM module
41+
GOOS=js GOARCH=wasm go build -o main.wasm main.go
42+
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" .
43+
44+
# Terminal 2 (continued): Start HTTP server
45+
python3 -m http.server 8000
46+
47+
# Open your browser to
48+
# http://localhost:8000
49+
```
50+
51+
## Browser Compatibility
52+
53+
This demo requires a browser that supports:
54+
- WebAssembly (all modern browsers)
55+
- WebSocket API (all modern browsers)
56+
- ES6+ JavaScript (all modern browsers)
57+
58+
## How It Works
59+
60+
1. **WASM Module**: The `main.go` file compiles to WebAssembly and contains:
61+
- socksgo client configured for WebSocket transport
62+
- HTTP client that routes requests through the SOCKS proxy
63+
- JavaScript bridge functions for DOM interaction
64+
65+
2. **WebSocket Connection**: The client connects to the gost SOCKS5-over-WebSocket server
66+
67+
3. **HTTP Request**: HTTP requests are made through the SOCKS connection, appearing to originate from the gost server
68+
69+
## References
70+
71+
- [socksgo Documentation](../README.md)
72+
- [coder/websocket](https://github.com/coder/websocket)
73+
- [gost Proxy Tool](https://github.com/go-gost/gost)
74+
- [Go WebAssembly](https://go.dev/wiki/WebAssembly)
75+
- [SOCKS Protocol](https://en.wikipedia.org/wiki/SOCKS)

0 commit comments

Comments
 (0)