github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/browserforwarder/forwarder.go (about)

     1  package browserforwarder
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"net/http"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/v2fly/BrowserBridge/handler"
    12  
    13  	"github.com/v2fly/v2ray-core/v5/common"
    14  	"github.com/v2fly/v2ray-core/v5/common/net"
    15  	"github.com/v2fly/v2ray-core/v5/common/platform/securedload"
    16  	"github.com/v2fly/v2ray-core/v5/features/extension"
    17  	"github.com/v2fly/v2ray-core/v5/transport/internet"
    18  )
    19  
    20  //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
    21  
    22  type Forwarder struct {
    23  	ctx context.Context
    24  
    25  	forwarder  *handler.HTTPHandle
    26  	httpserver *http.Server
    27  
    28  	config *Config
    29  }
    30  
    31  func (f *Forwarder) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    32  	requestPath := request.URL.Path[1:]
    33  
    34  	switch requestPath {
    35  	case "":
    36  		fallthrough
    37  	case "index.js":
    38  		BridgeResource(writer, request, requestPath)
    39  	case "link":
    40  		f.forwarder.ServeBridge(writer, request)
    41  	}
    42  }
    43  
    44  func (f *Forwarder) DialWebsocket(url string, header http.Header) (io.ReadWriteCloser, error) {
    45  	protocolHeader := false
    46  	protocolHeaderValue := ""
    47  	unsupportedHeader := false
    48  	for k, v := range header {
    49  		if k == "Sec-Websocket-Protocol" {
    50  			protocolHeader = true
    51  			protocolHeaderValue = v[0]
    52  		} else {
    53  			unsupportedHeader = true
    54  		}
    55  	}
    56  	if unsupportedHeader {
    57  		return nil, newError("unsupported header used, only Sec-Websocket-Protocol is supported for forwarder")
    58  	}
    59  	if !protocolHeader {
    60  		return f.forwarder.Dial(url)
    61  	}
    62  	return f.forwarder.Dial2(url, protocolHeaderValue)
    63  }
    64  
    65  func (f *Forwarder) Type() interface{} {
    66  	return extension.BrowserForwarderType()
    67  }
    68  
    69  func (f *Forwarder) Start() error {
    70  	if f.config.ListenAddr != "" {
    71  		f.forwarder = handler.NewHttpHandle()
    72  		f.httpserver = &http.Server{Handler: f}
    73  
    74  		var listener net.Listener
    75  		var err error
    76  		address := net.ParseAddress(f.config.ListenAddr)
    77  
    78  		switch {
    79  		case address.Family().IsIP():
    80  			listener, err = internet.ListenSystem(f.ctx, &net.TCPAddr{IP: address.IP(), Port: int(f.config.ListenPort)}, nil)
    81  		case strings.EqualFold(address.Domain(), "localhost"):
    82  			listener, err = internet.ListenSystem(f.ctx, &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: int(f.config.ListenPort)}, nil)
    83  		default:
    84  			return newError("forwarder cannot listen on the address: ", address)
    85  		}
    86  		if err != nil {
    87  			return newError("forwarder cannot listen on the port ", f.config.ListenPort).Base(err)
    88  		}
    89  
    90  		go func() {
    91  			if err := f.httpserver.Serve(listener); err != nil {
    92  				newError("cannot serve http forward server").Base(err).WriteToLog()
    93  			}
    94  		}()
    95  	}
    96  	return nil
    97  }
    98  
    99  func (f *Forwarder) Close() error {
   100  	if f.httpserver != nil {
   101  		return f.httpserver.Close()
   102  	}
   103  	return nil
   104  }
   105  
   106  func BridgeResource(rw http.ResponseWriter, r *http.Request, path string) {
   107  	content := path
   108  	if content == "" {
   109  		content = "index.html"
   110  	}
   111  	data, err := securedload.GetAssetSecured("browserforwarder/" + content)
   112  	if err != nil {
   113  		err = newError("cannot load necessary resources").Base(err)
   114  		http.Error(rw, err.Error(), http.StatusForbidden)
   115  		return
   116  	}
   117  
   118  	http.ServeContent(rw, r, path, time.Now(), bytes.NewReader(data))
   119  }
   120  
   121  func NewForwarder(ctx context.Context, cfg *Config) *Forwarder {
   122  	return &Forwarder{config: cfg, ctx: ctx}
   123  }
   124  
   125  func init() {
   126  	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
   127  		return NewForwarder(ctx, cfg.(*Config)), nil
   128  	}))
   129  }