mirror of
https://github.com/kataras/iris.git
synced 2025-03-15 11:16:26 +01:00
iris.TLS starts a secondary http redirection server now (like AutoTLS did) and add 'iris.TLSNoRedirect' to disable it (on both TLS and AutoTLS)
Former-commit-id: c7a535bf860a67604de3d09ade30599611e096f1
This commit is contained in:
parent
116503a9a5
commit
b6a36bf28d
|
@ -371,6 +371,9 @@ Other Improvements:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
- `iris.TLS` can now accept certificates as raw contents too.
|
||||||
|
- `iris.TLS` registers a secondary http server which redirects "http://" to their "https://" equivalent requests, unless the new `iris.TLSNoRedirect` host Configurator is provided on `iris.TLS` (or `iris.AutoTLS`), e.g. `app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key", iris.TLSNoRedirect))`.
|
||||||
|
|
||||||
- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code.
|
- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code.
|
||||||
|
|
||||||
- Server will not return neither log the `ErrServerClosed` if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`).
|
- Server will not return neither log the `ErrServerClosed` if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`).
|
||||||
|
|
|
@ -24,4 +24,9 @@ func main() {
|
||||||
// use real whitelisted domain(or domains split by whitespaces)
|
// use real whitelisted domain(or domains split by whitespaces)
|
||||||
// and a non-public e-mail instead or edit your hosts file.
|
// and a non-public e-mail instead or edit your hosts file.
|
||||||
app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))
|
app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))
|
||||||
|
|
||||||
|
// Note: to disable automatic "http://" to "https://" redirections pass the `iris.TLSNoRedirect`
|
||||||
|
// host configurator to TLS or AutoTLS functions, e.g:
|
||||||
|
//
|
||||||
|
// app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com", iris.TLSNoRedirect))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/core/host"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -19,11 +15,13 @@ func main() {
|
||||||
ctx.Writef("Hello from the SECURE server on path /mypath")
|
ctx.Writef("Hello from the SECURE server on path /mypath")
|
||||||
})
|
})
|
||||||
|
|
||||||
// to start a new server listening at :80 and redirects
|
// Start the server (HTTPS) on port 443,
|
||||||
// to the secure address, then:
|
// and a secondary of (HTTP) on port :80 which redirects requests to their HTTPS version.
|
||||||
target, _ := url.Parse("https://127.0.0.1:443")
|
// This is a blocking func.
|
||||||
go host.NewRedirection("127.0.0.1:80", target, iris.StatusMovedPermanently).ListenAndServe()
|
|
||||||
|
|
||||||
// start the server (HTTPS) on port 443, this is a blocking func
|
|
||||||
app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))
|
app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))
|
||||||
|
|
||||||
|
// Note: to disable automatic "http://" to "https://" redirections pass the `iris.TLSNoRedirect`
|
||||||
|
// host configurator to TLS or AutoTLS functions, e.g:
|
||||||
|
//
|
||||||
|
// app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key", iris.TLSNoRedirect))
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,6 @@
|
||||||
|
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
option java_multiple_files = true;
|
|
||||||
option java_package = "io.grpc.examples.helloworld";
|
|
||||||
option java_outer_classname = "HelloWorldProto";
|
|
||||||
|
|
||||||
package helloworld;
|
package helloworld;
|
||||||
|
|
||||||
// The greeting service definition.
|
// The greeting service definition.
|
||||||
|
|
|
@ -84,6 +84,19 @@ func NewProxy(hostAddr string, target *url.URL) *Supervisor {
|
||||||
// r := NewRedirection(":80", target, 307)
|
// r := NewRedirection(":80", target, 307)
|
||||||
// r.ListenAndServe() // use of `r.Shutdown` to close this server.
|
// r.ListenAndServe() // use of `r.Shutdown` to close this server.
|
||||||
func NewRedirection(hostAddr string, target *url.URL, redirectStatus int) *Supervisor {
|
func NewRedirection(hostAddr string, target *url.URL, redirectStatus int) *Supervisor {
|
||||||
|
redirectSrv := &http.Server{
|
||||||
|
ReadTimeout: 30 * time.Second,
|
||||||
|
WriteTimeout: 60 * time.Second,
|
||||||
|
Addr: hostAddr,
|
||||||
|
Handler: RedirectHandler(target, redirectStatus),
|
||||||
|
}
|
||||||
|
|
||||||
|
return New(redirectSrv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectHandler returns a simple redirect handler.
|
||||||
|
// See `NewProxy` or `ProxyHandler` for more features.
|
||||||
|
func RedirectHandler(target *url.URL, redirectStatus int) http.Handler {
|
||||||
targetURI := target.String()
|
targetURI := target.String()
|
||||||
if redirectStatus <= 300 {
|
if redirectStatus <= 300 {
|
||||||
// here we should use StatusPermanentRedirect but
|
// here we should use StatusPermanentRedirect but
|
||||||
|
@ -96,18 +109,11 @@ func NewRedirection(hostAddr string, target *url.URL, redirectStatus int) *Super
|
||||||
redirectStatus = http.StatusTemporaryRedirect
|
redirectStatus = http.StatusTemporaryRedirect
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectSrv := &http.Server{
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ReadTimeout: 30 * time.Second,
|
redirectTo := singleJoiningSlash(targetURI, r.URL.Path)
|
||||||
WriteTimeout: 60 * time.Second,
|
if len(r.URL.RawQuery) > 0 {
|
||||||
Addr: hostAddr,
|
redirectTo += "?" + r.URL.RawQuery
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
}
|
||||||
redirectTo := singleJoiningSlash(targetURI, r.URL.Path)
|
http.Redirect(w, r, redirectTo, redirectStatus)
|
||||||
if len(r.URL.RawQuery) > 0 {
|
})
|
||||||
redirectTo += "?" + r.URL.RawQuery
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, redirectTo, redirectStatus)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
return New(redirectSrv)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -27,12 +29,13 @@ type Configurator func(su *Supervisor)
|
||||||
//
|
//
|
||||||
// Interfaces are separated to return relative functionality to them.
|
// Interfaces are separated to return relative functionality to them.
|
||||||
type Supervisor struct {
|
type Supervisor struct {
|
||||||
Server *http.Server
|
Server *http.Server
|
||||||
closedManually uint32 // future use, accessed atomically (non-zero means we've called the Shutdown)
|
disableHTTP1ToHTTP2Redirection bool // if true then no secondary server on `ListenAndServeTLS/AutoTLS` will be registered, exposed through `NoRedirect`.
|
||||||
closedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose.
|
closedManually uint32 // future use, accessed atomically (non-zero means we've called the Shutdown)
|
||||||
manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin.
|
closedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose.
|
||||||
shouldWait int32 // non-zero means that the host should wait for unblocking
|
manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin.
|
||||||
unblockChan chan struct{}
|
shouldWait int32 // non-zero means that the host should wait for unblocking
|
||||||
|
unblockChan chan struct{}
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
|
@ -77,6 +80,14 @@ func (su *Supervisor) Configure(configurators ...Configurator) *Supervisor {
|
||||||
return su
|
return su
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoRedirect should be called before `ListenAndServeTLS/AutoTLS` 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.
|
||||||
|
func (su *Supervisor) NoRedirect() {
|
||||||
|
su.disableHTTP1ToHTTP2Redirection = true
|
||||||
|
}
|
||||||
|
|
||||||
// DeferFlow defers the flow of the exeuction,
|
// DeferFlow defers the flow of the exeuction,
|
||||||
// i.e: when server should return error and exit
|
// i.e: when server should return error and exit
|
||||||
// from app, a DeferFlow call inside a Task
|
// from app, a DeferFlow call inside a Task
|
||||||
|
@ -234,38 +245,58 @@ func (su *Supervisor) ListenAndServe() error {
|
||||||
return su.Serve(l)
|
return su.Serve(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadCertificate(c, k string) (*tls.Certificate, error) {
|
||||||
|
var (
|
||||||
|
cert tls.Certificate
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if fileExists(c) && fileExists(k) {
|
||||||
|
// act them as files in the system.
|
||||||
|
cert, err = tls.LoadX509KeyPair(c, k)
|
||||||
|
} else {
|
||||||
|
// act them as raw contents.
|
||||||
|
cert, err = tls.X509KeyPair([]byte(c), []byte(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ListenAndServeTLS acts identically to ListenAndServe, except that it
|
// ListenAndServeTLS acts identically to ListenAndServe, except that it
|
||||||
// expects HTTPS connections. Additionally, files containing a certificate and
|
// expects HTTPS connections. Additionally, files containing a certificate and
|
||||||
// matching private key for the server must be provided. If the certificate
|
// matching private key for the server must be provided. If the certificate
|
||||||
// is signed by a certificate authority, the certFile should be the concatenation
|
// is signed by a certificate authority, the certFile should be the concatenation
|
||||||
// of the server's certificate, any intermediates, and the CA's certificate.
|
// of the server's certificate, any intermediates, and the CA's certificate.
|
||||||
func (su *Supervisor) ListenAndServeTLS(certFile string, keyFile string) error {
|
func (su *Supervisor) ListenAndServeTLS(certFileOrContents string, keyFileOrContents string) error {
|
||||||
su.manuallyTLS = true
|
var getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)
|
||||||
|
|
||||||
if certFile != "" && keyFile != "" {
|
// If tls.Config configured manually through a host configurator then skip that
|
||||||
cfg := new(tls.Config)
|
// and let the redirection service registered alone.
|
||||||
var err error
|
// e.g. https://github.com/kataras/iris/issues/1481#issuecomment-605621255
|
||||||
cfg.Certificates = make([]tls.Certificate, 1)
|
if su.Server.TLSConfig == nil {
|
||||||
if cfg.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile); err != nil {
|
if certFileOrContents == "" && keyFileOrContents == "" {
|
||||||
|
return errors.New("empty certFileOrContents or keyFileOrContents and Server.TLSConfig")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := loadCertificate(certFileOrContents, keyFileOrContents)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// manually inserted as pre-go 1.9 for any case.
|
getCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
cfg.NextProtos = []string{"h2", "http/1.1"}
|
return cert, nil
|
||||||
su.Server.TLSConfig = cfg
|
}
|
||||||
|
|
||||||
// It does nothing more than the su.Server.ListenAndServeTLS anymore.
|
|
||||||
// - no hurt if we let it as it is
|
|
||||||
// - no problem if we remove it as well
|
|
||||||
// but let's comment this as proposed, fewer code is better:
|
|
||||||
// return su.ListenAndServe()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if su.Server.TLSConfig == nil {
|
target, _ := url.Parse("https://" + netutil.ResolveVHost(su.Server.Addr)) // e.g. https://localhost:443
|
||||||
return errors.New("empty certFile or keyFile and Server.TLSConfig")
|
http1Handler := RedirectHandler(target, http.StatusMovedPermanently)
|
||||||
}
|
|
||||||
|
|
||||||
return su.supervise(func() error { return su.Server.ListenAndServeTLS("", "") })
|
su.manuallyTLS = true
|
||||||
|
return su.runTLS(getCertificate, http1Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeAutoTLS acts identically to ListenAndServe, except that it
|
// ListenAndServeAutoTLS acts identically to ListenAndServe, except that it
|
||||||
|
@ -310,40 +341,67 @@ func (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDi
|
||||||
HostPolicy: hostPolicy,
|
HostPolicy: hostPolicy,
|
||||||
Email: email,
|
Email: email,
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
ForceRSA: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srv2 := &http.Server{
|
return su.runTLS(autoTLSManager.GetCertificate, autoTLSManager.HTTPHandler(nil /* nil for redirect */))
|
||||||
ReadTimeout: 30 * time.Second,
|
}
|
||||||
WriteTimeout: 60 * time.Second,
|
|
||||||
Addr: ":http",
|
func (su *Supervisor) runTLS(getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error), http1Handler http.Handler) error {
|
||||||
Handler: autoTLSManager.HTTPHandler(nil), // nil for redirect.
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
})
|
||||||
|
go http1RedirectServer.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// register a shutdown callback to this
|
if su.Server.TLSConfig == nil {
|
||||||
// supervisor in order to close the "secondary redirect server" as well.
|
// If tls.Config is NOT configured manually through a host configurator,
|
||||||
su.RegisterOnShutdown(func() {
|
// then create it.
|
||||||
// give it some time to close itself...
|
su.Server.TLSConfig = &tls.Config{
|
||||||
timeout := 10 * time.Second
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
srv2.Shutdown(ctx)
|
|
||||||
})
|
|
||||||
go srv2.ListenAndServe()
|
|
||||||
|
|
||||||
su.Server.TLSConfig = &tls.Config{
|
MinVersion: tls.VersionTLS12,
|
||||||
MinVersion: tls.VersionTLS10,
|
GetCertificate: getCertificate,
|
||||||
GetCertificate: autoTLSManager.GetCertificate,
|
PreferServerCipherSuites: true,
|
||||||
PreferServerCipherSuites: true,
|
NextProtos: []string{"h2", "http/1.1"},
|
||||||
// Keep the defaults.
|
CurvePreferences: []tls.CurveID{
|
||||||
CurvePreferences: []tls.CurveID{
|
tls.CurveP521,
|
||||||
tls.X25519,
|
tls.CurveP384,
|
||||||
tls.CurveP256,
|
tls.CurveP256,
|
||||||
tls.CurveP384,
|
},
|
||||||
tls.CurveP521,
|
CipherSuites: []uint16{
|
||||||
},
|
tls.TLS_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||||
|
tls.TLS_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
0xC028, /* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 */
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return su.ListenAndServeTLS("", "")
|
|
||||||
|
return su.supervise(func() error { return su.Server.ListenAndServeTLS("", "") })
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterOnShutdown registers a function to call on Shutdown.
|
// RegisterOnShutdown registers a function to call on Shutdown.
|
||||||
|
@ -377,3 +435,13 @@ func (su *Supervisor) shutdownOnInterrupt(ctx context.Context) {
|
||||||
atomic.StoreUint32(&su.closedByInterruptHandler, 1)
|
atomic.StoreUint32(&su.closedByInterruptHandler, 1)
|
||||||
su.Shutdown(ctx)
|
su.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fileExists tries to report whether a local physical file of "filename" exists.
|
||||||
|
func fileExists(filename string) bool {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !info.IsDir()
|
||||||
|
}
|
||||||
|
|
14
iris.go
14
iris.go
|
@ -904,15 +904,21 @@ func Addr(addr string, hostConfigs ...host.Configurator) Runner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSNoRedirect is a `host.Configurator` which can be passed as last argument
|
||||||
|
// to the `TLS` and `AutoTLS` functions. It disables the automatic
|
||||||
|
// registration of redirection from "http://" to "https://" requests.
|
||||||
|
var TLSNoRedirect = func(su *host.Supervisor) { su.NoRedirect() }
|
||||||
|
|
||||||
// TLS can be used as an argument for the `Run` method.
|
// TLS can be used as an argument for the `Run` method.
|
||||||
// It will start the Application's secure server.
|
// It will start the Application's secure server.
|
||||||
//
|
//
|
||||||
// Use it like you used to use the http.ListenAndServeTLS function.
|
// Use it like you used to use the http.ListenAndServeTLS function.
|
||||||
//
|
//
|
||||||
// Addr should have the form of [host]:port, i.e localhost:443 or :443.
|
// Addr should have the form of [host]:port, i.e localhost:443 or :443.
|
||||||
// CertFile & KeyFile should be filenames with their extensions.
|
// "certFileOrContents" & "keyFileOrContents" should be filenames with their extensions
|
||||||
|
// or raw contents of the certificate and the private key.
|
||||||
//
|
//
|
||||||
// Second argument is optional, it accepts one or more
|
// Last argument is optional, it accepts one or more
|
||||||
// `func(*host.Configurator)` that are being executed
|
// `func(*host.Configurator)` that are being executed
|
||||||
// on that specific host that this function will create to start the server.
|
// on that specific host that this function will create to start the server.
|
||||||
// Via host configurators you can configure the back-end host supervisor,
|
// Via host configurators you can configure the back-end host supervisor,
|
||||||
|
@ -922,11 +928,11 @@ func Addr(addr string, hostConfigs ...host.Configurator) Runner {
|
||||||
// Look at the `ConfigureHost` too.
|
// Look at the `ConfigureHost` too.
|
||||||
//
|
//
|
||||||
// See `Run` for more.
|
// See `Run` for more.
|
||||||
func TLS(addr string, certFile, keyFile string, hostConfigs ...host.Configurator) Runner {
|
func TLS(addr string, certFileOrContents, keyFileOrContents string, hostConfigs ...host.Configurator) Runner {
|
||||||
return func(app *Application) error {
|
return func(app *Application) error {
|
||||||
return app.NewHost(&http.Server{Addr: addr}).
|
return app.NewHost(&http.Server{Addr: addr}).
|
||||||
Configure(hostConfigs...).
|
Configure(hostConfigs...).
|
||||||
ListenAndServeTLS(certFile, keyFile)
|
ListenAndServeTLS(certFileOrContents, keyFileOrContents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user