package grpc import ( "errors" "github.com/Terry-Mao/goim/pkg/bufio" "github.com/Terry-Mao/goim/pkg/bytes" "github.com/Terry-Mao/goim/pkg/encoding/binary" "github.com/Terry-Mao/goim/pkg/websocket" gws "github.com/gorilla/websocket" ) const ( // MaxBodySize max proto body size MaxBodySize = int32(1 << 14) ) const ( // size _packSize = 4 _headerSize = 2 _verSize = 2 _opSize = 4 _seqSize = 4 _ackSize = 4 _heartSize = 4 _rawHeaderSize = _packSize + _headerSize + _verSize + _opSize + _seqSize + _ackSize _maxPackSize = MaxBodySize + int32(_rawHeaderSize) // offset _packOffset = 0 _headerOffset = _packOffset + _packSize _verOffset = _headerOffset + _headerSize _opOffset = _verOffset + _verSize _seqOffset = _opOffset + _opSize _ackOffset = _seqOffset + _seqSize _heartOffset = _ackOffset + _ackSize ) var ( // ErrProtoPackLen proto packet len error ErrProtoPackLen = errors.New("default server codec pack length error") // ErrProtoHeaderLen proto header len error ErrProtoHeaderLen = errors.New("default server codec header length error") ) var ( // ProtoReady proto ready ProtoReady = &Proto{Op: int32(Op_ProtoReady)} // ProtoFinish proto finish ProtoFinish = &Proto{Op: int32(Op_ProtoFinish)} ) // WriteTo write a proto to bytes writer. func (p *Proto) WriteTo(b *bytes.Writer) { var ( packLen = _rawHeaderSize + int32(len(p.Body)) buf = b.Peek(_rawHeaderSize) ) binary.BigEndian.PutInt32(buf[_packOffset:], packLen) binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) if p.Body != nil { b.Write(p.Body) } } // ReadTCP read a proto from TCP reader. func (p *Proto) ReadTCP(rr *bufio.Reader) (err error) { var ( bodyLen int headerLen int16 packLen int32 buf []byte ) if buf, err = rr.Pop(_rawHeaderSize); err != nil { return } packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset]) headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset]) p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset])) p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset]) p.Seq = binary.BigEndian.Int32(buf[_seqOffset:_ackOffset]) p.Ack = binary.BigEndian.Int32(buf[_ackOffset:]) if packLen > _maxPackSize { return ErrProtoPackLen } if headerLen != _rawHeaderSize { return ErrProtoHeaderLen } if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 { p.Body, err = rr.Pop(bodyLen) } else { p.Body = nil } return } // WriteTCP write a proto to TCP writer. func (p *Proto) WriteTCP(wr *bufio.Writer) (err error) { var ( buf []byte packLen int32 ) if p.Op == int32(Op_Raw) { // write without buffer, job concact proto into raw buffer _, err = wr.WriteRaw(p.Body) return } packLen = _rawHeaderSize + int32(len(p.Body)) if buf, err = wr.Peek(_rawHeaderSize); err != nil { return } binary.BigEndian.PutInt32(buf[_packOffset:], packLen) binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) if p.Body != nil { _, err = wr.Write(p.Body) } return } // WriteTCPHeart write TCP heartbeat with room online. func (p *Proto) WriteTCPHeart(wr *bufio.Writer, online int32) (err error) { var ( buf []byte packLen int ) packLen = _rawHeaderSize + _heartSize if buf, err = wr.Peek(packLen); err != nil { return } // header binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen)) binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) // body binary.BigEndian.PutInt32(buf[_heartOffset:], online) return } // ReadWebsocket read a proto from websocket connection. func (p *Proto) ReadWebsocket(ws *websocket.Conn) (err error) { var ( bodyLen int headerLen int16 packLen int32 buf []byte ) if _, buf, err = ws.ReadMessage(); err != nil { return } if len(buf) < _rawHeaderSize { return ErrProtoPackLen } packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset]) headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset]) p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset])) p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset]) p.Seq = binary.BigEndian.Int32(buf[_seqOffset:_ackOffset]) p.Ack = binary.BigEndian.Int32(buf[_ackOffset:]) if packLen < 0 || packLen > _maxPackSize { return ErrProtoPackLen } if headerLen != _rawHeaderSize { return ErrProtoHeaderLen } if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 { p.Body = buf[headerLen:packLen] } else { p.Body = nil } return } // WriteWebsocket write a proto to websocket connection. func (p *Proto) WriteWebsocket(ws *websocket.Conn) (err error) { var ( buf []byte packLen int ) packLen = _rawHeaderSize + len(p.Body) if err = ws.WriteHeader(websocket.BinaryMessage, packLen); err != nil { return } if buf, err = ws.Peek(_rawHeaderSize); err != nil { return } binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen)) binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) if p.Body != nil { err = ws.WriteBody(p.Body) } return } // WriteWebsocketHeart write websocket heartbeat with room online. func (p *Proto) WriteWebsocketHeart(wr *websocket.Conn, online int32) (err error) { var ( buf []byte packLen int ) packLen = _rawHeaderSize + _heartSize // websocket header if err = wr.WriteHeader(websocket.BinaryMessage, packLen); err != nil { return } if buf, err = wr.Peek(packLen); err != nil { return } // proto header binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen)) binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) // proto body binary.BigEndian.PutInt32(buf[_heartOffset:], online) return } // WriteWebsocket write a proto to websocket connection. func (p *Proto) WriteWebsocket2(ws *gws.Conn) (err error) { var ( buf []byte packLen int32 ) wc, err := ws.NextWriter(websocket.BinaryMessage) if err != nil { return err } wr := bufio.NewWriter(wc) packLen = _rawHeaderSize + int32(len(p.Body)) if buf, err = wr.Peek(_rawHeaderSize); err != nil { return } binary.BigEndian.PutInt32(buf[_packOffset:], packLen) binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) if p.Body != nil { _, err = wr.Write(p.Body) } if err != nil { return } err = wr.Flush() if err != nil { return } return wc.Close() } // ReadWebsocket read a proto from websocket connection. func (p *Proto) ReadWebsocket2(ws *gws.Conn) (err error) { var ( bodyLen int headerLen int16 packLen int32 buf []byte ) if _, buf, err = ws.ReadMessage(); err != nil { return } if len(buf) < _rawHeaderSize { return ErrProtoPackLen } packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset]) headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset]) p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset])) p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset]) p.Seq = binary.BigEndian.Int32(buf[_seqOffset:_ackOffset]) p.Ack = binary.BigEndian.Int32(buf[_ackOffset:]) if packLen > _maxPackSize { return ErrProtoPackLen } if headerLen != _rawHeaderSize { return ErrProtoHeaderLen } if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 { p.Body = buf[headerLen:packLen] } else { p.Body = nil } return }