// NewServer builds a new server which reads requests from the given Reader // and writes responses to the given Writer. func NewServer(r io.Reader, w io.Writer) *Server { return &Server{ r: NewReader(r), w: NewWriter(w), running: atomic.NewBool(false), } }
// NewClient starts up the given external process and communicates with it over // stdin and stdout using framed requests and responses. // // The Cmd MUST NOT have Stdout or Stdin set. func NewClient(cmd *exec.Cmd) (*Client, error) { stdout, err := cmd.StdoutPipe() if err != nil { return nil, fmt.Errorf("failed to create stdout pipe to %q: %v", cmd.Path, err) } stdin, err := cmd.StdinPipe() if err != nil { return nil, fmt.Errorf("failed to create stdin pipe to %q: %v", cmd.Path, err) } if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start %q: %v", cmd.Path, err) } return &Client{ stdout: stdout, stdin: stdin, running: atomic.NewBool(true), client: frame.NewClient(stdin, stdout), cmd: cmd, }, nil }
// NewTransportHandle builds a new Handle which speaks to the given transport. // // If the transport is an io.Closer, it will be closed when the handle is closed. func NewTransportHandle(name string, t envelope.Transport) (Handle, error) { client := api.NewPluginClient(multiplex.NewClient( "Plugin", envelope.NewClient(_proto, t), )) handshake, err := client.Handshake(&api.HandshakeRequest{}) if err != nil { return nil, errHandshakeFailed{Name: name, Reason: err} } if handshake.Name != name { return nil, errHandshakeFailed{ Name: name, Reason: errNameMismatch{Want: name, Got: handshake.Name}, } } if handshake.APIVersion != api.APIVersion { return nil, errHandshakeFailed{ Name: name, Reason: errAPIVersionMismatch{Want: api.APIVersion, Got: handshake.APIVersion}, } } // If we got here, the API version matches so the plugin must have // provided the Version if handshake.LibraryVersion == nil { return nil, errHandshakeFailed{ Name: name, Reason: errVersionIsRequired, } } version, err := semver.Parse(*handshake.LibraryVersion) if err != nil { return nil, errHandshakeFailed{Name: name, Reason: err} } if !compatRange.Contains(version) { return nil, errHandshakeFailed{ Name: name, Reason: errVersionMismatch{ Want: compatRange, Got: *handshake.LibraryVersion, }, } } features := make(map[api.Feature]struct{}, len(handshake.Features)) for _, feature := range handshake.Features { features[feature] = struct{}{} } return &transportHandle{ name: name, Transport: t, Client: client, Running: atomic.NewBool(true), Features: features, }, nil }