lxd/cluster/gateway.go
func (g *Gateway) HandlerFuncs() map[string]http.HandlerFunc {
	database := func(w http.ResponseWriter, r *http.Request) {
		if !tlsCheckCert(r, g.cert) {
			http.Error(w, "403 invalid client certificate", http.StatusForbidden)
			return
		}

		// Handle heatbeats.
		if r.Method == "PUT" {
			var nodes []db.RaftNode
			err := shared.ReadToJSON(r.Body, &nodes)
			if err != nil {
				http.Error(w, "400 invalid raft nodes payload", http.StatusBadRequest)
				return
			}
			logger.Debugf("Replace current raft nodes with %+v", nodes)
			err = g.db.Transaction(func(tx *db.NodeTx) error {
				return tx.RaftNodesReplace(nodes)
			})
			if err != nil {
				http.Error(w, "500 failed to update raft nodes", http.StatusInternalServerError)
				return
			}
			return
		}

		// From here on we require that this node is part of the raft cluster.
		if g.server == nil || g.memoryDial != nil {
			http.NotFound(w, r)
			return
		}

		// Handle database upgrade notifications.
		if r.Method == "PATCH" {
			select {
			case g.upgradeCh <- struct{}{}:
			default:
			}
			return
		}

		// Before actually establishing the gRPC SQL connection, our
		// dialer probes the node to see if it's currently the leader
		// (otherwise it tries with another node or retry later).
		if r.Method == "HEAD" {
			if g.raft.Raft().State() != raft.Leader {
				http.Error(w, "503 not leader", http.StatusServiceUnavailable)
				return
			}
			return
		}

		// Handle leader address requests.
		if r.Method == "GET" {
			leader, err := g.LeaderAddress()
			if err != nil {
				http.Error(w, "500 no elected leader", http.StatusInternalServerError)
				return
			}
			util.WriteJSON(w, map[string]string{"leader": leader}, false)
			return
		}

		if r.Header.Get("Upgrade") != "dqlite" {
			http.Error(w, "Missing or invalid upgrade header", http.StatusBadRequest)
			return
		}

		hijacker, ok := w.(http.Hijacker)
		if !ok {
			http.Error(w, "Webserver doesn't support hijacking", http.StatusInternalServerError)
			return
		}

		conn, _, err := hijacker.Hijack()
		if err != nil {
			message := errors.Wrap(err, "Failed to hijack connection").Error()
			http.Error(w, message, http.StatusInternalServerError)
			return
		}

		// Write the status line and upgrade header by hand since w.WriteHeader()
		// would fail after Hijack()
		data := []byte("HTTP/1.1 101 Switching Protocols\r\nUpgrade: dqlite\r\n\r\n")
		if n, err := conn.Write(data); err != nil || n != len(data) {
			conn.Close()
			return
		}

		g.acceptCh <- conn
	}
	raft := func(w http.ResponseWriter, r *http.Request) {
		// If we are not part of the raft cluster, reply with a
		// redirect to one of the raft nodes that we know about.
		if g.raft == nil {
			var address string
			err := g.db.Transaction(func(tx *db.NodeTx) error {
				nodes, err := tx.RaftNodes()
				if err != nil {
					return err
				}
				address = nodes[0].Address
				return nil
			})
			if err != nil {
				http.Error(w, "500 failed to fetch raft nodes", http.StatusInternalServerError)
				return
			}
			url := &url.URL{
				Scheme:   "http",
				Path:     r.URL.Path,
				RawQuery: r.URL.RawQuery,
				Host:     address,
			}
			http.Redirect(w, r, url.String(), http.StatusPermanentRedirect)
			return
		}

		// If this node is not clustered return a 404.
		if g.raft.HandlerFunc() == nil {
			http.NotFound(w, r)
			return
		}

		g.raft.HandlerFunc()(w, r)
	}

	return map[string]http.HandlerFunc{
		databaseEndpoint: database,
		raftEndpoint:     raft,
	}
}
func (g *Gateway) HandlerFuncs() map[string]http.HandlerFunc {
	database := func(w http.ResponseWriter, r *http.Request) {
		if !tlsCheckCert(r, g.cert) {
			http.Error(w, "403 invalid client certificate", http.StatusForbidden)
			return
		}

		// Handle heatbeats.
		if r.Method == "PUT" {
			var nodes []db.RaftNode
			err := shared.ReadToJSON(r.Body, &nodes)
			if err != nil {
				http.Error(w, "400 invalid raft nodes payload", http.StatusBadRequest)
				return
			}
			logger.Debugf("Replace current raft nodes with %+v", nodes)
			err = g.db.Transaction(func(tx *db.NodeTx) error {
				return tx.RaftNodesReplace(nodes)
			})
			if err != nil {
				http.Error(w, "500 failed to update raft nodes", http.StatusInternalServerError)
				return
			}
			return
		}

		// From here on we require that this node is part of the raft cluster.
		if g.server == nil || g.memoryDial != nil {
			http.NotFound(w, r)
			return
		}

		// Handle database upgrade notifications.
		if r.Method == "PATCH" {
			select {
			case g.upgradeCh <- struct{}{}:
			default:
			}
			return
		}

		// Before actually establishing the gRPC SQL connection, our
		// dialer probes the node to see if it's currently the leader
		// (otherwise it tries with another node or retry later).
		if r.Method == "HEAD" {
			if g.raft.Raft().State() != raft.Leader {
				http.Error(w, "503 not leader", http.StatusServiceUnavailable)
				return
			}
			return
		}

		// Handle leader address requests.
		if r.Method == "GET" {
			leader, err := g.LeaderAddress()
			if err != nil {
				http.Error(w, "500 no elected leader", http.StatusInternalServerError)
				return
			}
			util.WriteJSON(w, map[string]string{"leader": leader}, false)
			return
		}

		if r.Header.Get("Upgrade") != "dqlite" {
			http.Error(w, "Missing or invalid upgrade header", http.StatusBadRequest)
			return
		}

		hijacker, ok := w.(http.Hijacker)
		if !ok {
			http.Error(w, "Webserver doesn't support hijacking", http.StatusInternalServerError)
			return
		}

		conn, _, err := hijacker.Hijack()
		if err != nil {
			message := errors.Wrap(err, "Failed to hijack connection").Error()
			http.Error(w, message, http.StatusInternalServerError)
			return
		}

		// Write the status line and upgrade header by hand since w.WriteHeader()
		// would fail after Hijack()
		data := []byte("HTTP/1.1 101 Switching Protocols\r\nUpgrade: dqlite\r\n\r\n")
		if n, err := conn.Write(data); err != nil || n != len(data) {
			conn.Close()
			return
		}

		g.acceptCh <- conn
	}
	raft := func(w http.ResponseWriter, r *http.Request) {
		// If we are not part of the raft cluster, reply with a
		// redirect to one of the raft nodes that we know about.
		if g.raft == nil {
			var address string
			err := g.db.Transaction(func(tx *db.NodeTx) error {
				nodes, err := tx.RaftNodes()
				if err != nil {
					return err
				}
				address = nodes[0].Address
				return nil
			})
			if err != nil {
				http.Error(w, "500 failed to fetch raft nodes", http.StatusInternalServerError)
				return
			}
			url := &url.URL{
				Scheme:   "http",
				Path:     r.URL.Path,
				RawQuery: r.URL.RawQuery,
				Host:     address,
			}
			http.Redirect(w, r, url.String(), http.StatusPermanentRedirect)
			return
		}

		// If this node is not clustered return a 404.
		if g.raft.HandlerFunc() == nil {
			http.NotFound(w, r)
			return
		}

		g.raft.HandlerFunc()(w, r)
	}

	return map[string]http.HandlerFunc{
		databaseEndpoint: database,
		raftEndpoint:     raft,
	}
}
// HandlerFuncs returns the HTTP handlers that should be added to the REST API
// endpoint in order to handle database-related requests.
//
// There are two handlers, one for the /internal/raft endpoint and the other
// for /internal/db, which handle respectively raft and gRPC-SQL requests.
//
// These handlers might return 404, either because this LXD node is a
// non-clustered node not available over the network or because it is not a
// database node part of the dqlite cluster.