github.com/go-graphite/carbonapi@v0.17.0/cmd/carbonapi/http/functions_handler.go (about)

     1  package http
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/go-graphite/carbonapi/carbonapipb"
    11  	"github.com/go-graphite/carbonapi/expr/metadata"
    12  	"github.com/go-graphite/carbonapi/expr/types"
    13  	utilctx "github.com/go-graphite/carbonapi/util/ctx"
    14  	"github.com/lomik/zapwriter"
    15  	"go.uber.org/zap"
    16  )
    17  
    18  func functionsHandler(w http.ResponseWriter, r *http.Request) {
    19  	// TODO: Implement helper for specific functions
    20  	t0 := time.Now()
    21  	username, _, _ := r.BasicAuth()
    22  
    23  	srcIP, srcPort := splitRemoteAddr(r.RemoteAddr)
    24  
    25  	accessLogger := zapwriter.Logger("access")
    26  	var accessLogDetails = carbonapipb.AccessLogDetails{
    27  		Handler:        "functions",
    28  		Username:       username,
    29  		URL:            r.URL.RequestURI(),
    30  		PeerIP:         srcIP,
    31  		PeerPort:       srcPort,
    32  		Host:           r.Host,
    33  		Referer:        r.Referer(),
    34  		URI:            r.RequestURI,
    35  		RequestHeaders: utilctx.GetLogHeaders(r.Context()),
    36  	}
    37  
    38  	logAsError := false
    39  	defer func() {
    40  		deferredAccessLogging(accessLogger, &accessLogDetails, t0, logAsError)
    41  	}()
    42  
    43  	ApiMetrics.RequestsH.Add(1)
    44  
    45  	err := r.ParseForm()
    46  	if err != nil {
    47  		http.Error(w, http.StatusText(http.StatusBadRequest)+": "+err.Error(), http.StatusBadRequest)
    48  		accessLogDetails.HTTPCode = http.StatusBadRequest
    49  		accessLogDetails.Reason = err.Error()
    50  		logAsError = true
    51  		return
    52  	}
    53  
    54  	grouped := false
    55  	nativeOnly := false
    56  	groupedStr := r.FormValue("grouped")
    57  	prettyStr := r.FormValue("pretty")
    58  	nativeOnlyStr := r.FormValue("nativeOnly")
    59  	var marshaler func(interface{}) ([]byte, error)
    60  
    61  	if groupedStr == "1" {
    62  		grouped = true
    63  	}
    64  
    65  	if prettyStr == "1" {
    66  		marshaler = func(v interface{}) ([]byte, error) {
    67  			return json.MarshalIndent(v, "", "\t")
    68  		}
    69  	} else {
    70  		marshaler = json.Marshal
    71  	}
    72  
    73  	if nativeOnlyStr == "1" {
    74  		nativeOnly = true
    75  	}
    76  
    77  	path := strings.Split(r.URL.EscapedPath(), "/")
    78  	function := ""
    79  	if len(path) >= 3 {
    80  		function = path[2]
    81  	}
    82  
    83  	var b []byte
    84  	if !nativeOnly {
    85  		metadata.FunctionMD.RLock()
    86  		if function != "" {
    87  			b, err = marshaler(metadata.FunctionMD.Descriptions[function])
    88  		} else if grouped {
    89  			b, err = marshaler(metadata.FunctionMD.DescriptionsGrouped)
    90  		} else {
    91  			b, err = marshaler(metadata.FunctionMD.Descriptions)
    92  		}
    93  		metadata.FunctionMD.RUnlock()
    94  	} else {
    95  		metadata.FunctionMD.RLock()
    96  		if function != "" {
    97  			if !metadata.FunctionMD.Descriptions[function].Proxied {
    98  				b, err = marshaler(metadata.FunctionMD.Descriptions[function])
    99  			} else {
   100  				err = fmt.Errorf("%v is proxied to graphite-web and nativeOnly was specified", function)
   101  			}
   102  		} else if grouped {
   103  			descGrouped := make(map[string]map[string]types.FunctionDescription)
   104  			for groupName, description := range metadata.FunctionMD.DescriptionsGrouped {
   105  				desc := make(map[string]types.FunctionDescription)
   106  				for f, d := range description {
   107  					if d.Proxied {
   108  						continue
   109  					}
   110  					desc[f] = d
   111  				}
   112  				if len(desc) > 0 {
   113  					descGrouped[groupName] = desc
   114  				}
   115  			}
   116  			b, err = marshaler(descGrouped)
   117  		} else {
   118  			desc := make(map[string]types.FunctionDescription)
   119  			for f, d := range metadata.FunctionMD.Descriptions {
   120  				if d.Proxied {
   121  					continue
   122  				}
   123  				desc[f] = d
   124  			}
   125  			b, err = marshaler(desc)
   126  		}
   127  		metadata.FunctionMD.RUnlock()
   128  	}
   129  
   130  	if err != nil {
   131  		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
   132  		accessLogDetails.HTTPCode = http.StatusInternalServerError
   133  		accessLogDetails.Reason = err.Error()
   134  		logAsError = true
   135  		return
   136  	}
   137  
   138  	_, _ = w.Write(b)
   139  	accessLogDetails.Runtime = time.Since(t0).Seconds()
   140  	accessLogDetails.HTTPCode = http.StatusOK
   141  
   142  	accessLogger.Info("request served", zap.Any("data", accessLogDetails))
   143  }