github.com/MetalBlockchain/metalgo@v1.11.9/api/health/handler.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package health
     5  
     6  import (
     7  	"encoding/json"
     8  	"net/http"
     9  
    10  	"github.com/gorilla/rpc/v2"
    11  
    12  	"github.com/MetalBlockchain/metalgo/utils/logging"
    13  
    14  	avajson "github.com/MetalBlockchain/metalgo/utils/json"
    15  )
    16  
    17  // NewGetAndPostHandler returns a health handler that supports GET and jsonrpc
    18  // POST requests.
    19  func NewGetAndPostHandler(log logging.Logger, reporter Reporter) (http.Handler, error) {
    20  	newServer := rpc.NewServer()
    21  	codec := avajson.NewCodec()
    22  	newServer.RegisterCodec(codec, "application/json")
    23  	newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
    24  
    25  	getHandler := NewGetHandler(reporter.Health)
    26  
    27  	// If a GET request is sent, we respond with a 200 if the node is healthy or
    28  	// a 503 if the node isn't healthy.
    29  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    30  		if r.Method != http.MethodGet {
    31  			newServer.ServeHTTP(w, r)
    32  			return
    33  		}
    34  
    35  		getHandler.ServeHTTP(w, r)
    36  	})
    37  
    38  	err := newServer.RegisterService(
    39  		&Service{
    40  			log:    log,
    41  			health: reporter,
    42  		},
    43  		"health",
    44  	)
    45  	return handler, err
    46  }
    47  
    48  // NewGetHandler return a health handler that supports GET requests reporting
    49  // the result of the provided [reporter].
    50  func NewGetHandler(reporter func(tags ...string) (map[string]Result, bool)) http.Handler {
    51  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    52  		// Make sure the content type is set before writing the header.
    53  		w.Header().Set("Content-Type", "application/json")
    54  
    55  		tags := r.URL.Query()["tag"]
    56  		checks, healthy := reporter(tags...)
    57  		if !healthy {
    58  			// If a health check has failed, we should return a 503.
    59  			w.WriteHeader(http.StatusServiceUnavailable)
    60  		}
    61  		// The encoder will call write on the writer, which will write the
    62  		// header with a 200.
    63  		_ = json.NewEncoder(w).Encode(APIReply{
    64  			Checks:  checks,
    65  			Healthy: healthy,
    66  		})
    67  	})
    68  }