@@ -19,6 +19,9 @@ type Sudoku struct {
1919 option * SudokuOption
2020 baseConf sudoku.ProtocolConfig
2121
22+ httpMaskMu sync.Mutex
23+ httpMaskClient * sudoku.HTTPMaskTunnelClient
24+
2225 muxMu sync.Mutex
2326 muxClient * sudoku.MultiplexClient
2427 muxBackoffUntil time.Time
@@ -40,7 +43,7 @@ type SudokuOption struct {
4043 HTTPMaskMode string `proxy:"http-mask-mode,omitempty"` // "legacy" (default), "stream", "poll", "auto"
4144 HTTPMaskTLS bool `proxy:"http-mask-tls,omitempty"` // only for http-mask-mode stream/poll/auto
4245 HTTPMaskHost string `proxy:"http-mask-host,omitempty"` // optional Host/SNI override (domain or domain:port)
43- HTTPMaskMultiplex string `proxy:"http-mask-multiplex,omitempty"` // "off" (default), "auto", "on"
46+ HTTPMaskMultiplex string `proxy:"http-mask-multiplex,omitempty"` // "off" (default), "auto" (reuse h1/h2) , "on" (single tunnel, multi-target)
4447 CustomTable string `proxy:"custom-table,omitempty"` // optional custom byte layout, e.g. xpxvvpvv
4548 CustomTables []string `proxy:"custom-tables,omitempty"` // optional table rotation patterns, overrides custom-table when non-empty
4649}
@@ -53,18 +56,12 @@ func (s *Sudoku) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Con
5356 }
5457
5558 muxMode := normalizeHTTPMaskMultiplex (cfg .HTTPMaskMultiplex )
56- if ! cfg .DisableHTTPMask && muxMode != "off" {
57- shouldTry := muxMode == "on" || (muxMode == "auto" && httpTunnelModeEnabled (cfg .HTTPMaskMode ))
58- if shouldTry {
59- stream , muxErr := s .dialMultiplex (ctx , cfg .TargetAddress , muxMode )
60- if muxErr == nil {
61- return NewConn (stream , s ), nil
62- }
63- if muxMode != "auto" {
64- return nil , muxErr
65- }
66- s .noteMuxFailure (muxMode , muxErr )
59+ if muxMode == "on" && ! cfg .DisableHTTPMask && httpTunnelModeEnabled (cfg .HTTPMaskMode ) {
60+ stream , muxErr := s .dialMultiplex (ctx , cfg .TargetAddress , muxMode )
61+ if muxErr == nil {
62+ return NewConn (stream , s ), nil
6763 }
64+ return nil , muxErr
6865 }
6966
7067 c , err := s .dialAndHandshake (ctx , cfg )
@@ -229,6 +226,7 @@ func NewSudoku(option SudokuOption) (*Sudoku, error) {
229226
230227func (s * Sudoku ) Close () error {
231228 s .resetMuxClient ()
229+ s .resetHTTPMaskClient ()
232230 return s .Base .Close ()
233231}
234232
@@ -261,7 +259,17 @@ func (s *Sudoku) dialAndHandshake(ctx context.Context, cfg *sudoku.ProtocolConfi
261259
262260 var c net.Conn
263261 if ! cfg .DisableHTTPMask && httpTunnelModeEnabled (cfg .HTTPMaskMode ) {
264- c , err = sudoku .DialHTTPMaskTunnel (ctx , cfg .ServerAddress , cfg , s .dialer .DialContext )
262+ muxMode := normalizeHTTPMaskMultiplex (cfg .HTTPMaskMultiplex )
263+ switch muxMode {
264+ case "auto" , "on" :
265+ client , errX := s .getOrCreateHTTPMaskClient (cfg )
266+ if errX != nil {
267+ return nil , errX
268+ }
269+ c , err = client .Dial (ctx )
270+ default :
271+ c , err = sudoku .DialHTTPMaskTunnel (ctx , cfg .ServerAddress , cfg , s .dialer .DialContext )
272+ }
265273 }
266274 if c == nil && err == nil {
267275 c , err = s .dialer .DialContext (ctx , "tcp" , s .addr )
@@ -380,3 +388,35 @@ func (s *Sudoku) resetMuxClient() {
380388 s .muxClient = nil
381389 }
382390}
391+
392+ func (s * Sudoku ) getOrCreateHTTPMaskClient (cfg * sudoku.ProtocolConfig ) (* sudoku.HTTPMaskTunnelClient , error ) {
393+ if s == nil {
394+ return nil , fmt .Errorf ("nil adapter" )
395+ }
396+ if cfg == nil {
397+ return nil , fmt .Errorf ("config is required" )
398+ }
399+
400+ s .httpMaskMu .Lock ()
401+ defer s .httpMaskMu .Unlock ()
402+
403+ if s .httpMaskClient != nil {
404+ return s .httpMaskClient , nil
405+ }
406+
407+ c , err := sudoku .NewHTTPMaskTunnelClient (cfg .ServerAddress , cfg , s .dialer .DialContext )
408+ if err != nil {
409+ return nil , err
410+ }
411+ s .httpMaskClient = c
412+ return c , nil
413+ }
414+
415+ func (s * Sudoku ) resetHTTPMaskClient () {
416+ s .httpMaskMu .Lock ()
417+ defer s .httpMaskMu .Unlock ()
418+ if s .httpMaskClient != nil {
419+ s .httpMaskClient .CloseIdleConnections ()
420+ s .httpMaskClient = nil
421+ }
422+ }
0 commit comments