1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-12-21 09:36:34 -05:00

Support http outbound

This commit is contained in:
wuxiaolong 2019-07-24 09:15:05 +08:00
parent 0f2db9d7f7
commit 97764114ea
6 changed files with 303 additions and 30 deletions

View File

@ -1,7 +1,11 @@
package conf
import (
"encoding/json"
"github.com/golang/protobuf/proto"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/http"
)
@ -10,6 +14,13 @@ type HttpAccount struct {
Password string `json:"pass"`
}
func (v *HttpAccount) Build() *http.Account {
return &http.Account{
Username: v.Username,
Password: v.Password,
}
}
type HttpServerConfig struct {
Timeout uint32 `json:"timeout"`
Accounts []*HttpAccount `json:"accounts"`
@ -33,3 +44,37 @@ func (c *HttpServerConfig) Build() (proto.Message, error) {
return config, nil
}
type HttpRemoteConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Users []json.RawMessage `json:"users"`
}
type HttpClientConfig struct {
Servers []*HttpRemoteConfig `json:"servers"`
}
func (v *HttpClientConfig) Build() (proto.Message, error) {
config := new(http.ClientConfig)
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
for idx, serverConfig := range v.Servers {
server := &protocol.ServerEndpoint{
Address: serverConfig.Address.Build(),
Port: uint32(serverConfig.Port),
}
for _, rawUser := range serverConfig.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, newError("failed to parse HTTP user").Base(err).AtError()
}
account := new(HttpAccount)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, newError("failed to parse HTTP account").Base(err).AtError()
}
user.Account = serial.ToTypedMessage(account.Build())
server.User = append(server.User, user)
}
config.Server[idx] = server
}
return config, nil
}

View File

@ -24,6 +24,7 @@ var (
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"blackhole": func() interface{} { return new(BlackholeConfig) },
"freedom": func() interface{} { return new(FreedomConfig) },
"http": func() interface{} { return new(HttpClientConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
"socks": func() interface{} { return new(SocksClientConfig) },

View File

@ -1,6 +1,147 @@
package http
/*
import (
"context"
"encoding/base64"
"io"
"strings"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry"
"v2ray.com/core/common/session"
"v2ray.com/core/common/signal"
"v2ray.com/core/common/task"
"v2ray.com/core/features/policy"
"v2ray.com/core/transport"
"v2ray.com/core/transport/internet"
)
type Client struct {
serverPicker protocol.ServerPicker
policyManager policy.Manager
}
// NewClient create a new http client based on the given config.
func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
serverList := protocol.NewServerList()
for _, rec := range config.Server {
s, err := protocol.NewServerSpecFromPB(*rec)
if err != nil {
return nil, newError("failed to get server spec").Base(err)
}
serverList.AddServer(s)
}
if serverList.Size() == 0 {
return nil, newError("0 target server")
}
v := core.MustFromContext(ctx)
return &Client{
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
}, nil
}
// Process implements proxy.Outbound.Process.
// 使用connect方法连接http代理服务器获得一个隧道然后通过该隧道通信
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
outbound := session.OutboundFromContext(ctx)
if outbound == nil || !outbound.Target.IsValid() {
return newError("target not specified.")
}
destination := outbound.Target
if destination.Network == net.Network_UDP {
return newError("UDP is not supported by HTTP outbound")
}
var server *protocol.ServerSpec
var conn internet.Connection
if err := retry.ExponentialBackoff(5, 100).On(func() error {
server = c.serverPicker.PickServer()
dest := server.Destination()
rawConn, err := dialer.Dial(ctx, dest)
if err != nil {
return err
}
conn = rawConn
return nil
}); err != nil {
return newError("failed to find an available destination").Base(err)
}
defer func() {
if err := conn.Close(); err != nil {
newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
}()
p := c.policyManager.ForLevel(0)
user := server.PickUser()
if user != nil {
p = c.policyManager.ForLevel(user.Level)
}
if err := setUpHttpTunnel(conn, conn, &destination, user); err != nil {
return err
}
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
requestFunc := func() error {
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
}
responseFunc := func() error {
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
}
var responseDonePost = task.OnSuccess(responseFunc, task.Close(link.Writer))
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
return newError("connection ends").Base(err)
}
return nil
}
// 使用http connect方法建立一个隧道
func setUpHttpTunnel(reader io.Reader, writer io.Writer, destination *net.Destination, user *protocol.MemoryUser) error {
var headers []string
destNetAddr := destination.NetAddr()
headers = append(headers, "CONNECT "+destNetAddr+" HTTP/1.1")
headers = append(headers, "Host: "+destNetAddr)
if user != nil && user.Account != nil {
account := user.Account.(*Account)
auth := account.GetUsername() + ":" + account.GetPassword()
headers = append(headers, "Proxy-Authorization: Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
}
headers = append(headers, "Proxy-Connection: Keep-Alive")
b := buf.New()
b.WriteString(strings.Join(headers, "\r\n") + "\r\n\r\n")
if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil {
return err
}
b.Clear()
if _, err := b.ReadFrom(reader); err != nil {
return err
}
return nil
}
func init() {
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewClient(ctx, config.(*ClientConfig))
}))
}
*/

View File

@ -1,5 +1,20 @@
package http
import (
"v2ray.com/core/common/protocol"
)
func (a *Account) Equals(another protocol.Account) bool {
if account, ok := another.(*Account); ok {
return a.Username == account.Username
}
return false
}
func (a *Account) AsAccount() (protocol.Account, error) {
return a, nil
}
func (sc *ServerConfig) HasAccount(username, password string) bool {
if sc.Accounts == nil {
return false

View File

@ -4,6 +4,7 @@ import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
protocol "v2ray.com/core/common/protocol"
)
// Reference imports to suppress errors if they are not otherwise used.
@ -17,6 +18,53 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Account struct {
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Account) Reset() { *m = Account{} }
func (m *Account) String() string { return proto.CompactTextString(m) }
func (*Account) ProtoMessage() {}
func (*Account) Descriptor() ([]byte, []int) {
return fileDescriptor_e66c3db3a635d8e4, []int{0}
}
func (m *Account) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Account.Unmarshal(m, b)
}
func (m *Account) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Account.Marshal(b, m, deterministic)
}
func (m *Account) XXX_Merge(src proto.Message) {
xxx_messageInfo_Account.Merge(m, src)
}
func (m *Account) XXX_Size() int {
return xxx_messageInfo_Account.Size(m)
}
func (m *Account) XXX_DiscardUnknown() {
xxx_messageInfo_Account.DiscardUnknown(m)
}
var xxx_messageInfo_Account proto.InternalMessageInfo
func (m *Account) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *Account) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
// Config for HTTP proxy server.
type ServerConfig struct {
Timeout uint32 `protobuf:"varint,1,opt,name=timeout,proto3" json:"timeout,omitempty"` // Deprecated: Do not use.
@ -32,7 +80,7 @@ func (m *ServerConfig) Reset() { *m = ServerConfig{} }
func (m *ServerConfig) String() string { return proto.CompactTextString(m) }
func (*ServerConfig) ProtoMessage() {}
func (*ServerConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_e66c3db3a635d8e4, []int{0}
return fileDescriptor_e66c3db3a635d8e4, []int{1}
}
func (m *ServerConfig) XXX_Unmarshal(b []byte) error {
@ -82,18 +130,20 @@ func (m *ServerConfig) GetUserLevel() uint32 {
return 0
}
// ClientConfig for HTTP proxy client.
// ClientConfig is the protobuf config for HTTP proxy client.
type ClientConfig struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
// Sever is a list of HTTP server addresses.
Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ClientConfig) Reset() { *m = ClientConfig{} }
func (m *ClientConfig) String() string { return proto.CompactTextString(m) }
func (*ClientConfig) ProtoMessage() {}
func (*ClientConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_e66c3db3a635d8e4, []int{1}
return fileDescriptor_e66c3db3a635d8e4, []int{2}
}
func (m *ClientConfig) XXX_Unmarshal(b []byte) error {
@ -114,7 +164,15 @@ func (m *ClientConfig) XXX_DiscardUnknown() {
var xxx_messageInfo_ClientConfig proto.InternalMessageInfo
func (m *ClientConfig) GetServer() []*protocol.ServerEndpoint {
if m != nil {
return m.Server
}
return nil
}
func init() {
proto.RegisterType((*Account)(nil), "v2ray.core.proxy.http.Account")
proto.RegisterType((*ServerConfig)(nil), "v2ray.core.proxy.http.ServerConfig")
proto.RegisterMapType((map[string]string)(nil), "v2ray.core.proxy.http.ServerConfig.AccountsEntry")
proto.RegisterType((*ClientConfig)(nil), "v2ray.core.proxy.http.ClientConfig")
@ -125,24 +183,29 @@ func init() {
}
var fileDescriptor_e66c3db3a635d8e4 = []byte{
// 296 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xcf, 0x4a, 0x33, 0x31,
0x14, 0xc5, 0x99, 0x69, 0xbf, 0xcf, 0xf6, 0xda, 0x4a, 0x0d, 0x16, 0x46, 0x51, 0x28, 0x5d, 0x48,
0x41, 0xc8, 0x60, 0xdd, 0x88, 0x5d, 0xd9, 0x22, 0xb8, 0x50, 0x28, 0x51, 0x5c, 0xb8, 0x29, 0x31,
0x5c, 0xb5, 0x98, 0x26, 0x43, 0xe6, 0xce, 0xe8, 0xec, 0x7d, 0x1a, 0x9f, 0x52, 0x92, 0x5a, 0xff,
0x40, 0x57, 0x49, 0x7e, 0xe7, 0xe4, 0xe4, 0x9e, 0xc0, 0x61, 0x39, 0x74, 0xb2, 0xe2, 0xca, 0x2e,
0x52, 0x65, 0x1d, 0xa6, 0x99, 0xb3, 0x6f, 0x55, 0xfa, 0x4c, 0x94, 0xa5, 0xca, 0x9a, 0xc7, 0xf9,
0x13, 0xcf, 0x9c, 0x25, 0xcb, 0xba, 0x2b, 0x9f, 0x43, 0x1e, 0x3c, 0xdc, 0x7b, 0xfa, 0xef, 0x31,
0xb4, 0x6e, 0xd0, 0x95, 0xe8, 0x26, 0xc1, 0xcd, 0xf6, 0x61, 0x83, 0xe6, 0x0b, 0xb4, 0x05, 0x25,
0x51, 0x2f, 0x1a, 0xb4, 0xc7, 0x71, 0x12, 0x89, 0x15, 0x62, 0xd7, 0xd0, 0x90, 0x4a, 0xd9, 0xc2,
0x50, 0x9e, 0xc4, 0xbd, 0xda, 0x60, 0x73, 0x78, 0xcc, 0xd7, 0x06, 0xf3, 0xdf, 0xa1, 0xfc, 0xfc,
0xeb, 0xce, 0x85, 0x21, 0x57, 0x89, 0xef, 0x08, 0x76, 0x04, 0xdb, 0x52, 0x6b, 0xfb, 0x3a, 0x23,
0x27, 0x4d, 0x9e, 0x49, 0x87, 0x86, 0x92, 0x5a, 0x2f, 0x1a, 0x34, 0x44, 0x27, 0x08, 0xb7, 0x3f,
0x9c, 0x1d, 0x00, 0x14, 0x39, 0xba, 0x99, 0xc6, 0x12, 0x75, 0x52, 0xf7, 0xc3, 0x89, 0xa6, 0x27,
0x57, 0x1e, 0xec, 0x8d, 0xa0, 0xfd, 0xe7, 0x19, 0xd6, 0x81, 0xda, 0x0b, 0x56, 0xa1, 0x45, 0x53,
0xf8, 0x2d, 0xdb, 0x81, 0x7f, 0xa5, 0xd4, 0x05, 0x26, 0x71, 0x60, 0xcb, 0xc3, 0x59, 0x7c, 0x1a,
0xf5, 0xb7, 0xa0, 0x35, 0xd1, 0x73, 0x34, 0xb4, 0x1c, 0x78, 0x3c, 0x82, 0x5d, 0x65, 0x17, 0xeb,
0xab, 0x4d, 0xa3, 0xfb, 0xba, 0x5f, 0x3f, 0xe2, 0xee, 0xdd, 0x50, 0xc8, 0x8a, 0x4f, 0xbc, 0x3e,
0x0d, 0xfa, 0x25, 0x51, 0xf6, 0xf0, 0x3f, 0xfc, 0xf8, 0xc9, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff,
0x69, 0x94, 0x9f, 0xa7, 0x9b, 0x01, 0x00, 0x00,
// 375 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x51, 0x4d, 0x6b, 0xe3, 0x30,
0x10, 0xc5, 0x4e, 0x36, 0x1f, 0xda, 0x04, 0xb2, 0x62, 0x03, 0xde, 0xb0, 0x0b, 0x21, 0x87, 0x25,
0xb4, 0x20, 0xb7, 0xe9, 0xa5, 0x34, 0xa7, 0x24, 0x04, 0x7a, 0x68, 0x21, 0xa8, 0xa5, 0x87, 0x5e,
0x82, 0xaa, 0xa8, 0xad, 0xa9, 0x2d, 0x09, 0x49, 0x76, 0xea, 0x7b, 0x7f, 0x4d, 0x7f, 0x65, 0x91,
0x6c, 0xa7, 0x69, 0xc9, 0xc9, 0x9e, 0xf7, 0x66, 0x9e, 0xe6, 0xbd, 0x01, 0xff, 0xb3, 0x89, 0x22,
0x39, 0xa2, 0x22, 0x09, 0xa9, 0x50, 0x2c, 0x94, 0x4a, 0xbc, 0xe6, 0xe1, 0xb3, 0x31, 0x32, 0xa4,
0x82, 0x3f, 0x46, 0x4f, 0x48, 0x2a, 0x61, 0x04, 0xec, 0x57, 0x7d, 0x8a, 0x21, 0xd7, 0x83, 0x6c,
0xcf, 0xe0, 0xe4, 0xdb, 0x38, 0x15, 0x49, 0x22, 0x78, 0xe8, 0x66, 0xa8, 0x88, 0x43, 0xcd, 0x54,
0xc6, 0xd4, 0x5a, 0x4b, 0x46, 0x0b, 0xa1, 0xd1, 0x0c, 0x34, 0x67, 0x94, 0x8a, 0x94, 0x1b, 0x38,
0x00, 0xad, 0x54, 0x33, 0xc5, 0x49, 0xc2, 0x02, 0x6f, 0xe8, 0x8d, 0xdb, 0x78, 0x57, 0x5b, 0x4e,
0x12, 0xad, 0xb7, 0x42, 0x6d, 0x02, 0xbf, 0xe0, 0xaa, 0x7a, 0xf4, 0xe6, 0x83, 0xce, 0x8d, 0x13,
0x5e, 0xb8, 0x15, 0xe1, 0x5f, 0xd0, 0x34, 0x51, 0xc2, 0x44, 0x6a, 0x9c, 0x4e, 0x77, 0xee, 0x07,
0x1e, 0xae, 0x20, 0x78, 0x0d, 0x5a, 0xa4, 0x78, 0x51, 0x07, 0xfe, 0xb0, 0x36, 0xfe, 0x39, 0x39,
0x45, 0x07, 0xdd, 0xa0, 0x7d, 0x51, 0x54, 0x6e, 0xa9, 0x97, 0xdc, 0xa8, 0x1c, 0xef, 0x24, 0xe0,
0x31, 0xf8, 0x45, 0xe2, 0x58, 0x6c, 0xd7, 0x46, 0x11, 0xae, 0x25, 0x51, 0x8c, 0x9b, 0xa0, 0x36,
0xf4, 0xc6, 0x2d, 0xdc, 0x73, 0xc4, 0xed, 0x27, 0x0e, 0xff, 0x01, 0x60, 0x2d, 0xad, 0x63, 0x96,
0xb1, 0x38, 0xa8, 0xdb, 0xe5, 0x70, 0xdb, 0x22, 0x57, 0x16, 0x18, 0x4c, 0x41, 0xf7, 0xcb, 0x33,
0xb0, 0x07, 0x6a, 0x2f, 0x2c, 0x2f, 0xd3, 0xb0, 0xbf, 0xf0, 0x37, 0xf8, 0x91, 0x91, 0x38, 0x65,
0x65, 0x0a, 0x45, 0x71, 0xe1, 0x9f, 0x7b, 0x23, 0x0c, 0x3a, 0x8b, 0x38, 0x62, 0xdc, 0x94, 0x29,
0xcc, 0x41, 0xa3, 0x88, 0x3b, 0xf0, 0x9c, 0xcb, 0xa3, 0x7d, 0x97, 0xc5, 0x61, 0x50, 0x75, 0x98,
0xd2, 0xea, 0x92, 0x6f, 0xa4, 0x88, 0xb8, 0xc1, 0xe5, 0xe4, 0x7c, 0x0a, 0xfe, 0x50, 0x91, 0x1c,
0x8e, 0x67, 0xe5, 0xdd, 0xd7, 0xed, 0xf7, 0xdd, 0xef, 0xdf, 0x4d, 0x30, 0xc9, 0xd1, 0xc2, 0xf2,
0x2b, 0xc7, 0x5f, 0x1a, 0x23, 0x1f, 0x1a, 0x4e, 0xfd, 0xec, 0x23, 0x00, 0x00, 0xff, 0xff, 0xac,
0x7a, 0x67, 0x04, 0x54, 0x02, 0x00, 0x00,
}

View File

@ -6,6 +6,13 @@ option go_package = "http";
option java_package = "com.v2ray.core.proxy.http";
option java_multiple_files = true;
import "v2ray.com/core/common/protocol/server_spec.proto";
message Account {
string username = 1;
string password = 2;
}
// Config for HTTP proxy server.
message ServerConfig {
uint32 timeout = 1 [deprecated = true];
@ -14,7 +21,8 @@ message ServerConfig {
uint32 user_level = 4;
}
// ClientConfig for HTTP proxy client.
// ClientConfig is the protobuf config for HTTP proxy client.
message ClientConfig {
// Sever is a list of HTTP server addresses.
repeated v2ray.core.common.protocol.ServerEndpoint server = 1;
}