github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/rpc/server.go (about) 1 package rpc 2 3 import ( 4 "io" 5 "log" 6 "net" 7 "net/rpc" 8 9 "github.com/hashicorp/otto/app" 10 "github.com/hashicorp/yamux" 11 ) 12 13 // Server listens for network connections and then dispenses interface 14 // implementations over net/rpc. 15 type Server struct { 16 AppFunc AppFunc 17 18 // Stdout, Stderr are what this server will use instead of the 19 // normal stdin/out/err. This is because due to the multi-process nature 20 // of our plugin system, we can't use the normal process values so we 21 // make our own custom one we pipe across. 22 Stdout io.Reader 23 Stderr io.Reader 24 } 25 26 // AppFunc creates app.App when they're requested from the server. 27 type AppFunc func() app.App 28 29 // Accept accepts connections on a listener and serves requests for 30 // each incoming connection. Accept blocks; the caller typically invokes 31 // it in a go statement. 32 func (s *Server) Accept(lis net.Listener) { 33 for { 34 conn, err := lis.Accept() 35 if err != nil { 36 log.Printf("[ERR] plugin server: %s", err) 37 return 38 } 39 40 go s.ServeConn(conn) 41 } 42 } 43 44 // ServeConn runs a single connection. 45 // 46 // ServeConn blocks, serving the connection until the client hangs up. 47 func (s *Server) ServeConn(conn io.ReadWriteCloser) { 48 // First create the yamux server to wrap this connection 49 mux, err := yamux.Server(conn, nil) 50 if err != nil { 51 conn.Close() 52 log.Printf("[ERR] plugin: %s", err) 53 return 54 } 55 56 // Accept the control connection 57 control, err := mux.Accept() 58 if err != nil { 59 mux.Close() 60 log.Printf("[ERR] plugin: %s", err) 61 return 62 } 63 64 // Connect the stdstreams (in, out, err) 65 stdstream := make([]net.Conn, 2) 66 for i, _ := range stdstream { 67 stdstream[i], err = mux.Accept() 68 if err != nil { 69 mux.Close() 70 log.Printf("[ERR] plugin: accepting stream %d: %s", i, err) 71 return 72 } 73 } 74 75 // Copy std streams out to the proper place 76 go copyStream("stdout", stdstream[0], s.Stdout) 77 go copyStream("stderr", stdstream[1], s.Stderr) 78 79 // Create the broker and start it up 80 broker := newMuxBroker(mux) 81 go broker.Run() 82 83 // Use the control connection to build the dispenser and serve the 84 // connection. 85 server := rpc.NewServer() 86 server.RegisterName("Dispenser", &dispenseServer{ 87 AppFunc: s.AppFunc, 88 89 broker: broker, 90 }) 91 server.ServeConn(control) 92 } 93 94 // dispenseServer dispenses variousinterface implementations for Terraform. 95 type dispenseServer struct { 96 AppFunc AppFunc 97 98 broker *muxBroker 99 } 100 101 func (d *dispenseServer) App( 102 args interface{}, response *uint32) error { 103 id := d.broker.NextId() 104 *response = id 105 106 go func() { 107 conn, err := d.broker.Accept(id) 108 if err != nil { 109 log.Printf("[ERR] Plugin dispense: %s", err) 110 return 111 } 112 113 serve(conn, "App", &AppServer{ 114 Broker: d.broker, 115 App: d.AppFunc(), 116 }) 117 }() 118 119 return nil 120 } 121 122 func acceptAndServe(mux *muxBroker, id uint32, n string, v interface{}) { 123 conn, err := mux.Accept(id) 124 if err != nil { 125 log.Printf("[ERR] Plugin acceptAndServe: %s", err) 126 return 127 } 128 129 serve(conn, n, v) 130 } 131 132 func serve(conn io.ReadWriteCloser, name string, v interface{}) { 133 server := rpc.NewServer() 134 if err := server.RegisterName(name, v); err != nil { 135 log.Printf("[ERR] Plugin dispense: %s", err) 136 return 137 } 138 139 server.ServeConn(conn) 140 }