in gymvnc/gymvnc.go [416:590]
func (c *VNCSession) connect(updates chan *vncclient.FramebufferUpdateMessage) error {
log.Infof("[%s] opening connection to VNC server", c.label)
errorCh := make(chan error, 1)
serverMessageCh := make(chan vncclient.ServerMessage)
var conn *vncclient.ClientConn
totalSleep := 0 * time.Second
for i := 0; ; i++ {
soft := true
target, err := net.Dial("tcp", c.config.Address)
if err == nil {
conn, err, soft = vncclient.Client(target, &vncclient.ClientConfig{
Auth: []vncclient.ClientAuth{
&vncclient.PasswordAuth{
Password: c.config.Password,
},
},
ServerMessageCh: serverMessageCh,
ErrorCh: errorCh,
})
}
if soft == true && c.config.StartTimeout > 0 {
if totalSleep >= c.config.StartTimeout {
return errors.Annotatef(err, "could not establish VNC connection to server, and exceeded start timeout of %s by sleeping for %s", c.config.StartTimeout, totalSleep)
}
// Choose how long to sleep
sleepTarget := time.Duration(2*(i+1)) * time.Second
if sleepTarget > time.Duration(30)*time.Second {
sleepTarget = time.Duration(30) * time.Second
}
log.Infof("[%s] Waiting on VNC server: %s. Retrying in %s (%s/%s)", c.label, err, sleepTarget, totalSleep, c.config.StartTimeout)
totalSleep += sleepTarget
time.Sleep(sleepTarget)
} else if err != nil {
return errors.Annotate(err, "could not establish VNC connection to server")
} else {
break
}
}
defer func() {
c.lock.Lock()
if c.conn == nil {
// We never stored the conn object, so we need
// to close it ourselves.
log.Infof("[%s] autoclosing connection to VNC server", c.label)
conn.Close()
}
c.lock.Unlock()
}()
go func() {
select {
case err := <-errorCh:
c.mgr.Error <- errors.Annotatef(err, "[%s] vnc error", c.label)
case <-c.mgr.Done:
}
}()
// While the VNC protocol supports more exotic formats, we
// only want straight RGB with 1 byte per color.
c.frontScreen = NewScreen(conn.FramebufferWidth, conn.FramebufferHeight)
c.backScreen = NewScreen(conn.FramebufferWidth, conn.FramebufferHeight)
err := conn.SetPixelFormat(&vncclient.PixelFormat{
BPP: 32,
Depth: 24,
BigEndian: false,
TrueColor: true,
RedMax: 255,
GreenMax: 255,
BlueMax: 255,
RedShift: 0,
GreenShift: 8,
BlueShift: 16,
})
if err != nil {
return errors.Annotate(err, "could not set pixel format")
}
var encoding vncclient.Encoding
switch c.config.Encoding {
case "tight":
encoding = &vncclient.TightEncoding{}
case "zrle":
encoding = &vncclient.ZRLEEncoding{}
case "raw":
encoding = &vncclient.RawEncoding{}
default:
return errors.Errorf("invalid encoding: %s", c.config.Encoding)
}
encodings := []vncclient.Encoding{
encoding,
}
if c.config.QualityLevel != -1 {
encodings = append(encodings, vncclient.QualityLevel(c.config.QualityLevel))
}
if c.config.CompressLevel != -1 {
encodings = append(encodings, vncclient.CompressLevel(c.config.CompressLevel))
}
if c.config.FineQualityLevel != -1 {
encodings = append(encodings, vncclient.FineQualityLevel(c.config.FineQualityLevel))
}
if c.config.SubsampleLevel != -1 {
encodings = append(encodings, vncclient.SubsampleLevel(c.config.SubsampleLevel))
}
err = conn.SetEncodings(encodings)
if err != nil {
return errors.Annotate(err, "could not set encodings")
}
c.lock.Lock()
// Make the connection visible so it can be used in requestUpdate
c.conn = conn
err = c.requestUpdate()
if err != nil {
c.conn = nil
c.lock.Unlock()
return errors.Annotate(err, "could not send framebuffer update request")
}
if c.closed {
c.conn = nil
c.lock.Unlock()
// Conn will be closed by our earlier defer
return errors.Errorf("[%s] VNCSession object was closed before connection was established", c.label)
}
log.Infof("[%s] connection established", c.label)
c.lock.Unlock()
// Spin up a screenbuffer thread
go func() {
err := c.maintainFrameBuffer(updates)
if err != nil {
// Report the error, if any
select {
case c.mgr.Error <- err:
default:
}
}
}()
for {
select {
case msg := <-serverMessageCh:
log.Debugf("[%s] Just received: %T %+v", c.label, msg, msg)
switch msg := msg.(type) {
case *vncclient.FramebufferUpdateMessage:
updates <- msg
// Keep re-requesting!
c.updated.L.Lock()
if !c.pauseUpdates {
err := c.requestUpdate()
if err != nil {
select {
case c.mgr.Error <- err:
default:
}
}
}
c.updated.L.Unlock()
}
case <-c.mgr.Done:
log.Debugf("[%s] server message goroutine exiting", c.label)
return nil
}
}
}