summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/lighttpd/apiclient.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/lighttpd/apiclient.go170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/lighttpd/apiclient.go b/src/go/collectors/go.d.plugin/modules/lighttpd/apiclient.go
new file mode 100644
index 000000000..2d4bf0fc7
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/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/go.d.plugin/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()
+ }
+}