120 lines
3.7 KiB
Go
120 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"math/rand/v2"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func simulateLatencyAndErrors(latencyMean time.Duration, errorRate float64, next http.HandlerFunc) http.HandlerFunc {
|
|
if latencyMean == 0 && errorRate == 0 {
|
|
return next
|
|
}
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Add random latency (50%-150% of mean)
|
|
if latencyMean > 0 {
|
|
jitter := time.Duration(float64(latencyMean) * (0.5 + rand.Float64())) // #nosec G404 -- load testing
|
|
time.Sleep(jitter)
|
|
}
|
|
|
|
// Occasionally return errors
|
|
if errorRate > 0 && rand.Float64() < errorRate { // #nosec G404 -- load testing
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if rand.Float64() < 0.5 { // #nosec G404 -- load testing
|
|
w.WriteHeader(http.StatusTooManyRequests)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"error": map[string]any{
|
|
"code": 429,
|
|
"message": "simulated rate limit",
|
|
"status": "RESOURCE_EXHAUSTED",
|
|
},
|
|
})
|
|
} else {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"error": map[string]any{
|
|
"code": 500,
|
|
"message": "simulated server error",
|
|
"status": "INTERNAL",
|
|
},
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
next(w, r)
|
|
}
|
|
}
|
|
|
|
// forwardForRealDevice returns middleware that checks if a device-specific request
|
|
// targets a registered fake device. If not, it forwards to Google via the authenticated client.
|
|
func forwardForRealDevice(store *deviceStore, google *googleForwarder) func(http.HandlerFunc) http.HandlerFunc {
|
|
return func(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
name := deviceName(r)
|
|
if store.getByName(name) != nil {
|
|
next(w, r)
|
|
return
|
|
}
|
|
// Real device — forward via Google SDK if available
|
|
if google != nil {
|
|
hasSeenRealDevice.Store(true)
|
|
log.Printf("Real device request: %s %s (device: %q)", r.Method, r.URL.Path, name) // #nosec G706 -- load testing tool
|
|
switch r.Method {
|
|
case "GET":
|
|
google.ForwardDevicesGet(w, r)
|
|
case "PATCH":
|
|
google.ForwardDevicesPatch(w, r)
|
|
case "DELETE":
|
|
google.ForwardDevicesDelete(w, r)
|
|
case "POST":
|
|
google.ForwardIssueCommand(w, r)
|
|
default:
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"error": map[string]any{
|
|
"code": 405,
|
|
"message": "unsupported method " + r.Method + " for device forwarding",
|
|
"status": "METHOD_NOT_ALLOWED",
|
|
},
|
|
})
|
|
}
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusNotFound)
|
|
fmt.Fprintf(w, `{"error":{"code":404,"message":"Device not found","status":"NOT_FOUND"}}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
// forwardOrMock forwards to Google if credentials are configured,
|
|
// otherwise falls back to the local mock handler.
|
|
func forwardOrMock(google *googleForwarder, fallback http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if google == nil {
|
|
fallback(w, r)
|
|
return
|
|
}
|
|
// Route to the appropriate Google forwarder method based on the path
|
|
path := r.URL.Path
|
|
switch {
|
|
case r.Method == "POST" && strings.Contains(path, "/enrollmentTokens"):
|
|
google.ForwardEnrollmentTokenCreate(w, r)
|
|
case r.Method == "GET" && strings.Contains(path, "/applications/"):
|
|
google.ForwardApplicationsGet(w, r)
|
|
case r.Method == "POST" && strings.Contains(path, "/webApps"):
|
|
google.ForwardWebAppsCreate(w, r)
|
|
case r.Method == "GET" && (path == "/v1/enterprises" || strings.HasSuffix(path, "/enterprises")):
|
|
google.ForwardEnterprisesList(w, r)
|
|
default:
|
|
fallback(w, r)
|
|
}
|
|
}
|
|
}
|