Add listen and proxy tests

Former-commit-id: 7c89556942af8e9c6966022323cb9746db446cfc
This commit is contained in:
Gerasimos (Makis) Maropoulos 2017-02-17 02:55:26 +02:00
parent 57e5b77853
commit 2a4997cadf
4 changed files with 324 additions and 31 deletions

View File

@ -746,23 +746,24 @@ This pattern allows us to be very pluggable and add features that the *Framework
it knows only the main policies which implement but their features are our(as users) business. it knows only the main policies which implement but their features are our(as users) business.
We have 7 policies,so far, and some of them have 'subpolicies' (the RouterReversionPolicy for example). We have 8 policies, so far, and some of them have 'subpolicies' (the RouterReversionPolicy for example).
- LoggerPolicy - LoggerPolicy
- EventPolicy - EventPolicy
- Boot - Boot
- Build - Build
- Interrupted - Interrupted
- Recover - Recover
- RouterReversionPolicy - RouterReversionPolicy
- StaticPath - StaticPath
- WildcardPath - WildcardPath
- URLPath - URLPath
- RouteContextLinker - RouteContextLinker
- RouterBuilderPolicy - RouterBuilderPolicy
- RouterWrapperPolicy - RouterWrapperPolicy
- RenderPolicy - RenderPolicy
- TemplateFuncsPolicy - TemplateFuncsPolicy
- SessionsPolicy
**Details** of these can be found at [policy.go](https://github.com/kataras/iris/blob/master/policy.go). **Details** of these can be found at [policy.go](https://github.com/kataras/iris/blob/master/policy.go).

View File

@ -288,12 +288,14 @@ func Proxy(proxyAddr string, redirectSchemeAndHost string) func() error {
// override the handler and redirect all requests to this addr // override the handler and redirect all requests to this addr
h := ProxyHandler(redirectSchemeAndHost) h := ProxyHandler(redirectSchemeAndHost)
prx := New(OptionDisableBanner(true)) prx := New(OptionDisableBanner(true))
prx.Adapt(DevLogger())
prx.Adapt(RouterBuilderPolicy(func(RouteRepository, ContextPool) http.Handler { prx.Adapt(RouterBuilderPolicy(func(RouteRepository, ContextPool) http.Handler {
return h return h
})) }))
go prx.Listen(proxyAddr) go prx.Listen(proxyAddr)
time.Sleep(300 * time.Millisecond) time.Sleep(150 * time.Millisecond)
return func() error { return prx.Close() } return func() error { return prx.Close() }
} }

250
addr_test.go Normal file
View File

@ -0,0 +1,250 @@
// Black-box Testing
package iris_test
import (
"io/ioutil"
"math/rand"
"net/http"
"os"
"strconv"
"testing"
"time"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/httptest"
)
func TestParseAddr(t *testing.T) {
// test hosts
expectedHost1 := "mydomain.com:1993"
expectedHost2 := "mydomain.com"
expectedHost3 := iris.DefaultServerHostname + ":9090"
expectedHost4 := "mydomain.com:443"
host1 := iris.ParseHost(expectedHost1)
host2 := iris.ParseHost(expectedHost2)
host3 := iris.ParseHost(":9090")
host4 := iris.ParseHost(expectedHost4)
if host1 != expectedHost1 {
t.Fatalf("Expecting server 1's host to be %s but we got %s", expectedHost1, host1)
}
if host2 != expectedHost2 {
t.Fatalf("Expecting server 2's host to be %s but we got %s", expectedHost2, host2)
}
if host3 != expectedHost3 {
t.Fatalf("Expecting server 3's host to be %s but we got %s", expectedHost3, host3)
}
if host4 != expectedHost4 {
t.Fatalf("Expecting server 4's host to be %s but we got %s", expectedHost4, host4)
}
// test hostname
expectedHostname1 := "mydomain.com"
expectedHostname2 := "mydomain.com"
expectedHostname3 := iris.DefaultServerHostname
expectedHostname4 := "mydomain.com"
hostname1 := iris.ParseHostname(host1)
hostname2 := iris.ParseHostname(host2)
hostname3 := iris.ParseHostname(host3)
hostname4 := iris.ParseHostname(host4)
if hostname1 != expectedHostname1 {
t.Fatalf("Expecting server 1's hostname to be %s but we got %s", expectedHostname1, hostname1)
}
if hostname2 != expectedHostname2 {
t.Fatalf("Expecting server 2's hostname to be %s but we got %s", expectedHostname2, hostname2)
}
if hostname3 != expectedHostname3 {
t.Fatalf("Expecting server 3's hostname to be %s but we got %s", expectedHostname3, hostname3)
}
if hostname4 != expectedHostname4 {
t.Fatalf("Expecting server 4's hostname to be %s but we got %s", expectedHostname4, hostname4)
}
// test scheme, no need to test fullhost(scheme+host)
expectedScheme1 := iris.SchemeHTTP
expectedScheme2 := iris.SchemeHTTP
expectedScheme3 := iris.SchemeHTTP
expectedScheme4 := iris.SchemeHTTPS
scheme1 := iris.ParseScheme(host1)
scheme2 := iris.ParseScheme(host2)
scheme3 := iris.ParseScheme(host3)
scheme4 := iris.ParseScheme(host4)
if scheme1 != expectedScheme1 {
t.Fatalf("Expecting server 1's hostname to be %s but we got %s", expectedScheme1, scheme1)
}
if scheme2 != expectedScheme2 {
t.Fatalf("Expecting server 2's hostname to be %s but we got %s", expectedScheme2, scheme2)
}
if scheme3 != expectedScheme3 {
t.Fatalf("Expecting server 3's hostname to be %s but we got %s", expectedScheme3, scheme3)
}
if scheme4 != expectedScheme4 {
t.Fatalf("Expecting server 4's hostname to be %s but we got %s", expectedScheme4, scheme4)
}
}
const (
testTLSCert = `-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIJAP0pWSuIYyQCMA0GCSqGSIb3DQEBBQUAMBgxFjAUBgNV
BAMMDWxvY2FsaG9zdDozMzEwHhcNMTYxMjI1MDk1OTI3WhcNMjYxMjIzMDk1OTI3
WjAYMRYwFAYDVQQDDA1sb2NhbGhvc3Q6MzMxMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA5vETjLa+8W856rWXO1xMF/CLss9vn5xZhPXKhgz+D7ogSAXm
mWP53eeBUGC2r26J++CYfVqwOmfJEu9kkGUVi8cGMY9dHeIFPfxD31MYX175jJQe
tu0WeUII7ciNsSUDyBMqsl7yi1IgN7iLONM++1+QfbbmNiEbghRV6icEH6M+bWlz
3YSAMEdpK3mg2gsugfLKMwJkaBKEehUNMySRlIhyLITqt1exYGaggRd1zjqUpqpD
sL2sRVHJ3qHGkSh8nVy8MvG8BXiFdYQJP3mCQDZzruCyMWj5/19KAyu7Cto3Bcvu
PgujnwRtU+itt8WhZUVtU1n7Ivf6lMJTBcc4OQIDAQABo1AwTjAdBgNVHQ4EFgQU
MXrBvbILQmiwjUj19aecF2N+6IkwHwYDVR0jBBgwFoAUMXrBvbILQmiwjUj19aec
F2N+6IkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA4zbFml1t9KXJ
OijAV8gALePR8v04DQwJP+jsRxXw5zzhc8Wqzdd2hjUd07mfRWAvmyywrmhCV6zq
OHznR+aqIqHtm0vV8OpKxLoIQXavfBd6axEXt3859RDM4xJNwIlxs3+LWGPgINud
wjJqjyzSlhJpQpx4YZ5Da+VMiqAp8N1UeaZ5lBvmSDvoGh6HLODSqtPlWMrldRW9
AfsXVxenq81MIMeKW2fSOoPnWZ4Vjf1+dSlbJE/DD4zzcfbyfgY6Ep/RrUltJ3ag
FQbuNTQlgKabe21dSL9zJ2PengVKXl4Trl+4t/Kina9N9Jw535IRCSwinD6a/2Ca
m7DnVXFiVA==
-----END CERTIFICATE-----
`
testTLSKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA5vETjLa+8W856rWXO1xMF/CLss9vn5xZhPXKhgz+D7ogSAXm
mWP53eeBUGC2r26J++CYfVqwOmfJEu9kkGUVi8cGMY9dHeIFPfxD31MYX175jJQe
tu0WeUII7ciNsSUDyBMqsl7yi1IgN7iLONM++1+QfbbmNiEbghRV6icEH6M+bWlz
3YSAMEdpK3mg2gsugfLKMwJkaBKEehUNMySRlIhyLITqt1exYGaggRd1zjqUpqpD
sL2sRVHJ3qHGkSh8nVy8MvG8BXiFdYQJP3mCQDZzruCyMWj5/19KAyu7Cto3Bcvu
PgujnwRtU+itt8WhZUVtU1n7Ivf6lMJTBcc4OQIDAQABAoIBAQCTLE0eHpPevtg0
+FaRUMd5diVA5asoF3aBIjZXaU47bY0G+SO02x6wSMmDFK83a4Vpy/7B3Bp0jhF5
DLCUyKaLdmE/EjLwSUq37ty+JHFizd7QtNBCGSN6URfpmSabHpCjX3uVQqblHIhF
mki3BQCdJ5CoXPemxUCHjDgYSZb6JVNIPJExjekc0+4A2MYWMXV6Wr86C7AY3659
KmveZpC3gOkLA/g/IqDQL/QgTq7/3eloHaO+uPBihdF56do4eaOO0jgFYpl8V7ek
PZhHfhuPZV3oq15+8Vt77ngtjUWVI6qX0E3ilh+V5cof+03q0FzHPVe3zBUNXcm0
OGz19u/FAoGBAPSm4Aa4xs/ybyjQakMNix9rak66ehzGkmlfeK5yuQ/fHmTg8Ac+
ahGs6A3lFWQiyU6hqm6Qp0iKuxuDh35DJGCWAw5OUS/7WLJtu8fNFch6iIG29rFs
s+Uz2YLxJPebpBsKymZUp7NyDRgEElkiqsREmbYjLrc8uNKkDy+k14YnAoGBAPGn
ZlN0Mo5iNgQStulYEP5pI7WOOax9KOYVnBNguqgY9c7fXVXBxChoxt5ebQJWG45y
KPG0hB0bkA4YPu4bTRf5acIMpjFwcxNlmwdc4oCkT4xqAFs9B/AKYZgkf4IfKHqW
P9PD7TbUpkaxv25bPYwUSEB7lPa+hBtRyN9Wo6qfAoGAPBkeISiU1hJE0i7YW55h
FZfKZoqSYq043B+ywo+1/Dsf+UH0VKM1ZSAnZPpoVc/hyaoW9tAb98r0iZ620wJl
VkCjgYklknbY5APmw/8SIcxP6iVq1kzQqDYjcXIRVa3rEyWEcLzM8VzL8KFXbIQC
lPIRHFfqKuMEt+HLRTXmJ7MCgYAHGvv4QjdmVl7uObqlG9DMGj1RjlAF0VxNf58q
NrLmVG2N2qV86wigg4wtZ6te4TdINfUcPkmQLYpLz8yx5Z2bsdq5OPP+CidoD5nC
WqnSTIKGR2uhQycjmLqL5a7WHaJsEFTqHh2wego1k+5kCUzC/KmvM7MKmkl6ICp+
3qZLUwKBgQCDOhKDwYo1hdiXoOOQqg/LZmpWOqjO3b4p99B9iJqhmXN0GKXIPSBh
5nqqmGsG8asSQhchs7EPMh8B80KbrDTeidWskZuUoQV27Al1UEmL6Zcl83qXD6sf
k9X9TwWyZtp5IL1CAEd/Il9ZTXFzr3lNaN8LCFnU+EIsz1YgUW8LTg==
-----END RSA PRIVATE KEY-----
`
)
func getRandomNumber(min int, max int) int {
rand.Seed(time.Now().Unix())
return rand.Intn(max-min) + min
}
// works as
// defer listenTLS(iris.Default, hostTLS)()
func listenTLS(app *iris.Framework, hostTLS string) func() {
app.Close() // close any prev listener
app.Config.DisableBanner = true
// create the key and cert files on the fly, and delete them when this test finished
certFile, ferr := ioutil.TempFile("", "cert")
if ferr != nil {
panic(ferr)
}
keyFile, ferr := ioutil.TempFile("", "key")
if ferr != nil {
panic(ferr)
}
certFile.WriteString(testTLSCert)
keyFile.WriteString(testTLSKey)
go app.ListenTLS(hostTLS, certFile.Name(), keyFile.Name())
time.Sleep(200 * time.Millisecond)
return func() {
certFile.Close()
time.Sleep(50 * time.Millisecond)
os.Remove(certFile.Name())
keyFile.Close()
time.Sleep(50 * time.Millisecond)
os.Remove(keyFile.Name())
}
}
// Contains the server test for multi running servers
func TestMultiRunningServers_v1_PROXY(t *testing.T) {
app := iris.New()
app.Adapt(newTestNativeRouter())
host := "localhost"
hostTLS := host + ":" + strconv.Itoa(getRandomNumber(1919, 2021))
app.Get("/", func(ctx *iris.Context) {
ctx.Writef("Hello from %s", hostTLS)
})
defer listenTLS(app, hostTLS)()
e := httptest.New(app, t, httptest.ExplicitURL(true))
e.Request("GET", "/").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS)
// proxy http to https
proxyHost := host + ":" + strconv.Itoa(getRandomNumber(3300, 3340))
// println("running proxy on: " + proxyHost)
iris.Proxy(proxyHost, "https://"+hostTLS)
// proxySrv := &http.Server{Addr: proxyHost, Handler: iris.ProxyHandler("https://" + hostTLS)}
// go proxySrv.ListenAndServe()
// time.Sleep(3 * time.Second)
eproxy := httptest.NewInsecure("http://"+proxyHost, t, httptest.ExplicitURL(true))
eproxy.Request("GET", "/").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS)
}
// Contains the server test for multi running servers
func TestMultiRunningServers_v2(t *testing.T) {
app := iris.New()
app.Adapt(newTestNativeRouter())
domain := "localhost"
hostTLS := domain + ":" + strconv.Itoa(getRandomNumber(2222, 2229))
srv1Host := domain + ":" + strconv.Itoa(getRandomNumber(4446, 5444))
srv2Host := domain + ":" + strconv.Itoa(getRandomNumber(7778, 8887))
app.Get("/", func(ctx *iris.Context) {
ctx.Writef("Hello from %s", hostTLS)
})
defer listenTLS(app, hostTLS)()
// using the same iris' handler but not as proxy, just the same handler
srv2 := &http.Server{Handler: app.Router, Addr: srv2Host}
go srv2.ListenAndServe()
// using the proxy handler
srv1 := &http.Server{Handler: iris.ProxyHandler("https://" + hostTLS), Addr: srv1Host}
go srv1.ListenAndServe()
time.Sleep(200 * time.Millisecond) // wait a little for the http servers
e := httptest.New(app, t, httptest.ExplicitURL(true))
e.Request("GET", "/").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS)
eproxy1 := httptest.NewInsecure("http://"+srv1Host, t, httptest.ExplicitURL(true))
eproxy1.Request("GET", "/").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS)
eproxy2 := httptest.NewInsecure("http://"+srv2Host, t)
eproxy2.Request("GET", "/").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS)
}

84
iris.go
View File

@ -62,12 +62,43 @@ func init() {
// Framework is our God |\| Google.Search('Greek mythology Iris'). // Framework is our God |\| Google.Search('Greek mythology Iris').
type Framework struct { type Framework struct {
// Router is the Router API, REST Routing, Static files & favicon,
// Grouping, Custom HTTP Errors, Subdomains and more.
//
// This field is available before 'Boot' but the routes are actually registered after 'Boot'
// if no RouterBuilderPolicy was .Adapt(ed) by user then
// it throws a panic with detailed information of how-to-fix it.
*Router *Router
// Config contains the configuration fields
// all fields defaults to something that is working, developers don't have to set it.
//
// can be setted via .New, .Set and .New(.YAML)
Config *Configuration
// policies contains the necessary information about the application's components.
// - LoggerPolicy
// - EventPolicy
// - Boot
// - Build
// - Interrupted
// - Recover
// - RouterReversionPolicy
// - StaticPath
// - WildcardPath
// - URLPath
// - RouteContextLinker
// - RouterBuilderPolicy
// - RouterWrapperPolicy
// - RenderPolicy
// - TemplateFuncsPolicy
// - SessionsPolicy
//
// These are setted by user's call to .Adapt
policies Policies policies Policies
// HTTP Server runtime fields is the iris' defined main server, developer can use unlimited number of servers ln net.Listener // setted on Listten/Serve funcions, available after 'Boot'
// note: they're available after .Build, and .Serve/Listen/ListenTLS/ListenLETSENCRYPT/ListenUNIX
ln net.Listener
// TLSNextProto optionally specifies a function to take over // TLSNextProto optionally specifies a function to take over
// ownership of the provided TLS connection when an NPN/ALPN // ownership of the provided TLS connection when an NPN/ALPN
// protocol upgrade has occurred. The map key is the protocol // protocol upgrade has occurred. The map key is the protocol
@ -76,16 +107,16 @@ type Framework struct {
// and RemoteAddr if not already set. The connection is // and RemoteAddr if not already set. The connection is
// automatically closed when the function returns. // automatically closed when the function returns.
// If TLSNextProto is nil, HTTP/2 support is enabled automatically. // If TLSNextProto is nil, HTTP/2 support is enabled automatically.
TLSNextProto map[string]func(*http.Server, *tls.Conn, http.Handler) TLSNextProto map[string]func(*http.Server, *tls.Conn, http.Handler) // same as http.Server.TLSNextProto
// ConnState specifies an optional callback function that is // ConnState specifies an optional callback function that is
// called when a client connection changes state. See the // called when a client connection changes state. See the
// ConnState type and associated constants for details. // ConnState type and associated constants for details.
ConnState func(net.Conn, http.ConnState) ConnState func(net.Conn, http.ConnState) // same as http.Server.ConnState
closedManually bool closedManually bool // true if closed via .Close, used to not throw an error when closing the app's server
once sync.Once once sync.Once // used to 'Boot' once
Config *Configuration
} }
var defaultGlobalLoggerOuput = log.New(os.Stdout, "[iris] ", log.LstdFlags) var defaultGlobalLoggerOuput = log.New(os.Stdout, "[iris] ", log.LstdFlags)
@ -269,27 +300,36 @@ func New(setters ...OptionSetter) *Framework {
} }
s.Adapt(EventPolicy{Build: func(*Framework) { s.Adapt(EventPolicy{Build: func(*Framework) {
// user has registered routes // Author's notes:
if s.Router.repository.Len() > 0 { // Proxy for example has 0 routes registered but still uses the RouterBuilderPolicy
// first check if it's not setted already by any Boot event. // so we can't check only for it, we can check if it's nil and it has more than one registered
if s.Router.handler == nil { // routes, then panic, if has no registered routes the user don't want to get errors about the router.
// and most importantly, check if the user has provided a router
// adaptor, if not then it should panic here, iris can't run without a router attached to it // first check if it's not setted already by any Boot event.
// and default router not any more, user should select one from ./adaptors or if s.Router.handler == nil {
// any other third-party adaptor may done by community. hasRoutes := s.Router.repository.Len() > 0
// I was coding the new iris version for more than 20 days(~200+ hours of code) routerBuilder := s.policies.RouterBuilderPolicy
// and I hope that once per application the addition of +1 line users have to put, // and most importantly, check if the user has provided a router adaptor
// is not a big deal. // at the same time has registered at least one route,
if s.policies.RouterBuilderPolicy == nil { // if not then it should panic here, iris can't run without a router attached to it
// and default router not any more, user should select one from ./adaptors or
// any other third-party adaptor may done by community.
// I was coding the new iris version for more than 20 days(~200+ hours of code)
// and I hope that once per application the addition of +1 line users have to put,
// is not a big deal.
if hasRoutes {
if routerBuilder == nil {
// this is important panic and app can't continue as we said. // this is important panic and app can't continue as we said.
s.handlePanic(errRouterIsMissing.Format(s.Config.VHost)) s.handlePanic(errRouterIsMissing.Format(s.Config.VHost))
// don't trace anything else, // don't trace anything else,
// the detailed errRouterIsMissing message will tell the user what to do to fix that. // the detailed errRouterIsMissing message will tell the user what to do to fix that.
os.Exit(0) os.Exit(0)
} }
}
if routerBuilder != nil {
// buid the router using user's selection build policy // buid the router using user's selection build policy
s.Router.build(s.policies.RouterBuilderPolicy) s.Router.build(routerBuilder)
} }
} }
}}) }})