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