mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Add support for more than one listening server to one station, virtual and no virtual
This commit is contained in:
parent
d76b73427b
commit
2cc75817b7
|
@ -1,15 +1,11 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
import "github.com/imdario/mergo"
|
||||
|
||||
// Default values for base Iris conf
|
||||
const (
|
||||
DefaultDisablePathCorrection = false
|
||||
DefaultDisablePathEscape = false
|
||||
DefaultMaxRequestBodySize = fasthttp.DefaultMaxRequestBodySize
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -54,13 +50,6 @@ type (
|
|||
// Default is false
|
||||
DisableBanner bool
|
||||
|
||||
// MaxRequestBodySize Maximum request body size.
|
||||
//
|
||||
// The server rejects requests with bodies exceeding this limit.
|
||||
//
|
||||
// By default request body size is 4MB.
|
||||
MaxRequestBodySize int64
|
||||
|
||||
// ProfilePath a the route path, set it to enable http pprof tool
|
||||
// Default is empty, if you set it to a $path, these routes will handled:
|
||||
// $path/cmdline
|
||||
|
@ -135,13 +124,12 @@ func Default() Iris {
|
|||
DisablePathCorrection: DefaultDisablePathCorrection,
|
||||
DisablePathEscape: DefaultDisablePathEscape,
|
||||
DisableBanner: false,
|
||||
MaxRequestBodySize: DefaultMaxRequestBodySize,
|
||||
ProfilePath: "",
|
||||
Logger: DefaultLogger(),
|
||||
Sessions: DefaultSessions(),
|
||||
Render: DefaultRender(),
|
||||
Websocket: DefaultWebsocket(),
|
||||
Tester: Tester{Debug: false},
|
||||
Tester: DefaultTester(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,17 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/kataras/fasthttp"
|
||||
)
|
||||
|
||||
// Default values for base Server conf
|
||||
const (
|
||||
// DefaultServerHostname returns the default hostname which is 127.0.0.1
|
||||
DefaultServerHostname = "127.0.0.1"
|
||||
// DefaultServerPort returns the default port which is 8080
|
||||
DefaultServerPort = 8080
|
||||
// DefaultMaxRequestBodySize is 4MB
|
||||
DefaultMaxRequestBodySize = fasthttp.DefaultMaxRequestBodySize
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -30,6 +34,12 @@ type Server struct {
|
|||
KeyFile string
|
||||
// Mode this is for unix only
|
||||
Mode os.FileMode
|
||||
// MaxRequestBodySize Maximum request body size.
|
||||
//
|
||||
// The server rejects requests with bodies exceeding this limit.
|
||||
//
|
||||
// By default request body size is 4MB.
|
||||
MaxRequestBodySize int64
|
||||
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
|
||||
//
|
||||
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
|
||||
|
@ -43,7 +53,8 @@ type Server struct {
|
|||
|
||||
// DefaultServer returns the default configs for the server
|
||||
func DefaultServer() Server {
|
||||
return Server{ListeningAddr: DefaultServerAddr}
|
||||
return Server{ListeningAddr: DefaultServerAddr,
|
||||
MaxRequestBodySize: DefaultMaxRequestBodySize}
|
||||
}
|
||||
|
||||
// Merge merges the default with the given config and returns the result
|
||||
|
@ -59,3 +70,12 @@ func (c Server) Merge(cfg []Server) (config Server) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// MergeSingle merges the default with the given config and returns the result
|
||||
func (c Server) MergeSingle(cfg Server) (config Server) {
|
||||
|
||||
config = cfg
|
||||
mergo.Merge(&config, c)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,5 +2,12 @@ package config
|
|||
|
||||
// Tester configuration
|
||||
type Tester struct {
|
||||
Debug bool
|
||||
Debug bool
|
||||
ListeningAddr string
|
||||
}
|
||||
|
||||
// DefaultTester returns the default configuration for a tester
|
||||
// the ListeningAddr is used as virtual only when no running server is founded
|
||||
func DefaultTester() Tester {
|
||||
return Tester{Debug: false, ListeningAddr: "iris-go.com:1993"}
|
||||
}
|
||||
|
|
|
@ -218,7 +218,7 @@ func (ctx *Context) HostString() string {
|
|||
func (ctx *Context) VirtualHostname() string {
|
||||
realhost := ctx.HostString()
|
||||
hostname := realhost
|
||||
virtualhost := ctx.framework.HTTPServer.VirtualHostname()
|
||||
virtualhost := ctx.framework.Servers.Main().VirtualHostname()
|
||||
|
||||
if portIdx := strings.IndexByte(hostname, ':'); portIdx > 0 {
|
||||
hostname = hostname[0:portIdx]
|
||||
|
@ -480,7 +480,11 @@ func (ctx *Context) RenderWithStatus(status int, name string, binding interface{
|
|||
|
||||
// Render same as .RenderWithStatus but with status to iris.StatusOK (200)
|
||||
func (ctx *Context) Render(name string, binding interface{}, layout ...string) error {
|
||||
return ctx.RenderWithStatus(StatusOK, name, binding, layout...)
|
||||
errCode := ctx.RequestCtx.Response.StatusCode()
|
||||
if errCode <= 0 {
|
||||
errCode = StatusOK
|
||||
}
|
||||
return ctx.RenderWithStatus(errCode, name, binding, layout...)
|
||||
}
|
||||
|
||||
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
|
||||
|
|
|
@ -26,6 +26,7 @@ type testBinderXMLData struct {
|
|||
|
||||
func TestBindForm(t *testing.T) {
|
||||
initDefault()
|
||||
|
||||
Post("/form", func(ctx *Context) {
|
||||
obj := testBinderData{}
|
||||
err := ctx.ReadForm(&obj)
|
||||
|
@ -36,6 +37,7 @@ func TestBindForm(t *testing.T) {
|
|||
})
|
||||
|
||||
e := Tester(t)
|
||||
|
||||
passed := map[string]interface{}{"Username": "myusername", "Mail": "mymail@iris-go.com", "mydata": url.Values{"[0]": []string{"mydata1"},
|
||||
"[1]": []string{"mydata2"}}}
|
||||
|
||||
|
|
111
context_test.go
Normal file
111
context_test.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package iris
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestContextReset(t *testing.T) {
|
||||
var context Context
|
||||
context.Params = PathParameters{PathParameter{Key: "testkey", Value: "testvalue"}}
|
||||
context.Reset(nil)
|
||||
if len(context.Params) > 0 {
|
||||
t.Fatalf("Expecting to have %d params but got: %d", 0, len(context.Params))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextClone(t *testing.T) {
|
||||
var context Context
|
||||
context.Params = PathParameters{
|
||||
PathParameter{Key: "testkey", Value: "testvalue"},
|
||||
PathParameter{Key: "testkey2", Value: "testvalue2"},
|
||||
}
|
||||
c := context.Clone()
|
||||
if v := c.Param("testkey"); v != context.Param("testkey") {
|
||||
t.Fatalf("Expecting to have parameter value: %s but got: %s", context.Param("testkey"), v)
|
||||
}
|
||||
if v := c.Param("testkey2"); v != context.Param("testkey2") {
|
||||
t.Fatalf("Expecting to have parameter value: %s but got: %s", context.Param("testkey2"), v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextDoNextStop(t *testing.T) {
|
||||
var context Context
|
||||
ok := false
|
||||
afterStop := false
|
||||
context.middleware = Middleware{HandlerFunc(func(*Context) {
|
||||
ok = true
|
||||
}), HandlerFunc(func(*Context) {
|
||||
ok = true
|
||||
}), HandlerFunc(func(*Context) {
|
||||
// this will never execute
|
||||
afterStop = true
|
||||
})}
|
||||
context.Do()
|
||||
if context.pos != 0 {
|
||||
t.Fatalf("Expecting position 0 for context's middleware but we got: %d", context.pos)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected behavior, first context's middleware didn't executed")
|
||||
}
|
||||
ok = false
|
||||
|
||||
context.Next()
|
||||
|
||||
if int(context.pos) != 1 {
|
||||
t.Fatalf("Expecting to have position %d but we got: %d", 1, context.pos)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("Next context's middleware didn't executed")
|
||||
}
|
||||
|
||||
context.StopExecution()
|
||||
if context.pos != stopExecutionPosition {
|
||||
t.Fatalf("Context's StopExecution didn't worked, we expected to have position %d but we got %d", stopExecutionPosition, context.pos)
|
||||
}
|
||||
|
||||
if !context.IsStopped() {
|
||||
t.Fatalf("Should be stopped")
|
||||
}
|
||||
|
||||
context.Next()
|
||||
|
||||
if afterStop {
|
||||
t.Fatalf("We stopped the execution but the next handler was executed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextParam(t *testing.T) {
|
||||
var context Context
|
||||
params := PathParameters{
|
||||
PathParameter{Key: "testkey", Value: "testvalue"},
|
||||
PathParameter{Key: "testkey2", Value: "testvalue2"},
|
||||
PathParameter{Key: "id", Value: "3"},
|
||||
PathParameter{Key: "bigint", Value: "548921854390354"},
|
||||
}
|
||||
context.Params = params
|
||||
|
||||
if v := context.Param(params[0].Key); v != params[0].Value {
|
||||
t.Fatalf("Expecting parameter value to be %s but we got %s", params[0].Value, context.Param("testkey"))
|
||||
}
|
||||
if v := context.Param(params[1].Key); v != params[1].Value {
|
||||
t.Fatalf("Expecting parameter value to be %s but we got %s", params[1].Value, context.Param("testkey2"))
|
||||
}
|
||||
|
||||
if len(context.Params) != len(params) {
|
||||
t.Fatalf("Expecting to have %d parameters but we got %d", len(params), len(context.Params))
|
||||
}
|
||||
|
||||
if vi, err := context.ParamInt(params[2].Key); err != nil {
|
||||
t.Fatalf("Unexpecting error on context's ParamInt while trying to get the integer of the %s", params[2].Value)
|
||||
} else if vi != 3 {
|
||||
t.Fatalf("Expecting to receive %d but we got %d", 3, vi)
|
||||
}
|
||||
|
||||
if vi, err := context.ParamInt64(params[3].Key); err != nil {
|
||||
t.Fatalf("Unexpecting error on context's ParamInt while trying to get the integer of the %s", params[2].Value)
|
||||
} else if vi != 548921854390354 {
|
||||
t.Fatalf("Expecting to receive %d but we got %d", 548921854390354, vi)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextURLParam(t *testing.T) {
|
||||
|
||||
}
|
181
http.go
181
http.go
|
@ -10,6 +10,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/iris/config"
|
||||
|
@ -236,35 +237,47 @@ var (
|
|||
errServerChmod = errors.New("Cannot chmod %#o for %q: %s")
|
||||
)
|
||||
|
||||
// Server the http server
|
||||
type Server struct {
|
||||
*fasthttp.Server
|
||||
listener net.Listener
|
||||
Config *config.Server
|
||||
tls bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
type (
|
||||
// Server the http server
|
||||
Server struct {
|
||||
*fasthttp.Server
|
||||
listener net.Listener
|
||||
Config config.Server
|
||||
tls bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
// ServerList contains the servers connected to the Iris station
|
||||
ServerList struct {
|
||||
mux *serveMux
|
||||
servers []*Server
|
||||
}
|
||||
)
|
||||
|
||||
// newServer returns a pointer to a Server object, and set it's options if any, nothing more
|
||||
func newServer(c *config.Server) *Server {
|
||||
s := &Server{Server: &fasthttp.Server{Name: config.ServerName}, Config: c}
|
||||
func newServer(cfg config.Server) *Server {
|
||||
s := &Server{Server: &fasthttp.Server{Name: config.ServerName}, Config: cfg}
|
||||
return s
|
||||
}
|
||||
|
||||
// SetHandler sets the handler in order to listen on client requests
|
||||
func (s *Server) SetHandler(mux *serveMux) {
|
||||
if s.Server != nil {
|
||||
s.Server.Handler = mux.ServeRequest()
|
||||
}
|
||||
}
|
||||
|
||||
// IsListening returns true if server is listening/started, otherwise false
|
||||
func (s *Server) IsListening() bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.listener != nil && s.listener.Addr().String() != ""
|
||||
}
|
||||
|
||||
// IsOpened checks if handler is not nil and returns true if not, otherwise false
|
||||
// this is used to see if a server has opened, use IsListening if you want to see if the server is actually ready to serve connections
|
||||
func (s *Server) IsOpened() bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
return s.Server != nil && s.Server.Handler != nil
|
||||
}
|
||||
|
||||
// IsSecure returns true if server uses TLS, otherwise false
|
||||
func (s *Server) IsSecure() bool {
|
||||
return s.tls
|
||||
|
@ -398,7 +411,11 @@ func (s *Server) serve(l net.Listener) error {
|
|||
}
|
||||
|
||||
// Open opens/starts/runs/listens (to) the server, listen tls if Cert && Key is registed, listenUNIX if Mode is registed, otherwise listen
|
||||
func (s *Server) Open() error {
|
||||
func (s *Server) Open(h fasthttp.RequestHandler) error {
|
||||
if h == nil {
|
||||
return errServerHandlerMissing.Return()
|
||||
}
|
||||
|
||||
if s.IsListening() {
|
||||
return errServerAlreadyStarted.Return()
|
||||
}
|
||||
|
@ -407,10 +424,6 @@ func (s *Server) Open() error {
|
|||
return errServerConfigMissing.Return()
|
||||
}
|
||||
|
||||
if s.Handler == nil {
|
||||
return errServerHandlerMissing.Return()
|
||||
}
|
||||
|
||||
// check the addr if :8080 do it 0.0.0.0:8080 ,we need the hostname for many cases
|
||||
a := s.Config.ListeningAddr
|
||||
//check if contains hostname, we need the full host, :8080 should be : 127.0.0.1:8080
|
||||
|
@ -419,9 +432,13 @@ func (s *Server) Open() error {
|
|||
s.Config.ListeningAddr = config.DefaultServerHostname + a
|
||||
}
|
||||
|
||||
if s.Config.MaxRequestBodySize > config.DefaultMaxRequestBodySize {
|
||||
s.Server.MaxRequestBodySize = int(s.Config.MaxRequestBodySize)
|
||||
}
|
||||
|
||||
if s.Config.RedirectTo != "" {
|
||||
// override the handler and redirect all requests to this addr
|
||||
s.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
||||
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
||||
path := string(reqCtx.Path())
|
||||
redirectTo := s.Config.RedirectTo
|
||||
if path != "/" {
|
||||
|
@ -429,6 +446,8 @@ func (s *Server) Open() error {
|
|||
}
|
||||
reqCtx.Redirect(redirectTo, StatusMovedPermanently)
|
||||
}
|
||||
} else {
|
||||
s.Server.Handler = h
|
||||
}
|
||||
|
||||
if s.Config.Virtual {
|
||||
|
@ -452,6 +471,122 @@ func (s *Server) Close() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// --------------------------------ServerList implementation-----------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
// Add adds a server to the list by its config
|
||||
// returns the new server
|
||||
func (s *ServerList) Add(cfg config.Server) *Server {
|
||||
srv := newServer(cfg)
|
||||
s.servers = append(s.servers, srv)
|
||||
return srv
|
||||
}
|
||||
|
||||
// Len returns the size of the server list
|
||||
func (s *ServerList) Len() int {
|
||||
return len(s.servers)
|
||||
}
|
||||
|
||||
// Main returns the main server,
|
||||
// the last added server is the main server, even if's Virtual
|
||||
func (s *ServerList) Main() (srv *Server) {
|
||||
l := len(s.servers) - 1
|
||||
for i := range s.servers {
|
||||
if i == l {
|
||||
return s.servers[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the server by it's registered Address
|
||||
func (s *ServerList) Get(addr string) (srv *Server) {
|
||||
for i := range s.servers {
|
||||
srv = s.servers[i]
|
||||
if srv.Config.ListeningAddr == addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAll returns all registered servers
|
||||
func (s *ServerList) GetAll() []*Server {
|
||||
return s.servers
|
||||
}
|
||||
|
||||
// GetByIndex returns a server from the list by it's index
|
||||
func (s *ServerList) GetByIndex(i int) *Server {
|
||||
if len(s.servers) >= i+1 {
|
||||
return s.servers[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove deletes a server by it's registered Address
|
||||
// returns true if something was removed, otherwise returns false
|
||||
func (s *ServerList) Remove(addr string) bool {
|
||||
servers := s.servers
|
||||
for i := range servers {
|
||||
srv := servers[i]
|
||||
if srv.Config.ListeningAddr == addr {
|
||||
copy(servers[i:], servers[i+1:])
|
||||
servers[len(servers)-1] = nil
|
||||
s.servers = servers[:len(servers)-1]
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CloseAll terminates all listening servers
|
||||
// returns the first error, if erro happens it continues to closes the rest of the servers
|
||||
func (s *ServerList) CloseAll() (err error) {
|
||||
for i := range s.servers {
|
||||
if err == nil {
|
||||
err = s.servers[i].Close()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpenAll starts all servers
|
||||
// returns the first error happens to one of these servers
|
||||
// if one server gets error it closes the previous servers and exits from this process
|
||||
func (s *ServerList) OpenAll() error {
|
||||
l := len(s.servers) - 1
|
||||
h := s.mux.ServeRequest()
|
||||
for i := range s.servers {
|
||||
|
||||
if err := s.servers[i].Open(h); err != nil {
|
||||
time.Sleep(2 * time.Second)
|
||||
// for any case,
|
||||
// we don't care about performance on initialization,
|
||||
// we must make sure that the previous servers are running before closing them
|
||||
s.CloseAll()
|
||||
break
|
||||
}
|
||||
if i == l {
|
||||
s.mux.setHostname(s.servers[i].VirtualHostname())
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllOpened returns all opened/started servers
|
||||
func (s *ServerList) GetAllOpened() (servers []*Server) {
|
||||
for i := range s.servers {
|
||||
if s.servers[i].IsOpened() {
|
||||
servers = append(servers, s.servers[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// errHandler returns na error with message: 'Passed argument is not func(*Context) neither an object which implements the iris.Handler with Serve(ctx *Context)
|
||||
// It seems to be a +type Points to: +pointer.'
|
||||
var errHandler = errors.New("Passed argument is not func(*Context) neither an object which implements the iris.Handler with Serve(ctx *Context)\n It seems to be a %T Points to: %v.")
|
||||
|
|
206
initiatory.go
206
initiatory.go
|
@ -1,206 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
"github.com/kataras/iris/render/template"
|
||||
"github.com/kataras/iris/sessions"
|
||||
"github.com/kataras/iris/websocket"
|
||||
///NOTE: register the session providers, but the s.Config.Sessions.Provider will be used only, if this empty then sessions are disabled.
|
||||
_ "github.com/kataras/iris/sessions/providers/memory"
|
||||
_ "github.com/kataras/iris/sessions/providers/redis"
|
||||
)
|
||||
|
||||
// Default entry, use it with iris.$anyPublicFunc
|
||||
var (
|
||||
Default *Framework
|
||||
Config *config.Iris
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
HTTPServer *Server
|
||||
// Available is a channel type of bool, fired to true when the server is opened and all plugins ran
|
||||
// never fires false, if the .Close called then the channel is re-allocating.
|
||||
// the channel is closed only when .ListenVirtual is used, otherwise it remains open until you close it.
|
||||
//
|
||||
// Note: it is a simple channel and decided to put it here and no inside HTTPServer, doesn't have statuses just true and false, simple as possible
|
||||
// Where to use that?
|
||||
// this is used on extreme cases when you don't know which .Listen/.NoListen will be called
|
||||
// and you want to run/declare something external-not-Iris (all Iris functionality declared before .Listen/.NoListen) AFTER the server is started and plugins finished.
|
||||
// see the server_test.go for an example
|
||||
Available chan bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
initDefault()
|
||||
}
|
||||
|
||||
func initDefault() {
|
||||
Default = New()
|
||||
Config = Default.Config
|
||||
Logger = Default.Logger
|
||||
Plugins = Default.Plugins
|
||||
Websocket = Default.Websocket
|
||||
HTTPServer = Default.HTTPServer
|
||||
Available = Default.Available
|
||||
}
|
||||
|
||||
const (
|
||||
/* conversional */
|
||||
|
||||
// HTMLEngine conversion for config.HTMLEngine
|
||||
HTMLEngine = config.HTMLEngine
|
||||
// PongoEngine conversion for config.PongoEngine
|
||||
PongoEngine = config.PongoEngine
|
||||
// MarkdownEngine conversion for config.MarkdownEngine
|
||||
MarkdownEngine = config.MarkdownEngine
|
||||
// JadeEngine conversion for config.JadeEngine
|
||||
JadeEngine = config.JadeEngine
|
||||
// AmberEngine conversion for config.AmberEngine
|
||||
AmberEngine = config.AmberEngine
|
||||
// HandlebarsEngine conversion for config.HandlebarsEngine
|
||||
HandlebarsEngine = config.HandlebarsEngine
|
||||
// DefaultEngine conversion for config.DefaultEngine
|
||||
DefaultEngine = config.DefaultEngine
|
||||
// NoEngine conversion for config.NoEngine
|
||||
NoEngine = config.NoEngine
|
||||
// NoLayout to disable layout for a particular template file
|
||||
// conversion for config.NoLayout
|
||||
NoLayout = config.NoLayout
|
||||
/* end conversional */
|
||||
)
|
||||
|
||||
// Framework is our God |\| Google.Search('Greek mythology Iris')
|
||||
//
|
||||
// Implements the FrameworkAPI
|
||||
type Framework struct {
|
||||
*muxAPI
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
// fields which are useful to the user/dev
|
||||
HTTPServer *Server
|
||||
Config *config.Iris
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
Available chan bool
|
||||
// this is setted once when .Tester(t) is called
|
||||
testFramework *httpexpect.Expect
|
||||
}
|
||||
|
||||
// New creates and returns a new Iris station aka Framework.
|
||||
//
|
||||
// Receives an optional config.Iris as parameter
|
||||
// If empty then config.Default() is used instead
|
||||
func New(cfg ...config.Iris) *Framework {
|
||||
c := config.Default().Merge(cfg)
|
||||
|
||||
// we always use 's' no 'f' because 's' is easier for me to remember because of 'station'
|
||||
// some things never change :)
|
||||
s := &Framework{Config: &c, Available: make(chan bool)}
|
||||
{
|
||||
///NOTE: set all with s.Config pointer
|
||||
// set the Logger
|
||||
s.Logger = logger.New(s.Config.Logger)
|
||||
// set the plugin container
|
||||
s.Plugins = &pluginContainer{logger: s.Logger}
|
||||
// set the websocket server
|
||||
s.Websocket = websocket.NewServer(s.Config.Websocket)
|
||||
// set the servemux, which will provide us the public API also, with its context pool
|
||||
mux := newServeMux(sync.Pool{New: func() interface{} { return &Context{framework: s} }}, s.Logger)
|
||||
// set the public router API (and party)
|
||||
s.muxAPI = &muxAPI{mux: mux, relativePath: "/"}
|
||||
// set the server with the default configuration, which is changed on Listen functions
|
||||
defaultServerCfg := config.DefaultServer()
|
||||
s.HTTPServer = newServer(&defaultServerCfg)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Framework) initialize() {
|
||||
// set sessions
|
||||
if s.Config.Sessions.Provider != "" {
|
||||
s.sessions = sessions.New(s.Config.Sessions)
|
||||
}
|
||||
|
||||
// set the rest
|
||||
s.rest = rest.New(s.Config.Render.Rest)
|
||||
|
||||
// set templates if not already setted
|
||||
s.prepareTemplates()
|
||||
|
||||
// listen to websocket connections
|
||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
||||
|
||||
// prepare the mux & the server
|
||||
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
|
||||
s.mux.setEscapePath(!s.Config.DisablePathEscape)
|
||||
s.mux.setHostname(s.HTTPServer.VirtualHostname())
|
||||
// set the debug profiling handlers if ProfilePath is setted
|
||||
if debugPath := s.Config.ProfilePath; debugPath != "" {
|
||||
s.Handle(MethodGet, debugPath+"/*action", profileMiddleware(debugPath)...)
|
||||
}
|
||||
|
||||
if s.Config.MaxRequestBodySize > config.DefaultMaxRequestBodySize {
|
||||
s.HTTPServer.MaxRequestBodySize = int(s.Config.MaxRequestBodySize)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
|
||||
func (s *Framework) prepareTemplates() {
|
||||
// prepare the templates
|
||||
if s.templates == nil {
|
||||
// These functions are directly contact with Iris' functionality.
|
||||
funcs := map[string]interface{}{
|
||||
"url": s.URL,
|
||||
"urlpath": s.Path,
|
||||
}
|
||||
|
||||
template.RegisterSharedFuncs(funcs)
|
||||
|
||||
s.templates = template.New(s.Config.Render.Template)
|
||||
}
|
||||
}
|
||||
|
||||
// openServer is internal method, open the server with specific options passed by the Listen and ListenTLS
|
||||
// it's a blocking func
|
||||
func (s *Framework) openServer() (err error) {
|
||||
s.initialize()
|
||||
s.Plugins.DoPreListen(s)
|
||||
// set the server's handler now, in order to give the chance to the plugins to add their own middlewares and routes to this station
|
||||
s.HTTPServer.SetHandler(s.mux)
|
||||
if err = s.HTTPServer.Open(); err == nil {
|
||||
|
||||
// print the banner
|
||||
if !s.Config.DisableBanner {
|
||||
s.Logger.PrintBanner(banner,
|
||||
fmt.Sprintf("%s: Running at %s\n", time.Now().Format(config.TimeFormat),
|
||||
s.HTTPServer.Host()))
|
||||
}
|
||||
s.Plugins.DoPostListen(s)
|
||||
|
||||
go func() { s.Available <- true }()
|
||||
|
||||
ch := make(chan os.Signal)
|
||||
<-ch
|
||||
s.Close()
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// closeServer is used to close the tcp listener from the server, returns an error
|
||||
func (s *Framework) closeServer() error {
|
||||
s.Plugins.DoPreClose(s)
|
||||
s.Available = make(chan bool)
|
||||
return s.HTTPServer.Close()
|
||||
}
|
407
iris.go
407
iris.go
|
@ -61,26 +61,96 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
"github.com/kataras/iris/render/template"
|
||||
"github.com/kataras/iris/sessions"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/kataras/iris/websocket"
|
||||
"github.com/valyala/fasthttp"
|
||||
///NOTE: register the session providers, but the s.Config.Sessions.Provider will be used only, if this empty then sessions are disabled.
|
||||
_ "github.com/kataras/iris/sessions/providers/memory"
|
||||
_ "github.com/kataras/iris/sessions/providers/redis"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version of the iris
|
||||
Version = "3.0.0-rc.4"
|
||||
banner = ` _____ _
|
||||
|
||||
// HTMLEngine conversion for config.HTMLEngine
|
||||
HTMLEngine = config.HTMLEngine
|
||||
// PongoEngine conversion for config.PongoEngine
|
||||
PongoEngine = config.PongoEngine
|
||||
// MarkdownEngine conversion for config.MarkdownEngine
|
||||
MarkdownEngine = config.MarkdownEngine
|
||||
// JadeEngine conversion for config.JadeEngine
|
||||
JadeEngine = config.JadeEngine
|
||||
// AmberEngine conversion for config.AmberEngine
|
||||
AmberEngine = config.AmberEngine
|
||||
// HandlebarsEngine conversion for config.HandlebarsEngine
|
||||
HandlebarsEngine = config.HandlebarsEngine
|
||||
// DefaultEngine conversion for config.DefaultEngine
|
||||
DefaultEngine = config.DefaultEngine
|
||||
// NoEngine conversion for config.NoEngine
|
||||
NoEngine = config.NoEngine
|
||||
// NoLayout to disable layout for a particular template file
|
||||
// conversion for config.NoLayout
|
||||
NoLayout = config.NoLayout
|
||||
|
||||
banner = ` _____ _
|
||||
|_ _| (_)
|
||||
| | ____ _ ___
|
||||
| | | __|| |/ __|
|
||||
_| |_| | | |\__ \
|
||||
|_____|_| |_||___/ ` + Version + `
|
||||
`
|
||||
|_____|_| |_||___/ ` + Version + ` `
|
||||
)
|
||||
|
||||
// Default entry, use it with iris.$anyPublicFunc
|
||||
var (
|
||||
Default *Framework
|
||||
Config *config.Iris
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
Servers *ServerList
|
||||
// Available is a channel type of bool, fired to true when the server is opened and all plugins ran
|
||||
// never fires false, if the .Close called then the channel is re-allocating.
|
||||
// the channel is closed only when .ListenVirtual is used, otherwise it remains open until you close it.
|
||||
//
|
||||
// Note: it is a simple channel and decided to put it here and no inside HTTPServer, doesn't have statuses just true and false, simple as possible
|
||||
// Where to use that?
|
||||
// this is used on extreme cases when you don't know which .Listen/.NoListen will be called
|
||||
// and you want to run/declare something external-not-Iris (all Iris functionality declared before .Listen/.NoListen) AFTER the server is started and plugins finished.
|
||||
// see the server_test.go for an example
|
||||
Available chan bool
|
||||
)
|
||||
|
||||
func initDefault() {
|
||||
Default = New()
|
||||
Config = Default.Config
|
||||
Logger = Default.Logger
|
||||
Plugins = Default.Plugins
|
||||
Websocket = Default.Websocket
|
||||
Servers = Default.Servers
|
||||
Available = Default.Available
|
||||
}
|
||||
|
||||
func init() {
|
||||
initDefault()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// --------------------------------Framework implementation-----------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
type (
|
||||
// FrameworkAPI contains the main Iris Public API
|
||||
FrameworkAPI interface {
|
||||
|
@ -110,56 +180,140 @@ type (
|
|||
Tester(t *testing.T) *httpexpect.Expect
|
||||
}
|
||||
|
||||
// RouteNameFunc the func returns from the MuxAPi's methods, optionally sets the name of the Route (*route)
|
||||
RouteNameFunc func(string)
|
||||
// MuxAPI the visible api for the serveMux
|
||||
MuxAPI interface {
|
||||
Party(string, ...HandlerFunc) MuxAPI
|
||||
// middleware serial, appending
|
||||
Use(...Handler)
|
||||
UseFunc(...HandlerFunc)
|
||||
|
||||
// main handlers
|
||||
Handle(string, string, ...Handler) RouteNameFunc
|
||||
HandleFunc(string, string, ...HandlerFunc) RouteNameFunc
|
||||
// H_ is used to convert a context.IContext handler func to iris.HandlerFunc, is used only inside iris internal package to avoid import cycles
|
||||
H_(string, string, func(context.IContext)) func(string)
|
||||
API(string, HandlerAPI, ...HandlerFunc)
|
||||
|
||||
// http methods
|
||||
Get(string, ...HandlerFunc) RouteNameFunc
|
||||
Post(string, ...HandlerFunc) RouteNameFunc
|
||||
Put(string, ...HandlerFunc) RouteNameFunc
|
||||
Delete(string, ...HandlerFunc) RouteNameFunc
|
||||
Connect(string, ...HandlerFunc) RouteNameFunc
|
||||
Head(string, ...HandlerFunc) RouteNameFunc
|
||||
Options(string, ...HandlerFunc) RouteNameFunc
|
||||
Patch(string, ...HandlerFunc) RouteNameFunc
|
||||
Trace(string, ...HandlerFunc) RouteNameFunc
|
||||
Any(string, ...HandlerFunc)
|
||||
|
||||
// static content
|
||||
StaticHandler(string, int, bool, bool, []string) HandlerFunc
|
||||
Static(string, string, int) RouteNameFunc
|
||||
StaticFS(string, string, int) RouteNameFunc
|
||||
StaticWeb(string, string, int) RouteNameFunc
|
||||
StaticServe(string, ...string) RouteNameFunc
|
||||
StaticContent(string, string, []byte) func(string)
|
||||
Favicon(string, ...string) RouteNameFunc
|
||||
|
||||
// templates
|
||||
Layout(string) MuxAPI // returns itself
|
||||
// Framework is our God |\| Google.Search('Greek mythology Iris')
|
||||
//
|
||||
// Implements the FrameworkAPI
|
||||
Framework struct {
|
||||
*muxAPI
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
// fields which are useful to the user/dev
|
||||
// the last added server is the main server
|
||||
Servers *ServerList
|
||||
Config *config.Iris
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
Available chan bool
|
||||
// this is setted once when .Tester(t) is called
|
||||
testFramework *httpexpect.Expect
|
||||
}
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// --------------------------------Framework implementation-----------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
var _ FrameworkAPI = &Framework{}
|
||||
|
||||
// New creates and returns a new Iris station aka Framework.
|
||||
//
|
||||
// Receives an optional config.Iris as parameter
|
||||
// If empty then config.Default() is used instead
|
||||
func New(cfg ...config.Iris) *Framework {
|
||||
c := config.Default().Merge(cfg)
|
||||
|
||||
// we always use 's' no 'f' because 's' is easier for me to remember because of 'station'
|
||||
// some things never change :)
|
||||
s := &Framework{Config: &c, Available: make(chan bool)}
|
||||
{
|
||||
///NOTE: set all with s.Config pointer
|
||||
// set the Logger
|
||||
s.Logger = logger.New(s.Config.Logger)
|
||||
// set the plugin container
|
||||
s.Plugins = &pluginContainer{logger: s.Logger}
|
||||
// set the websocket server
|
||||
s.Websocket = websocket.NewServer(s.Config.Websocket)
|
||||
// set the servemux, which will provide us the public API also, with its context pool
|
||||
mux := newServeMux(sync.Pool{New: func() interface{} { return &Context{framework: s} }}, s.Logger)
|
||||
// set the public router API (and party)
|
||||
s.muxAPI = &muxAPI{mux: mux, relativePath: "/"}
|
||||
|
||||
s.Servers = &ServerList{mux: mux, servers: make([]*Server, 0)}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Framework) initialize() {
|
||||
// set sessions
|
||||
if s.Config.Sessions.Provider != "" {
|
||||
s.sessions = sessions.New(s.Config.Sessions)
|
||||
}
|
||||
|
||||
// set the rest
|
||||
s.rest = rest.New(s.Config.Render.Rest)
|
||||
|
||||
// set templates if not already setted
|
||||
s.prepareTemplates()
|
||||
|
||||
// listen to websocket connections
|
||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
||||
|
||||
// prepare the mux & the server
|
||||
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
|
||||
s.mux.setEscapePath(!s.Config.DisablePathEscape)
|
||||
|
||||
// set the debug profiling handlers if ProfilePath is setted
|
||||
if debugPath := s.Config.ProfilePath; debugPath != "" {
|
||||
s.Handle(MethodGet, debugPath+"/*action", profileMiddleware(debugPath)...)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
|
||||
func (s *Framework) prepareTemplates() {
|
||||
// prepare the templates
|
||||
if s.templates == nil {
|
||||
// These functions are directly contact with Iris' functionality.
|
||||
funcs := map[string]interface{}{
|
||||
"url": s.URL,
|
||||
"urlpath": s.Path,
|
||||
}
|
||||
|
||||
template.RegisterSharedFuncs(funcs)
|
||||
|
||||
s.templates = template.New(s.Config.Render.Template)
|
||||
}
|
||||
}
|
||||
|
||||
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
|
||||
func Go() error {
|
||||
return Default.Go()
|
||||
}
|
||||
|
||||
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
|
||||
func (s *Framework) Go() error {
|
||||
s.initialize()
|
||||
s.Plugins.DoPreListen(s)
|
||||
|
||||
if firstErr := s.Servers.OpenAll(); firstErr != nil {
|
||||
panic("iris:287")
|
||||
return firstErr
|
||||
}
|
||||
|
||||
// print the banner
|
||||
if !s.Config.DisableBanner {
|
||||
serversMessage := time.Now().Format(config.TimeFormat) + ": Running at "
|
||||
openedServers := s.Servers.GetAllOpened()
|
||||
if len(openedServers) == 1 {
|
||||
// if only one server then don't need to add a new line
|
||||
serversMessage += openedServers[0].Host()
|
||||
} else {
|
||||
for _, srv := range openedServers {
|
||||
serversMessage += "\n" + srv.Host()
|
||||
}
|
||||
}
|
||||
|
||||
s.Logger.PrintBanner(banner, serversMessage)
|
||||
}
|
||||
|
||||
s.Plugins.DoPostListen(s)
|
||||
|
||||
go func() { s.Available <- true }()
|
||||
ch := make(chan os.Signal)
|
||||
<-ch
|
||||
s.CloseWithErr() // btw, don't panic here
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Must panics on error, it panics on registed iris' logger
|
||||
func Must(err error) {
|
||||
Default.Must(err)
|
||||
|
@ -180,9 +334,9 @@ func ListenTo(cfg config.Server) error {
|
|||
|
||||
// ListenTo listens to a server but receives the full server's configuration
|
||||
// it's a blocking func
|
||||
func (s *Framework) ListenTo(cfg config.Server) error {
|
||||
s.HTTPServer.Config = &cfg
|
||||
return s.openServer()
|
||||
func (s *Framework) ListenTo(cfg config.Server) (err error) {
|
||||
s.Servers.Add(cfg)
|
||||
return s.Go()
|
||||
}
|
||||
|
||||
// ListenWithErr starts the standalone http server
|
||||
|
@ -214,12 +368,7 @@ func Listen(addr string) {
|
|||
// if you need a func to panic on error use the Listen
|
||||
// ex: log.Fatal(iris.ListenWithErr(":8080"))
|
||||
func (s *Framework) ListenWithErr(addr string) error {
|
||||
cfg := config.DefaultServer()
|
||||
if len(addr) > 0 {
|
||||
cfg.ListeningAddr = addr
|
||||
}
|
||||
|
||||
return s.ListenTo(cfg)
|
||||
return s.ListenTo(config.Server{ListeningAddr: addr})
|
||||
}
|
||||
|
||||
// Listen starts the standalone http server
|
||||
|
@ -267,14 +416,10 @@ func ListenTLS(addr string, certFile string, keyFile string) {
|
|||
// if you need a func to panic on error use the ListenTLS
|
||||
// ex: log.Fatal(iris.ListenTLSWithErr(":8080","yourfile.cert","yourfile.key"))
|
||||
func (s *Framework) ListenTLSWithErr(addr string, certFile string, keyFile string) error {
|
||||
cfg := config.DefaultServer()
|
||||
if certFile == "" || keyFile == "" {
|
||||
return fmt.Errorf("You should provide certFile and keyFile for TLS/SSL")
|
||||
}
|
||||
cfg.ListeningAddr = addr
|
||||
cfg.CertFile = certFile
|
||||
cfg.KeyFile = keyFile
|
||||
return s.ListenTo(cfg)
|
||||
return s.ListenTo(config.Server{ListeningAddr: addr, CertFile: certFile, KeyFile: keyFile})
|
||||
}
|
||||
|
||||
// ListenTLS Starts a https server with certificates,
|
||||
|
@ -304,10 +449,7 @@ func ListenUNIX(addr string, mode os.FileMode) {
|
|||
// ListenUNIXWithErr starts the process of listening to the new requests using a 'socket file', this works only on unix
|
||||
// returns an error if something bad happens when trying to listen to
|
||||
func (s *Framework) ListenUNIXWithErr(addr string, mode os.FileMode) error {
|
||||
cfg := config.DefaultServer()
|
||||
cfg.ListeningAddr = addr
|
||||
cfg.Mode = mode
|
||||
return s.ListenTo(cfg)
|
||||
return s.ListenTo(config.Server{ListeningAddr: addr, Mode: mode})
|
||||
}
|
||||
|
||||
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
|
||||
|
@ -316,6 +458,9 @@ func (s *Framework) ListenUNIX(addr string, mode os.FileMode) {
|
|||
s.Must(s.ListenUNIXWithErr(addr, mode))
|
||||
}
|
||||
|
||||
// SecondaryListen NOTE: This will be deprecated
|
||||
// Use .Servers.Add(config.Server) instead
|
||||
//
|
||||
// SecondaryListen starts a server which listens to this station
|
||||
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https)
|
||||
//
|
||||
|
@ -331,6 +476,9 @@ func SecondaryListen(cfg config.Server) *Server {
|
|||
return Default.SecondaryListen(cfg)
|
||||
}
|
||||
|
||||
// SecondaryListen NOTE: This will be deprecated
|
||||
// Use .Servers.Add(config.Server) instead
|
||||
//
|
||||
// SecondaryListen starts a server which listens to this station
|
||||
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https)
|
||||
//
|
||||
|
@ -343,22 +491,7 @@ func SecondaryListen(cfg config.Server) *Server {
|
|||
//
|
||||
// this is a NOT A BLOCKING version, the main iris.Listen should be always executed LAST, so this function goes before the main .Listen.
|
||||
func (s *Framework) SecondaryListen(cfg config.Server) *Server {
|
||||
srv := newServer(&cfg)
|
||||
// add a post listen event to start this server after the previous started
|
||||
s.Plugins.Add(PostListenFunc(func(*Framework) {
|
||||
go func() { // goroutine in order to not block any runtime post listeners
|
||||
srv.Handler = s.HTTPServer.Handler
|
||||
if err := srv.Open(); err == nil {
|
||||
if !cfg.Virtual {
|
||||
ch := make(chan os.Signal)
|
||||
<-ch
|
||||
srv.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}))
|
||||
|
||||
return srv
|
||||
return s.Servers.Add(cfg)
|
||||
}
|
||||
|
||||
// NoListen is useful only when you want to test Iris, it doesn't starts the server but it configures and returns it
|
||||
|
@ -391,34 +524,41 @@ func (s *Framework) ListenVirtual(optionalAddr ...string) *Server {
|
|||
s.Config.DisableBanner = true
|
||||
cfg := config.DefaultServer()
|
||||
|
||||
if len(optionalAddr) > 0 {
|
||||
if len(optionalAddr) > 0 && optionalAddr[0] != "" {
|
||||
cfg.ListeningAddr = optionalAddr[0]
|
||||
}
|
||||
cfg.Virtual = true
|
||||
go s.ListenTo(cfg)
|
||||
|
||||
go func() {
|
||||
s.Must(s.ListenTo(cfg))
|
||||
}()
|
||||
|
||||
if ok := <-s.Available; !ok {
|
||||
s.Logger.Panic("Unexpected error:Virtual server cannot start, please report this as bug!!")
|
||||
}
|
||||
|
||||
close(s.Available)
|
||||
return s.HTTPServer
|
||||
return s.Servers.Main()
|
||||
}
|
||||
|
||||
// CloseWithErr terminates the server and returns an error if any
|
||||
// CloseWithErr terminates all the registered servers and returns an error if any
|
||||
func CloseWithErr() error {
|
||||
return Default.CloseWithErr()
|
||||
}
|
||||
|
||||
//Close terminates the server and panic if error occurs
|
||||
//Close terminates all the registered servers and panic if error occurs
|
||||
func Close() {
|
||||
Default.Close()
|
||||
}
|
||||
|
||||
// CloseWithErr terminates the server and returns an error if any
|
||||
// CloseWithErr terminates all the registered servers and returns an error if any
|
||||
func (s *Framework) CloseWithErr() error {
|
||||
return s.closeServer()
|
||||
s.Plugins.DoPreClose(s)
|
||||
s.Available = make(chan bool)
|
||||
return s.Servers.CloseAll()
|
||||
}
|
||||
|
||||
//Close terminates the server and panic if error occurs
|
||||
//Close terminates all the registered servers and panic if error occurs
|
||||
func (s *Framework) Close() {
|
||||
s.Must(s.CloseWithErr())
|
||||
}
|
||||
|
@ -594,13 +734,13 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
|
|||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
srv := s.Servers.Main()
|
||||
scheme := "http://"
|
||||
if s.HTTPServer.IsSecure() {
|
||||
if srv.IsSecure() {
|
||||
scheme = "https://"
|
||||
}
|
||||
|
||||
host := s.HTTPServer.VirtualHost()
|
||||
host := srv.VirtualHost()
|
||||
arguments := args[0:]
|
||||
|
||||
// join arrays as arguments
|
||||
|
@ -661,16 +801,31 @@ func (s *Framework) TemplateString(templateFile string, pageContext interface{},
|
|||
// NewTester Prepares and returns a new test framework based on the api
|
||||
// is useful when you need to have more than one test framework for the same iris insttance, otherwise you can use the iris.Tester(t *testing.T)/variable.Tester(t *testing.T)
|
||||
func NewTester(api *Framework, t *testing.T) *httpexpect.Expect {
|
||||
if !api.HTTPServer.IsListening() { // maybe the user called this after .Listen/ListenTLS/ListenUNIX, the tester can be used as standalone (with no running iris instance) or inside a running instance/app
|
||||
api.ListenVirtual()
|
||||
srv := api.Servers.Main()
|
||||
if srv == nil { // maybe the user called this after .Listen/ListenTLS/ListenUNIX, the tester can be used as standalone (with no running iris instance) or inside a running instance/app
|
||||
srv = api.ListenVirtual(api.Config.Tester.ListeningAddr)
|
||||
}
|
||||
|
||||
handler := api.HTTPServer.Handler
|
||||
opened := api.Servers.GetAllOpened()
|
||||
h := srv.Handler
|
||||
baseURL := srv.FullHost()
|
||||
if len(opened) > 1 {
|
||||
baseURL = ""
|
||||
//we have more than one server, so we will create a handler here and redirect by registered listening addresses
|
||||
h = func(reqCtx *fasthttp.RequestCtx) {
|
||||
for _, s := range opened {
|
||||
if strings.HasPrefix(reqCtx.URI().String(), s.FullHost()) { // yes on :80 should be passed :80 also, this is inneed for multiserver testing
|
||||
s.Handler(reqCtx)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testConfiguration := httpexpect.Config{
|
||||
BaseURL: api.HTTPServer.FullHost(),
|
||||
BaseURL: baseURL,
|
||||
Client: &http.Client{
|
||||
Transport: httpexpect.NewFastBinder(handler),
|
||||
Transport: httpexpect.NewFastBinder(h),
|
||||
Jar: httpexpect.NewJar(),
|
||||
},
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
|
@ -703,12 +858,56 @@ func (s *Framework) Tester(t *testing.T) *httpexpect.Expect {
|
|||
// ----------------------------------MuxAPI implementation------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
type (
|
||||
// RouteNameFunc the func returns from the MuxAPi's methods, optionally sets the name of the Route (*route)
|
||||
RouteNameFunc func(string)
|
||||
// MuxAPI the visible api for the serveMux
|
||||
MuxAPI interface {
|
||||
Party(string, ...HandlerFunc) MuxAPI
|
||||
// middleware serial, appending
|
||||
Use(...Handler)
|
||||
UseFunc(...HandlerFunc)
|
||||
|
||||
type muxAPI struct {
|
||||
mux *serveMux
|
||||
relativePath string
|
||||
middleware Middleware
|
||||
}
|
||||
// main handlers
|
||||
Handle(string, string, ...Handler) RouteNameFunc
|
||||
HandleFunc(string, string, ...HandlerFunc) RouteNameFunc
|
||||
// H_ is used to convert a context.IContext handler func to iris.HandlerFunc, is used only inside iris internal package to avoid import cycles
|
||||
H_(string, string, func(context.IContext)) func(string)
|
||||
API(string, HandlerAPI, ...HandlerFunc)
|
||||
|
||||
// http methods
|
||||
Get(string, ...HandlerFunc) RouteNameFunc
|
||||
Post(string, ...HandlerFunc) RouteNameFunc
|
||||
Put(string, ...HandlerFunc) RouteNameFunc
|
||||
Delete(string, ...HandlerFunc) RouteNameFunc
|
||||
Connect(string, ...HandlerFunc) RouteNameFunc
|
||||
Head(string, ...HandlerFunc) RouteNameFunc
|
||||
Options(string, ...HandlerFunc) RouteNameFunc
|
||||
Patch(string, ...HandlerFunc) RouteNameFunc
|
||||
Trace(string, ...HandlerFunc) RouteNameFunc
|
||||
Any(string, ...HandlerFunc)
|
||||
|
||||
// static content
|
||||
StaticHandler(string, int, bool, bool, []string) HandlerFunc
|
||||
Static(string, string, int) RouteNameFunc
|
||||
StaticFS(string, string, int) RouteNameFunc
|
||||
StaticWeb(string, string, int) RouteNameFunc
|
||||
StaticServe(string, ...string) RouteNameFunc
|
||||
StaticContent(string, string, []byte) func(string)
|
||||
Favicon(string, ...string) RouteNameFunc
|
||||
|
||||
// templates
|
||||
Layout(string) MuxAPI // returns itself
|
||||
}
|
||||
|
||||
muxAPI struct {
|
||||
mux *serveMux
|
||||
relativePath string
|
||||
middleware Middleware
|
||||
}
|
||||
)
|
||||
|
||||
var _ MuxAPI = &muxAPI{}
|
||||
|
||||
var (
|
||||
// errAPIContextNotFound returns an error with message: 'From .API: "Context *iris.Context could not be found..'
|
||||
|
@ -717,8 +916,6 @@ var (
|
|||
errDirectoryFileNotFound = errors.New("Directory or file %s couldn't found. Trace: %s")
|
||||
)
|
||||
|
||||
var _ MuxAPI = &muxAPI{}
|
||||
|
||||
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.
|
||||
// Party can also be named as 'Join' or 'Node' or 'Group' , Party chosen because it has more fun
|
||||
func Party(relativePath string, handlersFn ...HandlerFunc) MuxAPI {
|
||||
|
|
|
@ -16,12 +16,12 @@ const (
|
|||
)
|
||||
|
||||
func testSubdomainHost() string {
|
||||
return testSubdomain + strconv.Itoa(HTTPServer.Port())
|
||||
return testSubdomain + strconv.Itoa(Servers.Main().Port())
|
||||
}
|
||||
|
||||
func testSubdomainURL() (subdomainURL string) {
|
||||
subdomainHost := testSubdomainHost()
|
||||
if HTTPServer.IsSecure() {
|
||||
if Servers.Main().IsSecure() {
|
||||
subdomainURL = "https://" + subdomainHost
|
||||
} else {
|
||||
subdomainURL = "http://" + subdomainHost
|
||||
|
@ -159,7 +159,7 @@ func TestMuxSimpleParty(t *testing.T) {
|
|||
request := func(reqPath string) {
|
||||
e.Request("GET", reqPath).
|
||||
Expect().
|
||||
Status(StatusOK).Body().Equal(HTTPServer.Host() + reqPath)
|
||||
Status(StatusOK).Body().Equal(Servers.Main().Host() + reqPath)
|
||||
}
|
||||
|
||||
// run the tests
|
||||
|
|
|
@ -2,12 +2,10 @@ package iris
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
|
@ -63,10 +61,10 @@ const (
|
|||
)
|
||||
|
||||
// Contains the server test for multi running servers
|
||||
// Note: this test runs two standalone (real) servers
|
||||
func TestMultiRunningServers(t *testing.T) {
|
||||
func TestMultiRunningServers_v1(t *testing.T) {
|
||||
host := "mydomain.com:443" // you have to add it to your hosts file( for windows, as 127.0.0.1 mydomain.com)
|
||||
|
||||
initDefault()
|
||||
Config.DisableBanner = true
|
||||
// create the key and cert files on the fly, and delete them when this test finished
|
||||
certFile, ferr := ioutil.TempFile("", "cert")
|
||||
|
||||
|
@ -92,48 +90,77 @@ func TestMultiRunningServers(t *testing.T) {
|
|||
os.Remove(keyFile.Name())
|
||||
}()
|
||||
|
||||
initDefault()
|
||||
Config.DisableBanner = true
|
||||
|
||||
Get("/", func(ctx *Context) {
|
||||
ctx.Write("Hello from %s", ctx.HostString())
|
||||
})
|
||||
|
||||
// start the secondary server
|
||||
secondary := SecondaryListen(config.Server{ListeningAddr: ":80", RedirectTo: "https://" + host, Virtual: true})
|
||||
SecondaryListen(config.Server{ListeningAddr: "mydomain.com:80", RedirectTo: "https://" + host, Virtual: true})
|
||||
// start the main server
|
||||
go ListenTo(config.Server{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
|
||||
|
||||
defer func() {
|
||||
go secondary.Close()
|
||||
go CloseWithErr()
|
||||
close(Available)
|
||||
}()
|
||||
// prepare test framework
|
||||
if ok := <-Available; !ok {
|
||||
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
|
||||
}
|
||||
|
||||
handler := HTTPServer.Handler
|
||||
|
||||
testConfiguration := httpexpect.Config{
|
||||
Client: &http.Client{
|
||||
Transport: httpexpect.NewFastBinder(handler),
|
||||
Jar: httpexpect.NewJar(),
|
||||
},
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
}
|
||||
|
||||
if Config.Tester.Debug {
|
||||
testConfiguration.Printers = []httpexpect.Printer{
|
||||
httpexpect.NewDebugPrinter(t, true),
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
e := httpexpect.WithConfig(testConfiguration)
|
||||
e := Tester(t)
|
||||
|
||||
e.Request("GET", "http://mydomain.com:80").Expect().Status(StatusOK).Body().Equal("Hello from " + host)
|
||||
e.Request("GET", "https://"+host).Expect().Status(StatusOK).Body().Equal("Hello from " + host)
|
||||
|
||||
}
|
||||
|
||||
// Contains the server test for multi running servers
|
||||
func TestMultiRunningServers_v2(t *testing.T) {
|
||||
domain := "mydomain.com"
|
||||
host := domain + ":443"
|
||||
initDefault()
|
||||
Config.DisableBanner = true
|
||||
Config.Tester.ListeningAddr = host
|
||||
// create the key and cert files on the fly, and delete them when this test finished
|
||||
certFile, ferr := ioutil.TempFile("", "cert")
|
||||
|
||||
if ferr != nil {
|
||||
t.Fatal(ferr.Error())
|
||||
}
|
||||
|
||||
keyFile, ferr := ioutil.TempFile("", "key")
|
||||
if ferr != nil {
|
||||
t.Fatal(ferr.Error())
|
||||
}
|
||||
|
||||
certFile.WriteString(testTLSCert)
|
||||
keyFile.WriteString(testTLSKey)
|
||||
|
||||
defer func() {
|
||||
certFile.Close()
|
||||
time.Sleep(350 * time.Millisecond)
|
||||
os.Remove(certFile.Name())
|
||||
|
||||
keyFile.Close()
|
||||
time.Sleep(350 * time.Millisecond)
|
||||
os.Remove(keyFile.Name())
|
||||
}()
|
||||
|
||||
Get("/", func(ctx *Context) {
|
||||
ctx.Write("Hello from %s", ctx.HostString())
|
||||
})
|
||||
|
||||
// add a secondary server
|
||||
Servers.Add(config.Server{ListeningAddr: domain + ":80", RedirectTo: "https://" + host, Virtual: true})
|
||||
// add our primary/main server
|
||||
Servers.Add(config.Server{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
|
||||
|
||||
go Go()
|
||||
|
||||
// prepare test framework
|
||||
if ok := <-Available; !ok {
|
||||
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
|
||||
}
|
||||
|
||||
e := Tester(t)
|
||||
|
||||
e.Request("GET", "http://"+domain+":80").Expect().Status(StatusOK).Body().Equal("Hello from " + host)
|
||||
e.Request("GET", "https://"+host).Expect().Status(StatusOK).Body().Equal("Hello from " + host)
|
||||
e.Request("GET", "http://"+host).Expect().Status(StatusOK).Body().Equal("Hello from " + host)
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ func TestSessions(t *testing.T) {
|
|||
}
|
||||
|
||||
initDefault()
|
||||
HTTPServer.Config.ListeningAddr = "127.0.0.1:8080" // in order to test the sessions
|
||||
Config.Sessions.Cookie = "mycustomsessionid"
|
||||
|
||||
writeValues := func(ctx *Context) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user