github.com/v2fly/v2ray-core/v4@v4.45.2/proxy/http/client.go (about) 1 //go:build !confonly 2 // +build !confonly 3 4 package http 5 6 import ( 7 "bufio" 8 "context" 9 "encoding/base64" 10 "io" 11 "net/http" 12 "net/url" 13 "sync" 14 15 "golang.org/x/net/http2" 16 17 core "github.com/v2fly/v2ray-core/v4" 18 "github.com/v2fly/v2ray-core/v4/common" 19 "github.com/v2fly/v2ray-core/v4/common/buf" 20 "github.com/v2fly/v2ray-core/v4/common/bytespool" 21 "github.com/v2fly/v2ray-core/v4/common/net" 22 "github.com/v2fly/v2ray-core/v4/common/protocol" 23 "github.com/v2fly/v2ray-core/v4/common/retry" 24 "github.com/v2fly/v2ray-core/v4/common/session" 25 "github.com/v2fly/v2ray-core/v4/common/signal" 26 "github.com/v2fly/v2ray-core/v4/common/task" 27 "github.com/v2fly/v2ray-core/v4/features/policy" 28 "github.com/v2fly/v2ray-core/v4/transport" 29 "github.com/v2fly/v2ray-core/v4/transport/internet" 30 "github.com/v2fly/v2ray-core/v4/transport/internet/tls" 31 ) 32 33 type Client struct { 34 serverPicker protocol.ServerPicker 35 policyManager policy.Manager 36 } 37 38 type h2Conn struct { 39 rawConn net.Conn 40 h2Conn *http2.ClientConn 41 } 42 43 var ( 44 cachedH2Mutex sync.Mutex 45 cachedH2Conns map[net.Destination]h2Conn 46 ) 47 48 // NewClient create a new http client based on the given config. 49 func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { 50 serverList := protocol.NewServerList() 51 for _, rec := range config.Server { 52 s, err := protocol.NewServerSpecFromPB(rec) 53 if err != nil { 54 return nil, newError("failed to get server spec").Base(err) 55 } 56 serverList.AddServer(s) 57 } 58 if serverList.Size() == 0 { 59 return nil, newError("0 target server") 60 } 61 62 v := core.MustFromContext(ctx) 63 return &Client{ 64 serverPicker: protocol.NewRoundRobinServerPicker(serverList), 65 policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), 66 }, nil 67 } 68 69 // Process implements proxy.Outbound.Process. We first create a socket tunnel via HTTP CONNECT method, then redirect all inbound traffic to that tunnel. 70 func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { 71 outbound := session.OutboundFromContext(ctx) 72 if outbound == nil || !outbound.Target.IsValid() { 73 return newError("target not specified.") 74 } 75 target := outbound.Target 76 targetAddr := target.NetAddr() 77 78 if target.Network == net.Network_UDP { 79 return newError("UDP is not supported by HTTP outbound") 80 } 81 82 var user *protocol.MemoryUser 83 var conn internet.Connection 84 85 mbuf, _ := link.Reader.ReadMultiBuffer() 86 len := mbuf.Len() 87 firstPayload := bytespool.Alloc(len) 88 mbuf, _ = buf.SplitBytes(mbuf, firstPayload) 89 firstPayload = firstPayload[:len] 90 91 buf.ReleaseMulti(mbuf) 92 defer bytespool.Free(firstPayload) 93 94 if err := retry.ExponentialBackoff(5, 100).On(func() error { 95 server := c.serverPicker.PickServer() 96 dest := server.Destination() 97 user = server.PickUser() 98 99 netConn, err := setUpHTTPTunnel(ctx, dest, targetAddr, user, dialer, firstPayload) 100 if netConn != nil { 101 if _, ok := netConn.(*http2Conn); !ok { 102 if _, err := netConn.Write(firstPayload); err != nil { 103 netConn.Close() 104 return err 105 } 106 } 107 conn = internet.Connection(netConn) 108 } 109 return err 110 }); err != nil { 111 return newError("failed to find an available destination").Base(err) 112 } 113 114 defer func() { 115 if err := conn.Close(); err != nil { 116 newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) 117 } 118 }() 119 120 p := c.policyManager.ForLevel(0) 121 if user != nil { 122 p = c.policyManager.ForLevel(user.Level) 123 } 124 125 ctx, cancel := context.WithCancel(ctx) 126 timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) 127 128 requestFunc := func() error { 129 defer timer.SetTimeout(p.Timeouts.DownlinkOnly) 130 return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) 131 } 132 responseFunc := func() error { 133 defer timer.SetTimeout(p.Timeouts.UplinkOnly) 134 return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) 135 } 136 137 responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) 138 if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { 139 return newError("connection ends").Base(err) 140 } 141 142 return nil 143 } 144 145 // setUpHTTPTunnel will create a socket tunnel via HTTP CONNECT method 146 func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, user *protocol.MemoryUser, dialer internet.Dialer, firstPayload []byte) (net.Conn, error) { 147 req := &http.Request{ 148 Method: http.MethodConnect, 149 URL: &url.URL{Host: target}, 150 Header: make(http.Header), 151 Host: target, 152 } 153 154 if user != nil && user.Account != nil { 155 account := user.Account.(*Account) 156 auth := account.GetUsername() + ":" + account.GetPassword() 157 req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) 158 } 159 160 connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) { 161 req.Header.Set("Proxy-Connection", "Keep-Alive") 162 163 err := req.Write(rawConn) 164 if err != nil { 165 rawConn.Close() 166 return nil, err 167 } 168 169 resp, err := http.ReadResponse(bufio.NewReader(rawConn), req) 170 if err != nil { 171 rawConn.Close() 172 return nil, err 173 } 174 defer resp.Body.Close() 175 176 if resp.StatusCode != http.StatusOK { 177 rawConn.Close() 178 return nil, newError("Proxy responded with non 200 code: " + resp.Status) 179 } 180 return rawConn, nil 181 } 182 183 connectHTTP2 := func(rawConn net.Conn, h2clientConn *http2.ClientConn) (net.Conn, error) { 184 pr, pw := io.Pipe() 185 req.Body = pr 186 187 var pErr error 188 var wg sync.WaitGroup 189 wg.Add(1) 190 191 go func() { 192 _, pErr = pw.Write(firstPayload) 193 wg.Done() 194 }() 195 196 resp, err := h2clientConn.RoundTrip(req) // nolint: bodyclose 197 if err != nil { 198 rawConn.Close() 199 return nil, err 200 } 201 202 wg.Wait() 203 if pErr != nil { 204 rawConn.Close() 205 return nil, pErr 206 } 207 208 if resp.StatusCode != http.StatusOK { 209 rawConn.Close() 210 return nil, newError("Proxy responded with non 200 code: " + resp.Status) 211 } 212 return newHTTP2Conn(rawConn, pw, resp.Body), nil 213 } 214 215 cachedH2Mutex.Lock() 216 cachedConn, cachedConnFound := cachedH2Conns[dest] 217 cachedH2Mutex.Unlock() 218 219 if cachedConnFound { 220 rc, cc := cachedConn.rawConn, cachedConn.h2Conn 221 if cc.CanTakeNewRequest() { 222 proxyConn, err := connectHTTP2(rc, cc) 223 if err != nil { 224 return nil, err 225 } 226 227 return proxyConn, nil 228 } 229 } 230 231 rawConn, err := dialer.Dial(ctx, dest) 232 if err != nil { 233 return nil, err 234 } 235 236 iConn := rawConn 237 if statConn, ok := iConn.(*internet.StatCouterConnection); ok { 238 iConn = statConn.Connection 239 } 240 241 nextProto := "" 242 if tlsConn, ok := iConn.(*tls.Conn); ok { 243 if err := tlsConn.Handshake(); err != nil { 244 rawConn.Close() 245 return nil, err 246 } 247 nextProto = tlsConn.ConnectionState().NegotiatedProtocol 248 } 249 250 switch nextProto { 251 case "", "http/1.1": 252 return connectHTTP1(rawConn) 253 case "h2": 254 t := http2.Transport{} 255 h2clientConn, err := t.NewClientConn(rawConn) 256 if err != nil { 257 rawConn.Close() 258 return nil, err 259 } 260 261 proxyConn, err := connectHTTP2(rawConn, h2clientConn) 262 if err != nil { 263 rawConn.Close() 264 return nil, err 265 } 266 267 cachedH2Mutex.Lock() 268 if cachedH2Conns == nil { 269 cachedH2Conns = make(map[net.Destination]h2Conn) 270 } 271 272 cachedH2Conns[dest] = h2Conn{ 273 rawConn: rawConn, 274 h2Conn: h2clientConn, 275 } 276 cachedH2Mutex.Unlock() 277 278 return proxyConn, err 279 default: 280 return nil, newError("negotiated unsupported application layer protocol: " + nextProto) 281 } 282 } 283 284 func newHTTP2Conn(c net.Conn, pipedReqBody *io.PipeWriter, respBody io.ReadCloser) net.Conn { 285 return &http2Conn{Conn: c, in: pipedReqBody, out: respBody} 286 } 287 288 type http2Conn struct { 289 net.Conn 290 in *io.PipeWriter 291 out io.ReadCloser 292 } 293 294 func (h *http2Conn) Read(p []byte) (n int, err error) { 295 return h.out.Read(p) 296 } 297 298 func (h *http2Conn) Write(p []byte) (n int, err error) { 299 return h.in.Write(p) 300 } 301 302 func (h *http2Conn) Close() error { 303 h.in.Close() 304 return h.out.Close() 305 } 306 307 func init() { 308 common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { 309 return NewClient(ctx, config.(*ClientConfig)) 310 })) 311 }