launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/apiserver/apiserver.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver
     5  
     6  import (
     7  	"crypto/tls"
     8  	"net"
     9  	"net/http"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"code.google.com/p/go.net/websocket"
    15  	"github.com/loggo/loggo"
    16  	"launchpad.net/errgo/errors"
    17  	"launchpad.net/tomb"
    18  
    19  	"launchpad.net/juju-core/rpc"
    20  	"launchpad.net/juju-core/rpc/jsoncodec"
    21  	"launchpad.net/juju-core/state"
    22  	"launchpad.net/juju-core/state/apiserver/common"
    23  )
    24  
    25  var logger = loggo.GetLogger("juju.state.apiserver")
    26  
    27  var mask = errors.Mask
    28  
    29  // Server holds the server side of the API.
    30  type Server struct {
    31  	tomb    tomb.Tomb
    32  	wg      sync.WaitGroup
    33  	state   *state.State
    34  	addr    net.Addr
    35  	dataDir string
    36  }
    37  
    38  // Serve serves the given state by accepting requests on the given
    39  // listener, using the given certificate and key (in PEM format) for
    40  // authentication.
    41  func NewServer(s *state.State, addr string, cert, key []byte, datadir string) (*Server, error) {
    42  	lis, err := net.Listen("tcp", addr)
    43  	if err != nil {
    44  		return nil, mask(err)
    45  	}
    46  	logger.Infof("listening on %q", lis.Addr())
    47  	tlsCert, err := tls.X509KeyPair(cert, key)
    48  	if err != nil {
    49  		return nil, mask(err)
    50  	}
    51  	srv := &Server{
    52  		state:   s,
    53  		addr:    lis.Addr(),
    54  		dataDir: datadir,
    55  	}
    56  	// TODO(rog) check that *srvRoot is a valid type for using
    57  	// as an RPC server.
    58  	lis = tls.NewListener(lis, &tls.Config{
    59  		Certificates: []tls.Certificate{tlsCert},
    60  	})
    61  	go srv.run(lis)
    62  	return srv, nil
    63  }
    64  
    65  // Dead returns a channel that signals when the server has exited.
    66  func (srv *Server) Dead() <-chan struct{} {
    67  	return srv.tomb.Dead()
    68  }
    69  
    70  // Stop stops the server and returns when all running requests
    71  // have completed.
    72  func (srv *Server) Stop() error {
    73  	srv.tomb.Kill(nil)
    74  	return srv.tomb.Wait()
    75  }
    76  
    77  // Kill implements worker.Worker.Kill.
    78  func (srv *Server) Kill() {
    79  	srv.tomb.Kill(nil)
    80  }
    81  
    82  // Wait implements worker.Worker.Wait.
    83  func (srv *Server) Wait() error {
    84  	return srv.tomb.Wait()
    85  }
    86  
    87  type requestNotifier struct {
    88  	id    int64
    89  	start time.Time
    90  
    91  	mu   sync.Mutex
    92  	tag_ string
    93  }
    94  
    95  var globalCounter int64
    96  
    97  func newRequestNotifier() *requestNotifier {
    98  	return &requestNotifier{
    99  		id:    atomic.AddInt64(&globalCounter, 1),
   100  		tag_:  "<unknown>",
   101  		start: time.Now(),
   102  	}
   103  }
   104  
   105  func (n *requestNotifier) login(tag string) {
   106  	n.mu.Lock()
   107  	n.tag_ = tag
   108  	n.mu.Unlock()
   109  }
   110  
   111  func (n *requestNotifier) tag() (tag string) {
   112  	n.mu.Lock()
   113  	tag = n.tag_
   114  	n.mu.Unlock()
   115  	return
   116  }
   117  
   118  func (n *requestNotifier) ServerRequest(hdr *rpc.Header, body interface{}) {
   119  	if hdr.Request.Type == "Pinger" && hdr.Request.Action == "Ping" {
   120  		return
   121  	}
   122  	// TODO(rog) 2013-10-11 remove secrets from some requests.
   123  	logger.Debugf("<- [%X] %s %s", n.id, n.tag(), jsoncodec.DumpRequest(hdr, body))
   124  }
   125  
   126  func (n *requestNotifier) ServerReply(req rpc.Request, hdr *rpc.Header, body interface{}, timeSpent time.Duration) {
   127  	if req.Type == "Pinger" && req.Action == "Ping" {
   128  		return
   129  	}
   130  	logger.Debugf("-> [%X] %s %s %s %s[%q].%s", n.id, n.tag(), timeSpent, jsoncodec.DumpRequest(hdr, body), req.Type, req.Id, req.Action)
   131  }
   132  
   133  func (n *requestNotifier) join(req *http.Request) {
   134  	logger.Infof("[%X] API connection from %s", n.id, req.RemoteAddr)
   135  }
   136  
   137  func (n *requestNotifier) leave() {
   138  	logger.Infof("[%X] %s API connection terminated after %v", n.id, n.tag(), time.Since(n.start))
   139  }
   140  
   141  func (n requestNotifier) ClientRequest(hdr *rpc.Header, body interface{}) {
   142  }
   143  
   144  func (n requestNotifier) ClientReply(req rpc.Request, hdr *rpc.Header, body interface{}) {
   145  }
   146  
   147  func (srv *Server) run(lis net.Listener) {
   148  	defer srv.tomb.Done()
   149  	defer srv.wg.Wait() // wait for any outstanding requests to complete.
   150  	srv.wg.Add(1)
   151  	go func() {
   152  		<-srv.tomb.Dying()
   153  		lis.Close()
   154  		srv.wg.Done()
   155  	}()
   156  	mux := http.NewServeMux()
   157  	mux.HandleFunc("/", srv.apiHandler)
   158  	mux.Handle("/charms", &charmsHandler{state: srv.state})
   159  	// The error from http.Serve is not interesting.
   160  	http.Serve(lis, mux)
   161  }
   162  
   163  func (srv *Server) apiHandler(w http.ResponseWriter, req *http.Request) {
   164  	reqNotifier := newRequestNotifier()
   165  	reqNotifier.join(req)
   166  	defer reqNotifier.leave()
   167  	wsServer := websocket.Server{
   168  		Handler: func(conn *websocket.Conn) {
   169  			srv.wg.Add(1)
   170  			defer srv.wg.Done()
   171  			// If we've got to this stage and the tomb is still
   172  			// alive, we know that any tomb.Kill must occur after we
   173  			// have called wg.Add, so we avoid the possibility of a
   174  			// handler goroutine running after Stop has returned.
   175  			if srv.tomb.Err() != tomb.ErrStillAlive {
   176  				return
   177  			}
   178  			if err := srv.serveConn(conn, reqNotifier); err != nil {
   179  				logger.Errorf("error serving RPCs: %v", err)
   180  			}
   181  		},
   182  	}
   183  	wsServer.ServeHTTP(w, req)
   184  }
   185  
   186  // Addr returns the address that the server is listening on.
   187  func (srv *Server) Addr() string {
   188  	return srv.addr.String()
   189  }
   190  
   191  func (srv *Server) serveConn(wsConn *websocket.Conn, reqNotifier *requestNotifier) error {
   192  	codec := jsoncodec.NewWebsocket(wsConn)
   193  	if loggo.GetLogger("juju.rpc.jsoncodec").EffectiveLogLevel() <= loggo.TRACE {
   194  		codec.SetLogging(true)
   195  	}
   196  	var notifier rpc.RequestNotifier
   197  	if logger.EffectiveLogLevel() <= loggo.DEBUG {
   198  		// Incur request monitoring overhead only if we
   199  		// know we'll need it.
   200  		notifier = reqNotifier
   201  	}
   202  	conn := rpc.NewConn(codec, notifier)
   203  	conn.Serve(newStateServer(srv, conn, reqNotifier), serverError)
   204  	conn.Start()
   205  	select {
   206  	case <-conn.Dead():
   207  	case <-srv.tomb.Dying():
   208  	}
   209  	return conn.Close()
   210  }
   211  
   212  func serverError(err error) error {
   213  	return common.ServerError(err)
   214  }
   215  
   216  var logRequests = true