master
go 146 lines 3.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package lighttpd
4
5 import (
6 "bufio"
7 "fmt"
8 "io"
9 "strconv"
10 "strings"
11 )
12
13 const (
14 busyWorkers = "BusyWorkers"
15 idleWorkers = "IdleWorkers"
16
17 busyServers = "BusyServers"
18 idleServers = "IdleServers"
19 totalAccesses = "Total Accesses"
20 totalkBytes = "Total kBytes"
21 uptime = "Uptime"
22 scoreBoard = "Scoreboard"
23 )
24
25 type (
26 serverStatus struct {
27 Total struct {
28 Accesses *int64 `stm:"accesses"`
29 KBytes *int64 `stm:"kBytes"`
30 } `stm:"total"`
31 Servers struct {
32 Busy *int64 `stm:"busy_servers"`
33 Idle *int64 `stm:"idle_servers"`
34 } `stm:""`
35 Uptime *int64 `stm:"uptime"`
36 Scoreboard *scoreboard `stm:"scoreboard"`
37 }
38 scoreboard struct {
39 Waiting int64 `stm:"waiting"`
40 Open int64 `stm:"open"`
41 Close int64 `stm:"close"`
42 HardError int64 `stm:"hard_error"`
43 KeepAlive int64 `stm:"keepalive"`
44 Read int64 `stm:"read"`
45 ReadPost int64 `stm:"read_post"`
46 Write int64 `stm:"write"`
47 HandleRequest int64 `stm:"handle_request"`
48 RequestStart int64 `stm:"request_start"`
49 RequestEnd int64 `stm:"request_end"`
50 ResponseStart int64 `stm:"response_start"`
51 ResponseEnd int64 `stm:"response_end"`
52 }
53 )
54
55 func parseResponse(r io.Reader) (*serverStatus, error) {
56 s := bufio.NewScanner(r)
57 var status serverStatus
58
59 for s.Scan() {
60 parts := strings.Split(s.Text(), ":")
61 if len(parts) != 2 {
62 continue
63 }
64 key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
65
66 switch key {
67 default:
68 case busyWorkers, idleWorkers:
69 return nil, fmt.Errorf("found '%s', apache data", key)
70 case busyServers:
71 status.Servers.Busy = mustParseInt(value)
72 case idleServers:
73 status.Servers.Idle = mustParseInt(value)
74 case totalAccesses:
75 status.Total.Accesses = mustParseInt(value)
76 case totalkBytes:
77 status.Total.KBytes = mustParseInt(value)
78 case uptime:
79 status.Uptime = mustParseInt(value)
80 case scoreBoard:
81 status.Scoreboard = parseScoreboard(value)
82 }
83 }
84
85 return &status, nil
86 }
87
88 func parseScoreboard(value string) *scoreboard {
89 // Descriptions from https://blog.serverdensity.com/monitor-lighttpd/
90 //
91 // “.” = Opening the TCP connection (connect)
92 // “C” = Closing the TCP connection if no other HTTP request will use it (close)
93 // “E” = hard error
94 // “k” = Keeping the TCP connection open for more HTTP requests from the same client to avoid the TCP handling overhead (keep-alive)
95 // “r” = ReadAsMap the content of the HTTP request (read)
96 // “R” = ReadAsMap the content of the HTTP request (read-POST)
97 // “W” = Write the HTTP response to the socket (write)
98 // “h” = Decide action to take with the request (handle-request)
99 // “q” = Start of HTTP request (request-start)
100 // “Q” = End of HTTP request (request-end)
101 // “s” = Start of the HTTP request response (response-start)
102 // “S” = End of the HTTP request response (response-end)
103 // “_” Waiting for Connection (NOTE: not sure, copied the description from apache score board)
104
105 var sb scoreboard
106 for s := range strings.SplitSeq(value, "") {
107 switch s {
108 case "_":
109 sb.Waiting++
110 case ".":
111 sb.Open++
112 case "C":
113 sb.Close++
114 case "E":
115 sb.HardError++
116 case "k":
117 sb.KeepAlive++
118 case "r":
119 sb.Read++
120 case "R":
121 sb.ReadPost++
122 case "W":
123 sb.Write++
124 case "h":
125 sb.HandleRequest++
126 case "q":
127 sb.RequestStart++
128 case "Q":
129 sb.RequestEnd++
130 case "s":
131 sb.ResponseStart++
132 case "S":
133 sb.ResponseEnd++
134 }
135 }
136
137 return &sb
138 }
139
140 func mustParseInt(value string) *int64 {
141 v, err := strconv.ParseInt(value, 10, 64)
142 if err != nil {
143 panic(err)
144 }
145 return &v
146 }