func (cli *HyperClient) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}, hostname string) error { defer func() { if started != nil { close(started) } }() params, err := cli.encodeData(data) if err != nil { return err } req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", utils.APIVERSION, path), params) if err != nil { return err } req.Header.Set("User-Agent", "Hyper-Client/"+utils.VERSION) req.Header.Set("Content-Type", "text/plain") req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "tcp") req.Host = cli.addr dial, err := cli.dial() if err != nil { return err } // When we set up a TCP connection for hijack, there could be long periods // of inactivity (a long running command with no output) that in certain // network setups may cause ECONNTIMEOUT, leaving the client in an unknown // state. Setting TCP KeepAlive on the socket connection will prohibit // ECONNTIMEOUT unless the socket connection truly is broken if tcpConn, ok := dial.(*net.TCPConn); ok { tcpConn.SetKeepAlive(true) tcpConn.SetKeepAlivePeriod(30 * time.Second) } if err != nil { if strings.Contains(err.Error(), "connection refused") { return fmt.Errorf("Cannot connect to the Hyper daemon. Is 'hyperd' running on this host?") } return err } clientconn := httputil.NewClientConn(dial, nil) defer clientconn.Close() // Server hijacks the connection, error 'connection closed' expected _, err = clientconn.Do(req) if err != nil { fmt.Printf("Client DO: %s\n", err.Error()) } rwc, _ := clientconn.Hijack() defer rwc.Close() if started != nil { started <- rwc } _, err = term.TtySplice(rwc) return err }
// stdin/stdout <-> conn func containerTtySplice(root, container string, conn net.Conn, isContainer bool) (int, error) { tag, err := runvGetTag(conn) if err != nil { return -1, err } fmt.Printf("tag=%s\n", tag) outFd, isTerminalOut := term.GetFdInfo(os.Stdout) newTty(root, container, tag, outFd, isTerminalOut).monitorTtySize() _, err = term.TtySplice(conn) if err != nil { return -1, err } cmd := &ttyTagCmd{Root: root, Container: "", Tag: tag} if isContainer { cmd.Container = container } conn, err = runvRequest(root, container, RUNV_EXITSTATUS, cmd) if err != nil { fmt.Printf("runvRequest failed: %v\n", err) return -1, err } defer conn.Close() msg, err := hypervisor.ReadVmMessage(conn.(*net.UnixConn)) if err != nil { fmt.Printf("read runv server data failed: %v\n", err) return -1, err } if msg.Code != RUNV_EXITSTATUS { return -1, fmt.Errorf("unexpected respond code") } return int(msg.Message[0]), nil }