boulder/test/chall-test-srv/history.go

123 lines
3.7 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/letsencrypt/challtestsrv"
)
// clearHistory handles an HTTP POST request to clear the challenge server
// request history for a specific hostname and type of event.
//
// The POST body is expected to have two parameters:
// "host" - the hostname to clear history for.
// "type" - the type of event to clear. May be "http", "dns", or "tlsalpn".
//
// A successful POST will write http.StatusOK to the client.
func (srv *managementServer) clearHistory(w http.ResponseWriter, r *http.Request) {
var request struct {
Host string
Type string `json:"type"`
}
if err := mustParsePOST(&request, r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
typeMap := map[string]challtestsrv.RequestEventType{
"http": challtestsrv.HTTPRequestEventType,
"dns": challtestsrv.DNSRequestEventType,
"tlsalpn": challtestsrv.TLSALPNRequestEventType,
}
if request.Host == "" {
http.Error(w, "host parameter must not be empty", http.StatusBadRequest)
return
}
if code, ok := typeMap[request.Type]; ok {
srv.challSrv.ClearRequestHistory(request.Host, code)
srv.log.Printf("Cleared challenge server request history for %q %q events\n",
request.Host, request.Type)
w.WriteHeader(http.StatusOK)
return
}
http.Error(w, fmt.Sprintf("%q event type unknown", request.Type), http.StatusBadRequest)
}
// getHTTPHistory returns only the HTTPRequestEvents for the given hostname
// from the challenge server's request history in JSON form.
func (srv *managementServer) getHTTPHistory(w http.ResponseWriter, r *http.Request) {
host, err := requestHost(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
srv.writeHistory(
srv.challSrv.RequestHistory(host, challtestsrv.HTTPRequestEventType),
w)
}
// getDNSHistory returns only the DNSRequestEvents from the challenge
// server's request history in JSON form.
func (srv *managementServer) getDNSHistory(w http.ResponseWriter, r *http.Request) {
host, err := requestHost(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
srv.writeHistory(
srv.challSrv.RequestHistory(host, challtestsrv.DNSRequestEventType),
w)
}
// getTLSALPNHistory returns only the TLSALPNRequestEvents from the challenge
// server's request history in JSON form.
func (srv *managementServer) getTLSALPNHistory(w http.ResponseWriter, r *http.Request) {
host, err := requestHost(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
srv.writeHistory(
srv.challSrv.RequestHistory(host, challtestsrv.TLSALPNRequestEventType),
w)
}
// requestHost extracts the Host parameter of a JSON POST body in the provided
// request, or returns an error.
func requestHost(r *http.Request) (string, error) {
var request struct {
Host string
}
if err := mustParsePOST(&request, r); err != nil {
return "", err
}
if request.Host == "" {
return "", errors.New("host parameter of POST body must not be empty")
}
return request.Host, nil
}
// writeHistory writes the provided list of challtestsrv.RequestEvents to the
// provided http.ResponseWriter in JSON form.
func (srv *managementServer) writeHistory(
history []challtestsrv.RequestEvent, w http.ResponseWriter,
) {
// Always write an empty JSON list instead of `null`
if history == nil {
history = []challtestsrv.RequestEvent{}
}
jsonHistory, err := json.MarshalIndent(history, "", " ")
if err != nil {
srv.log.Printf("Error marshaling history: %v\n", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(jsonHistory)
}