mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-23 02:26:49 -05:00
130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package browserforwarder
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/v2fly/BrowserBridge/handler"
|
|
|
|
"github.com/v2fly/v2ray-core/v5/common"
|
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
|
"github.com/v2fly/v2ray-core/v5/common/platform/securedload"
|
|
"github.com/v2fly/v2ray-core/v5/features/extension"
|
|
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
|
)
|
|
|
|
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
|
|
|
|
type Forwarder struct {
|
|
ctx context.Context
|
|
|
|
forwarder *handler.HTTPHandle
|
|
httpserver *http.Server
|
|
|
|
config *Config
|
|
}
|
|
|
|
func (f *Forwarder) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
|
requestPath := request.URL.Path[1:]
|
|
|
|
switch requestPath {
|
|
case "":
|
|
fallthrough
|
|
case "index.js":
|
|
BridgeResource(writer, request, requestPath)
|
|
case "link":
|
|
f.forwarder.ServeBridge(writer, request)
|
|
}
|
|
}
|
|
|
|
func (f *Forwarder) DialWebsocket(url string, header http.Header) (io.ReadWriteCloser, error) {
|
|
protocolHeader := false
|
|
protocolHeaderValue := ""
|
|
unsupportedHeader := false
|
|
for k, v := range header {
|
|
if k == "Sec-Websocket-Protocol" {
|
|
protocolHeader = true
|
|
protocolHeaderValue = v[0]
|
|
} else {
|
|
unsupportedHeader = true
|
|
}
|
|
}
|
|
if unsupportedHeader {
|
|
return nil, newError("unsupported header used, only Sec-Websocket-Protocol is supported for forwarder")
|
|
}
|
|
if !protocolHeader {
|
|
return f.forwarder.Dial(url)
|
|
}
|
|
return f.forwarder.Dial2(url, protocolHeaderValue)
|
|
}
|
|
|
|
func (f *Forwarder) Type() interface{} {
|
|
return extension.BrowserForwarderType()
|
|
}
|
|
|
|
func (f *Forwarder) Start() error {
|
|
if f.config.ListenAddr != "" {
|
|
f.forwarder = handler.NewHttpHandle()
|
|
f.httpserver = &http.Server{Handler: f}
|
|
|
|
var listener net.Listener
|
|
var err error
|
|
address := net.ParseAddress(f.config.ListenAddr)
|
|
|
|
switch {
|
|
case address.Family().IsIP():
|
|
listener, err = internet.ListenSystem(f.ctx, &net.TCPAddr{IP: address.IP(), Port: int(f.config.ListenPort)}, nil)
|
|
case strings.EqualFold(address.Domain(), "localhost"):
|
|
listener, err = internet.ListenSystem(f.ctx, &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: int(f.config.ListenPort)}, nil)
|
|
default:
|
|
return newError("forwarder cannot listen on the address: ", address)
|
|
}
|
|
if err != nil {
|
|
return newError("forwarder cannot listen on the port ", f.config.ListenPort).Base(err)
|
|
}
|
|
|
|
go func() {
|
|
if err := f.httpserver.Serve(listener); err != nil {
|
|
newError("cannot serve http forward server").Base(err).WriteToLog()
|
|
}
|
|
}()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Forwarder) Close() error {
|
|
if f.httpserver != nil {
|
|
return f.httpserver.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BridgeResource(rw http.ResponseWriter, r *http.Request, path string) {
|
|
content := path
|
|
if content == "" {
|
|
content = "index.html"
|
|
}
|
|
data, err := securedload.GetAssetSecured("browserforwarder/" + content)
|
|
if err != nil {
|
|
err = newError("cannot load necessary resources").Base(err)
|
|
http.Error(rw, err.Error(), http.StatusForbidden)
|
|
return
|
|
}
|
|
|
|
http.ServeContent(rw, r, path, time.Now(), bytes.NewReader(data))
|
|
}
|
|
|
|
func NewForwarder(ctx context.Context, cfg *Config) *Forwarder {
|
|
return &Forwarder{config: cfg, ctx: ctx}
|
|
}
|
|
|
|
func init() {
|
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
|
return NewForwarder(ctx, cfg.(*Config)), nil
|
|
}))
|
|
}
|