summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/nginx/apiclient.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/nginx/apiclient.go168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/nginx/apiclient.go b/src/go/collectors/go.d.plugin/modules/nginx/apiclient.go
new file mode 100644
index 000000000..8e1003b44
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/nginx/apiclient.go
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package nginx
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net/http"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/web"
+)
+
+const (
+ connActive = "connActive"
+ connAccepts = "connAccepts"
+ connHandled = "connHandled"
+ requests = "requests"
+ requestTime = "requestTime"
+ connReading = "connReading"
+ connWriting = "connWriting"
+ connWaiting = "connWaiting"
+)
+
+var (
+ nginxSeq = []string{
+ connActive,
+ connAccepts,
+ connHandled,
+ requests,
+ connReading,
+ connWriting,
+ connWaiting,
+ }
+ tengineSeq = []string{
+ connActive,
+ connAccepts,
+ connHandled,
+ requests,
+ requestTime,
+ connReading,
+ connWriting,
+ connWaiting,
+ }
+
+ reStatus = regexp.MustCompile(`^Active connections: ([0-9]+)\n[^\d]+([0-9]+) ([0-9]+) ([0-9]+) ?([0-9]+)?\nReading: ([0-9]+) Writing: ([0-9]+) Waiting: ([0-9]+)`)
+)
+
+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) getStubStatus() (*stubStatus, 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 := parseStubStatus(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("error on parsing response : %v", err)
+ }
+
+ return status, nil
+}
+
+func (a apiClient) doRequestOK(req *http.Request) (*http.Response, error) {
+ resp, err := a.httpClient.Do(req)
+ if err != nil {
+ return resp, 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, err
+}
+
+func closeBody(resp *http.Response) {
+ if resp != nil && resp.Body != nil {
+ _, _ = io.Copy(io.Discard, resp.Body)
+ _ = resp.Body.Close()
+ }
+}
+
+func parseStubStatus(r io.Reader) (*stubStatus, error) {
+ sc := bufio.NewScanner(r)
+ var lines []string
+
+ for sc.Scan() {
+ lines = append(lines, strings.Trim(sc.Text(), "\r\n "))
+ }
+
+ parsed := reStatus.FindStringSubmatch(strings.Join(lines, "\n"))
+
+ if len(parsed) == 0 {
+ return nil, fmt.Errorf("can't parse '%v'", lines)
+ }
+
+ parsed = parsed[1:]
+
+ var (
+ seq []string
+ status stubStatus
+ )
+
+ switch len(parsed) {
+ default:
+ return nil, fmt.Errorf("invalid number of fields, got %d, expect %d or %d", len(parsed), len(nginxSeq), len(tengineSeq))
+ case len(nginxSeq):
+ seq = nginxSeq
+ case len(tengineSeq):
+ seq = tengineSeq
+ }
+
+ for i, key := range seq {
+ strValue := parsed[i]
+ if strValue == "" {
+ continue
+ }
+ value := mustParseInt(strValue)
+ switch key {
+ default:
+ return nil, fmt.Errorf("unknown key in seq : %s", key)
+ case connActive:
+ status.Connections.Active = value
+ case connAccepts:
+ status.Connections.Accepts = value
+ case connHandled:
+ status.Connections.Handled = value
+ case requests:
+ status.Requests.Total = value
+ case connReading:
+ status.Connections.Reading = value
+ case connWriting:
+ status.Connections.Writing = value
+ case connWaiting:
+ status.Connections.Waiting = value
+ case requestTime:
+ status.Requests.Time = &value
+ }
+ }
+
+ return &status, nil
+}
+
+func mustParseInt(value string) int64 {
+ v, err := strconv.ParseInt(value, 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}