2018-05-31 07:30:04 +00:00
package acme
2018-03-26 12:12:03 +00:00
import (
"fmt"
"net"
"net/http"
"strings"
2018-05-31 07:30:04 +00:00
"github.com/xenolf/lego/log"
2018-03-26 12:12:03 +00:00
)
// HTTPProviderServer implements ChallengeProvider for `http-01` challenge
// It may be instantiated without using the NewHTTPProviderServer function if
// you want only to use the default values.
type HTTPProviderServer struct {
iface string
port string
done chan bool
listener net . Listener
}
// NewHTTPProviderServer creates a new HTTPProviderServer on the selected interface and port.
// Setting iface and / or port to an empty string will make the server fall back to
// the "any" interface and port 80 respectively.
func NewHTTPProviderServer ( iface , port string ) * HTTPProviderServer {
return & HTTPProviderServer { iface : iface , port : port }
}
// Present starts a web server and makes the token available at `HTTP01ChallengePath(token)` for web requests.
func ( s * HTTPProviderServer ) Present ( domain , token , keyAuth string ) error {
if s . port == "" {
s . port = "80"
}
var err error
s . listener , err = net . Listen ( "tcp" , net . JoinHostPort ( s . iface , s . port ) )
if err != nil {
2018-10-10 14:28:04 +00:00
return fmt . Errorf ( "could not start HTTP server for challenge -> %v" , err )
2018-03-26 12:12:03 +00:00
}
s . done = make ( chan bool )
go s . serve ( domain , token , keyAuth )
return nil
}
// CleanUp closes the HTTP server and removes the token from `HTTP01ChallengePath(token)`
func ( s * HTTPProviderServer ) CleanUp ( domain , token , keyAuth string ) error {
if s . listener == nil {
return nil
}
s . listener . Close ( )
<- s . done
return nil
}
func ( s * HTTPProviderServer ) serve ( domain , token , keyAuth string ) {
path := HTTP01ChallengePath ( token )
// The handler validates the HOST header and request type.
// For validation it then writes the token the server returned with the challenge
mux := http . NewServeMux ( )
mux . HandleFunc ( path , func ( w http . ResponseWriter , r * http . Request ) {
2018-07-03 10:44:04 +00:00
if strings . HasPrefix ( r . Host , domain ) && r . Method == http . MethodGet {
2018-03-26 12:12:03 +00:00
w . Header ( ) . Add ( "Content-Type" , "text/plain" )
2018-10-10 14:28:04 +00:00
_ , err := w . Write ( [ ] byte ( keyAuth ) )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2018-07-03 10:44:04 +00:00
log . Infof ( "[%s] Served key authentication" , domain )
2018-03-26 12:12:03 +00:00
} else {
2018-07-03 10:44:04 +00:00
log . Warnf ( "Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly." , r . Host , r . Method )
2018-10-10 14:28:04 +00:00
_ , err := w . Write ( [ ] byte ( "TEST" ) )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2018-03-26 12:12:03 +00:00
}
} )
2018-10-10 14:28:04 +00:00
httpServer := & http . Server { Handler : mux }
2018-03-26 12:12:03 +00:00
// Once httpServer is shut down we don't want any lingering
// connections, so disable KeepAlives.
httpServer . SetKeepAlivesEnabled ( false )
2018-10-10 14:28:04 +00:00
err := httpServer . Serve ( s . listener )
if err != nil {
log . Println ( err )
}
2018-03-26 12:12:03 +00:00
s . done <- true
}