// This unexported open method is used both directly above in the Open // function, and also the OpenWithVersion function below to explicitly cause // the API server to think that the client is older than it really is. func open(info *Info, opts DialOpts, loginFunc func(st *State, tag, pwd, nonce string) error) (*State, error) { conn, err := Connect(info, "", nil, opts) if err != nil { return nil, errors.Trace(err) } client := rpc.NewConn(jsoncodec.NewWebsocket(conn), nil) client.Start() st := &State{ client: client, conn: conn, addr: conn.Config().Location.Host, serverScheme: "https", serverRootAddress: conn.Config().Location.Host, // why are the contents of the tag (username and password) written into the // state structure BEFORE login ?!? tag: toString(info.Tag), password: info.Password, certPool: conn.Config().TlsConfig.RootCAs, } if info.Tag != nil || info.Password != "" { if err := loginFunc(st, info.Tag.String(), info.Password, info.Nonce); err != nil { conn.Close() return nil, err } } st.broken = make(chan struct{}) st.closed = make(chan struct{}) go st.heartbeatMonitor() return st, nil }
func (srv *Server) serveConn(wsConn *websocket.Conn, reqNotifier *requestNotifier, modelUUID string) error { codec := jsoncodec.NewWebsocket(wsConn) if loggo.GetLogger("juju.rpc.jsoncodec").EffectiveLogLevel() <= loggo.TRACE { codec.SetLogging(true) } var notifier rpc.RequestNotifier if logger.EffectiveLogLevel() <= loggo.DEBUG { // Incur request monitoring overhead only if we // know we'll need it. notifier = reqNotifier } conn := rpc.NewConn(codec, notifier) h, err := srv.newAPIHandler(conn, reqNotifier, modelUUID) if err != nil { conn.ServeFinder(&errRoot{err}, serverError) } else { adminApis := make(map[int]interface{}) for apiVersion, factory := range srv.adminApiFactories { adminApis[apiVersion] = factory(srv, h, reqNotifier) } conn.ServeFinder(newAnonRoot(h, adminApis), serverError) } conn.Start() select { case <-conn.Dead(): case <-srv.tomb.Dying(): } return conn.Close() }
func (srv *Server) serveConn(wsConn *websocket.Conn, reqNotifier *requestNotifier, envUUID string) error { codec := jsoncodec.NewWebsocket(wsConn) if loggo.GetLogger("juju.rpc.jsoncodec").EffectiveLogLevel() <= loggo.TRACE { codec.SetLogging(true) } var notifier rpc.RequestNotifier if logger.EffectiveLogLevel() <= loggo.DEBUG { // Incur request monitoring overhead only if we // know we'll need it. notifier = reqNotifier } conn := rpc.NewConn(codec, notifier) err := srv.validateEnvironUUID(envUUID) if err != nil { conn.Serve(&errRoot{err}, serverError) } else { conn.Serve(newStateServer(srv, conn, reqNotifier, srv.limiter), serverError) } conn.Start() select { case <-conn.Dead(): case <-srv.tomb.Dying(): } return conn.Close() }
// newRPCClientServer starts an RPC server serving a connection from a // single client. When the server has finished serving the connection, // it sends a value on the returned channel. // If bidir is true, requests can flow in both directions. func newRPCClientServer( c *gc.C, root interface{}, tfErr func(error) error, bidir bool, ) (client *rpc.Conn, srvDone chan error, serverNotifier *notifier) { l, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, jc.ErrorIsNil) srvDone = make(chan error, 1) serverNotifier = new(notifier) go func() { conn, err := l.Accept() if err != nil { srvDone <- nil return } defer l.Close() role := roleServer if bidir { role = roleBoth } rpcConn := rpc.NewConn(NewJSONCodec(conn, role), serverNotifier) if custroot, ok := root.(*CustomRoot); ok { rpcConn.ServeRoot(custroot, tfErr) custroot.root.conn = rpcConn } else { rpcConn.Serve(root, tfErr) } if root, ok := root.(*Root); ok { root.conn = rpcConn } rpcConn.Start() <-rpcConn.Dead() srvDone <- rpcConn.Close() }() conn, err := net.Dial("tcp", l.Addr().String()) c.Assert(err, jc.ErrorIsNil) role := roleClient if bidir { role = roleBoth } client = rpc.NewConn(NewJSONCodec(conn, role), ¬ifier{}) client.Start() return client, srvDone, serverNotifier }
func (srv *Server) serveConn(wsConn *websocket.Conn, modelUUID string) { codec := jsoncodec.NewWebsocket(wsConn) conn := rpc.NewConn(codec, &fakeobserver.Instance{}) root := allVersions{ rpcreflect.ValueOf(reflect.ValueOf(srv.newRoot(modelUUID))), } conn.ServeRoot(root, nil) conn.Start() <-conn.Dead() conn.Close() }
func (s *dispatchSuite) SetUpSuite(c *gc.C) { rpcServer := func(ws *websocket.Conn) { codec := jsoncodec.NewWebsocket(ws) conn := rpc.NewConn(codec, nil) conn.Serve(&DispatchRoot{}, nil) conn.Start() <-conn.Dead() } http.Handle("/rpc", websocket.Handler(rpcServer)) s.server = httptest.NewServer(nil) s.serverAddr = s.server.Listener.Addr().String() s.ready = make(chan struct{}, 1) }
func (srv *Server) serveConn(wsConn *websocket.Conn, modelUUID string, apiObserver observer.Observer, host string) error { codec := jsoncodec.NewWebsocket(wsConn) conn := rpc.NewConn(codec, apiObserver) // Note that we don't overwrite modelUUID here because // newAPIHandler treats an empty modelUUID as signifying // the API version used. resolvedModelUUID, err := validateModelUUID(validateArgs{ statePool: srv.statePool, modelUUID: modelUUID, }) var ( st *state.State h *apiHandler ) if err == nil { st, err = srv.statePool.Get(resolvedModelUUID) } if err == nil { defer func() { err := srv.statePool.Release(resolvedModelUUID) if err != nil { logger.Errorf("error releasing %v back into the state pool:", err) } }() h, err = newAPIHandler(srv, st, conn, modelUUID, host) } if err != nil { conn.ServeRoot(&errRoot{errors.Trace(err)}, serverError) } else { adminAPIs := make(map[int]interface{}) for apiVersion, factory := range srv.adminAPIFactories { adminAPIs[apiVersion] = factory(srv, h, apiObserver) } conn.ServeRoot(newAnonRoot(h, adminAPIs), serverError) } conn.Start() select { case <-conn.Dead(): case <-srv.tomb.Dying(): } return conn.Close() }
func (srv *Server) serveConn(wsConn *websocket.Conn, modelUUID string, apiObserver observer.Observer, host string) error { codec := jsoncodec.NewWebsocket(wsConn) conn := rpc.NewConn(codec, apiObserver) h, err := srv.newAPIHandler(conn, modelUUID, host) if err != nil { conn.ServeRoot(&errRoot{err}, serverError) } else { adminAPIs := make(map[int]interface{}) for apiVersion, factory := range srv.adminAPIFactories { adminAPIs[apiVersion] = factory(srv, h, apiObserver) } conn.ServeRoot(newAnonRoot(h, adminAPIs), serverError) } conn.Start() select { case <-conn.Dead(): case <-srv.tomb.Dying(): } return conn.Close() }
// open is the unexported version of open that also includes // an explicit clock instance argument. func open( info *Info, opts DialOpts, clock clock.Clock, ) (Connection, error) { if err := info.Validate(); err != nil { return nil, errors.Annotate(err, "validating info for opening an API connection") } if clock == nil { return nil, errors.NotValidf("nil clock") } conn, tlsConfig, err := dialAPI(info, opts) if err != nil { return nil, errors.Trace(err) } client := rpc.NewConn(jsoncodec.NewWebsocket(conn), observer.None()) client.Start() bakeryClient := opts.BakeryClient if bakeryClient == nil { bakeryClient = httpbakery.NewClient() } else { // Make a copy of the bakery client and its HTTP client c := *opts.BakeryClient bakeryClient = &c httpc := *bakeryClient.Client bakeryClient.Client = &httpc } apiHost := conn.Config().Location.Host // Technically when there's no CACert, we don't need this // machinery, because we could just use http.DefaultTransport // for everything, but it's easier just to leave it in place. bakeryClient.Client.Transport = &hostSwitchingTransport{ primaryHost: apiHost, primary: utils.NewHttpTLSTransport(tlsConfig), fallback: http.DefaultTransport, } st := &state{ client: client, conn: conn, clock: clock, addr: apiHost, cookieURL: &url.URL{ Scheme: "https", Host: conn.Config().Location.Host, Path: "/", }, pingerFacadeVersion: facadeVersions["Pinger"], serverScheme: "https", serverRootAddress: conn.Config().Location.Host, // We populate the username and password before // login because, when doing HTTP requests, we'll want // to use the same username and password for authenticating // those. If login fails, we discard the connection. tag: tagToString(info.Tag), password: info.Password, macaroons: info.Macaroons, nonce: info.Nonce, tlsConfig: tlsConfig, bakeryClient: bakeryClient, modelTag: info.ModelTag, } if !info.SkipLogin { if err := st.Login(info.Tag, info.Password, info.Nonce, info.Macaroons); err != nil { conn.Close() return nil, errors.Trace(err) } } st.broken = make(chan struct{}) st.closed = make(chan struct{}) go (&monitor{ clock: clock, ping: st.Ping, pingPeriod: PingPeriod, pingTimeout: pingTimeout, closed: st.closed, dead: client.Dead(), broken: st.broken, }).run() return st, nil }
// This unexported open method is used both directly above in the Open // function, and also the OpenWithVersion function below to explicitly cause // the API server to think that the client is older than it really is. func open( info *Info, opts DialOpts, loginFunc func(st *state, tag names.Tag, pwd, nonce string, ms []macaroon.Slice) error, ) (Connection, error) { if err := info.Validate(); err != nil { return nil, errors.Annotate(err, "validating info for opening an API connection") } conn, tlsConfig, err := connectWebsocket(info, opts) if err != nil { return nil, errors.Trace(err) } client := rpc.NewConn(jsoncodec.NewWebsocket(conn), nil) client.Start() bakeryClient := opts.BakeryClient if bakeryClient == nil { bakeryClient = httpbakery.NewClient() } else { // Make a copy of the bakery client and its // HTTP client c := *opts.BakeryClient bakeryClient = &c httpc := *bakeryClient.Client bakeryClient.Client = &httpc } apiHost := conn.Config().Location.Host bakeryClient.Client.Transport = &hostSwitchingTransport{ primaryHost: apiHost, primary: utils.NewHttpTLSTransport(tlsConfig), fallback: http.DefaultTransport, } st := &state{ client: client, conn: conn, addr: apiHost, cookieURL: &url.URL{ Scheme: "https", Host: conn.Config().Location.Host, Path: "/", }, serverScheme: "https", serverRootAddress: conn.Config().Location.Host, // why are the contents of the tag (username and password) written into the // state structure BEFORE login ?!? tag: tagToString(info.Tag), password: info.Password, macaroons: info.Macaroons, nonce: info.Nonce, tlsConfig: tlsConfig, bakeryClient: bakeryClient, } if !info.SkipLogin { if err := loginFunc(st, info.Tag, info.Password, info.Nonce, info.Macaroons); err != nil { conn.Close() return nil, err } } st.broken = make(chan struct{}) st.closed = make(chan struct{}) go st.heartbeatMonitor() return st, nil }
func Open(info *Info, opts DialOpts) (*State, error) { if len(info.Addrs) == 0 { return nil, fmt.Errorf("no API addresses to connect to") } pool := x509.NewCertPool() xcert, err := cert.ParseCert(info.CACert) if err != nil { return nil, err } pool.AddCert(xcert) var environUUID string if info.EnvironTag != nil { environUUID = info.EnvironTag.Id() } // Dial all addresses at reasonable intervals. try := parallel.NewTry(0, nil) defer try.Kill() var addrs []string for _, addr := range info.Addrs { if strings.HasPrefix(addr, "localhost:") { addrs = append(addrs, addr) break } } if len(addrs) == 0 { addrs = info.Addrs } for _, addr := range addrs { err := dialWebsocket(addr, environUUID, opts, pool, try) if err == parallel.ErrStopped { break } if err != nil { return nil, err } select { case <-time.After(opts.DialAddressInterval): case <-try.Dead(): } } try.Close() result, err := try.Result() if err != nil { return nil, err } conn := result.(*websocket.Conn) logger.Infof("connection established to %q", conn.RemoteAddr()) client := rpc.NewConn(jsoncodec.NewWebsocket(conn), nil) client.Start() st := &State{ client: client, conn: conn, addr: conn.Config().Location.Host, serverRoot: "https://" + conn.Config().Location.Host, // why are the contents of the tag (username and password) written into the // state structure BEFORE login ?!? tag: toString(info.Tag), password: info.Password, certPool: pool, } if info.Tag != nil || info.Password != "" { if err := st.Login(info.Tag.String(), info.Password, info.Nonce); err != nil { conn.Close() return nil, err } } st.broken = make(chan struct{}) st.closed = make(chan struct{}) go st.heartbeatMonitor() return st, nil }