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}