Beispiel #1
0
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()
}
Beispiel #2
0
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()
}
Beispiel #3
0
// 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
}
Beispiel #4
0
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()
}
Beispiel #5
0
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)
}
Beispiel #6
0
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()
}
Beispiel #7
0
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()
}
Beispiel #8
0
// 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
}
Beispiel #9
0
// 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
}
Beispiel #10
0
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
}