diff options
Diffstat (limited to 'src/go/plugin/go.d/modules/lighttpd/apiclient.go')
-rw-r--r-- | src/go/plugin/go.d/modules/lighttpd/apiclient.go | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/lighttpd/apiclient.go b/src/go/plugin/go.d/modules/lighttpd/apiclient.go new file mode 100644 index 000000000..1686272cd --- /dev/null +++ b/src/go/plugin/go.d/modules/lighttpd/apiclient.go @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package lighttpd + +import ( + "bufio" + "fmt" + "io" + "net/http" + "strconv" + "strings" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web" +) + +const ( + busyWorkers = "BusyWorkers" + idleWorkers = "IdleWorkers" + + busyServers = "BusyServers" + idleServers = "IdleServers" + totalAccesses = "Total Accesses" + totalkBytes = "Total kBytes" + uptime = "Uptime" + scoreBoard = "Scoreboard" +) + +func newAPIClient(client *http.Client, request web.Request) *apiClient { + return &apiClient{httpClient: client, request: request} +} + +type apiClient struct { + httpClient *http.Client + request web.Request +} + +func (a apiClient) getServerStatus() (*serverStatus, error) { + req, err := web.NewHTTPRequest(a.request) + + if err != nil { + return nil, fmt.Errorf("error on creating request : %v", err) + } + + resp, err := a.doRequestOK(req) + + defer closeBody(resp) + + if err != nil { + return nil, err + } + + status, err := parseResponse(resp.Body) + + if err != nil { + return nil, fmt.Errorf("error on parsing response from %s : %v", req.URL, err) + } + + return status, nil +} + +func (a apiClient) doRequestOK(req *http.Request) (*http.Response, error) { + resp, err := a.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error on request : %v", err) + } + if resp.StatusCode != http.StatusOK { + return resp, fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode) + } + return resp, nil +} + +func parseResponse(r io.Reader) (*serverStatus, error) { + s := bufio.NewScanner(r) + var status serverStatus + + for s.Scan() { + parts := strings.Split(s.Text(), ":") + if len(parts) != 2 { + continue + } + key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) + + switch key { + default: + case busyWorkers, idleWorkers: + return nil, fmt.Errorf("found '%s', apache data", key) + case busyServers: + status.Servers.Busy = mustParseInt(value) + case idleServers: + status.Servers.Idle = mustParseInt(value) + case totalAccesses: + status.Total.Accesses = mustParseInt(value) + case totalkBytes: + status.Total.KBytes = mustParseInt(value) + case uptime: + status.Uptime = mustParseInt(value) + case scoreBoard: + status.Scoreboard = parseScoreboard(value) + } + } + + return &status, nil +} + +func parseScoreboard(value string) *scoreboard { + // Descriptions from https://blog.serverdensity.com/monitor-lighttpd/ + // + // “.” = Opening the TCP connection (connect) + // “C” = Closing the TCP connection if no other HTTP request will use it (close) + // “E” = hard error + // “k” = Keeping the TCP connection open for more HTTP requests from the same client to avoid the TCP handling overhead (keep-alive) + // “r” = ReadAsMap the content of the HTTP request (read) + // “R” = ReadAsMap the content of the HTTP request (read-POST) + // “W” = Write the HTTP response to the socket (write) + // “h” = Decide action to take with the request (handle-request) + // “q” = Start of HTTP request (request-start) + // “Q” = End of HTTP request (request-end) + // “s” = Start of the HTTP request response (response-start) + // “S” = End of the HTTP request response (response-end) + // “_” Waiting for Connection (NOTE: not sure, copied the description from apache score board) + + var sb scoreboard + for _, s := range strings.Split(value, "") { + switch s { + case "_": + sb.Waiting++ + case ".": + sb.Open++ + case "C": + sb.Close++ + case "E": + sb.HardError++ + case "k": + sb.KeepAlive++ + case "r": + sb.Read++ + case "R": + sb.ReadPost++ + case "W": + sb.Write++ + case "h": + sb.HandleRequest++ + case "q": + sb.RequestStart++ + case "Q": + sb.RequestEnd++ + case "s": + sb.ResponseStart++ + case "S": + sb.ResponseEnd++ + } + } + + return &sb +} + +func mustParseInt(value string) *int64 { + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + panic(err) + } + return &v +} + +func closeBody(resp *http.Response) { + if resp != nil && resp.Body != nil { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } +} |