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