-
Notifications
You must be signed in to change notification settings - Fork 0
/
chaosbackend.go
196 lines (181 loc) · 5.94 KB
/
chaosbackend.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package main
import (
"flag"
"fmt"
"html/template"
"log"
"math/rand"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
type connection struct {
statusCode int
statusCodeFreq int
slowc int
slowcSpan int
slowcFreq int
slowbb int
slowbbSpan int
reset int
resetFreq int
}
func defaultHandler(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("template.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("X-Backend", "default")
tmpl.Execute(w, "This is the default page.")
}
func slowResponse(w http.ResponseWriter, sleepBeforeFirstByte time.Duration, sleepBetweenBytes time.Duration) {
content := "Example content delivered slowly. Connect:" + sleepBeforeFirstByte.String() + " Betweenbytes:" + sleepBetweenBytes.String()
log.Println(content)
time.Sleep(sleepBeforeFirstByte)
w.Header().Set("Content-Type", "text/event-stream")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
return
}
for _, c := range content {
fmt.Fprintf(w, "%c", c)
flusher.Flush() // Manually flush the buffer
time.Sleep(sleepBetweenBytes)
}
}
func slowHandler(w http.ResponseWriter, r *http.Request) {
sleepBeforeFirstByte, _ := strconv.Atoi(r.URL.Query().Get("sleep"))
sleepBetweenBytes, _ := strconv.Atoi(r.URL.Query().Get("sleepBetweenBytes"))
slowResponse(w, time.Duration(sleepBeforeFirstByte)*time.Millisecond, time.Duration(sleepBetweenBytes)*time.Millisecond)
}
func errorHandler(w http.ResponseWriter, r *http.Request) {
statusCode, _ := strconv.Atoi(r.URL.Query().Get("status"))
if statusCode == 0 {
// Default to 500
statusCode = 500
}
sleepBeforeFirstByte, _ := strconv.Atoi(r.URL.Query().Get("sleep"))
log.Println("Returning Statuscode:", statusCode, "Sleeping for", sleepBeforeFirstByte, "ms")
time.Sleep(time.Duration(sleepBeforeFirstByte) * time.Millisecond)
http.Error(w, "Returning Statuscode: "+strconv.Itoa(statusCode), statusCode)
}
func resetConnectionHandler(w http.ResponseWriter, r *http.Request) {
// Take over the connection
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Printf("Hijacking failed: %v\n", err)
http.Error(w, "Hijacking failed", http.StatusInternalServerError)
return
}
// Close the connection immediately
conn.Close()
}
func addHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Backends", "snuskepus")
next.ServeHTTP(w, r)
})
}
func newHandler(w http.ResponseWriter, r *http.Request) {
conn := connection{statusCode: 200, statusCodeFreq: 0, slowc: 0, slowcSpan: 0, slowcFreq: 10, slowbbSpan: 0, reset: 0, resetFreq: 0}
if r.URL.Query().Get("status") != "" {
statusParts := strings.Split(r.URL.Query().Get("status"), ",")
if len(statusParts) > 1 {
randomness := rand.Intn(100)
conn.statusCodeFreq, _ = strconv.Atoi(statusParts[1])
log.Println("Frequency:", randomness, "StatusCodeFreq:", conn.statusCodeFreq)
if randomness <= conn.statusCodeFreq {
conn.statusCode, _ = strconv.Atoi(statusParts[0])
}
} else {
conn.statusCode, _ = strconv.Atoi(statusParts[0])
}
}
if r.URL.Query().Get("slow") != "" {
slowParts := strings.Split(r.URL.Query().Get("slow"), ",")
conn.slowc, _ = strconv.Atoi(slowParts[0])
if len(slowParts) > 2 {
conn.slowcFreq, _ = strconv.Atoi(slowParts[2])
}
if len(slowParts) > 1 {
conn.slowcSpan, _ = strconv.Atoi(slowParts[1])
randomness := rand.Intn(100)
log.Println("Frequency:", randomness, "SlowcFreq:", conn.slowcFreq)
if randomness <= conn.slowcFreq {
conn.slowc = conn.slowc + rand.Intn(conn.slowcSpan)
}
}
}
log.Println("Sleeping for", conn.slowc, "ms")
time.Sleep(time.Duration(conn.slowc) * time.Millisecond)
if r.URL.Query().Get("reset") != "" {
thisconnection, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Printf("Hijacking failed: %v\n", err)
http.Error(w, "Hijacking failed", http.StatusInternalServerError)
return
}
// Close the connection immediately
thisconnection.Close()
}
http.Error(w, "Returning Statuscode: "+strconv.Itoa(conn.statusCode)+" in "+strconv.Itoa(conn.slowc)+"ms", conn.statusCode)
}
func main() {
var (
addressesInput string
portsInput string
)
flag.StringVar(&addressesInput, "a", "127.0.0.1", "Comma-separated list of addresses")
flag.StringVar(&portsInput, "p", "8080", "Comma-separated list of ports or port ranges (e.g., 4000-4020)")
flag.Parse()
// Split the addresses and ports
addresses := strings.Split(addressesInput, ",")
portParts := strings.Split(portsInput, ",")
// Expand port ranges
var ports []string
for _, part := range portParts {
if strings.Contains(part, "-") {
rangeParts := strings.Split(part, "-")
start, err := strconv.Atoi(rangeParts[0])
if err != nil {
fmt.Printf("Invalid port range start: %s\n", rangeParts[0])
continue
}
end, err := strconv.Atoi(rangeParts[1])
if err != nil {
fmt.Printf("Invalid port range end: %s\n", rangeParts[1])
continue
}
for p := start; p <= end; p++ {
ports = append(ports, strconv.Itoa(p))
}
} else {
ports = append(ports, part)
}
}
var wg sync.WaitGroup
for _, address := range addresses {
for _, port := range ports {
fullAddr := fmt.Sprintf("%s:%s", address, port)
wg.Add(1)
go func(addr string) {
defer wg.Done()
mux := http.NewServeMux()
finalHandler := http.HandlerFunc(defaultHandler)
mux.HandleFunc("/slow", slowHandler)
mux.HandleFunc("/error", errorHandler)
mux.HandleFunc("/reset", resetConnectionHandler)
mux.HandleFunc("/new", newHandler)
mux.Handle("/", addHeaders(finalHandler)) // Register the default handler
log.Println("Starting server on", addr)
log.Fatal(http.ListenAndServe(addr, mux))
}(fullAddr)
}
}
log.Println("Number of servers:", len(addresses)*len(ports))
wg.Wait() // Wait for all servers to finish
}