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  }