mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
[hysteria2] Masquerade inbound option style refactor. Mainly for supporting rewriteHost
This commit is contained in:
parent
4d1c52eeb1
commit
000f720947
@ -21,7 +21,15 @@
|
||||
],
|
||||
"ignore_client_bandwidth": false,
|
||||
"tls": {},
|
||||
"masquerade": "",
|
||||
"masquerade": {
|
||||
"type": "proxy",
|
||||
"proxy": {
|
||||
"url": "",
|
||||
"rewriteHost": true
|
||||
},
|
||||
"file": "/var/www",
|
||||
"string": "Some-Stuffs"
|
||||
},
|
||||
"brutal_debug": false
|
||||
}
|
||||
```
|
||||
@ -81,10 +89,13 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
|
||||
HTTP3 server behavior when authentication fails.
|
||||
|
||||
| Scheme | Example | Description |
|
||||
|--------------|-------------------------|--------------------|
|
||||
| `file` | `file:///var/www` | As a file server |
|
||||
| `http/https` | `http://127.0.0.1:8080` | As a reverse proxy |
|
||||
| Key | Example | Description |
|
||||
|--------------|--------------------------------|----------------------|
|
||||
| `type` | `file \| proxy \| string` | masquerade modes |
|
||||
| `file` | `/var/www` | As a file server |
|
||||
| `proxy.url` | `http://127.0.0.1:8080` | As a reverse proxy |
|
||||
| `proxy.rewriteHost` | `true \| false` | Rewrite the Host header to match the proxied website |
|
||||
| `string` | `Some-Stuffs` | as a constant string server |
|
||||
|
||||
A 404 page will be returned if empty.
|
||||
|
||||
|
@ -21,7 +21,15 @@
|
||||
],
|
||||
"ignore_client_bandwidth": false,
|
||||
"tls": {},
|
||||
"masquerade": "",
|
||||
"masquerade": {
|
||||
"type": "proxy",
|
||||
"proxy": {
|
||||
"url": "",
|
||||
"rewriteHost": true
|
||||
},
|
||||
"file": "/var/www",
|
||||
"string": "Some-Stuffs"
|
||||
},
|
||||
"brutal_debug": false
|
||||
}
|
||||
```
|
||||
@ -83,6 +91,14 @@ HTTP3 服务器认证失败时的行为。
|
||||
| `file` | `file:///var/www` | 作为文件服务器 |
|
||||
| `http/https` | `http://127.0.0.1:8080` | 作为反向代理 |
|
||||
|
||||
| Key | 示例 | 描述 |
|
||||
|--------------|--------------------------------|----------------------|
|
||||
| `type` | `file \| proxy \| string` | 模式 |
|
||||
| `file` | `/var/www` | 作为文件服务器 |
|
||||
| `proxy.url` | `http://127.0.0.1:8080` | 作为反向代理 |
|
||||
| `proxy.rewriteHost` | `true \| false` | 重写 `Host` 头以匹配被代理的网站 |
|
||||
| `string` | `Some-Stuffs` | 作为常量字符服务器 |
|
||||
|
||||
如果为空,则返回 404 页。
|
||||
|
||||
#### brutal_debug
|
||||
|
@ -1,5 +1,11 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/sagernet/sing/common/json"
|
||||
)
|
||||
|
||||
type Hysteria2InboundOptions struct {
|
||||
ListenOptions
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
@ -8,7 +14,7 @@ type Hysteria2InboundOptions struct {
|
||||
Users []Hysteria2User `json:"users,omitempty"`
|
||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||
InboundTLSOptionsContainer
|
||||
Masquerade string `json:"masquerade,omitempty"`
|
||||
Masquerade Hysteria2Masquerade `json:"masquerade,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
}
|
||||
|
||||
@ -33,3 +39,49 @@ type Hysteria2OutboundOptions struct {
|
||||
OutboundTLSOptionsContainer
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
}
|
||||
|
||||
type Hysteria2Masquerade struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
Proxy Hysteria2MasqueradeProxy `json:"proxy,omitempty"`
|
||||
String string `json:"string,omitempty"`
|
||||
}
|
||||
|
||||
type Hysteria2MasqueradeProxy struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
RewriteHost bool `json:"rewriteHost,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Hysteria2Masquerade) UnmarshalJSON(data []byte) error {
|
||||
// Attempt to unmarshal data as a string
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
masqueradeURL, err := url.Parse(str)
|
||||
if err != nil || masqueradeURL.Scheme == "" {
|
||||
m.String = str
|
||||
m.Type = "string"
|
||||
return nil
|
||||
}
|
||||
switch masqueradeURL.Scheme {
|
||||
case "file":
|
||||
m.File = masqueradeURL.Path
|
||||
m.Type = "file"
|
||||
case "http", "https":
|
||||
m.Proxy.URL = str
|
||||
m.Type = "proxy"
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// If not a string, attempt to unmarshal into the struct
|
||||
type Alias Hysteria2Masquerade
|
||||
aux := &struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(m),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -60,25 +60,37 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
}
|
||||
}
|
||||
var masqueradeHandler http.Handler
|
||||
if options.Masquerade != "" {
|
||||
masqueradeURL, err := url.Parse(options.Masquerade)
|
||||
if options.Masquerade != (option.Hysteria2Masquerade{}) {
|
||||
switch options.Masquerade.Type {
|
||||
case "file":
|
||||
masqueradeHandler = http.FileServer(http.Dir(options.Masquerade.File))
|
||||
case "proxy":
|
||||
masqueradeURL, err := url.Parse(options.Masquerade.Proxy.URL)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse masquerade URL")
|
||||
}
|
||||
switch masqueradeURL.Scheme {
|
||||
case "file":
|
||||
masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))
|
||||
case "http", "https":
|
||||
masqueradeHandler = &httputil.ReverseProxy{
|
||||
Rewrite: func(r *httputil.ProxyRequest) {
|
||||
r.SetURL(masqueradeURL)
|
||||
// SetURL rewrites the Host header,
|
||||
// but we don't want that if rewriteHost is false
|
||||
if !options.Masquerade.Proxy.RewriteHost {
|
||||
r.Out.Host = r.In.Host
|
||||
}
|
||||
},
|
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
},
|
||||
}
|
||||
case "string":
|
||||
if options.Masquerade.String != "" {
|
||||
masqueradeHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK) // Use 200 OK by default
|
||||
_, _ = w.Write([]byte(options.Masquerade.String))
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
||||
return nil, E.New("unknown masquerade type: ", options.Masquerade.Type)
|
||||
}
|
||||
}
|
||||
inbound := &Inbound{
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
@ -11,6 +12,35 @@ import (
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
func TestHysteria2InboundOptionsMasqueradeUnmarshalJSON(t *testing.T) {
|
||||
t.Run("schema-file", func(t *testing.T) {
|
||||
m := testMasqueradeUnmarshalJSON(t, []byte(`"file:///var/www"`))
|
||||
if m.Type != "file" || m.File != "/var/www" {
|
||||
t.Errorf("Unexpected values: %+v", m)
|
||||
}
|
||||
})
|
||||
t.Run("schema-https", func(t *testing.T) {
|
||||
m := testMasqueradeUnmarshalJSON(t, []byte(`"https://example.org:443"`))
|
||||
if m.Type != "proxy" || m.Proxy.URL != "https://example.org:443" || m.Proxy.RewriteHost != false {
|
||||
t.Errorf("Unexpected values: %+v", m)
|
||||
}
|
||||
})
|
||||
t.Run("schema-string", func(t *testing.T) {
|
||||
m := testMasqueradeUnmarshalJSON(t, []byte(`"Some-Stuffs"`))
|
||||
if m.Type != "string" || m.String != "Some-Stuffs" {
|
||||
t.Errorf("Unexpected values: %+v", m)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testMasqueradeUnmarshalJSON(t *testing.T, jsonData []byte) option.Hysteria2Masquerade {
|
||||
var m option.Hysteria2Masquerade
|
||||
if err := json.Unmarshal(jsonData, &m); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func TestHysteria2Self(t *testing.T) {
|
||||
t.Run("self", func(t *testing.T) {
|
||||
testHysteria2Self(t, "")
|
||||
|
Loading…
x
Reference in New Issue
Block a user