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 }