package  winrmimport  (	"bytes" 	"context" 	"errors" 	"io" 	"strings" 	"sync" )type  commandWriter struct  {	*Command 	mutex sync .Mutex 	eof   bool }type  commandReader struct  {	*Command 	write  *io .PipeWriter 	read   *io .PipeReader 	stream string }type  Command  struct  {	client   *Client 	shell    *Shell 	id       string 	exitCode int 	err      error 	Stdin  *commandWriter 	Stdout *commandReader 	Stderr *commandReader 	done   chan  struct {}	cancel chan  struct {}}func  newCommand(ctx  context .Context , shell  *Shell , ids  string ) *Command  {	command  := &Command {		shell :    shell ,		client :   shell .client ,		id :       ids ,		exitCode : 0 ,		err :      nil ,		done :     make (chan  struct {}),		cancel :   make (chan  struct {}),	}	command .Stdout  = newCommandReader ("stdout" , command )	command .Stdin  = &commandWriter {		Command : command ,		eof :     false ,	}	command .Stderr  = newCommandReader ("stderr" , command )	go  fetchOutput (ctx , command )	return  command }func  newCommandReader(stream  string , command  *Command ) *commandReader  {	read , write  := io .Pipe ()	return  &commandReader {		Command : command ,		stream :  stream ,		write :   write ,		read :    read ,	}}func  fetchOutput(ctx  context .Context , command  *Command ) {	ctxDone  := ctx .Done ()	for  {		select 		case  <- command .cancel :			_, _ = command .slurpAllOutput ()			err  := errors .New ("canceled" )			command .Stderr .write .CloseWithError (err )			command .Stdout .write .CloseWithError (err )			close (command .done )			return 		case  <- ctxDone :			command .err  = ctx .Err ()			ctxDone  = nil 			command .Close ()		default :			finished , err  := command .slurpAllOutput ()			if  finished  {				command .err  = err 				close (command .done )				return 			}		}	}}func  (c  *Command ) check () error  {	if  c .id  == ""  {		return  errors .New ("Command has already been closed" )	}	if  c .shell  == nil  {		return  errors .New ("Command has no associated shell" )	}	if  c .client  == nil  {		return  errors .New ("Command has no associated client" )	}	return  nil }func  (c  *Command ) Close () error  {	if  err  := c .check (); err  != nil  {		return  err 	}	select 	case  <- c .cancel :	default :		close (c .cancel )	}	request  := NewSignalRequest (c .client .url , c .shell .id , c .id , &c .client .Parameters )	defer  request .Free ()	_ , err  := c .client .sendRequest (request )	return  err }func  (c  *Command ) slurpAllOutput () (bool , error ) {	if  err  := c .check (); err  != nil  {		c .Stderr .write .CloseWithError (err )		c .Stdout .write .CloseWithError (err )		return  true , err 	}	request  := NewGetOutputRequest (c .client .url , c .shell .id , c .id , "stdout stderr" , &c .client .Parameters )	defer  request .Free ()	response , err  := c .client .sendRequest (request )	if  err  != nil  {		if  strings .Contains (err .Error(), "OperationTimeout" ) {						return  false , err 		}		if  strings .Contains (err .Error(), "EOF" ) {			c .exitCode  = 16001 		}		c .Stderr .write .CloseWithError (err )		c .Stdout .write .CloseWithError (err )		return  true , err 	}	var  exitCode  int 	var  stdout , stderr  bytes .Buffer 	finished , exitCode , err  := ParseSlurpOutputErrResponse (response , &stdout , &stderr )	if  err  != nil  {		c .Stderr .write .CloseWithError (err )		c .Stdout .write .CloseWithError (err )		return  true , err 	}	if  stdout .Len () > 0  {		_, _ = c .Stdout .write .Write (stdout .Bytes ())	}	if  stderr .Len () > 0  {		_, _ = c .Stderr .write .Write (stderr .Bytes ())	}	if  finished  {		c .exitCode  = exitCode 		_ = c .Stderr .write .Close ()		_ = c .Stdout .write .Close ()	}	return  finished , nil }func  (c  *Command ) sendInput (data  []byte , eof  bool ) error  {	if  err  := c .check (); err  != nil  {		return  err 	}	request  := NewSendInputRequest (c .client .url , c .shell .id , c .id , data , eof , &c .client .Parameters )	defer  request .Free ()	_ , err  := c .client .sendRequest (request )	return  err }func  (c  *Command ) ExitCode () int  {	return  c .exitCode }func  (c  *Command ) Wait () {		<-c .done }func  (w  *commandWriter ) Write (data  []byte ) (int , error ) {	w .mutex .Lock ()	defer  w .mutex .Unlock ()	if  w .eof  {		return  0 , io .ErrClosedPipe 	}	var  (		written  int 		err      error 	)	origLen  := len (data )	for  len (data ) > 0  {				n  := min (w .client .Parameters .EnvelopeSize -1000 , len (data ))		if  err  := w .sendInput (data [:n ], false ); err  != nil  {			break 		}		data  = data [n :]		written  += n 	}		if  err  == nil  && written  < origLen  {		err  = io .ErrShortWrite 	}	return  written , err }func  (w  *commandWriter ) WriteClose (data  []byte ) (int , error ) {	w .eof  = true 	return  w .Write (data )}func  min(a  int , b  int ) int  {	if  a  < b  {		return  a 	}	return  b }func  (w  *commandWriter ) Close () error  {	w .mutex .Lock ()	defer  w .mutex .Unlock ()	if  w .eof  {		return  io .ErrClosedPipe 	}	w .eof  = true 	return  w .sendInput (nil , w .eof )}func  (r  *commandReader ) Read (buf  []byte ) (int , error ) {	n , err  := r .read .Read (buf )	if  err  != nil  && errors .Is (err , io .EOF ) {		return  0 , err 	}	return  n , err } The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds  is a Go 101  project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1  (reachable from the left QR code) to get the latest news of Golds .