github.com/letsencrypt/boulder@v0.20251208.0/test/s3-test-srv/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "crypto/x509" 6 "flag" 7 "fmt" 8 "io" 9 "net/http" 10 "sync" 11 "time" 12 13 "github.com/letsencrypt/boulder/cmd" 14 "github.com/letsencrypt/boulder/core" 15 "github.com/letsencrypt/boulder/revocation" 16 ) 17 18 type s3TestSrv struct { 19 sync.RWMutex 20 allSerials map[string]revocation.Reason 21 allShards map[string][]byte 22 } 23 24 func (srv *s3TestSrv) handleS3(w http.ResponseWriter, r *http.Request) { 25 if r.Method == "PUT" { 26 srv.handleUpload(w, r) 27 } else if r.Method == "GET" { 28 srv.handleDownload(w, r) 29 } else { 30 w.WriteHeader(http.StatusMethodNotAllowed) 31 } 32 } 33 34 func (srv *s3TestSrv) handleUpload(w http.ResponseWriter, r *http.Request) { 35 body, err := io.ReadAll(r.Body) 36 if err != nil { 37 w.WriteHeader(http.StatusInternalServerError) 38 w.Write([]byte("failed to read request body")) 39 return 40 } 41 42 crl, err := x509.ParseRevocationList(body) 43 if err != nil { 44 w.WriteHeader(http.StatusInternalServerError) 45 w.Write(fmt.Appendf(nil, "failed to parse body: %s", err)) 46 return 47 } 48 49 srv.Lock() 50 defer srv.Unlock() 51 srv.allShards[r.URL.Path] = body 52 for _, rc := range crl.RevokedCertificateEntries { 53 srv.allSerials[core.SerialToString(rc.SerialNumber)] = revocation.Reason(rc.ReasonCode) 54 } 55 56 w.WriteHeader(http.StatusOK) 57 w.Write([]byte("{}")) 58 } 59 60 func (srv *s3TestSrv) handleDownload(w http.ResponseWriter, r *http.Request) { 61 srv.RLock() 62 defer srv.RUnlock() 63 body, ok := srv.allShards[r.URL.Path] 64 if !ok { 65 w.WriteHeader(http.StatusNotFound) 66 return 67 } 68 w.WriteHeader(http.StatusOK) 69 w.Write(body) 70 } 71 72 func (srv *s3TestSrv) handleQuery(w http.ResponseWriter, r *http.Request) { 73 if r.Method != "GET" { 74 w.WriteHeader(http.StatusMethodNotAllowed) 75 return 76 } 77 78 serial := r.URL.Query().Get("serial") 79 if serial == "" { 80 w.WriteHeader(http.StatusBadRequest) 81 return 82 } 83 84 srv.RLock() 85 defer srv.RUnlock() 86 reason, ok := srv.allSerials[serial] 87 if !ok { 88 w.WriteHeader(http.StatusNotFound) 89 return 90 } 91 92 w.WriteHeader(http.StatusOK) 93 w.Write(fmt.Appendf(nil, "%d", reason)) 94 } 95 96 func (srv *s3TestSrv) handleReset(w http.ResponseWriter, r *http.Request) { 97 if r.Method != "POST" { 98 w.WriteHeader(http.StatusMethodNotAllowed) 99 return 100 } 101 102 srv.Lock() 103 defer srv.Unlock() 104 srv.allSerials = make(map[string]revocation.Reason) 105 srv.allShards = make(map[string][]byte) 106 107 w.WriteHeader(http.StatusOK) 108 } 109 110 func main() { 111 listenAddr := flag.String("listen", "0.0.0.0:4501", "Address to listen on") 112 flag.Parse() 113 114 srv := s3TestSrv{ 115 allSerials: make(map[string]revocation.Reason), 116 allShards: make(map[string][]byte), 117 } 118 119 http.HandleFunc("/", srv.handleS3) 120 http.HandleFunc("/query", srv.handleQuery) 121 http.HandleFunc("/reset", srv.handleReset) 122 123 s := http.Server{ 124 ReadTimeout: 30 * time.Second, 125 Addr: *listenAddr, 126 } 127 128 go func() { 129 err := s.ListenAndServe() 130 if err != nil && err != http.ErrServerClosed { 131 cmd.FailOnError(err, "Running TLS server") 132 } 133 }() 134 135 defer func() { 136 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 137 defer cancel() 138 _ = s.Shutdown(ctx) 139 }() 140 141 cmd.WaitForSignal() 142 }