1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-29 07:47:22 +00:00

General Improvements (UseRouter per Party, fix AutoTLS). Read HISTORY.md

relative to: https://github.com/kataras/iris/issues/1577 and https://github.com/kataras/iris/issues/1578
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-08-12 07:20:07 +03:00
parent da029d6f37
commit 0761bc35ee
15 changed files with 639 additions and 120 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"net/url"
@@ -30,7 +31,7 @@ type Configurator func(su *Supervisor)
// Interfaces are separated to return relative functionality to them.
type Supervisor struct {
Server *http.Server
disableHTTP1ToHTTP2Redirection bool // if true then no secondary server on `ListenAndServeTLS/AutoTLS` will be registered, exposed through `NoRedirect`.
disableHTTP1ToHTTP2Redirection bool
closedManually uint32 // future use, accessed atomically (non-zero means we've called the Shutdown)
closedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose.
manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin.
@@ -49,6 +50,21 @@ type Supervisor struct {
IgnoredErrors []string
onErr []func(error)
// Fallback should return a http.Server, which may already running
// to handle the HTTP/1.1 clients when TLS/AutoTLS.
// On manual TLS the accepted "challengeHandler" just returns the passed handler,
// otherwise it binds to the acme challenge wrapper.
// Example:
// Fallback = func(h func(fallback http.Handler) http.Handler) *http.Server {
// s := &http.Server{
// Handler: h(myServerHandler),
// ...otherOptions
// }
// go s.ListenAndServe()
// return s
// }
Fallback func(challegeHandler func(fallback http.Handler) http.Handler) *http.Server
// See `iris.Configuration.SocketSharding`.
SocketSharding bool
}
@@ -83,7 +99,7 @@ func (su *Supervisor) Configure(configurators ...Configurator) *Supervisor {
return su
}
// NoRedirect should be called before `ListenAndServeTLS/AutoTLS` when
// NoRedirect should be called before `ListenAndServeTLS` when
// secondary http1 to http2 server is not required. This method will disable
// the automatic registration of secondary http.Server
// which would redirect "http://" requests to their "https://" equivalent.
@@ -295,11 +311,8 @@ func (su *Supervisor) ListenAndServeTLS(certFileOrContents string, keyFileOrCont
}
}
target, _ := url.Parse("https://" + netutil.ResolveVHost(su.Server.Addr)) // e.g. https://localhost:443
http1Handler := RedirectHandler(target, http.StatusMovedPermanently)
su.manuallyTLS = true
return su.runTLS(getCertificate, http1Handler)
return su.runTLS(getCertificate, nil)
}
// ListenAndServeAutoTLS acts identically to ListenAndServe, except that it
@@ -346,44 +359,77 @@ func (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDi
Cache: cache,
}
return su.runTLS(autoTLSManager.GetCertificate, autoTLSManager.HTTPHandler(nil /* nil for redirect */))
return su.runTLS(autoTLSManager.GetCertificate, autoTLSManager.HTTPHandler)
}
func (su *Supervisor) runTLS(getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error), http1Handler http.Handler) error {
if !su.disableHTTP1ToHTTP2Redirection && http1Handler != nil {
// Note: no need to use a function like ping(":http") to see
// if there is another server running, if it is
// then this server will errored and not start at all.
http1RedirectServer := &http.Server{
ReadTimeout: 30 * time.Second,
WriteTimeout: 60 * time.Second,
Addr: ":http",
Handler: http1Handler,
func (su *Supervisor) runTLS(getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error), challengeHandler func(fallback http.Handler) http.Handler) error {
if su.manuallyTLS && !su.disableHTTP1ToHTTP2Redirection {
// If manual TLS and auto-redirection is enabled,
// then create an empty challenge handler so the :80 server starts.
challengeHandler = func(h http.Handler) http.Handler { // it is always nil on manual TLS.
target, _ := url.Parse("https://" + netutil.ResolveVHost(su.Server.Addr)) // e.g. https://localhost:443
http1Handler := RedirectHandler(target, http.StatusMovedPermanently)
return http1Handler
}
}
if challengeHandler != nil {
http1Server := &http.Server{
Addr: ":http",
Handler: challengeHandler(nil), // nil for redirection.
ReadTimeout: su.Server.ReadTimeout,
ReadHeaderTimeout: su.Server.ReadHeaderTimeout,
WriteTimeout: su.Server.WriteTimeout,
IdleTimeout: su.Server.IdleTimeout,
MaxHeaderBytes: su.Server.MaxHeaderBytes,
}
if su.Fallback == nil {
if !su.manuallyTLS && su.disableHTTP1ToHTTP2Redirection {
// automatic redirection was disabled but Fallback was not registered.
return fmt.Errorf("autotls: use iris.AutoTLSNoRedirect instead")
}
go http1Server.ListenAndServe()
} else {
// if it's manual TLS still can have its own Fallback server here,
// the handler will be the redirect one, the difference is that it can run on any port.
srv := su.Fallback(challengeHandler)
if srv == nil {
if !su.manuallyTLS {
return fmt.Errorf("autotls: relies on an HTTP/1.1 server")
}
// for any case the end-developer decided to return nil here,
// we proceed with the automatic redirection.
srv = http1Server
go srv.ListenAndServe()
} else {
if srv.Addr == "" {
srv.Addr = ":http"
} else if !su.manuallyTLS && srv.Addr != ":80" && srv.Addr != ":http" {
return fmt.Errorf("autotls: The HTTP-01 challenge relies on http://%s:80/.well-known/acme-challenge/", netutil.ResolveVHost(su.Server.Addr))
}
if srv.Handler == nil {
// handler was nil, caller wanted to change the server's options like read/write timeout.
srv.Handler = http1Server.Handler
go srv.ListenAndServe() // automatically start it, we assume the above ^
}
http1Server = srv // to register the shutdown event.
}
}
// register a shutdown callback to this
// supervisor in order to close the "secondary redirect server" as well.
su.RegisterOnShutdown(func() {
// give it some time to close itself...
timeout := 10 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
http1RedirectServer.Shutdown(ctx)
http1Server.Shutdown(ctx)
})
ln, err := netutil.TCP(":http", su.SocketSharding)
if err != nil {
return err
}
go http1RedirectServer.Serve(ln)
}
if su.Server.TLSConfig == nil {
// If tls.Config is NOT configured manually through a host configurator,
// then create it.
su.Server.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
GetCertificate: getCertificate,
PreferServerCipherSuites: true,