87 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
package acme
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	tosAgreementError = "Must agree to subscriber agreement before any further actions"
 | 
						|
)
 | 
						|
 | 
						|
// RemoteError is the base type for all errors specific to the ACME protocol.
 | 
						|
type RemoteError struct {
 | 
						|
	StatusCode int    `json:"status,omitempty"`
 | 
						|
	Type       string `json:"type"`
 | 
						|
	Detail     string `json:"detail"`
 | 
						|
}
 | 
						|
 | 
						|
func (e RemoteError) Error() string {
 | 
						|
	return fmt.Sprintf("acme: Error %d - %s - %s", e.StatusCode, e.Type, e.Detail)
 | 
						|
}
 | 
						|
 | 
						|
// TOSError represents the error which is returned if the user needs to
 | 
						|
// accept the TOS.
 | 
						|
// TODO: include the new TOS url if we can somehow obtain it.
 | 
						|
type TOSError struct {
 | 
						|
	RemoteError
 | 
						|
}
 | 
						|
 | 
						|
type domainError struct {
 | 
						|
	Domain string
 | 
						|
	Error  error
 | 
						|
}
 | 
						|
 | 
						|
type challengeError struct {
 | 
						|
	RemoteError
 | 
						|
	records []validationRecord
 | 
						|
}
 | 
						|
 | 
						|
func (c challengeError) Error() string {
 | 
						|
 | 
						|
	var errStr string
 | 
						|
	for _, validation := range c.records {
 | 
						|
		errStr = errStr + fmt.Sprintf("\tValidation for %s:%s\n\tResolved to:\n\t\t%s\n\tUsed: %s\n\n",
 | 
						|
			validation.Hostname, validation.Port, strings.Join(validation.ResolvedAddresses, "\n\t\t"), validation.UsedAddress)
 | 
						|
	}
 | 
						|
 | 
						|
	return fmt.Sprintf("%s\nError Detail:\n%s", c.RemoteError.Error(), errStr)
 | 
						|
}
 | 
						|
 | 
						|
func handleHTTPError(resp *http.Response) error {
 | 
						|
	var errorDetail RemoteError
 | 
						|
 | 
						|
	contenType := resp.Header.Get("Content-Type")
 | 
						|
	// try to decode the content as JSON
 | 
						|
	if contenType == "application/json" || contenType == "application/problem+json" {
 | 
						|
		decoder := json.NewDecoder(resp.Body)
 | 
						|
		err := decoder.Decode(&errorDetail)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		errorDetail.Detail = string(detailBytes)
 | 
						|
	}
 | 
						|
 | 
						|
	errorDetail.StatusCode = resp.StatusCode
 | 
						|
 | 
						|
	// Check for errors we handle specifically
 | 
						|
	if errorDetail.StatusCode == http.StatusForbidden && errorDetail.Detail == tosAgreementError {
 | 
						|
		return TOSError{errorDetail}
 | 
						|
	}
 | 
						|
 | 
						|
	return errorDetail
 | 
						|
}
 | 
						|
 | 
						|
func handleChallengeError(chlng challenge) error {
 | 
						|
	return challengeError{chlng.Error, chlng.ValidationRecords}
 | 
						|
}
 |