func()

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
		}
	}
}