github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/websocket/x/codec.go (about)

     1  package websocket
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"google.golang.org/protobuf/encoding/protojson"
    10  	"google.golang.org/protobuf/proto"
    11  )
    12  
    13  const (
    14  	DefaultMaxPayloadBytes = 32 << 20 // 32MB
    15  )
    16  
    17  // Codec represents a symmetric pair of functions that implement a codec.
    18  type Codec struct {
    19  	Marshal   func(v any) (data []byte, payloadType opcode, err error)
    20  	Unmarshal func(data []byte, payloadType opcode, v any) (err error)
    21  }
    22  
    23  // Send sends v marshaled by cd.Marshal as single frame to ws.
    24  func (cd Codec) Send(ws *Conn, v any) (err error) {
    25  	data, payloadType, err := cd.Marshal(v)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	_, err = ws.WriteMsg(data, payloadType)
    30  	return err
    31  }
    32  
    33  // Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores
    34  // in v. The whole frame payload is read to an in-memory buffer; max size of
    35  // payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds
    36  // limit, ErrFrameTooLarge is returned; in this case frame is not read off wire
    37  // completely. The next call to Receive would read and discard leftover data of
    38  // previous oversized frame before processing next frame.
    39  func (cd Codec) Receive(ws *Conn, v any) error {
    40  	return ws.NextFrameReader(func(header *Header, frame io.ReadCloser) error {
    41  		if header.payloadLength > int64(DefaultMaxPayloadBytes) {
    42  			// payload size exceeds limit, no need to call Unmarshal
    43  			//
    44  			// set frameReader to current oversized frame so that
    45  			// the next call to this function can drain leftover
    46  			// data before processing the next frame
    47  			ws.Frame = frame
    48  			return errors.New("websocket: frame payload size exceeds limit")
    49  		}
    50  
    51  		data, err := io.ReadAll(frame)
    52  		if err != nil {
    53  			return err
    54  		}
    55  		return cd.Unmarshal(data, header.opcode, v)
    56  	})
    57  
    58  }
    59  
    60  func marshal(v any) (msg []byte, _ opcode, err error) {
    61  	switch data := v.(type) {
    62  	case string:
    63  		return []byte(data), opText, nil
    64  	case []byte:
    65  		return data, opBinary, nil
    66  	}
    67  	return nil, 8, ErrNotSupported
    68  }
    69  
    70  func unmarshal(msg []byte, _ opcode, v any) (err error) {
    71  	switch data := v.(type) {
    72  	case *string:
    73  		*data = string(msg)
    74  		return nil
    75  	case *[]byte:
    76  		*data = msg
    77  		return nil
    78  	}
    79  	return ErrNotSupported
    80  }
    81  
    82  /*
    83  Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
    84  To send/receive text frame, use string type.
    85  To send/receive binary frame, use []byte type.
    86  
    87  Trivial usage:
    88  
    89  	import "websocket"
    90  
    91  	// receive text frame
    92  	var message string
    93  	websocket.Message.Receive(ws, &message)
    94  
    95  	// send text frame
    96  	message = "hello"
    97  	websocket.Message.Send(ws, message)
    98  
    99  	// receive binary frame
   100  	var data []byte
   101  	websocket.Message.Receive(ws, &data)
   102  
   103  	// send binary frame
   104  	data = []byte{0, 1, 2}
   105  	websocket.Message.Send(ws, data)
   106  */
   107  var Message = Codec{marshal, unmarshal}
   108  
   109  func jsonMarshal(v any) (msg []byte, payloadType opcode, err error) {
   110  	msg, err = json.Marshal(v)
   111  	return msg, opText, err
   112  }
   113  
   114  func jsonUnmarshal(msg []byte, payloadType opcode, v any) (err error) {
   115  	return json.Unmarshal(msg, v)
   116  }
   117  
   118  /*
   119  JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
   120  
   121  Trivial usage:
   122  
   123  	import "websocket"
   124  
   125  	type T struct {
   126  		Msg string
   127  		Count int
   128  	}
   129  
   130  	// receive JSON type T
   131  	var data T
   132  	websocket.JSON.Receive(ws, &data)
   133  
   134  	// send JSON type T
   135  	websocket.JSON.Send(ws, data)
   136  */
   137  var JSON = Codec{jsonMarshal, jsonUnmarshal}
   138  
   139  func protoMarshal(v any) (msg []byte, payloadType opcode, err error) {
   140  	m, ok := v.(proto.Message)
   141  	if !ok {
   142  		return nil, 0, fmt.Errorf("data is not proto message")
   143  	}
   144  
   145  	msg, err = proto.Marshal(m)
   146  	return msg, opBinary, err
   147  }
   148  
   149  func protoUnmarshal(msg []byte, payloadType opcode, v any) (err error) {
   150  	m, ok := v.(proto.Message)
   151  	if !ok {
   152  		return fmt.Errorf("data is not proto message")
   153  	}
   154  
   155  	return proto.Unmarshal(msg, m)
   156  }
   157  
   158  var PROTO = Codec{protoMarshal, protoUnmarshal}
   159  
   160  func protoJsonMarshal(v any) (msg []byte, payloadType opcode, err error) {
   161  	m, ok := v.(proto.Message)
   162  	if !ok {
   163  		return nil, 0, fmt.Errorf("data is not proto message")
   164  	}
   165  
   166  	msg, err = protojson.Marshal(m)
   167  	return msg, opBinary, err
   168  }
   169  
   170  func protoJsonUnmarshal(msg []byte, payloadType opcode, v any) (err error) {
   171  	m, ok := v.(proto.Message)
   172  	if !ok {
   173  		return fmt.Errorf("data is not proto message")
   174  	}
   175  
   176  	return protojson.Unmarshal(msg, m)
   177  }
   178  
   179  var PROTOJSON = Codec{protoJsonMarshal, protoJsonUnmarshal}