- Revised comments in `config.example.yaml` to enhance understanding of Mihomo integration, including environment variable usage and Docker Compose setup. - Updated `docker-compose.yml` comments to clarify the relationship between the gateway and Mihomo service. - Enhanced `GATEWAY_RUN.md` to provide clearer instructions on configuring Mihomo parameters and their usage in the gateway.
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
// NewMihomoForward proxies /api/{alias}/mihomo/... to Mihomo external-controller (REST + WebSocket).
|
|
// Используется Rewrite (Go 1.20+): очищается RequestURI и задаётся абсолютный URL — иначе строгий upstream
|
|
// и WebSocket upgrade могут отвечать 400 (см. аналогично Telemt в forward.go).
|
|
func NewMihomoForward(
|
|
target *url.URL,
|
|
stripPrefix string,
|
|
auth string,
|
|
rt http.RoundTripper,
|
|
errHandler func(http.ResponseWriter, *http.Request, error),
|
|
) http.Handler {
|
|
if errHandler == nil {
|
|
errHandler = defaultForwardErrorHandler
|
|
}
|
|
if rt == nil {
|
|
rt = http.DefaultTransport
|
|
}
|
|
rp := &httputil.ReverseProxy{
|
|
Rewrite: func(pr *httputil.ProxyRequest) {
|
|
NormalizeRequestURLPath(pr.In)
|
|
p := pr.In.URL.Path
|
|
if !strings.HasPrefix(p, stripPrefix) {
|
|
return
|
|
}
|
|
rest := strings.TrimPrefix(strings.TrimPrefix(p, stripPrefix), "/")
|
|
dest := JoinPathPrefix(target, "/", rest)
|
|
du := *dest
|
|
du.RawQuery = pr.In.URL.RawQuery
|
|
out := pr.Out
|
|
out.URL = &du
|
|
out.Host = du.Host
|
|
out.RequestURI = ""
|
|
out.Proto = "HTTP/1.1"
|
|
out.ProtoMajor = 1
|
|
out.ProtoMinor = 1
|
|
out.Header.Del("Authorization")
|
|
if auth != "" {
|
|
out.Header.Set("Authorization", auth)
|
|
}
|
|
},
|
|
Transport: rt,
|
|
FlushInterval: -1,
|
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
|
errHandler(w, r, err)
|
|
},
|
|
}
|
|
return rp
|
|
}
|
|
|
|
// MihomoMetaJSON returns a JSON body for GET .../mihomo/meta (display URL without credentials).
|
|
func MihomoMetaJSON(target *url.URL) []byte {
|
|
display := strings.TrimSuffix(target.String(), "/")
|
|
b, _ := json.Marshal(map[string]any{
|
|
"ok": true,
|
|
"controller_base": display,
|
|
})
|
|
return b
|
|
}
|
|
|
|
// MihomoJSONError writes a JSON error for Mihomo routes when proxy is not configured.
|
|
func MihomoJSONError(w http.ResponseWriter, code, msg string) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(http.StatusNotFound)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"ok": false,
|
|
"error": map[string]string{"code": code, "message": msg},
|
|
})
|
|
}
|