github.com/letsencrypt/boulder@v0.20251208.0/test/chall-test-srv-client/client.go (about)

     1  package challtestsrvclient
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"encoding/base32"
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"net/http"
    12  	"net/url"
    13  	"strings"
    14  )
    15  
    16  // Client is an HTTP client for https://github.com/letsencrypt/challtestsrv's
    17  // management interface (test/chall-test-srv).
    18  type Client struct {
    19  	baseURL string
    20  }
    21  
    22  // NewClient creates a new Client using the provided baseURL, or defaults to
    23  // http://10.77.77.77:8055 if none is provided.
    24  func NewClient(baseURL string) *Client {
    25  	if baseURL == "" {
    26  		baseURL = "http://10.77.77.77:8055"
    27  	}
    28  	return &Client{baseURL: baseURL}
    29  }
    30  
    31  const (
    32  	setIPv4        = "set-default-ipv4"
    33  	setIPv6        = "set-default-ipv6"
    34  	delHistory     = "clear-request-history"
    35  	getHTTPHistory = "http-request-history"
    36  	getDNSHistory  = "dns-request-history"
    37  	getALPNHistory = "tlsalpn01-request-history"
    38  	addA           = "add-a"
    39  	delA           = "clear-a"
    40  	addAAAA        = "add-aaaa"
    41  	delAAAA        = "clear-aaaa"
    42  	addCAA         = "add-caa"
    43  	delCAA         = "clear-caa"
    44  	addRedirect    = "add-redirect"
    45  	delRedirect    = "del-redirect"
    46  	addHTTP        = "add-http01"
    47  	delHTTP        = "del-http01"
    48  	addTXT         = "set-txt"
    49  	delTXT         = "clear-txt"
    50  	addALPN        = "add-tlsalpn01"
    51  	delALPN        = "del-tlsalpn01"
    52  	addServfail    = "set-servfail"
    53  	delServfail    = "clear-servfail"
    54  )
    55  
    56  func (c *Client) postURL(path string, body any) ([]byte, error) {
    57  	endpoint, err := url.JoinPath(c.baseURL, path)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("joining URL %q with path %q: %w", c.baseURL, path, err)
    60  	}
    61  
    62  	payload, err := json.Marshal(body)
    63  	if err != nil {
    64  		return nil, fmt.Errorf("marshalling payload for %s: %w", endpoint, err)
    65  	}
    66  
    67  	resp, err := http.Post(endpoint, "application/json", bytes.NewBuffer(payload))
    68  	if err != nil {
    69  		return nil, fmt.Errorf("sending POST to %s: %w", endpoint, err)
    70  	}
    71  	defer resp.Body.Close()
    72  
    73  	if resp.StatusCode != http.StatusOK {
    74  		return nil, fmt.Errorf("unexpected status code %d from %s", resp.StatusCode, endpoint)
    75  	}
    76  	respBytes, err := io.ReadAll(resp.Body)
    77  	if err != nil {
    78  		return nil, fmt.Errorf("reading response from %s: %w", endpoint, err)
    79  	}
    80  	return respBytes, nil
    81  }
    82  
    83  // SetDefaultIPv4 sets the challenge server's default IPv4 address used to
    84  // respond to A queries when there are no specific mock A addresses for the
    85  // hostname being queried. Provide an empty string as the default address to
    86  // disable answering A queries except for hosts that have mock A addresses
    87  // added. Any failure returns an error that includes both the relevant operation
    88  // and the payload.
    89  func (c *Client) SetDefaultIPv4(addr string) ([]byte, error) {
    90  	payload := map[string]string{"ip": addr}
    91  	resp, err := c.postURL(setIPv4, payload)
    92  	if err != nil {
    93  		return nil, fmt.Errorf(
    94  			"while setting default IPv4 to %q (payload: %v): %w",
    95  			addr, payload, err,
    96  		)
    97  	}
    98  	return resp, nil
    99  }
   100  
   101  // SetDefaultIPv6 sets the challenge server's default IPv6 address used to
   102  // respond to AAAA queries when there are no specific mock AAAA addresses for
   103  // the hostname being queried. Provide an empty string as the default address to
   104  // disable answering AAAA queries except for hosts that have mock AAAA addresses
   105  // added. Any failure returns an error that includes both the relevant operation
   106  // and the payload.
   107  func (c *Client) SetDefaultIPv6(addr string) ([]byte, error) {
   108  	payload := map[string]string{"ip": addr}
   109  	resp, err := c.postURL(setIPv6, payload)
   110  	if err != nil {
   111  		return nil, fmt.Errorf(
   112  			"while setting default IPv6 to %q (payload: %v): %w",
   113  			addr, payload, err,
   114  		)
   115  	}
   116  	return resp, nil
   117  }
   118  
   119  // AddARecord adds a mock A response to the challenge server's DNS interface for
   120  // the given host and IPv4 addresses. Any failure returns an error that includes
   121  // both the relevant operation and the payload.
   122  func (c *Client) AddARecord(host string, addresses []string) ([]byte, error) {
   123  	payload := map[string]any{
   124  		"host":      host,
   125  		"addresses": addresses,
   126  	}
   127  	resp, err := c.postURL(addA, payload)
   128  	if err != nil {
   129  		return nil, fmt.Errorf(
   130  			"while adding A record for host %q (payload: %v): %w",
   131  			host, payload, err,
   132  		)
   133  	}
   134  	return resp, nil
   135  }
   136  
   137  // RemoveARecord removes a mock A response from the challenge server's DNS
   138  // interface for the given host. Any failure returns an error that includes both
   139  // the relevant operation and the payload.
   140  func (c *Client) RemoveARecord(host string) ([]byte, error) {
   141  	payload := map[string]string{"host": host}
   142  	resp, err := c.postURL(delA, payload)
   143  	if err != nil {
   144  		return nil, fmt.Errorf(
   145  			"while removing A record for host %q (payload: %v): %w",
   146  			host, payload, err,
   147  		)
   148  	}
   149  	return resp, nil
   150  }
   151  
   152  // AddAAAARecord adds a mock AAAA response to the challenge server's DNS
   153  // interface for the given host and IPv6 addresses. Any failure returns an error
   154  // that includes both the relevant operation and the payload.
   155  func (c *Client) AddAAAARecord(host string, addresses []string) ([]byte, error) {
   156  	payload := map[string]any{
   157  		"host":      host,
   158  		"addresses": addresses,
   159  	}
   160  	resp, err := c.postURL(addAAAA, payload)
   161  	if err != nil {
   162  		return nil, fmt.Errorf(
   163  			"while adding AAAA record for host %q (payload: %v): %w",
   164  			host, payload, err,
   165  		)
   166  	}
   167  	return resp, nil
   168  }
   169  
   170  // RemoveAAAARecord removes mock AAAA response from the challenge server's DNS
   171  // interface for the given host. Any failure returns an error that includes both
   172  // the relevant operation and the payload.
   173  func (c *Client) RemoveAAAARecord(host string) ([]byte, error) {
   174  	payload := map[string]string{"host": host}
   175  	resp, err := c.postURL(delAAAA, payload)
   176  	if err != nil {
   177  		return nil, fmt.Errorf(
   178  			"while removing AAAA record for host %q (payload: %v): %w",
   179  			host, payload, err,
   180  		)
   181  	}
   182  	return resp, nil
   183  }
   184  
   185  // AddCAAIssue adds a mock CAA response to the challenge server's DNS interface.
   186  // The mock CAA response will contain one policy with an "issue" tag specifying
   187  // the provided value. Any failure returns an error that includes both the
   188  // relevant operation and the payload.
   189  func (c *Client) AddCAAIssue(host, value string) ([]byte, error) {
   190  	payload := map[string]any{
   191  		"host": host,
   192  		"policies": []map[string]string{
   193  			{"tag": "issue", "value": value},
   194  		},
   195  	}
   196  	resp, err := c.postURL(addCAA, payload)
   197  	if err != nil {
   198  		return nil, fmt.Errorf(
   199  			"while adding CAA issue for host %q, val %q (payload: %v): %w",
   200  			host, value, payload, err,
   201  		)
   202  	}
   203  	return resp, nil
   204  }
   205  
   206  // RemoveCAAIssue removes a mock CAA response from the challenge server's DNS
   207  // interface for the given host. Any failure returns an error that includes both
   208  // the relevant operation and the payload.
   209  func (c *Client) RemoveCAAIssue(host string) ([]byte, error) {
   210  	payload := map[string]string{"host": host}
   211  	resp, err := c.postURL(delCAA, payload)
   212  	if err != nil {
   213  		return nil, fmt.Errorf(
   214  			"while removing CAA issue for host %q (payload: %v): %w",
   215  			host, payload, err,
   216  		)
   217  	}
   218  	return resp, nil
   219  }
   220  
   221  // HTTPRequest is a single HTTP request in the request history.
   222  type HTTPRequest struct {
   223  	URL        string `json:"URL"`
   224  	Host       string `json:"Host"`
   225  	HTTPS      bool   `json:"HTTPS"`
   226  	ServerName string `json:"ServerName"`
   227  	UserAgent  string `json:"UserAgent"`
   228  }
   229  
   230  // HTTPRequestHistory fetches the challenge server's HTTP request history for
   231  // the given host.
   232  func (c *Client) HTTPRequestHistory(host string) ([]HTTPRequest, error) {
   233  	payload := map[string]string{"host": host}
   234  	raw, err := c.postURL(getHTTPHistory, payload)
   235  	if err != nil {
   236  		return nil, fmt.Errorf(
   237  			"while fetching HTTP request history for host %q (payload: %v): %w",
   238  			host, payload, err,
   239  		)
   240  	}
   241  	var data []HTTPRequest
   242  	err = json.Unmarshal(raw, &data)
   243  	if err != nil {
   244  		return nil, fmt.Errorf("unmarshalling HTTP request history: %w", err)
   245  	}
   246  	return data, nil
   247  }
   248  
   249  func (c *Client) clearRequestHistory(host, typ string) ([]byte, error) {
   250  	return c.postURL(delHistory, map[string]string{"host": host, "type": typ})
   251  }
   252  
   253  // ClearHTTPRequestHistory clears the challenge server's HTTP request history
   254  // for the given host. Any failure returns an error that includes both the
   255  // relevant operation and the payload.
   256  func (c *Client) ClearHTTPRequestHistory(host string) ([]byte, error) {
   257  	resp, err := c.clearRequestHistory(host, "http")
   258  	if err != nil {
   259  		return nil, fmt.Errorf(
   260  			"while clearing HTTP request history for host %q: %w", host, err,
   261  		)
   262  	}
   263  	return resp, nil
   264  }
   265  
   266  // AddHTTPRedirect adds a redirect to the challenge server's HTTP interfaces for
   267  // HTTP requests to the given path directing the client to the targetURL.
   268  // Redirects are not served for HTTPS requests. Any failure returns an error
   269  // that includes both the relevant operation and the payload.
   270  func (c *Client) AddHTTPRedirect(path, targetURL string) ([]byte, error) {
   271  	payload := map[string]string{"path": path, "targetURL": targetURL}
   272  	resp, err := c.postURL(addRedirect, payload)
   273  	if err != nil {
   274  		return nil, fmt.Errorf(
   275  			"while adding HTTP redirect for path %q -> %q (payload: %v): %w",
   276  			path, targetURL, payload, err,
   277  		)
   278  	}
   279  	return resp, nil
   280  }
   281  
   282  // RemoveHTTPRedirect removes a redirect from the challenge server's HTTP
   283  // interfaces for the given path. Any failure returns an error that includes
   284  // both the relevant operation and the payload.
   285  func (c *Client) RemoveHTTPRedirect(path string) ([]byte, error) {
   286  	payload := map[string]string{"path": path}
   287  	resp, err := c.postURL(delRedirect, payload)
   288  	if err != nil {
   289  		return nil, fmt.Errorf(
   290  			"while removing HTTP redirect for path %q (payload: %v): %w",
   291  			path, payload, err,
   292  		)
   293  	}
   294  	return resp, nil
   295  }
   296  
   297  // AddHTTP01Response adds an ACME HTTP-01 challenge response for the provided
   298  // token under the /.well-known/acme-challenge/ path of the challenge test
   299  // server's HTTP interfaces. The given keyauth will be returned as the HTTP
   300  // response body for requests to the challenge token. Any failure returns an
   301  // error that includes both the relevant operation and the payload.
   302  func (c *Client) AddHTTP01Response(token, keyauth string) ([]byte, error) {
   303  	payload := map[string]string{"token": token, "content": keyauth}
   304  	resp, err := c.postURL(addHTTP, payload)
   305  	if err != nil {
   306  		return nil, fmt.Errorf(
   307  			"while adding HTTP-01 challenge response for token %q (payload: %v): %w",
   308  			token, payload, err,
   309  		)
   310  	}
   311  	return resp, nil
   312  }
   313  
   314  // RemoveHTTP01Response removes an ACME HTTP-01 challenge response for the
   315  // provided token from the challenge test server. Any failure returns an error
   316  // that includes both the relevant operation and the payload.
   317  func (c *Client) RemoveHTTP01Response(token string) ([]byte, error) {
   318  	payload := map[string]string{"token": token}
   319  	resp, err := c.postURL(delHTTP, payload)
   320  	if err != nil {
   321  		return nil, fmt.Errorf(
   322  			"while removing HTTP-01 challenge response for token %q (payload: %v): %w",
   323  			token, payload, err,
   324  		)
   325  	}
   326  	return resp, nil
   327  }
   328  
   329  // AddServfailResponse configures the challenge test server to return SERVFAIL
   330  // for all queries made for the provided host. This will override any other
   331  // mocks for the host until removed with remove_servfail_response. Any failure
   332  // returns an error that includes both the relevant operation and the payload.
   333  func (c *Client) AddServfailResponse(host string) ([]byte, error) {
   334  	payload := map[string]string{"host": host}
   335  	resp, err := c.postURL(addServfail, payload)
   336  	if err != nil {
   337  		return nil, fmt.Errorf(
   338  			"while adding SERVFAIL response for host %q (payload: %v): %w",
   339  			host, payload, err,
   340  		)
   341  	}
   342  	return resp, nil
   343  }
   344  
   345  // RemoveServfailResponse undoes the work of AddServfailResponse, removing the
   346  // SERVFAIL configuration for the given host. Any failure returns an error that
   347  // includes both the relevant operation and the payload.
   348  func (c *Client) RemoveServfailResponse(host string) ([]byte, error) {
   349  	payload := map[string]string{"host": host}
   350  	resp, err := c.postURL(delServfail, payload)
   351  	if err != nil {
   352  		return nil, fmt.Errorf(
   353  			"while removing SERVFAIL response for host %q (payload: %v): %w",
   354  			host, payload, err,
   355  		)
   356  	}
   357  	return resp, nil
   358  }
   359  
   360  // AddDNS01Response adds an ACME DNS-01 challenge response for the provided host
   361  // to the challenge test server's DNS interfaces. The value is hashed and
   362  // base64-encoded using RawURLEncoding, and served for TXT queries to
   363  // _acme-challenge.<host>. Any failure returns an error that includes both the
   364  // relevant operation and the payload.
   365  func (c *Client) AddDNS01Response(host, value string) ([]byte, error) {
   366  	host = "_acme-challenge." + host
   367  	if !strings.HasSuffix(host, ".") {
   368  		host += "."
   369  	}
   370  	h := sha256.Sum256([]byte(value))
   371  	value = base64.RawURLEncoding.EncodeToString(h[:])
   372  	payload := map[string]string{"host": host, "value": value}
   373  	resp, err := c.postURL(addTXT, payload)
   374  	if err != nil {
   375  		return nil, fmt.Errorf(
   376  			"while adding DNS-01 response for host %q, val %q (payload: %v): %w",
   377  			host, value, payload, err,
   378  		)
   379  	}
   380  	return resp, nil
   381  }
   382  
   383  // RemoveDNS01Response removes an ACME DNS-01 challenge response for the
   384  // provided host from the challenge test server's DNS interfaces. Any failure
   385  // returns an error that includes both the relevant operation and the payload.
   386  func (c *Client) RemoveDNS01Response(host string) ([]byte, error) {
   387  	if !strings.HasPrefix(host, "_acme-challenge.") {
   388  		host = "_acme-challenge." + host
   389  	}
   390  	if !strings.HasSuffix(host, ".") {
   391  		host += "."
   392  	}
   393  	payload := map[string]string{"host": host}
   394  	resp, err := c.postURL(delTXT, payload)
   395  	if err != nil {
   396  		return nil, fmt.Errorf(
   397  			"while removing DNS-01 response for host %q (payload: %v): %w",
   398  			host, payload, err,
   399  		)
   400  	}
   401  	return resp, nil
   402  }
   403  
   404  // AddDNSAccount01Response adds an ACME DNS-ACCOUNT-01 challenge response for the
   405  // provided host to the challenge test server's DNS interfaces. The TXT record
   406  // name is constructed using the accountURL, and the TXT record value is the
   407  // base64url encoded SHA-256 hash of the provided value. Any failure returns an
   408  // error that includes the relevant operation and the payload.
   409  func (c *Client) AddDNSAccount01Response(accountURL, host, value string) ([]byte, error) {
   410  	if accountURL == "" {
   411  		return nil, fmt.Errorf("accountURL cannot be empty")
   412  	}
   413  	if host == "" {
   414  		return nil, fmt.Errorf("host cannot be empty")
   415  	}
   416  	label, err := calculateDNSAccount01Label(accountURL)
   417  	if err != nil {
   418  		return nil, fmt.Errorf("error calculating DNS label: %v", err)
   419  	}
   420  	host = fmt.Sprintf("%s._acme-challenge.%s", label, host)
   421  	if !strings.HasSuffix(host, ".") {
   422  		host += "."
   423  	}
   424  	h := sha256.Sum256([]byte(value))
   425  	value = base64.RawURLEncoding.EncodeToString(h[:])
   426  	payload := map[string]string{"host": host, "value": value}
   427  	resp, err := c.postURL(addTXT, payload)
   428  	if err != nil {
   429  		return nil, fmt.Errorf(
   430  			"while adding DNS-ACCOUNT-01 response for host %q, val %q (payload: %v): %w",
   431  			host, value, payload, err,
   432  		)
   433  	}
   434  	return resp, nil
   435  }
   436  
   437  // RemoveDNSAccount01Response removes an ACME DNS-ACCOUNT-01 challenge
   438  // response for the provided host and accountURL combination from the
   439  // challenge test server's DNS interfaces. The TXT record name is
   440  // constructed using the accountURL. Any failure returns an error
   441  // that includes both the relevant operation and the payload.
   442  func (c *Client) RemoveDNSAccount01Response(accountURL, host string) ([]byte, error) {
   443  	if accountURL == "" {
   444  		return nil, fmt.Errorf("accountURL cannot be empty")
   445  	}
   446  	if host == "" {
   447  		return nil, fmt.Errorf("host cannot be empty")
   448  	}
   449  	label, err := calculateDNSAccount01Label(accountURL)
   450  	if err != nil {
   451  		return nil, fmt.Errorf("error calculating DNS label: %v", err)
   452  	}
   453  	host = fmt.Sprintf("%s._acme-challenge.%s", label, host)
   454  	if !strings.HasSuffix(host, ".") {
   455  		host += "."
   456  	}
   457  	payload := map[string]string{"host": host}
   458  	resp, err := c.postURL(delTXT, payload)
   459  	if err != nil {
   460  		return nil, fmt.Errorf(
   461  			"while removing DNS-ACCOUNT-01 response for host %q (payload: %v): %w",
   462  			host, payload, err,
   463  		)
   464  	}
   465  	return resp, nil
   466  }
   467  
   468  func calculateDNSAccount01Label(accountURL string) (string, error) {
   469  	if accountURL == "" {
   470  		return "", fmt.Errorf("account URL cannot be empty")
   471  	}
   472  
   473  	h := sha256.Sum256([]byte(accountURL))
   474  	label := fmt.Sprintf("_%s", strings.ToLower(base32.StdEncoding.EncodeToString(h[:10])))
   475  	return label, nil
   476  }
   477  
   478  // DNSRequest is a single DNS request in the request history.
   479  type DNSRequest struct {
   480  	Question struct {
   481  		Name   string `json:"Name"`
   482  		Qtype  uint16 `json:"Qtype"`
   483  		Qclass uint16 `json:"Qclass"`
   484  	} `json:"Question"`
   485  	UserAgent string `json:"UserAgent"`
   486  }
   487  
   488  // DNSRequestHistory returns the history of DNS requests made to the challenge
   489  // test server's DNS interfaces for the given host. Any failure returns an error
   490  // that includes both the relevant operation and the payload.
   491  func (c *Client) DNSRequestHistory(host string) ([]DNSRequest, error) {
   492  	payload := map[string]string{"host": host}
   493  	raw, err := c.postURL(getDNSHistory, payload)
   494  	if err != nil {
   495  		return nil, fmt.Errorf(
   496  			"while fetching DNS request history for host %q (payload: %v): %w",
   497  			host, payload, err,
   498  		)
   499  	}
   500  	var data []DNSRequest
   501  	err = json.Unmarshal(raw, &data)
   502  	if err != nil {
   503  		return nil, fmt.Errorf("unmarshalling DNS request history: %w", err)
   504  	}
   505  	return data, nil
   506  }
   507  
   508  // ClearDNSRequestHistory clears the history of DNS requests made to the
   509  // challenge test server's DNS interfaces for the given host. Any failure
   510  // returns an error that includes both the relevant operation and the payload.
   511  func (c *Client) ClearDNSRequestHistory(host string) ([]byte, error) {
   512  	resp, err := c.clearRequestHistory(host, "dns")
   513  	if err != nil {
   514  		return nil, fmt.Errorf(
   515  			"while clearing DNS request history for host %q: %w", host, err,
   516  		)
   517  	}
   518  	return resp, nil
   519  }
   520  
   521  // TLSALPN01Request is a single TLS-ALPN-01 request in the request history.
   522  type TLSALPN01Request struct {
   523  	ServerName      string   `json:"ServerName"`
   524  	SupportedProtos []string `json:"SupportedProtos"`
   525  }
   526  
   527  // AddTLSALPN01Response adds an ACME TLS-ALPN-01 challenge response certificate
   528  // to the challenge test server's TLS-ALPN-01 interface for the given host. The
   529  // provided key authorization value will be embedded in the response certificate
   530  // served to clients that initiate a TLS-ALPN-01 challenge validation with the
   531  // challenge test server for the provided host. Any failure returns an error
   532  // that includes both the relevant operation and the payload.
   533  func (c *Client) AddTLSALPN01Response(host, value string) ([]byte, error) {
   534  	payload := map[string]string{"host": host, "content": value}
   535  	resp, err := c.postURL(addALPN, payload)
   536  	if err != nil {
   537  		return nil, fmt.Errorf(
   538  			"while adding TLS-ALPN-01 response for host %q, val %q (payload: %v): %w",
   539  			host, value, payload, err,
   540  		)
   541  	}
   542  	return resp, nil
   543  }
   544  
   545  // RemoveTLSALPN01Response removes an ACME TLS-ALPN-01 challenge response
   546  // certificate from the challenge test server's TLS-ALPN-01 interface for the
   547  // given host. Any failure returns an error that includes both the relevant
   548  // operation and the payload.
   549  func (c *Client) RemoveTLSALPN01Response(host string) ([]byte, error) {
   550  	payload := map[string]string{"host": host}
   551  	resp, err := c.postURL(delALPN, payload)
   552  	if err != nil {
   553  		return nil, fmt.Errorf(
   554  			"while removing TLS-ALPN-01 response for host %q (payload: %v): %w",
   555  			host, payload, err,
   556  		)
   557  	}
   558  	return resp, nil
   559  }
   560  
   561  // TLSALPN01RequestHistory returns the history of TLS-ALPN-01 requests made to
   562  // the challenge test server's TLS-ALPN-01 interface for the given host. Any
   563  // failure returns an error that includes both the relevant operation and the
   564  // payload.
   565  func (c *Client) TLSALPN01RequestHistory(host string) ([]TLSALPN01Request, error) {
   566  	payload := map[string]string{"host": host}
   567  	raw, err := c.postURL(getALPNHistory, payload)
   568  	if err != nil {
   569  		return nil, fmt.Errorf(
   570  			"while fetching TLS-ALPN-01 request history for host %q (payload: %v): %w",
   571  			host, payload, err,
   572  		)
   573  	}
   574  	var data []TLSALPN01Request
   575  	err = json.Unmarshal(raw, &data)
   576  	if err != nil {
   577  		return nil, fmt.Errorf("unmarshalling TLS-ALPN-01 request history: %w", err)
   578  	}
   579  	return data, nil
   580  }
   581  
   582  // ClearTLSALPN01RequestHistory clears the history of TLS-ALPN-01 requests made
   583  // to the challenge test server's TLS-ALPN-01 interface for the given host. Any
   584  // failure returns an error that includes both the relevant operation and the
   585  // payload.
   586  func (c *Client) ClearTLSALPN01RequestHistory(host string) ([]byte, error) {
   587  	resp, err := c.clearRequestHistory(host, "tlsalpn")
   588  	if err != nil {
   589  		return nil, fmt.Errorf(
   590  			"while clearing TLS-ALPN-01 request history for host %q: %w", host, err,
   591  		)
   592  	}
   593  	return resp, nil
   594  }