@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 144 lines 3.69 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package reverseproxy
16
17 import (
18 "encoding/json"
19 "fmt"
20 "net/http"
21
22 "github.com/caddyserver/caddy/v2"
23 )
24
25 func init() {
26 caddy.RegisterModule(adminUpstreams{})
27 }
28
29 // adminUpstreams is a module that provides the
30 // /reverse_proxy/upstreams endpoint for the Caddy admin
31 // API. This allows for checking the health of configured
32 // reverse proxy upstreams in the pool.
33 type adminUpstreams struct{}
34
35 // upstreamStatus holds the status of a particular upstream
36 type upstreamStatus struct {
37 Address string `json:"address"`
38 NumRequests int `json:"num_requests"`
39 Fails int `json:"fails"`
40 }
41
42 // CaddyModule returns the Caddy module information.
43 func (adminUpstreams) CaddyModule() caddy.ModuleInfo {
44 return caddy.ModuleInfo{
45 ID: "admin.api.reverse_proxy",
46 New: func() caddy.Module { return new(adminUpstreams) },
47 }
48 }
49
50 // Routes returns a route for the /reverse_proxy/upstreams endpoint.
51 func (al adminUpstreams) Routes() []caddy.AdminRoute {
52 return []caddy.AdminRoute{
53 {
54 Pattern: "/reverse_proxy/upstreams",
55 Handler: caddy.AdminHandlerFunc(al.handleUpstreams),
56 },
57 }
58 }
59
60 // handleUpstreams reports the status of the reverse proxy
61 // upstream pool.
62 func (adminUpstreams) handleUpstreams(w http.ResponseWriter, r *http.Request) error {
63 if r.Method != http.MethodGet {
64 return caddy.APIError{
65 HTTPStatus: http.StatusMethodNotAllowed,
66 Err: fmt.Errorf("method not allowed"),
67 }
68 }
69
70 // Prep for a JSON response
71 w.Header().Set("Content-Type", "application/json")
72 enc := json.NewEncoder(w)
73
74 // Collect the results to respond with
75 results := []upstreamStatus{}
76 knownHosts := make(map[string]struct{})
77
78 // Iterate over the static upstream pool (needs to be fast)
79 var rangeErr error
80 hosts.Range(func(key, val any) bool {
81 address, ok := key.(string)
82 if !ok {
83 rangeErr = caddy.APIError{
84 HTTPStatus: http.StatusInternalServerError,
85 Err: fmt.Errorf("could not type assert upstream address"),
86 }
87 return false
88 }
89
90 upstream, ok := val.(*Host)
91 if !ok {
92 rangeErr = caddy.APIError{
93 HTTPStatus: http.StatusInternalServerError,
94 Err: fmt.Errorf("could not type assert upstream struct"),
95 }
96 return false
97 }
98
99 knownHosts[address] = struct{}{}
100
101 results = append(results, upstreamStatus{
102 Address: address,
103 NumRequests: upstream.NumRequests(),
104 Fails: upstream.Fails(),
105 })
106 return true
107 })
108
109 currentInFlight := getInFlightRequests()
110 for address, count := range currentInFlight {
111 if _, exists := knownHosts[address]; !exists && count > 0 {
112 results = append(results, upstreamStatus{
113 Address: address,
114 NumRequests: int(count),
115 Fails: 0,
116 })
117 }
118 }
119
120 if rangeErr != nil {
121 return rangeErr
122 }
123
124 // Also include dynamic upstreams
125 dynamicHostsMu.RLock()
126 for address, entry := range dynamicHosts {
127 results = append(results, upstreamStatus{
128 Address: address,
129 NumRequests: entry.host.NumRequests(),
130 Fails: entry.host.Fails(),
131 })
132 }
133 dynamicHostsMu.RUnlock()
134
135 err := enc.Encode(results)
136 if err != nil {
137 return caddy.APIError{
138 HTTPStatus: http.StatusInternalServerError,
139 Err: err,
140 }
141 }
142
143 return nil
144 }