func (c *Gcd) probeDebugPort() error {
	ticker := time.NewTicker(time.Millisecond * 100)
	timeoutTicker := time.NewTicker(time.Second * c.timeout)
	defer func() {
		ticker.Stop()
		timeoutTicker.Stop()
	}()
	for {
		select {
		case <-ticker.C:
			resp, err := http.Get(c.apiEndpoint)
			if err != nil {
				continue
			}
			defer resp.Body.Close()
			c.readyCh <- struct{}{}
			return nil
		case <-timeoutTicker.C:
			return fmt.Errorf("Unable to contact debugger at %s after %d seconds, gave up", c.apiEndpoint, c.timeout)
		}
	}
}
probes the debugger report and signals when it's available.
package gcd
import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"
	"github.com/wirepair/gcd/gcdapi"
)
var GCDVERSION = "v1.0.2"
var (
	ErrNoTabAvailable = errors.New("no available tab found")
)
type GcdBodyReadErr struct {
	Message string
}
func (g *GcdBodyReadErr) Error() string {
	return "error reading response body: " + g.Message
}
type GcdDecodingErr struct {
	Message string
}
func (g *GcdDecodingErr) Error() string {
	return "error decoding inspectable page: " + g.Message
}
type TerminatedHandler func(reason string)
type Gcd struct {
	timeout           time.Duration  
	chromeProcess     *os.Process
	chromeCmd         *exec.Cmd
	terminatedHandler TerminatedHandler
	port              string
	host              string
	addr              string
	profileDir        string
	deleteProfile     bool
	readyCh           chan struct{}
	apiEndpoint       string
	flags             []string
	env               []string
	chomeApiVersion   string
}
func NewChromeDebugger() *Gcd {
	c := &Gcd{}
	c.timeout = 15
	c.host = "localhost"
	c.readyCh = make(chan struct{})
	c.terminatedHandler = nil
	c.flags = make([]string, 0)
	c.env = make([]string, 0)
	return c
}
func (c *Gcd) SetTerminationHandler(handler TerminatedHandler) {
	c.terminatedHandler = handler
}
func (c *Gcd) SetTimeout(timeout time.Duration) {
	c.timeout = timeout
}
func (c *Gcd) AddFlags(flags []string) {
	c.flags = append(c.flags, flags...)
}
func (c *Gcd) AddEnvironmentVars(vars []string) {
	c.env = append(c.env, vars...)
}
func (c *Gcd) DeleteProfileOnExit() {
	c.deleteProfile = true
}
func (c *Gcd) StartProcess(exePath, userDir, port string) error {
	c.port = port
	c.addr = fmt.Sprintf("%s:%s", c.host, c.port)
	c.profileDir = userDir
	c.apiEndpoint = fmt.Sprintf("http://%s/json", c.addr)
	c.flags = append(c.flags, fmt.Sprintf("--user-data-dir=%s", c.profileDir))
	c.flags = append(c.flags, fmt.Sprintf("--remote-debugging-port=%s", port))
	c.flags = append(c.flags, "--no-first-run")
	c.flags = append(c.flags, "--no-default-browser-check")
	c.chromeCmd = exec.Command(exePath, c.flags...)
	c.chromeCmd.Env = os.Environ()
	c.chromeCmd.Env = append(c.chromeCmd.Env, c.env...)
	go func() {
		err := c.chromeCmd.Start()
		if err != nil {
			msg := fmt.Sprintf("failed to start chrome: %s", err)
			if c.terminatedHandler != nil {
				c.terminatedHandler(msg)
			} else {
				log.Println(msg)
			}
		}
		c.chromeProcess = c.chromeCmd.Process
		err = c.chromeCmd.Wait()
		c.removeProfileDir()
		closeMessage := "exited"
		if err != nil {
			closeMessage = err.Error()
		}
		if c.terminatedHandler != nil {
			c.terminatedHandler(closeMessage)
		}
	}()
	var err error
	go func(err error) {
		err = c.probeDebugPort()
	}(err)
	<-c.readyCh
	return err
}
func (c *Gcd) ExitProcess() error {
	err := c.chromeProcess.Kill()
	c.removeProfileDir()
	return err
}
func (c *Gcd) removeProfileDir() {
	if c.deleteProfile {
		time.Sleep(1 * time.Second)
		if err := os.RemoveAll(c.profileDir); err != nil {
			log.Printf("error deleting profile directory: %s\n", err)
		}
	}
}
func (c *Gcd) ConnectToInstance(host string, port string) error {
	c.host = host
	c.port = port
	c.addr = fmt.Sprintf("%s:%s", c.host, c.port)
	c.apiEndpoint = fmt.Sprintf("http://%s/json", c.addr)
	var err error
	go func(err error) {
		err = c.probeDebugPort()
	}(err)
	<-c.readyCh
	return err
}
func (c *Gcd) GetTargets() ([]*ChromeTarget, error) {
	empty := make(map[string]struct{}, 0)
	return c.GetNewTargets(empty)
}
func (c *Gcd) GetNewTargets(knownIds map[string]struct{}) ([]*ChromeTarget, error) {
	connectableTargets, err := c.getConnectableTargets()
	if err != nil {
		return nil, err
	}
	chromeTargets := make([]*ChromeTarget, 0)
	for _, v := range connectableTargets {
		if _, ok := knownIds[v.Id]; !ok {
			target, err := openChromeTarget(c.addr, v)
			if err != nil {
				return nil, err
			}
			chromeTargets = append(chromeTargets, target)
		}
	}
	return chromeTargets, nil
}
func (c *Gcd) getConnectableTargets() ([]*TargetInfo, error) {
	resp, err := http.Get(c.apiEndpoint)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, errRead := ioutil.ReadAll(resp.Body)
	if errRead != nil {
		return nil, &GcdBodyReadErr{Message: errRead.Error()}
	}
	targets := make([]*TargetInfo, 0)
	err = json.Unmarshal(body, &targets)
	if err != nil {
		return nil, &GcdDecodingErr{Message: err.Error()}
	}
	connectableTargets := make([]*TargetInfo, 0)
	for _, v := range targets {
		if v.WebSocketDebuggerUrl != "" {
			connectableTargets = append(connectableTargets, v)
		}
	}
	return connectableTargets, nil
}
func (c *Gcd) NewTab() (*ChromeTarget, error) {
	resp, err := http.Get(c.apiEndpoint + "/new")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, errRead := ioutil.ReadAll(resp.Body)
	if errRead != nil {
		return nil, &GcdBodyReadErr{Message: errRead.Error()}
	}
	tabTarget := &TargetInfo{}
	err = json.Unmarshal(body, &tabTarget)
	if err != nil {
		return nil, &GcdDecodingErr{Message: err.Error()}
	}
	return openChromeTarget(c.addr, tabTarget)
}
func (c *Gcd) GetFirstTab() (*ChromeTarget, error) {
	connectableTargets, err := c.getConnectableTargets()
	if err != nil {
		return nil, err
	}
	for _, tabTarget := range connectableTargets {
		if tabTarget.Type == "page" {
			return openChromeTarget(c.addr, tabTarget)
		}
	}
	return nil, ErrNoTabAvailable
}
func (c *Gcd) GetRevision() string {
	return gcdapi.CHROME_VERSION
}
func (c *Gcd) CloseTab(target *ChromeTarget) error {
	target.shutdown()  
	resp, err := http.Get(fmt.Sprintf("%s/close/%s", c.apiEndpoint, target.Target.Id))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, errRead := ioutil.ReadAll(resp.Body)
	return errRead
}
func (c *Gcd) ActivateTab(target *ChromeTarget) error {
	resp, err := http.Get(fmt.Sprintf("%s/activate/%s", c.apiEndpoint, target.Target.Id))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, errRead := ioutil.ReadAll(resp.Body)
	return errRead
}