github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/common/mux/server.go (about)

     1  package mux
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  
     7  	core "github.com/v2fly/v2ray-core/v5"
     8  	"github.com/v2fly/v2ray-core/v5/common"
     9  	"github.com/v2fly/v2ray-core/v5/common/buf"
    10  	"github.com/v2fly/v2ray-core/v5/common/errors"
    11  	"github.com/v2fly/v2ray-core/v5/common/log"
    12  	"github.com/v2fly/v2ray-core/v5/common/net"
    13  	"github.com/v2fly/v2ray-core/v5/common/protocol"
    14  	"github.com/v2fly/v2ray-core/v5/common/session"
    15  	"github.com/v2fly/v2ray-core/v5/features/routing"
    16  	"github.com/v2fly/v2ray-core/v5/transport"
    17  	"github.com/v2fly/v2ray-core/v5/transport/pipe"
    18  )
    19  
    20  type Server struct {
    21  	dispatcher routing.Dispatcher
    22  }
    23  
    24  // NewServer creates a new mux.Server.
    25  func NewServer(ctx context.Context) *Server {
    26  	s := &Server{}
    27  	core.RequireFeatures(ctx, func(d routing.Dispatcher) {
    28  		s.dispatcher = d
    29  	})
    30  	return s
    31  }
    32  
    33  // Type implements common.HasType.
    34  func (s *Server) Type() interface{} {
    35  	return s.dispatcher.Type()
    36  }
    37  
    38  // Dispatch implements routing.Dispatcher
    39  func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
    40  	if dest.Address != muxCoolAddress {
    41  		return s.dispatcher.Dispatch(ctx, dest)
    42  	}
    43  
    44  	opts := pipe.OptionsFromContext(ctx)
    45  	uplinkReader, uplinkWriter := pipe.New(opts...)
    46  	downlinkReader, downlinkWriter := pipe.New(opts...)
    47  
    48  	_, err := NewServerWorker(ctx, s.dispatcher, &transport.Link{
    49  		Reader: uplinkReader,
    50  		Writer: downlinkWriter,
    51  	})
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	return &transport.Link{Reader: downlinkReader, Writer: uplinkWriter}, nil
    57  }
    58  
    59  // Start implements common.Runnable.
    60  func (s *Server) Start() error {
    61  	return nil
    62  }
    63  
    64  // Close implements common.Closable.
    65  func (s *Server) Close() error {
    66  	return nil
    67  }
    68  
    69  type ServerWorker struct {
    70  	dispatcher     routing.Dispatcher
    71  	link           *transport.Link
    72  	sessionManager *SessionManager
    73  }
    74  
    75  func NewServerWorker(ctx context.Context, d routing.Dispatcher, link *transport.Link) (*ServerWorker, error) {
    76  	worker := &ServerWorker{
    77  		dispatcher:     d,
    78  		link:           link,
    79  		sessionManager: NewSessionManager(),
    80  	}
    81  	go worker.run(ctx)
    82  	return worker, nil
    83  }
    84  
    85  func handle(ctx context.Context, s *Session, output buf.Writer) {
    86  	writer := NewResponseWriter(s.ID, output, s.transferType)
    87  	if err := buf.Copy(s.input, writer); err != nil {
    88  		newError("session ", s.ID, " ends.").Base(err).WriteToLog(session.ExportIDToError(ctx))
    89  		writer.hasError = true
    90  	}
    91  
    92  	writer.Close()
    93  	s.Close()
    94  }
    95  
    96  func (w *ServerWorker) ActiveConnections() uint32 {
    97  	return uint32(w.sessionManager.Size())
    98  }
    99  
   100  func (w *ServerWorker) Closed() bool {
   101  	return w.sessionManager.Closed()
   102  }
   103  
   104  func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error {
   105  	if meta.Option.Has(OptionData) {
   106  		return buf.Copy(NewStreamReader(reader), buf.Discard)
   107  	}
   108  	return nil
   109  }
   110  
   111  func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
   112  	newError("received request for ", meta.Target).WriteToLog(session.ExportIDToError(ctx))
   113  	{
   114  		msg := &log.AccessMessage{
   115  			To:     meta.Target,
   116  			Status: log.AccessAccepted,
   117  			Reason: "",
   118  		}
   119  		if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() {
   120  			msg.From = inbound.Source
   121  			msg.Email = inbound.User.Email
   122  		}
   123  		ctx = log.ContextWithAccessMessage(ctx, msg)
   124  	}
   125  	link, err := w.dispatcher.Dispatch(ctx, meta.Target)
   126  	if err != nil {
   127  		if meta.Option.Has(OptionData) {
   128  			buf.Copy(NewStreamReader(reader), buf.Discard)
   129  		}
   130  		return newError("failed to dispatch request.").Base(err)
   131  	}
   132  	s := &Session{
   133  		input:        link.Reader,
   134  		output:       link.Writer,
   135  		parent:       w.sessionManager,
   136  		ID:           meta.SessionID,
   137  		transferType: protocol.TransferTypeStream,
   138  	}
   139  	if meta.Target.Network == net.Network_UDP {
   140  		s.transferType = protocol.TransferTypePacket
   141  	}
   142  	w.sessionManager.Add(s)
   143  	go handle(ctx, s, w.link.Writer)
   144  	if !meta.Option.Has(OptionData) {
   145  		return nil
   146  	}
   147  
   148  	rr := s.NewReader(reader)
   149  	if err := buf.Copy(rr, s.output); err != nil {
   150  		buf.Copy(rr, buf.Discard)
   151  		common.Interrupt(s.input)
   152  		return s.Close()
   153  	}
   154  	return nil
   155  }
   156  
   157  func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error {
   158  	if !meta.Option.Has(OptionData) {
   159  		return nil
   160  	}
   161  
   162  	s, found := w.sessionManager.Get(meta.SessionID)
   163  	if !found {
   164  		// Notify remote peer to close this session.
   165  		closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
   166  		closingWriter.Close()
   167  
   168  		return buf.Copy(NewStreamReader(reader), buf.Discard)
   169  	}
   170  
   171  	rr := s.NewReader(reader)
   172  	err := buf.Copy(rr, s.output)
   173  
   174  	if err != nil && buf.IsWriteError(err) {
   175  		newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
   176  
   177  		// Notify remote peer to close this session.
   178  		closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
   179  		closingWriter.Close()
   180  
   181  		drainErr := buf.Copy(rr, buf.Discard)
   182  		common.Interrupt(s.input)
   183  		s.Close()
   184  		return drainErr
   185  	}
   186  
   187  	return err
   188  }
   189  
   190  func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
   191  	if s, found := w.sessionManager.Get(meta.SessionID); found {
   192  		if meta.Option.Has(OptionError) {
   193  			common.Interrupt(s.input)
   194  			common.Interrupt(s.output)
   195  		}
   196  		s.Close()
   197  	}
   198  	if meta.Option.Has(OptionData) {
   199  		return buf.Copy(NewStreamReader(reader), buf.Discard)
   200  	}
   201  	return nil
   202  }
   203  
   204  func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedReader) error {
   205  	var meta FrameMetadata
   206  	err := meta.Unmarshal(reader)
   207  	if err != nil {
   208  		return newError("failed to read metadata").Base(err)
   209  	}
   210  
   211  	switch meta.SessionStatus {
   212  	case SessionStatusKeepAlive:
   213  		err = w.handleStatusKeepAlive(&meta, reader)
   214  	case SessionStatusEnd:
   215  		err = w.handleStatusEnd(&meta, reader)
   216  	case SessionStatusNew:
   217  		err = w.handleStatusNew(ctx, &meta, reader)
   218  	case SessionStatusKeep:
   219  		err = w.handleStatusKeep(&meta, reader)
   220  	default:
   221  		status := meta.SessionStatus
   222  		return newError("unknown status: ", status).AtError()
   223  	}
   224  
   225  	if err != nil {
   226  		return newError("failed to process data").Base(err)
   227  	}
   228  	return nil
   229  }
   230  
   231  func (w *ServerWorker) run(ctx context.Context) {
   232  	input := w.link.Reader
   233  	reader := &buf.BufferedReader{Reader: input}
   234  
   235  	defer w.sessionManager.Close()
   236  
   237  	for {
   238  		select {
   239  		case <-ctx.Done():
   240  			return
   241  		default:
   242  			err := w.handleFrame(ctx, reader)
   243  			if err != nil {
   244  				if errors.Cause(err) != io.EOF {
   245  					newError("unexpected EOF").Base(err).WriteToLog(session.ExportIDToError(ctx))
   246  					common.Interrupt(input)
   247  				}
   248  				return
   249  			}
   250  		}
   251  	}
   252  }