func (v *VMessOutboundHandler) handleRequest(session *encoding.ClientSession, conn internet.Connection, request *protocol.RequestHeader, payload *buf.Buffer, input buf.Reader, finish *sync.Mutex) { defer finish.Unlock() writer := bufio.NewWriter(conn) defer writer.Release() session.EncodeRequestHeader(request, writer) bodyWriter := session.EncodeRequestBody(request, writer) defer bodyWriter.Release() if !payload.IsEmpty() { if err := bodyWriter.Write(payload); err != nil { log.Info("VMess|Outbound: Failed to write payload. Disabling connection reuse.", err) conn.SetReusable(false) } payload.Release() } writer.SetCached(false) if err := buf.PipeUntilEOF(input, bodyWriter); err != nil { conn.SetReusable(false) } if request.Option.Has(protocol.RequestOptionChunkStream) { err := bodyWriter.Write(buf.NewLocal(8)) if err != nil { conn.SetReusable(false) } } return }
func (v *Stream) Write(data *buf.Buffer) (err error) { if data.IsEmpty() { return } select { case <-v.ctx.Done(): return io.ErrClosedPipe case <-v.err: return io.ErrClosedPipe case <-v.close: return io.ErrClosedPipe default: select { case <-v.ctx.Done(): return io.ErrClosedPipe case <-v.err: return io.ErrClosedPipe case <-v.close: return io.ErrClosedPipe case v.buffer <- data: v.inspector.Input(data) return nil } } }
func (v *Client) Dispatch(destination v2net.Destination, payload *buf.Buffer, ray ray.OutboundRay) { defer payload.Release() defer ray.OutboundInput().Release() defer ray.OutboundOutput().Close() network := destination.Network var server *protocol.ServerSpec var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { server = v.serverPicker.PickServer() dest := server.Destination() dest.Network = network rawConn, err := internet.Dial(v.meta.Address, dest, v.meta.GetDialerOptions()) if err != nil { return err } conn = rawConn return nil }) if err != nil { log.Warning("Shadowsocks|Client: Failed to find an available destination:", err) return } log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination()) conn.SetReusable(false) request := &protocol.RequestHeader{ Version: Version, Address: destination.Address, Port: destination.Port, } if destination.Network == v2net.Network_TCP { request.Command = protocol.RequestCommandTCP } else { request.Command = protocol.RequestCommandUDP } user := server.PickUser() rawAccount, err := user.GetTypedAccount() if err != nil { log.Warning("Shadowsocks|Client: Failed to get a valid user account: ", err) return } account := rawAccount.(*ShadowsocksAccount) request.User = user if account.OneTimeAuth == Account_Auto || account.OneTimeAuth == Account_Enabled { request.Option |= RequestOptionOneTimeAuth } if request.Command == protocol.RequestCommandTCP { bufferedWriter := bufio.NewWriter(conn) defer bufferedWriter.Release() bodyWriter, err := WriteTCPRequest(request, bufferedWriter) defer bodyWriter.Release() if err != nil { log.Info("Shadowsock|Client: Failed to write request: ", err) return } if err := bodyWriter.Write(payload); err != nil { log.Info("Shadowsocks|Client: Failed to write payload: ", err) return } var responseMutex sync.Mutex responseMutex.Lock() go func() { defer responseMutex.Unlock() responseReader, err := ReadTCPResponse(user, conn) if err != nil { log.Warning("Shadowsocks|Client: Failed to read response: ", err) return } if err := buf.PipeUntilEOF(responseReader, ray.OutboundOutput()); err != nil { log.Info("Shadowsocks|Client: Failed to transport all TCP response: ", err) } }() bufferedWriter.SetCached(false) if err := buf.PipeUntilEOF(ray.OutboundInput(), bodyWriter); err != nil { log.Info("Shadowsocks|Client: Failed to trasnport all TCP request: ", err) } responseMutex.Lock() } if request.Command == protocol.RequestCommandUDP { timedReader := v2net.NewTimeOutReader(16, conn) var responseMutex sync.Mutex responseMutex.Lock() go func() { defer responseMutex.Unlock() reader := &UDPReader{ Reader: timedReader, User: user, } if err := buf.PipeUntilEOF(reader, ray.OutboundOutput()); err != nil { log.Info("Shadowsocks|Client: Failed to transport all UDP response: ", err) } }() writer := &UDPWriter{ Writer: conn, Request: request, } if !payload.IsEmpty() { if err := writer.Write(payload); err != nil { log.Info("Shadowsocks|Client: Failed to write payload: ", err) return } } if err := buf.PipeUntilEOF(ray.OutboundInput(), writer); err != nil { log.Info("Shadowsocks|Client: Failed to transport all UDP request: ", err) } responseMutex.Lock() } }