Feature: add websocket headers support in vmess

This commit is contained in:
Dreamacro 2018-12-11 00:25:05 +08:00
parent 34c8655974
commit 1607d3253f
5 changed files with 118 additions and 34 deletions

View File

@ -5,7 +5,7 @@
<br> <br>
</h1> </h1>
<h4 align="center">A rule based tunnel in Go.</h4> <h4 align="center">A rule-based tunnel in Go.</h4>
<p align="center"> <p align="center">
<a href="https://travis-ci.org/Dreamacro/clash"> <a href="https://travis-ci.org/Dreamacro/clash">
@ -126,8 +126,8 @@ Proxy:
- { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, tls: true } - { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, tls: true }
# with tls and skip-cert-verify # with tls and skip-cert-verify
- { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, tls: true, skip-cert-verify: true } - { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, tls: true, skip-cert-verify: true }
# with ws # with ws-path and ws-headers
- { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, network: ws, ws-path: /path } - { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, network: ws, ws-path: /path, ws-headers: { Host: v2ray.com } }
# with ws + tls # with ws + tls
- { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, network: ws, ws-path: /path, tls: true } - { name: "vmess", type: vmess, server: server, port: 443, uuid: uuid, alterId: 32, cipher: auto, network: ws, ws-path: /path, tls: true }

View File

@ -41,6 +41,7 @@ type VmessOption struct {
TLS bool `proxy:"tls,omitempty"` TLS bool `proxy:"tls,omitempty"`
Network string `proxy:"network,omitempty"` Network string `proxy:"network,omitempty"`
WSPath string `proxy:"ws-path,omitempty"` WSPath string `proxy:"ws-path,omitempty"`
WSHeaders map[string]string `proxy:"ws-headers,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
} }
@ -79,6 +80,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
Port: strconv.Itoa(option.Port), Port: strconv.Itoa(option.Port),
NetWork: option.Network, NetWork: option.Network,
WebSocketPath: option.WSPath, WebSocketPath: option.WSPath,
WebSocketHeaders: option.WSHeaders,
SkipCertVerify: option.SkipCertVerify, SkipCertVerify: option.SkipCertVerify,
SessionCacahe: getClientSessionCache(), SessionCacahe: getClientSessionCache(),
}) })

View File

@ -1,5 +1,7 @@
package structure package structure
// references: https://github.com/mitchellh/mapstructure
import ( import (
"fmt" "fmt"
"reflect" "reflect"
@ -70,6 +72,8 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error
return d.decodeBool(name, data, val) return d.decodeBool(name, data, val)
case reflect.Slice: case reflect.Slice:
return d.decodeSlice(name, data, val) return d.decodeSlice(name, data, val)
case reflect.Map:
return d.decodeMap(name, data, val)
default: default:
return fmt.Errorf("type %s not support", val.Kind().String()) return fmt.Errorf("type %s not support", val.Kind().String())
} }
@ -158,3 +162,70 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
val.Set(valSlice) val.Set(valSlice)
return nil return nil
} }
func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error {
valType := val.Type()
valKeyType := valType.Key()
valElemType := valType.Elem()
valMap := val
if valMap.IsNil() {
mapType := reflect.MapOf(valKeyType, valElemType)
valMap = reflect.MakeMap(mapType)
}
dataVal := reflect.Indirect(reflect.ValueOf(data))
if dataVal.Kind() != reflect.Map {
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
}
return d.decodeMapFromMap(name, dataVal, val, valMap)
}
func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
valType := val.Type()
valKeyType := valType.Key()
valElemType := valType.Elem()
errors := make([]string, 0)
if dataVal.Len() == 0 {
if dataVal.IsNil() {
if !val.IsNil() {
val.Set(dataVal)
}
} else {
val.Set(valMap)
}
return nil
}
for _, k := range dataVal.MapKeys() {
fieldName := fmt.Sprintf("%s[%s]", name, k)
currentKey := reflect.Indirect(reflect.New(valKeyType))
if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {
errors = append(errors, err.Error())
continue
}
v := dataVal.MapIndex(k).Interface()
currentVal := reflect.Indirect(reflect.New(valElemType))
if err := d.decode(fieldName, v, currentVal); err != nil {
errors = append(errors, err.Error())
continue
}
valMap.SetMapIndex(currentKey, currentVal)
}
val.Set(valMap)
if len(errors) > 0 {
return fmt.Errorf(strings.Join(errors, ","))
}
return nil
}

View File

@ -83,6 +83,7 @@ type Config struct {
Port string Port string
NetWork string NetWork string
WebSocketPath string WebSocketPath string
WebSocketHeaders map[string]string
SkipCertVerify bool SkipCertVerify bool
SessionCacahe tls.ClientSessionCache SessionCacahe tls.ClientSessionCache
} }
@ -149,6 +150,7 @@ func NewClient(config Config) (*Client, error) {
wsConfig = &websocketConfig{ wsConfig = &websocketConfig{
host: host, host: host,
path: config.WebSocketPath, path: config.WebSocketPath,
headers: config.WebSocketHeaders,
tls: config.TLS, tls: config.TLS,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
} }

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"net/http"
"net/url" "net/url"
"strings" "strings"
"time" "time"
@ -21,6 +22,7 @@ type websocketConn struct {
type websocketConfig struct { type websocketConfig struct {
host string host string
path string path string
headers map[string]string
tls bool tls bool
tlsConfig *tls.Config tlsConfig *tls.Config
} }
@ -127,7 +129,14 @@ func newWebsocketConn(conn net.Conn, c *websocketConfig) (net.Conn, error) {
Path: c.path, Path: c.path,
} }
wsConn, resp, err := dialer.Dial(uri.String(), nil) headers := http.Header{}
if c.headers != nil {
for k, v := range c.headers {
headers.Set(k, v)
}
}
wsConn, resp, err := dialer.Dial(uri.String(), headers)
if err != nil { if err != nil {
var reason string var reason string
if resp != nil { if resp != nil {