diff --git a/README.md b/README.md
index 37ccf7a0..09099e13 100644
--- a/README.md
+++ b/README.md
@@ -135,11 +135,13 @@ $ go run main.go
Hello World with Go 1.9
-If you've installed Go 1.9 then you can omit the `github.com/kataras/iris/context` package from the imports statement.
+Go 1.9 just released.
+
+Dcumentation and examples will be updated soon to use the already-type aliases inside the framework, such as `iris.Context` instead of the origin pacage.
+
+If you've installed [Go 1.9](https://golang.org/dl) then you can omit the `github.com/kataras/iris/context` package from the imports statement.
```go
-// +build go1.9
-
package main
import "github.com/kataras/iris"
@@ -157,19 +159,6 @@ func main() {
}
```
-We expect Go version 1.9 to be released in August, however you can install Go 1.9 RC2 today.
-
-### Installing Go 1.9rc2
-
-1. Go to https://golang.org/dl/#go1.9rc2
-2. Download a compatible, with your OS, archive or executable, i.e `go1.9rc2.windows-amd64.zip`
-3. Unzip the contents of `go1.9rc2.windows-amd64.zip` folder to your $GOROOT, i.e `C:\Go` or just execute the executable you've just download
-4. Open a terminal and execute `go version`, it should output the go1.9rc2 version, i.e:
-```sh
-C:\Users\kataras>go version
-go version go1.9rc2 windows/amd64
-```
-
diff --git a/core/host/proxy.go b/core/host/proxy.go
index abacfa30..11cbdda4 100644
--- a/core/host/proxy.go
+++ b/core/host/proxy.go
@@ -6,6 +6,7 @@ import (
"net/http/httputil"
"net/url"
"strings"
+ "time"
"github.com/kataras/iris/core/netutil"
)
@@ -59,13 +60,13 @@ func ProxyHandler(target *url.URL) *httputil.ReverseProxy {
}
// NewProxy returns a new host (server supervisor) which
-// redirects all requests to the target.
+// proxies all requests to the target.
// It uses the httputil.NewSingleHostReverseProxy.
//
// Usage:
// target, _ := url.Parse("https://mydomain.com")
// proxy := NewProxy("mydomain.com:80", target)
-// proxy.ListenAndServe() // use of proxy.Shutdown to close the proxy server.
+// proxy.ListenAndServe() // use of `proxy.Shutdown` to close the proxy server.
func NewProxy(hostAddr string, target *url.URL) *Supervisor {
proxyHandler := ProxyHandler(target)
proxy := New(&http.Server{
@@ -75,3 +76,38 @@ func NewProxy(hostAddr string, target *url.URL) *Supervisor {
return proxy
}
+
+// NewRedirection returns a new host (server supervisor) which
+// redirects all requests to the target.
+// Usage:
+// target, _ := url.Parse("https://mydomain.com")
+// r := NewRedirection(":80", target, 307)
+// r.ListenAndServe() // use of `r.Shutdown` to close this server.
+func NewRedirection(hostAddr string, target *url.URL, redirectStatus int) *Supervisor {
+ targetURI := target.String()
+ if redirectStatus <= 300 {
+ // here we should use StatusPermanentRedirect but
+ // that may result on unexpected behavior
+ // for end-developers who might change their minds
+ // after a while, so keep status temporary.
+ // Note thatwe could also use StatusFound
+ // as we do on the `Context#Redirect`.
+ // It will also help us to prevent any post data issues.
+ redirectStatus = http.StatusTemporaryRedirect
+ }
+
+ redirectSrv := &http.Server{
+ ReadTimeout: 30 * time.Second,
+ WriteTimeout: 60 * time.Second,
+ Addr: hostAddr,
+ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ redirectTo := singleJoiningSlash(targetURI, r.URL.Path)
+ if len(r.URL.RawQuery) > 0 {
+ redirectTo += "?" + r.URL.RawQuery
+ }
+ http.Redirect(w, r, redirectTo, redirectStatus)
+ }),
+ }
+
+ return New(redirectSrv)
+}
diff --git a/core/host/supervisor.go b/core/host/supervisor.go
index 688f2f44..bad6599f 100644
--- a/core/host/supervisor.go
+++ b/core/host/supervisor.go
@@ -5,12 +5,16 @@ import (
"crypto/tls"
"net"
"net/http"
+ "net/url"
+ "strings"
"sync"
"sync/atomic"
+ "time"
+
+ "golang.org/x/crypto/acme/autocert"
"github.com/kataras/iris/core/errors"
"github.com/kataras/iris/core/netutil"
- "golang.org/x/crypto/acme/autocert"
)
// Configurator provides an easy way to modify
@@ -231,46 +235,119 @@ func (su *Supervisor) ListenAndServe() error {
return su.Serve(l)
}
-func setupHTTP2(cfg *tls.Config) {
- cfg.NextProtos = append(cfg.NextProtos, "h2") // HTTP2
-}
-
// ListenAndServeTLS acts identically to ListenAndServe, except that it
// expects HTTPS connections. Additionally, files containing a certificate and
// matching private key for the server must be provided. If the certificate
// is signed by a certificate authority, the certFile should be the concatenation
// of the server's certificate, any intermediates, and the CA's certificate.
func (su *Supervisor) ListenAndServeTLS(certFile string, keyFile string) error {
- if certFile == "" || keyFile == "" {
- return errors.New("certFile or keyFile missing")
- }
- cfg := new(tls.Config)
- var err error
- cfg.Certificates = make([]tls.Certificate, 1)
- if cfg.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile); err != nil {
- return err
+ su.manuallyTLS = true
+
+ if certFile != "" && keyFile != "" {
+ cfg := new(tls.Config)
+ var err error
+ cfg.Certificates = make([]tls.Certificate, 1)
+ if cfg.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile); err != nil {
+ return err
+ }
+
+ su.Server.TLSConfig = cfg
+ return su.ListenAndServe()
}
- setupHTTP2(cfg)
- su.Server.TLSConfig = cfg
- su.manuallyTLS = true
- return su.ListenAndServe()
+ if su.Server.TLSConfig == nil {
+ return errors.New("certFile or keyFile missing")
+ }
+
+ return su.supervise(func() error { return su.Server.ListenAndServeTLS("", "") })
}
// ListenAndServeAutoTLS acts identically to ListenAndServe, except that it
-// expects HTTPS connections. server's certificates are auto generated from LETSENCRYPT using
+// expects HTTPS connections. Server's certificates are auto generated from LETSENCRYPT using
// the golang/x/net/autocert package.
-func (su *Supervisor) ListenAndServeAutoTLS() error {
- autoTLSManager := autocert.Manager{
- Prompt: autocert.AcceptTOS,
+//
+// The whitelisted domains are separated by whitespace in "domain" argument, i.e "iris-go.com".
+// If empty, all hosts are currently allowed. This is not recommended,
+// as it opens a potential attack where clients connect to a server
+// by IP address and pretend to be asking for an incorrect host name.
+// Manager will attempt to obtain a certificate for that host, incorrectly,
+// eventually reaching the CA's rate limit for certificate requests
+// and making it impossible to obtain actual certificates.
+//
+// For an "e-mail" use a non-public one, letsencrypt needs that for your own security.
+//
+// The "cacheDir" is being, optionally, used to provide cache
+// stores and retrieves previously-obtained certificates.
+// If empty, certs will only be cached for the lifetime of the auto tls manager.
+//
+// Note: If domain is not empty and the server's port was "443" then
+// it will start a new server, automaticall for you, which will redirect all
+// http versions to their https as well.
+func (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDir string) error {
+ var (
+ cache autocert.Cache
+ hostPolicy autocert.HostPolicy
+ )
+
+ if cacheDir != "" {
+ cache = autocert.DirCache(cacheDir)
+ }
+
+ if domain != "" {
+ domains := strings.Split(domain, " ")
+ hostPolicy = autocert.HostWhitelist(domains...)
+ }
+
+ autoTLSManager := &autocert.Manager{
+ Prompt: autocert.AcceptTOS,
+ HostPolicy: hostPolicy,
+ Email: email,
+ Cache: cache,
+ }
+
+ cfg := &tls.Config{
+ GetCertificate: autoTLSManager.GetCertificate,
+ MinVersion: tls.VersionTLS10,
+ PreferServerCipherSuites: true,
+ CurvePreferences: []tls.CurveID{
+ tls.X25519,
+ },
}
- cfg := new(tls.Config)
- cfg.GetCertificate = autoTLSManager.GetCertificate
- setupHTTP2(cfg)
su.Server.TLSConfig = cfg
- su.manuallyTLS = true
- return su.ListenAndServe()
+
+ // Redirect all http://$path requests to their
+ // https://$path versions if a specific domain is passed on
+ // and the port was 443.
+ if hostPolicy != nil && netutil.ResolvePort(su.Server.Addr) == 443 {
+ // find the first domain if more than one.
+ spaceIdx := strings.IndexByte(domain, ' ')
+ if spaceIdx != -1 {
+ domain = domain[0:spaceIdx]
+ }
+ // create the url for the secured server.
+ target, err := url.Parse("https://" + domain)
+ if err != nil {
+ return err
+ }
+
+ // create the redirect server.
+ redirectSrv := NewRedirection(":80", target, -1)
+ // 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 := 5 * time.Second
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ redirectSrv.Shutdown(ctx)
+ })
+ // start that redirect server using a different goroutine.
+ go redirectSrv.ListenAndServe()
+ }
+
+ return su.ListenAndServeTLS("", "")
}
// RegisterOnShutdown registers a function to call on Shutdown.
diff --git a/iris.go b/iris.go
index bfe0e360..d9870401 100644
--- a/iris.go
+++ b/iris.go
@@ -552,9 +552,24 @@ func TLS(addr string, certFile, keyFile string, hostConfigs ...host.Configurator
// certifications created on the fly by the "autocert" golang/x package,
// so localhost may not be working, use it at "production" machine.
//
-// Addr should have the form of [host]:port, i.e mydomain.com:443.
+// Addr should have the form of [host]:port, i.e mydomain.com:443 or :443.
//
-// Second argument is optional, it accepts one or more
+// The whitelisted domains are separated by whitespace in "domain" argument,
+// i.e "iris-go.com", can be different than "addr".
+// If empty, all hosts are currently allowed. This is not recommended,
+// as it opens a potential attack where clients connect to a server
+// by IP address and pretend to be asking for an incorrect host name.
+// Manager will attempt to obtain a certificate for that host, incorrectly,
+// eventually reaching the CA's rate limit for certificate requests
+// and making it impossible to obtain actual certificates.
+//
+// For an "e-mail" use a non-public one, letsencrypt needs that for your own security.
+//
+// Note: If domain is not empty and the server's port was "443" then
+// it will start a new server, automaticall for you, which will redirect all
+// http versions to their https as well.
+//
+// Last argument is optional, it accepts one or more
// `func(*host.Configurator)` that are being executed
// on that specific host that this function will create to start the server.
// Via host configurators you can configure the back-end host supervisor,
@@ -563,12 +578,18 @@ func TLS(addr string, certFile, keyFile string, hostConfigs ...host.Configurator
// https://github.com/kataras/iris/blob/master/_examples/http-listening/notify-on-shutdown/main.go
// Look at the `ConfigureHost` too.
//
-// See `Run` for more.
-func AutoTLS(addr string, hostConfigs ...host.Configurator) Runner {
+// Usage:
+// app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))
+//
+// See `Run` and `core/host/Supervisor#ListenAndServeAutoTLS` for more.
+func AutoTLS(
+ addr string,
+ domain string, email string,
+ hostConfigs ...host.Configurator) Runner {
return func(app *Application) error {
return app.NewHost(&http.Server{Addr: addr}).
Configure(hostConfigs...).
- ListenAndServeAutoTLS()
+ ListenAndServeAutoTLS(domain, email, "letscache")
}
}