split messages
Signed-off-by: Vasiliy Tolstov <v.tolstov@selfip.ru>
This commit is contained in:
parent
213f98d0a7
commit
10414c41a1
279
client.go
279
client.go
@ -9,13 +9,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DefaultServerMessages = []ServerMessage{
|
|
||||||
&FramebufferUpdate{},
|
|
||||||
&SetColorMapEntries{},
|
|
||||||
&Bell{},
|
|
||||||
&ServerCutText{},
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultClientHandlers []ClientHandler = []ClientHandler{
|
DefaultClientHandlers []ClientHandler = []ClientHandler{
|
||||||
&DefaultClientVersionHandler{},
|
&DefaultClientVersionHandler{},
|
||||||
@ -193,278 +186,6 @@ func NewClientConn(c net.Conn, cfg *ClientConfig) (*ClientConn, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientMessage represents a Client-to-Server RFB message type.
|
|
||||||
type ClientMessageType uint8
|
|
||||||
|
|
||||||
//go:generate stringer -type=ClientMessageType
|
|
||||||
|
|
||||||
// Client-to-Server message types.
|
|
||||||
const (
|
|
||||||
SetPixelFormatMsgType ClientMessageType = iota
|
|
||||||
_
|
|
||||||
SetEncodingsMsgType
|
|
||||||
FramebufferUpdateRequestMsgType
|
|
||||||
KeyEventMsgType
|
|
||||||
PointerEventMsgType
|
|
||||||
ClientCutTextMsgType
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetPixelFormat holds the wire format message.
|
|
||||||
type SetPixelFormat struct {
|
|
||||||
_ [3]byte // padding
|
|
||||||
PF PixelFormat // pixel-format
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *SetPixelFormat) String() string {
|
|
||||||
return fmt.Sprintf("%s", msg.PF)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SetPixelFormat) Type() ClientMessageType {
|
|
||||||
return SetPixelFormatMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *SetPixelFormat) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := c.PixelFormat()
|
|
||||||
// Invalidate the color map.
|
|
||||||
if pf.TrueColor != 1 {
|
|
||||||
c.SetColorMap(ColorMap{})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SetPixelFormat) Read(c Conn) (ClientMessage, error) {
|
|
||||||
msg := SetPixelFormat{}
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEncodings holds the wire format message, sans encoding-type field.
|
|
||||||
type SetEncodings struct {
|
|
||||||
_ [1]byte // padding
|
|
||||||
EncNum uint16 // number-of-encodings
|
|
||||||
Encodings []EncodingType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *SetEncodings) String() string {
|
|
||||||
return fmt.Sprintf("encnum: %d, encodings[]: { %v }", msg.EncNum, msg.Encodings)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SetEncodings) Type() ClientMessageType {
|
|
||||||
return SetEncodingsMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SetEncodings) Read(c Conn) (ClientMessage, error) {
|
|
||||||
msg := SetEncodings{}
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.EncNum); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var enc EncodingType
|
|
||||||
for i := uint16(0); i < msg.EncNum; i++ {
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &enc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.Encodings = append(msg.Encodings, enc)
|
|
||||||
}
|
|
||||||
c.SetEncodings(msg.Encodings)
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *SetEncodings) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if uint16(len(msg.Encodings)) > msg.EncNum {
|
|
||||||
msg.EncNum = uint16(len(msg.Encodings))
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.EncNum); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, enc := range msg.Encodings {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, enc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FramebufferUpdateRequest holds the wire format message.
|
|
||||||
type FramebufferUpdateRequest struct {
|
|
||||||
Inc uint8 // incremental
|
|
||||||
X, Y uint16 // x-, y-position
|
|
||||||
Width, Height uint16 // width, height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *FramebufferUpdateRequest) String() string {
|
|
||||||
return fmt.Sprintf("incremental: %d, x: %d, y: %d, width: %d, height: %d", msg.Inc, msg.X, msg.Y, msg.Width, msg.Height)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FramebufferUpdateRequest) Type() ClientMessageType {
|
|
||||||
return FramebufferUpdateRequestMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FramebufferUpdateRequest) Read(c Conn) (ClientMessage, error) {
|
|
||||||
msg := FramebufferUpdateRequest{}
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *FramebufferUpdateRequest) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyEvent holds the wire format message.
|
|
||||||
type KeyEvent struct {
|
|
||||||
Down uint8 // down-flag
|
|
||||||
_ [2]byte // padding
|
|
||||||
Key Key // key
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *KeyEvent) String() string {
|
|
||||||
return fmt.Sprintf("down: %d, key: %v", msg.Down, msg.Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*KeyEvent) Type() ClientMessageType {
|
|
||||||
return KeyEventMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*KeyEvent) Read(c Conn) (ClientMessage, error) {
|
|
||||||
msg := KeyEvent{}
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *KeyEvent) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PointerEventMessage holds the wire format message.
|
|
||||||
type PointerEvent struct {
|
|
||||||
Mask uint8 // button-mask
|
|
||||||
X, Y uint16 // x-, y-position
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *PointerEvent) String() string {
|
|
||||||
return fmt.Sprintf("mask %d, x: %d, y: %d", msg.Mask, msg.X, msg.Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*PointerEvent) Type() ClientMessageType {
|
|
||||||
return PointerEventMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*PointerEvent) Read(c Conn) (ClientMessage, error) {
|
|
||||||
msg := PointerEvent{}
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *PointerEvent) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientCutText holds the wire format message, sans the text field.
|
|
||||||
type ClientCutText struct {
|
|
||||||
_ [3]byte // padding
|
|
||||||
Length uint32 // length
|
|
||||||
Text []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *ClientCutText) String() string {
|
|
||||||
return fmt.Sprintf("length: %d, text: %s", msg.Length, msg.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ClientCutText) Type() ClientMessageType {
|
|
||||||
return ClientCutTextMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ClientCutText) Read(c Conn) (ClientMessage, error) {
|
|
||||||
msg := ClientCutText{}
|
|
||||||
var pad [3]byte
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.Text = make([]byte, msg.Length)
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *ClientCutText) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pad [3]byte
|
|
||||||
if err := binary.Write(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if uint32(len(msg.Text)) > msg.Length {
|
|
||||||
msg.Length = uint32(len(msg.Text))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
type DefaultClientMessageHandler struct{}
|
type DefaultClientMessageHandler struct{}
|
||||||
|
|
||||||
// listens to a VNC server and handles server messages.
|
// listens to a VNC server and handles server messages.
|
||||||
|
522
messages.go
Normal file
522
messages.go
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultClientMessages = []ClientMessage{
|
||||||
|
&SetPixelFormat{},
|
||||||
|
&SetEncodings{},
|
||||||
|
&FramebufferUpdateRequest{},
|
||||||
|
&KeyEvent{},
|
||||||
|
&PointerEvent{},
|
||||||
|
&ClientCutText{},
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerInit struct {
|
||||||
|
FBWidth, FBHeight uint16
|
||||||
|
PixelFormat PixelFormat
|
||||||
|
NameLength uint32
|
||||||
|
NameText []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srvInit ServerInit) String() string {
|
||||||
|
return fmt.Sprintf("Width: %d, Height: %d, PixelFormat: %s, NameLength: %d, MameText: %s", srvInit.FBWidth, srvInit.FBHeight, srvInit.PixelFormat, srvInit.NameLength, srvInit.NameText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerMessage represents a Client-to-Server RFB message type.
|
||||||
|
type ServerMessageType uint8
|
||||||
|
|
||||||
|
//go:generate stringer -type=ServerMessageType
|
||||||
|
|
||||||
|
// Client-to-Server message types.
|
||||||
|
const (
|
||||||
|
FramebufferUpdateMsgType ServerMessageType = iota
|
||||||
|
SetColorMapEntriesMsgType
|
||||||
|
BellMsgType
|
||||||
|
ServerCutTextMsgType
|
||||||
|
)
|
||||||
|
|
||||||
|
// FramebufferUpdate holds a FramebufferUpdate wire format message.
|
||||||
|
type FramebufferUpdate struct {
|
||||||
|
_ [1]byte // pad
|
||||||
|
NumRect uint16 // number-of-rectangles
|
||||||
|
Rects []*Rectangle // rectangles
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdate) String() string {
|
||||||
|
return fmt.Sprintf("rects %d rectangle[]: { %v }", msg.NumRect, msg.Rects)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FramebufferUpdate) Type() ServerMessageType {
|
||||||
|
return FramebufferUpdateMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FramebufferUpdate) Read(c Conn) (ServerMessage, error) {
|
||||||
|
msg := FramebufferUpdate{}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.NumRect); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := uint16(0); i < msg.NumRect; i++ {
|
||||||
|
rect := NewRectangle()
|
||||||
|
if err := rect.Read(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Rects = append(msg.Rects, rect)
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdate) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.NumRect); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, rect := range msg.Rects {
|
||||||
|
if err := rect.Write(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerCutText struct {
|
||||||
|
_ [1]byte
|
||||||
|
Length uint32
|
||||||
|
Text []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ServerCutText) String() string {
|
||||||
|
return fmt.Sprintf("lenght: %d text: %s", msg.Length, msg.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ServerCutText) Type() ServerMessageType {
|
||||||
|
return ServerCutTextMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ServerCutText) Read(c Conn) (ServerMessage, error) {
|
||||||
|
msg := ServerCutText{}
|
||||||
|
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Text = make([]byte, msg.Length)
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ServerCutText) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Length < uint32(len(msg.Text)) {
|
||||||
|
msg.Length = uint32(len(msg.Text))
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bell struct{}
|
||||||
|
|
||||||
|
func (*Bell) String() string {
|
||||||
|
return fmt.Sprintf("bell")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Bell) Type() ServerMessageType {
|
||||||
|
return BellMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Bell) Read(c Conn) (ServerMessage, error) {
|
||||||
|
return &Bell{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Bell) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetColorMapEntries struct {
|
||||||
|
_ [1]byte
|
||||||
|
FirstColor uint16
|
||||||
|
ColorsNum uint16
|
||||||
|
Colors []Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetColorMapEntries) String() string {
|
||||||
|
return fmt.Sprintf("first color: %d, numcolors: %d, colors[]: { %v }", msg.FirstColor, msg.ColorsNum, msg.Colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetColorMapEntries) Type() ServerMessageType {
|
||||||
|
return SetColorMapEntriesMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetColorMapEntries) Read(c Conn) (ServerMessage, error) {
|
||||||
|
msg := SetColorMapEntries{}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.FirstColor); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.ColorsNum); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Colors = make([]Color, msg.ColorsNum)
|
||||||
|
colorMap := c.ColorMap()
|
||||||
|
|
||||||
|
for i := uint16(0); i < msg.ColorsNum; i++ {
|
||||||
|
color := &msg.Colors[i]
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &color); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
colorMap[msg.FirstColor+i] = *color
|
||||||
|
}
|
||||||
|
c.SetColorMap(colorMap)
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetColorMapEntries) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.FirstColor); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.ColorsNum < uint16(len(msg.Colors)) {
|
||||||
|
msg.ColorsNum = uint16(len(msg.Colors))
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.ColorsNum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(msg.Colors); i++ {
|
||||||
|
color := msg.Colors[i]
|
||||||
|
if err := binary.Write(c, binary.BigEndian, color); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultServerMessages = []ServerMessage{
|
||||||
|
&FramebufferUpdate{},
|
||||||
|
&SetColorMapEntries{},
|
||||||
|
&Bell{},
|
||||||
|
&ServerCutText{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientMessage represents a Client-to-Server RFB message type.
|
||||||
|
type ClientMessageType uint8
|
||||||
|
|
||||||
|
//go:generate stringer -type=ClientMessageType
|
||||||
|
|
||||||
|
// Client-to-Server message types.
|
||||||
|
const (
|
||||||
|
SetPixelFormatMsgType ClientMessageType = iota
|
||||||
|
_
|
||||||
|
SetEncodingsMsgType
|
||||||
|
FramebufferUpdateRequestMsgType
|
||||||
|
KeyEventMsgType
|
||||||
|
PointerEventMsgType
|
||||||
|
ClientCutTextMsgType
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetPixelFormat holds the wire format message.
|
||||||
|
type SetPixelFormat struct {
|
||||||
|
_ [3]byte // padding
|
||||||
|
PF PixelFormat // pixel-format
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetPixelFormat) String() string {
|
||||||
|
return fmt.Sprintf("%s", msg.PF)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetPixelFormat) Type() ClientMessageType {
|
||||||
|
return SetPixelFormatMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetPixelFormat) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pf := c.PixelFormat()
|
||||||
|
// Invalidate the color map.
|
||||||
|
if pf.TrueColor != 1 {
|
||||||
|
c.SetColorMap(ColorMap{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetPixelFormat) Read(c Conn) (ClientMessage, error) {
|
||||||
|
msg := SetPixelFormat{}
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEncodings holds the wire format message, sans encoding-type field.
|
||||||
|
type SetEncodings struct {
|
||||||
|
_ [1]byte // padding
|
||||||
|
EncNum uint16 // number-of-encodings
|
||||||
|
Encodings []EncodingType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetEncodings) String() string {
|
||||||
|
return fmt.Sprintf("encnum: %d, encodings[]: { %v }", msg.EncNum, msg.Encodings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetEncodings) Type() ClientMessageType {
|
||||||
|
return SetEncodingsMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SetEncodings) Read(c Conn) (ClientMessage, error) {
|
||||||
|
msg := SetEncodings{}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.EncNum); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var enc EncodingType
|
||||||
|
for i := uint16(0); i < msg.EncNum; i++ {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &enc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Encodings = append(msg.Encodings, enc)
|
||||||
|
}
|
||||||
|
c.SetEncodings(msg.Encodings)
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetEncodings) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint16(len(msg.Encodings)) > msg.EncNum {
|
||||||
|
msg.EncNum = uint16(len(msg.Encodings))
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.EncNum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, enc := range msg.Encodings {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, enc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FramebufferUpdateRequest holds the wire format message.
|
||||||
|
type FramebufferUpdateRequest struct {
|
||||||
|
Inc uint8 // incremental
|
||||||
|
X, Y uint16 // x-, y-position
|
||||||
|
Width, Height uint16 // width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdateRequest) String() string {
|
||||||
|
return fmt.Sprintf("incremental: %d, x: %d, y: %d, width: %d, height: %d", msg.Inc, msg.X, msg.Y, msg.Width, msg.Height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FramebufferUpdateRequest) Type() ClientMessageType {
|
||||||
|
return FramebufferUpdateRequestMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FramebufferUpdateRequest) Read(c Conn) (ClientMessage, error) {
|
||||||
|
msg := FramebufferUpdateRequest{}
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdateRequest) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyEvent holds the wire format message.
|
||||||
|
type KeyEvent struct {
|
||||||
|
Down uint8 // down-flag
|
||||||
|
_ [2]byte // padding
|
||||||
|
Key Key // key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *KeyEvent) String() string {
|
||||||
|
return fmt.Sprintf("down: %d, key: %v", msg.Down, msg.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*KeyEvent) Type() ClientMessageType {
|
||||||
|
return KeyEventMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*KeyEvent) Read(c Conn) (ClientMessage, error) {
|
||||||
|
msg := KeyEvent{}
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *KeyEvent) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PointerEventMessage holds the wire format message.
|
||||||
|
type PointerEvent struct {
|
||||||
|
Mask uint8 // button-mask
|
||||||
|
X, Y uint16 // x-, y-position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PointerEvent) String() string {
|
||||||
|
return fmt.Sprintf("mask %d, x: %d, y: %d", msg.Mask, msg.X, msg.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PointerEvent) Type() ClientMessageType {
|
||||||
|
return PointerEventMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PointerEvent) Read(c Conn) (ClientMessage, error) {
|
||||||
|
msg := PointerEvent{}
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PointerEvent) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientCutText holds the wire format message, sans the text field.
|
||||||
|
type ClientCutText struct {
|
||||||
|
_ [3]byte // padding
|
||||||
|
Length uint32 // length
|
||||||
|
Text []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ClientCutText) String() string {
|
||||||
|
return fmt.Sprintf("length: %d, text: %s", msg.Length, msg.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ClientCutText) Type() ClientMessageType {
|
||||||
|
return ClientCutTextMsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ClientCutText) Read(c Conn) (ClientMessage, error) {
|
||||||
|
msg := ClientCutText{}
|
||||||
|
var pad [3]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Text = make([]byte, msg.Length)
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ClientCutText) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [3]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(len(msg.Text)) > msg.Length {
|
||||||
|
msg.Length = uint32(len(msg.Text))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Flush()
|
||||||
|
}
|
238
server.go
238
server.go
@ -9,26 +9,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DefaultClientMessages = []ClientMessage{
|
|
||||||
&SetPixelFormat{},
|
|
||||||
&SetEncodings{},
|
|
||||||
&FramebufferUpdateRequest{},
|
|
||||||
&KeyEvent{},
|
|
||||||
&PointerEvent{},
|
|
||||||
&ClientCutText{},
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerInit struct {
|
|
||||||
FBWidth, FBHeight uint16
|
|
||||||
PixelFormat PixelFormat
|
|
||||||
NameLength uint32
|
|
||||||
NameText []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srvInit ServerInit) String() string {
|
|
||||||
return fmt.Sprintf("Width: %d, Height: %d, PixelFormat: %s, NameLength: %d, MameText: %s", srvInit.FBWidth, srvInit.FBHeight, srvInit.PixelFormat, srvInit.NameLength, srvInit.NameText)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Conn = (*ServerConn)(nil)
|
var _ Conn = (*ServerConn)(nil)
|
||||||
|
|
||||||
func (c *ServerConn) Config() interface{} {
|
func (c *ServerConn) Config() interface{} {
|
||||||
@ -121,74 +101,6 @@ func (c *ServerConn) SetHeight(h uint16) {
|
|||||||
c.fbHeight = h
|
c.fbHeight = h
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerMessage represents a Client-to-Server RFB message type.
|
|
||||||
type ServerMessageType uint8
|
|
||||||
|
|
||||||
//go:generate stringer -type=ServerMessageType
|
|
||||||
|
|
||||||
// Client-to-Server message types.
|
|
||||||
const (
|
|
||||||
FramebufferUpdateMsgType ServerMessageType = iota
|
|
||||||
SetColorMapEntriesMsgType
|
|
||||||
BellMsgType
|
|
||||||
ServerCutTextMsgType
|
|
||||||
)
|
|
||||||
|
|
||||||
// FramebufferUpdate holds a FramebufferUpdate wire format message.
|
|
||||||
type FramebufferUpdate struct {
|
|
||||||
_ [1]byte // pad
|
|
||||||
NumRect uint16 // number-of-rectangles
|
|
||||||
Rects []*Rectangle // rectangles
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *FramebufferUpdate) String() string {
|
|
||||||
return fmt.Sprintf("rects %d rectangle[]: { %v }", msg.NumRect, msg.Rects)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FramebufferUpdate) Type() ServerMessageType {
|
|
||||||
return FramebufferUpdateMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FramebufferUpdate) Read(c Conn) (ServerMessage, error) {
|
|
||||||
msg := FramebufferUpdate{}
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.NumRect); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fmt.Printf("fb update %s\n", msg)
|
|
||||||
for i := uint16(0); i < msg.NumRect; i++ {
|
|
||||||
rect := NewRectangle()
|
|
||||||
if err := rect.Read(c); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg.Rects = append(msg.Rects, rect)
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *FramebufferUpdate) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.NumRect); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, rect := range msg.Rects {
|
|
||||||
if err := rect.Write(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerConn struct {
|
type ServerConn struct {
|
||||||
c net.Conn
|
c net.Conn
|
||||||
cfg *ServerConfig
|
cfg *ServerConfig
|
||||||
@ -372,153 +284,3 @@ func (*DefaultServerMessageHandler) Handle(c Conn) error {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerCutText struct {
|
|
||||||
_ [1]byte
|
|
||||||
Length uint32
|
|
||||||
Text []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *ServerCutText) String() string {
|
|
||||||
return fmt.Sprintf("lenght: %d text: %s", msg.Length, msg.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ServerCutText) Type() ServerMessageType {
|
|
||||||
return ServerCutTextMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ServerCutText) Read(c Conn) (ServerMessage, error) {
|
|
||||||
msg := ServerCutText{}
|
|
||||||
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.Text = make([]byte, msg.Length)
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *ServerCutText) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Length < uint32(len(msg.Text)) {
|
|
||||||
msg.Length = uint32(len(msg.Text))
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bell struct{}
|
|
||||||
|
|
||||||
func (*Bell) String() string {
|
|
||||||
return fmt.Sprintf("bell")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Bell) Type() ServerMessageType {
|
|
||||||
return BellMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Bell) Read(c Conn) (ServerMessage, error) {
|
|
||||||
return &Bell{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *Bell) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
type SetColorMapEntries struct {
|
|
||||||
_ [1]byte
|
|
||||||
FirstColor uint16
|
|
||||||
ColorsNum uint16
|
|
||||||
Colors []Color
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *SetColorMapEntries) String() string {
|
|
||||||
return fmt.Sprintf("first color: %d, numcolors: %d, colors[]: { %v }", msg.FirstColor, msg.ColorsNum, msg.Colors)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SetColorMapEntries) Type() ServerMessageType {
|
|
||||||
return SetColorMapEntriesMsgType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SetColorMapEntries) Read(c Conn) (ServerMessage, error) {
|
|
||||||
msg := SetColorMapEntries{}
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.FirstColor); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &msg.ColorsNum); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.Colors = make([]Color, msg.ColorsNum)
|
|
||||||
colorMap := c.ColorMap()
|
|
||||||
|
|
||||||
for i := uint16(0); i < msg.ColorsNum; i++ {
|
|
||||||
color := &msg.Colors[i]
|
|
||||||
if err := binary.Read(c, binary.BigEndian, &color); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
colorMap[msg.FirstColor+i] = *color
|
|
||||||
}
|
|
||||||
c.SetColorMap(colorMap)
|
|
||||||
return &msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *SetColorMapEntries) Write(c Conn) error {
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pad [1]byte
|
|
||||||
if err := binary.Write(c, binary.BigEndian, &pad); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.FirstColor); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.ColorsNum < uint16(len(msg.Colors)) {
|
|
||||||
msg.ColorsNum = uint16(len(msg.Colors))
|
|
||||||
}
|
|
||||||
if err := binary.Write(c, binary.BigEndian, msg.ColorsNum); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(msg.Colors); i++ {
|
|
||||||
color := msg.Colors[i]
|
|
||||||
if err := binary.Write(c, binary.BigEndian, color); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Flush()
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user