1
mirror of https://github.com/XTLS/Xray-core.git synced 2025-12-12 12:42:26 +04:00
Files
xray-core/main/confloader/external/external.go

134 lines
3.0 KiB
Go
Raw Normal View History

2020-11-25 19:01:53 +08:00
package external
import (
"bytes"
"context"
"net"
2020-11-25 19:01:53 +08:00
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/main/confloader"
2020-11-25 19:01:53 +08:00
)
func ConfigLoader(arg string) (out io.Reader, err error) {
var data []byte
switch {
case strings.HasPrefix(arg, "http+unix://"):
data, err = FetchUnixSocketHTTPContent(arg)
2020-11-25 19:01:53 +08:00
case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"):
data, err = FetchHTTPContent(arg)
case arg == "stdin:":
data, err = io.ReadAll(os.Stdin)
2020-11-25 19:01:53 +08:00
default:
data, err = os.ReadFile(arg)
2020-11-25 19:01:53 +08:00
}
if err != nil {
return
}
out = bytes.NewBuffer(data)
return
}
func FetchHTTPContent(target string) ([]byte, error) {
parsedTarget, err := url.Parse(target)
if err != nil {
return nil, errors.New("invalid URL: ", target).Base(err)
2020-11-25 19:01:53 +08:00
}
if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
return nil, errors.New("invalid scheme: ", parsedTarget.Scheme)
2020-11-25 19:01:53 +08:00
}
client := &http.Client{
Timeout: 30 * time.Second,
}
resp, err := client.Do(&http.Request{
Method: "GET",
URL: parsedTarget,
Close: true,
})
if err != nil {
return nil, errors.New("failed to dial to ", target).Base(err)
2020-11-25 19:01:53 +08:00
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode)
2020-11-25 19:01:53 +08:00
}
content, err := buf.ReadAllToBytes(resp.Body)
if err != nil {
return nil, errors.New("failed to read HTTP response").Base(err)
2020-11-25 19:01:53 +08:00
}
return content, nil
}
// Format: http+unix:///path/to/socket.sock/api/endpoint
func FetchUnixSocketHTTPContent(target string) ([]byte, error) {
path := strings.TrimPrefix(target, "http+unix://")
if !strings.HasPrefix(path, "/") {
return nil, errors.New("unix socket path must be absolute")
}
var socketPath, httpPath string
sockIdx := strings.Index(path, ".sock")
if sockIdx != -1 {
socketPath = path[:sockIdx+5]
httpPath = path[sockIdx+5:]
if httpPath == "" {
httpPath = "/"
}
} else {
return nil, errors.New("cannot determine socket path, socket file should have .sock extension")
}
if _, err := os.Stat(socketPath); err != nil {
return nil, errors.New("socket file not found: ", socketPath).Base(err)
}
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", socketPath)
},
},
}
defer client.CloseIdleConnections()
resp, err := client.Get("http://localhost" + httpPath)
if err != nil {
return nil, errors.New("failed to fetch from unix socket: ", socketPath).Base(err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode)
}
content, err := buf.ReadAllToBytes(resp.Body)
if err != nil {
return nil, errors.New("failed to read response").Base(err)
}
return content, nil
}
2020-11-25 19:01:53 +08:00
func init() {
confloader.EffectiveConfigFileLoader = ConfigLoader
}