version 12.1.5

Former-commit-id: cda69f08955cb0d594e98bf26197ee573cbba4b2
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-02-02 16:29:06 +02:00
parent e04ea83c04
commit 3093d65363
76 changed files with 9647 additions and 366 deletions

View File

@ -11,19 +11,19 @@ env:
install: install:
- go get ./... - go get ./...
script: script:
- go test -v -cover ./... - go test -count=1 -v -cover ./...
after_script: after_script:
# examples # examples
- cd ./_examples - cd ./_examples
- go get ./... - go get ./...
- go test -v -cover ./... - go test -count=1 -v -cover ./...
- cd ../ - cd ../
# typescript examples # typescript examples
- cd ./typescript/_examples - cd ./typescript/_examples
- go get ./... - go get ./...
- go test -v -cover ./... - go test -count=1 -v -cover ./...
- cd ../../ - cd ../../
# make sure that the _benchmarks code is working # make sure that the _benchmarks code is working
- cd ./_benchmarks - cd ./_benchmarks
- go get ./... - go get ./...
- go test -v -cover ./... - go test -count=1 -v -cover ./...

View File

@ -21,6 +21,10 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`. **How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`.
# Su, 02 February 2020 | v12.1.5
Various improvements and linting.
# Su, 29 December 2019 | v12.1.4 # Su, 29 December 2019 | v12.1.4
Minor fix on serving [embedded files](https://github.com/kataras/iris/wiki/File-server). Minor fix on serving [embedded files](https://github.com/kataras/iris/wiki/File-server).

View File

@ -21,9 +21,9 @@ Los desarrolladores no están obligados a actualizar si realmente no lo necesita
**Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`. **Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`.
# Su, 29 December 2019 | v12.1.4 # Su, 02 February 2020 | v12.1.5
Not translated yet, please navigate to the [english version](HISTORY.md#su-29-december-2019--v1214) instead. Not translated yet, please navigate to the [english version](HISTORY.md#su-02-february-2020--v1215) instead.
# Sábado, 26 de octubre 2019 | v12.0.0 # Sábado, 26 de octubre 2019 | v12.0.0

View File

@ -1,6 +1,6 @@
# Happy New Year! # News
![](https://iris-go.com/images/release.png) Iris version **12.1.4** has been [released](HISTORY.md#su-29-december-2019--v1214)! ![](https://iris-go.com/images/release.png) Iris version **12.1.5** has been [released](HISTORY.md#su-02-february-2020--v1215)!
![](https://iris-go.com/images/cli.png) The official [Iris Command Line Interface](https://github.com/kataras/iris-cli) will soon be near you in 2020! ![](https://iris-go.com/images/cli.png) The official [Iris Command Line Interface](https://github.com/kataras/iris-cli) will soon be near you in 2020!

View File

@ -1 +1 @@
12.1.4:https://github.com/kataras/iris/releases/tag/v12.1.4 12.1.5:https://github.com/kataras/iris/releases/tag/v12.1.5

View File

@ -4,5 +4,5 @@ go 1.13
require ( require (
github.com/betacraft/yaag v1.0.1-0.20191027021412-565f65e36090 github.com/betacraft/yaag v1.0.1-0.20191027021412-565f65e36090
github.com/kataras/iris/v12 v12.1.4 github.com/kataras/iris/v12 v12.1.5
) )

View File

@ -0,0 +1 @@
just a text.

View File

@ -0,0 +1 @@
<h1>Hello App2App3 index</h1>

View File

@ -0,0 +1 @@
<h1>Hello App2 index</h1>

View File

@ -0,0 +1,3 @@
body {
background-color: black;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1 @@
<h1>Hello index</h1>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
127.0.0.1 examle.com
127.0.0.1 v1.examle.com

View File

@ -0,0 +1,29 @@
package main
import (
"github.com/kataras/iris/v12"
)
const (
addr = "example.com:80"
subdomain = "v1"
)
func newApp() *iris.Application {
app := iris.New()
app.Favicon("./assets/favicon.ico")
v1 := app.Subdomain(subdomain)
v1.HandleDir("/", "./assets", iris.DirOptions{})
// http://v1.example.com
// http://v1.example.com/css/main.css
// http://v1.example.com/js/jquery-2.1.1.js
// http://v1.example.com/favicon.ico
return app
}
func main() {
app := newApp()
app.Run(iris.Addr(addr))
}

View File

@ -0,0 +1,81 @@
package main
import (
"io/ioutil"
"net"
"path/filepath"
"testing"
"github.com/kataras/iris/v12/httptest"
)
type resource string
func (r resource) contentType() string {
switch filepath.Ext(r.String()) {
case ".js":
return "application/javascript"
case ".css":
return "text/css"
case ".ico":
return "image/x-icon"
case ".html", "":
return "text/html"
default:
return "text/plain"
}
}
func (r resource) String() string {
return string(r)
}
func (r resource) loadFromBase(dir string) string {
filename := r.String()
if filepath.Ext(filename) == "" {
// root /.
filename = filename + "/index.html"
}
fullpath := filepath.Join(dir, filename)
b, err := ioutil.ReadFile(fullpath)
if err != nil {
panic(fullpath + " failed with error: " + err.Error())
}
result := string(b)
return result
}
func TestFileServerSubdomainBasic(t *testing.T) {
urls := []resource{
"/css/main.css",
"/js/jquery-2.1.1.js",
"/favicon.ico",
"/app2",
"/app2/app2app3",
"/",
}
app := newApp()
e := httptest.New(t, app)
host, _, err := net.SplitHostPort(addr)
if err != nil {
t.Fatal(err)
}
host = "http://" + subdomain + "." + host
for _, u := range urls {
url := u.String()
contents := u.loadFromBase("./assets")
e.GET(url).WithURL(host).Expect().
Status(httptest.StatusOK).
ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()).
Body().Equal(contents)
}
}

View File

@ -4,5 +4,5 @@ go 1.13
require ( require (
github.com/googollee/go-socket.io v1.4.3-0.20191109153049-7451e2f8c2e0 // indirect github.com/googollee/go-socket.io v1.4.3-0.20191109153049-7451e2f8c2e0 // indirect
github.com/kataras/iris/v12 v12.1.4 github.com/kataras/iris/v12 v12.1.5
) )

12
cache/cache_test.go vendored
View File

@ -165,10 +165,8 @@ func TestCacheValidator(t *testing.T) {
managedCache := cache.Cache(cacheDuration) managedCache := cache.Cache(cacheDuration)
managedCache.AddRule(rule.Validator([]rule.PreValidator{ managedCache.AddRule(rule.Validator([]rule.PreValidator{
func(ctx context.Context) bool { func(ctx context.Context) bool {
if ctx.Request().URL.Path == "/invalid" { // should always invalid for cache, don't bother to go to try to get or set cache
return false // should always invalid for cache, don't bother to go to try to get or set cache return ctx.Request().URL.Path != "/invalid"
}
return true
}, },
}, nil)) }, nil))
@ -176,10 +174,8 @@ func TestCacheValidator(t *testing.T) {
managedCache2.AddRule(rule.Validator(nil, managedCache2.AddRule(rule.Validator(nil,
[]rule.PostValidator{ []rule.PostValidator{
func(ctx context.Context) bool { func(ctx context.Context) bool {
if ctx.ResponseWriter().Header().Get("DONT") != "" { // it's passed the Claim and now Valid checks if the response contains a header of "DONT"
return false // it's passed the Claim and now Valid checks if the response contains a header of "DONT" return ctx.ResponseWriter().Header().Get("DONT") == ""
}
return true
}, },
}, },
)) ))

View File

@ -153,7 +153,10 @@ func (h *ClientHandler) ServeHTTP(ctx context.Context) {
return return
} }
// go Client.Do(request) // go Client.Do(request)
Client.Do(request) _, err = Client.Do(request)
if err != nil {
return
}
} else { } else {
// get the status code , content type and the write the response body // get the status code , content type and the write the response body
ctx.ContentType(response.Header.Get(cfg.ContentTypeHeader)) ctx.ContentType(response.Header.Get(cfg.ContentTypeHeader))
@ -163,7 +166,6 @@ func (h *ClientHandler) ServeHTTP(ctx context.Context) {
if err != nil { if err != nil {
return return
} }
ctx.Write(responseBody) _, _ = ctx.Write(responseBody)
} }
} }

View File

@ -30,7 +30,7 @@ func NewHandler(expiration time.Duration) *Handler {
return &Handler{ return &Handler{
rule: DefaultRuleSet, rule: DefaultRuleSet,
expiration: expiration, expiration: expiration,
entries: make(map[string]*entry.Entry, 0), entries: make(map[string]*entry.Entry),
} }
} }

View File

@ -34,16 +34,10 @@ func Conditional(claimPredicate func() bool, validPredicate func() bool) Rule {
// Claim validator // Claim validator
func (c *conditionalRule) Claim(ctx context.Context) bool { func (c *conditionalRule) Claim(ctx context.Context) bool {
if !c.claimPredicate() { return c.claimPredicate()
return false
}
return true
} }
// Valid validator // Valid validator
func (c *conditionalRule) Valid(ctx context.Context) bool { func (c *conditionalRule) Valid(ctx context.Context) bool {
if !c.validPredicate() { return c.validPredicate()
return false
}
return true
} }

View File

@ -355,6 +355,8 @@ type Context interface {
// //
// Keep note that this checks the "User-Agent" request header. // Keep note that this checks the "User-Agent" request header.
IsMobile() bool IsMobile() bool
// IsScript reports whether a client is a script.
IsScript() bool
// GetReferrer extracts and returns the information from the "Referer" header as specified // GetReferrer extracts and returns the information from the "Referer" header as specified
// in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy // in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
// or by the URL query parameter "referer". // or by the URL query parameter "referer".
@ -1700,7 +1702,7 @@ func (ctx *context) IsAjax() bool {
return ctx.GetHeader("X-Requested-With") == "XMLHttpRequest" return ctx.GetHeader("X-Requested-With") == "XMLHttpRequest"
} }
var isMobileRegex = regexp.MustCompile(`(?i)(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)`) var isMobileRegex = regexp.MustCompile("(?:hpw|i|web)os|alamofire|alcatel|amoi|android|avantgo|blackberry|blazer|cell|cfnetwork|darwin|dolfin|dolphin|fennec|htc|ip(?:hone|od|ad)|ipaq|j2me|kindle|midp|minimo|mobi|motorola|nec-|netfront|nokia|opera m(ob|in)i|palm|phone|pocket|portable|psp|silk-accelerated|skyfire|sony|ucbrowser|up.browser|up.link|windows ce|xda|zte|zune")
// IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server. // IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server.
// If the return value is true that means that the http client using a mobile // If the return value is true that means that the http client using a mobile
@ -1708,10 +1710,18 @@ var isMobileRegex = regexp.MustCompile(`(?i)(android|avantgo|blackberry|bolt|boo
// //
// Keep note that this checks the "User-Agent" request header. // Keep note that this checks the "User-Agent" request header.
func (ctx *context) IsMobile() bool { func (ctx *context) IsMobile() bool {
s := ctx.GetHeader("User-Agent") s := strings.ToLower(ctx.GetHeader("User-Agent"))
return isMobileRegex.MatchString(s) return isMobileRegex.MatchString(s)
} }
var isScriptRegex = regexp.MustCompile("curl|wget|collectd|python|urllib|java|jakarta|httpclient|phpcrawl|libwww|perl|go-http|okhttp|lua-resty|winhttp|awesomium")
// IsScript reports whether a client is a script.
func (ctx *context) IsScript() bool {
s := strings.ToLower(ctx.GetHeader("User-Agent"))
return isScriptRegex.MatchString(s)
}
type ( type (
// Referrer contains the extracted information from the `GetReferrer` // Referrer contains the extracted information from the `GetReferrer`
// //
@ -3252,7 +3262,10 @@ var finishCallbackB = []byte(");")
// WriteJSONP marshals the given interface object and writes the JSON response to the writer. // WriteJSONP marshals the given interface object and writes the JSON response to the writer.
func WriteJSONP(writer io.Writer, v interface{}, options JSONP, enableOptimization ...bool) (int, error) { func WriteJSONP(writer io.Writer, v interface{}, options JSONP, enableOptimization ...bool) (int, error) {
if callback := options.Callback; callback != "" { if callback := options.Callback; callback != "" {
writer.Write([]byte(callback + "(")) n, err := writer.Write([]byte(callback + "("))
if err != nil {
return n, err
}
defer writer.Write(finishCallbackB) defer writer.Write(finishCallbackB)
} }
@ -3342,7 +3355,10 @@ func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
} }
for k, v := range m.entries { for k, v := range m.entries {
e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v}) err = e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
if err != nil {
return err
}
} }
return e.EncodeToken(start.End()) return e.EncodeToken(start.End())
@ -3351,7 +3367,10 @@ func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
// WriteXML marshals the given interface object and writes the XML response to the writer. // WriteXML marshals the given interface object and writes the XML response to the writer.
func WriteXML(writer io.Writer, v interface{}, options XML) (int, error) { func WriteXML(writer io.Writer, v interface{}, options XML) (int, error) {
if prefix := options.Prefix; prefix != "" { if prefix := options.Prefix; prefix != "" {
writer.Write([]byte(prefix)) n, err := writer.Write([]byte(prefix))
if err != nil {
return n, err
}
} }
if indent := options.Indent; indent != "" { if indent := options.Indent; indent != "" {

View File

@ -186,7 +186,7 @@ func AddGzipHeaders(w ResponseWriter) {
// FlushResponse validates the response headers in order to be compatible with the gzip written data // FlushResponse validates the response headers in order to be compatible with the gzip written data
// and writes the data to the underline ResponseWriter. // and writes the data to the underline ResponseWriter.
func (w *GzipResponseWriter) FlushResponse() { func (w *GzipResponseWriter) FlushResponse() {
w.WriteNow(w.chunks) _, _ = w.WriteNow(w.chunks)
w.ResponseWriter.FlushResponse() w.ResponseWriter.FlushResponse()
} }

View File

@ -223,7 +223,10 @@ func (p Problem) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
for k, v := range p { for k, v := range p {
// convert keys like "type" to "Type", "productName" to "ProductName" and e.t.c. when xml. // convert keys like "type" to "Type", "productName" to "ProductName" and e.t.c. when xml.
e.Encode(xmlMapEntry{XMLName: xml.Name{Local: strings.Title(k)}, Value: v}) err = e.Encode(xmlMapEntry{XMLName: xml.Name{Local: strings.Title(k)}, Value: v})
if err != nil {
return err
}
} }
return e.EncodeToken(start.End()) return e.EncodeToken(start.End())

View File

@ -24,6 +24,8 @@ type ResponseWriter interface {
http.ResponseWriter http.ResponseWriter
http.Flusher http.Flusher
http.Hijacker http.Hijacker
// Note:
// The http.CloseNotifier interface is deprecated. New code should use Request.Context instead.
http.CloseNotifier http.CloseNotifier
http.Pusher http.Pusher
@ -99,6 +101,7 @@ type ResponseWriter interface {
Flusher() (http.Flusher, bool) Flusher() (http.Flusher, bool)
// CloseNotifier indicates if the protocol supports the underline connection closure notification. // CloseNotifier indicates if the protocol supports the underline connection closure notification.
// Warning: The http.CloseNotifier interface is deprecated. New code should use Request.Context instead.
CloseNotifier() (http.CloseNotifier, bool) CloseNotifier() (http.CloseNotifier, bool)
} }

View File

@ -182,7 +182,7 @@ const delim = "\n"
func (g *Group) Error() (s string) { func (g *Group) Error() (s string) {
if len(g.Errors) > 0 { if len(g.Errors) > 0 {
msgs := make([]string, len(g.Errors), len(g.Errors)) msgs := make([]string, len(g.Errors))
for i, err := range g.Errors { for i, err := range g.Errors {
msgs[i] = err.Error() msgs[i] = err.Error()
} }
@ -320,21 +320,6 @@ func sortGroups(groups []*Group) {
}) })
} }
func tryGetTypeText(typ interface{}) string {
if typ == nil {
return ""
}
switch v := typ.(type) {
case string:
return v
case fmt.Stringer:
return v.String()
default:
return ""
}
}
func isNotNil(err error) bool { func isNotNil(err error) bool {
if g, ok := err.(*Group); ok { if g, ok := err.(*Group); ok {
if len(g.Errors) > 0 { if len(g.Errors) > 0 {

View File

@ -136,7 +136,7 @@ func TestGroup(t *testing.T) {
t.Run("Walk", func(t *testing.T) { t.Run("Walk", func(t *testing.T) {
expectedEntries := 4 expectedEntries := 4
Walk(g, func(typ interface{}, err error) { _ = Walk(g, func(typ interface{}, err error) {
g.IncludeChildren = false g.IncludeChildren = false
childAPIErrorsGroup.IncludeChildren = false childAPIErrorsGroup.IncludeChildren = false
childAPIErrorsGroup2.IncludeChildren = false childAPIErrorsGroup2.IncludeChildren = false

View File

@ -14,13 +14,13 @@ import (
// .FromStd(func(w http.ResponseWriter, r *http.Request)) // .FromStd(func(w http.ResponseWriter, r *http.Request))
// .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) // .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))
func FromStd(handler interface{}) context.Handler { func FromStd(handler interface{}) context.Handler {
switch handler.(type) { switch h := handler.(type) {
case context.Handler: case context.Handler:
{ {
// //
// it's already a iris handler // it's already a iris handler
// //
return handler.(context.Handler) return h
} }
case http.Handler: case http.Handler:
@ -28,7 +28,6 @@ func FromStd(handler interface{}) context.Handler {
// handlerFunc.ServeHTTP(w,r) // handlerFunc.ServeHTTP(w,r)
// //
{ {
h := handler.(http.Handler)
return func(ctx context.Context) { return func(ctx context.Context) {
h.ServeHTTP(ctx.ResponseWriter(), ctx.Request()) h.ServeHTTP(ctx.ResponseWriter(), ctx.Request())
} }
@ -39,7 +38,7 @@ func FromStd(handler interface{}) context.Handler {
// //
// handlerFunc(w,r) // handlerFunc(w,r)
// //
return FromStd(http.HandlerFunc(handler.(func(http.ResponseWriter, *http.Request)))) return FromStd(http.HandlerFunc(h))
} }
case func(http.ResponseWriter, *http.Request, http.HandlerFunc): case func(http.ResponseWriter, *http.Request, http.HandlerFunc):
@ -47,7 +46,7 @@ func FromStd(handler interface{}) context.Handler {
// //
// handlerFunc(w,r, http.HandlerFunc) // handlerFunc(w,r, http.HandlerFunc)
// //
return FromStdWithNext(handler.(func(http.ResponseWriter, *http.Request, http.HandlerFunc))) return FromStdWithNext(h)
} }
default: default:

View File

@ -15,7 +15,10 @@ import (
func TestFromStd(t *testing.T) { func TestFromStd(t *testing.T) {
expected := "ok" expected := "ok"
std := func(w http.ResponseWriter, r *http.Request) { std := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(expected)) _, err := w.Write([]byte(expected))
if err != nil {
t.Fatal(err)
}
} }
h := handlerconv.FromStd(http.HandlerFunc(std)) h := handlerconv.FromStd(http.HandlerFunc(std))

View File

@ -64,13 +64,11 @@ func (i *interruptListener) notifyAndFire() {
os.Interrupt, os.Interrupt,
syscall.SIGINT, // register that too, it should be ok syscall.SIGINT, // register that too, it should be ok
// os.Kill is equivalent with the syscall.SIGKILL // os.Kill is equivalent with the syscall.SIGKILL
os.Kill, // os.Kill,
syscall.SIGKILL, // register that too, it should be ok // syscall.SIGKILL, // register that too, it should be ok
// kill -SIGTERM XXXX // kill -SIGTERM XXXX
syscall.SIGTERM, syscall.SIGTERM,
) )
select { <-ch
case <-ch: i.FireNow()
i.FireNow()
}
} }

View File

@ -194,12 +194,8 @@ func (su *Supervisor) supervise(blockFunc func() error) error {
su.notifyErr(err) su.notifyErr(err)
if su.isWaiting() { if su.isWaiting() {
blockStatement: for range su.unblockChan {
for { break
select {
case <-su.unblockChan:
break blockStatement
}
} }
} }

View File

@ -28,7 +28,9 @@ func ExampleSupervisor_RegisterOnError() {
go su.ListenAndServe() go su.ListenAndServe()
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
su.Shutdown(context.TODO()) if err := su.Shutdown(context.TODO()); err != nil {
panic(err)
}
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
// Output: // Output:
@ -49,37 +51,30 @@ func (m myTestTask) OnServe(host TaskHost) {
ticker := time.NewTicker(m.restartEvery) ticker := time.NewTicker(m.restartEvery)
defer ticker.Stop() defer ticker.Stop()
rans := 0 rans := 0
for { for range ticker.C {
select { exitAfterXRestarts := m.maxRestarts
case _, ok := <-ticker.C: if rans == exitAfterXRestarts {
{ m.logger.Println("exit")
if !ok { ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
m.logger.Println("ticker issue, closed channel, exiting from this task...") defer cancel()
return _ = host.Supervisor.Shutdown(ctx) // total shutdown
} host.Supervisor.RestoreFlow() // free to exit (if shutdown)
exitAfterXRestarts := m.maxRestarts return
if rans == exitAfterXRestarts {
m.logger.Println("exit")
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancel()
host.Supervisor.Shutdown(ctx) // total shutdown
host.Supervisor.RestoreFlow() // free to exit (if shutdown)
return
}
rans++
m.logger.Println(fmt.Sprintf("closed %d times", rans))
host.Shutdown(context.TODO())
startDelay := 2 * time.Second
time.AfterFunc(startDelay, func() {
m.logger.Println("restart")
host.Serve() // restart
})
}
} }
rans++
m.logger.Println(fmt.Sprintf("closed %d times", rans))
host.Shutdown(context.TODO())
startDelay := 2 * time.Second
time.AfterFunc(startDelay, func() {
m.logger.Println("restart")
if err := host.Serve(); err != nil { // restart
panic(err)
}
})
} }
} }

View File

@ -61,7 +61,10 @@ func testSupervisor(t *testing.T, creator func(*http.Server, []func(TaskHost)) *
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(expectedBody)) _, err := w.Write([]byte(expectedBody))
if err != nil {
t.Fatal(err)
}
}) })
// host (server wrapper and adapter) construction // host (server wrapper and adapter) construction

View File

@ -27,7 +27,7 @@ func WriteStartupLogOnServe(w io.Writer) func(TaskHost) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
interruptkey = "CMD" interruptkey = "CMD"
} }
w.Write([]byte(fmt.Sprintf("Now listening on: %s\nApplication started. Press %s+C to shut down.\n", _, _ = w.Write([]byte(fmt.Sprintf("Now listening on: %s\nApplication started. Press %s+C to shut down.\n",
listeningURI, interruptkey))) listeningURI, interruptkey)))
} }
} }

View File

@ -711,7 +711,7 @@ func (r *Store) Save(key string, value interface{}, immutable bool) (Entry, bool
// we should allow this // we should allow this
kv.ValueRaw = value kv.ValueRaw = value
kv.immutable = immutable kv.immutable = immutable
} else if kv.immutable == false { } else if !kv.immutable {
// if it was not immutable then user can alt it via `Set` and `SetImmutable` // if it was not immutable then user can alt it via `Set` and `SetImmutable`
kv.ValueRaw = value kv.ValueRaw = value
kv.immutable = immutable kv.immutable = immutable

View File

@ -91,13 +91,8 @@ var IsLoopbackHost = func(requestHost string) bool {
const ( const (
// defaultServerHostname returns the default hostname which is "localhost" // defaultServerHostname returns the default hostname which is "localhost"
defaultServerHostname = "localhost" defaultServerHostname = "localhost"
// defaultServerPort returns the default port which is 8080, not used
defaultServerPort = 8080
) )
// defaultServerAddr the default server addr which is: localhost:8080
var defaultServerAddr = defaultServerHostname + ":" + strconv.Itoa(defaultServerPort)
// ResolveAddr tries to convert a given string to an address which is compatible with net.Listener and server // ResolveAddr tries to convert a given string to an address which is compatible with net.Listener and server
func ResolveAddr(addr string) string { func ResolveAddr(addr string) string {
// check if addr has :port, if not do it +:80 ,we need the hostname for many cases // check if addr has :port, if not do it +:80 ,we need the hostname for many cases

View File

@ -23,13 +23,17 @@ type tcpKeepAliveListener struct {
} }
// Accept accepts tcp connections aka clients. // Accept accepts tcp connections aka clients.
func (l tcpKeepAliveListener) Accept() (c net.Conn, err error) { func (l tcpKeepAliveListener) Accept() (net.Conn, error) {
tc, err := l.AcceptTCP() tc, err := l.AcceptTCP()
if err != nil { if err != nil {
return return tc, err
}
if err = tc.SetKeepAlive(true); err != nil {
return tc, err
}
if err = tc.SetKeepAlivePeriod(3 * time.Minute); err != nil {
return tc, err
} }
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil return tc, nil
} }

View File

@ -1,7 +1,6 @@
package router package router
import ( import (
"errors"
"net/http" "net/http"
"os" "os"
"path" "path"
@ -42,66 +41,6 @@ type repository struct {
pos map[string]int pos map[string]int
} }
func (repo *repository) remove(route *Route) bool {
for i, r := range repo.routes {
if r == route {
return repo.removeByIndex(i)
}
}
return false
}
func (repo *repository) removeByPath(tmplPath string) bool {
if repo.pos != nil {
if idx, ok := repo.pos[tmplPath]; ok {
return repo.removeByIndex(idx)
}
}
return false
}
func (repo *repository) removeByName(routeName string) bool {
for i, r := range repo.routes {
if r.Name == routeName {
return repo.removeByIndex(i)
}
}
return false
}
func (repo *repository) removeByIndex(idx int) bool {
n := len(repo.routes)
if n == 0 {
return false
}
if idx >= n {
return false
}
if n == 1 && idx == 0 {
repo.routes = repo.routes[0:0]
repo.pos = nil
return true
}
r := repo.routes[idx]
if r == nil {
return false
}
repo.routes = append(repo.routes[:idx], repo.routes[idx+1:]...)
if repo.pos != nil {
delete(repo.pos, r.Path)
}
return true
}
func (repo *repository) get(routeName string) *Route { func (repo *repository) get(routeName string) *Route {
for _, r := range repo.routes { for _, r := range repo.routes {
if r.Name == routeName { if r.Name == routeName {
@ -160,15 +99,6 @@ func (repo *repository) register(route *Route) {
repo.pos[route.tmpl.Src] = len(repo.routes) - 1 repo.pos[route.tmpl.Src] = len(repo.routes) - 1
} }
type apiError struct {
error
}
func (e *apiError) Is(err error) bool {
_, ok := err.(*apiError)
return ok
}
// APIBuilder the visible API for constructing the router // APIBuilder the visible API for constructing the router
// and child routers. // and child routers.
type APIBuilder struct { type APIBuilder struct {
@ -179,9 +109,6 @@ type APIBuilder struct {
// the api builder global routes repository // the api builder global routes repository
routes *repository routes *repository
// the api builder global route path reverser object
// used by the view engine but it can be used anywhere.
reverser *RoutePathReverser
// the api builder global errors, can be filled by the Subdomain, WildcardSubdomain, Handle... // the api builder global errors, can be filled by the Subdomain, WildcardSubdomain, Handle...
// the list of possible errors that can be // the list of possible errors that can be
// collected on the build state to log // collected on the build state to log
@ -344,7 +271,7 @@ func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handl
// if allowMethods are empty, then simply register with the passed, main, method. // if allowMethods are empty, then simply register with the passed, main, method.
methods = append(api.allowMethods, methods...) methods = append(api.allowMethods, methods...)
routes := make([]*Route, len(methods), len(methods)) routes := make([]*Route, len(methods))
for i, m := range methods { for i, m := range methods {
route, err := NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros) route, err := NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros)
@ -492,11 +419,22 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
continue continue
} }
slashIdx := strings.IndexByte(s.RequestPath, '/') if n := len(api.relativePath); n > 0 && api.relativePath[n-1] == SubdomainPrefix[0] {
if slashIdx == -1 { // this api is a subdomain-based.
slashIdx = 0 slashIdx := strings.IndexByte(s.RequestPath, '/')
if slashIdx == -1 {
slashIdx = 0
}
requestPath = s.RequestPath[slashIdx:]
} else {
requestPath = s.RequestPath[strings.Index(s.RequestPath, api.relativePath)+len(api.relativePath):]
} }
requestPath = s.RequestPath[slashIdx:]
if requestPath == "" {
requestPath = "/"
}
routes = append(routes, api.CreateRoutes([]string{http.MethodGet}, requestPath, h)...) routes = append(routes, api.CreateRoutes([]string{http.MethodGet}, requestPath, h)...)
getRoute.StaticSites = append(getRoute.StaticSites, s) getRoute.StaticSites = append(getRoute.StaticSites, s)
} }
@ -861,9 +799,6 @@ func (api *APIBuilder) StaticContent(reqPath string, cType string, content []byt
return api.registerResourceRoute(reqPath, h) return api.registerResourceRoute(reqPath, h)
} }
// errDirectoryFileNotFound returns an error with message: 'Directory or file %s couldn't found. Trace: +error trace'
var errDirectoryFileNotFound = errors.New("Directory or file %s couldn't found. Trace: %s")
// Favicon serves static favicon // Favicon serves static favicon
// accepts 2 parameters, second is optional // accepts 2 parameters, second is optional
// favPath (string), declare the system directory path of the __.ico // favPath (string), declare the system directory path of the __.ico

View File

@ -307,7 +307,10 @@ func FileServer(directory string, opts ...DirOptions) context.Handler {
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
ctx.ContentType(context.ContentHTMLHeaderValue) ctx.ContentType(context.ContentHTMLHeaderValue)
ctx.WriteString("<pre>\n") _, err = ctx.WriteString("<pre>\n")
if err != nil {
return err
}
for _, d := range dirs { for _, d := range dirs {
name := d.Name() name := d.Name()
if d.IsDir() { if d.IsDir() {
@ -317,10 +320,13 @@ func FileServer(directory string, opts ...DirOptions) context.Handler {
// part of the URL path, and not indicate the start of a query // part of the URL path, and not indicate the start of a query
// string or fragment. // string or fragment.
url := url.URL{Path: joinPath("./"+dirName, name)} // edit here to redirect correctly, standard library misses that. url := url.URL{Path: joinPath("./"+dirName, name)} // edit here to redirect correctly, standard library misses that.
ctx.Writef("<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name)) _, err = ctx.Writef("<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
if err != nil {
return err
}
} }
ctx.WriteString("</pre>\n") _, err = ctx.WriteString("</pre>\n")
return nil return err
} }
} }

View File

@ -21,18 +21,6 @@ type ExecutionRules struct {
Main ExecutionOptions Main ExecutionOptions
} }
func handlersNames(handlers context.Handlers) (names []string) {
for _, h := range handlers {
if h == nil {
continue
}
names = append(names, context.HandlerName(h))
}
return
}
func applyExecutionRules(rules ExecutionRules, begin, done, main *context.Handlers) { func applyExecutionRules(rules ExecutionRules, begin, done, main *context.Handlers) {
if !rules.Begin.Force && !rules.Done.Force && !rules.Main.Force { if !rules.Begin.Force && !rules.Done.Force && !rules.Main.Force {
return // do not proceed and spend buld-time here if nothing changed. return // do not proceed and spend buld-time here if nothing changed.

View File

@ -542,7 +542,7 @@ var types = map[string]string{
func init() { func init() {
for ext, typ := range types { for ext, typ := range types {
// skip errors // skip errors
mime.AddExtensionType(ext, typ) _ = mime.AddExtensionType(ext, typ)
} }
} }

View File

@ -62,13 +62,6 @@ func prefix(s string, prefix string) string {
return s return s
} }
func suffix(s string, suffix string) string {
if !strings.HasSuffix(s, suffix) {
return s + suffix
}
return s
}
func splitMethod(methodMany string) []string { func splitMethod(methodMany string) []string {
methodMany = strings.Trim(methodMany, " ") methodMany = strings.Trim(methodMany, " ")
return strings.Split(methodMany, " ") return strings.Split(methodMany, " ")
@ -348,7 +341,7 @@ func toStringSlice(args []interface{}) (argsString []string) {
return return
} }
argsString = make([]string, argsSize, argsSize) argsString = make([]string, argsSize)
for i, v := range args { for i, v := range args {
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
argsString[i] = s argsString[i] = s

View File

@ -95,12 +95,6 @@ type trie struct {
subdomain string subdomain string
} }
func newTrie() *trie {
return &trie{
root: newTrieNode(),
}
}
const ( const (
pathSep = "/" pathSep = "/"
pathSepB = '/' pathSepB = '/'

2
doc.go
View File

@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
12.1.4 12.1.5
Installation Installation

View File

@ -27,8 +27,6 @@ type (
// performance reasons. // performance reasons.
Has bool Has bool
trace string // for debug info.
lost []*missingInput // Author's note: don't change this to a map. lost []*missingInput // Author's note: don't change this to a map.
} }
) )
@ -208,7 +206,7 @@ func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
// the caller should be able to in[0] = receiver before injection, // the caller should be able to in[0] = receiver before injection,
// then the `Inject` method should be used instead. // then the `Inject` method should be used instead.
func (s *FuncInjector) Call(ctx ...reflect.Value) []reflect.Value { func (s *FuncInjector) Call(ctx ...reflect.Value) []reflect.Value {
in := make([]reflect.Value, s.Length, s.Length) in := make([]reflect.Value, s.Length)
s.Inject(&in, ctx...) s.Inject(&in, ctx...)
return s.fn.Call(in) return s.fn.Call(in)
} }

View File

@ -146,7 +146,9 @@ func equalTypes(got reflect.Type, expected reflect.Type) bool {
// implement this "expected" user handler's input argument. // implement this "expected" user handler's input argument.
if expected.Kind() == reflect.Interface { if expected.Kind() == reflect.Interface {
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String()) // fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String())
return got.Implements(expected) // return got.Implements(expected)
// return expected.AssignableTo(got)
return got.AssignableTo(expected)
} }
// if got.String() == "interface {}" { // if got.String() == "interface {}" {

View File

@ -121,7 +121,7 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
// } // }
// } // }
visited := make(map[int]struct{}, 0) // add a visited to not add twice a single value (09-Jul-2019). visited := make(map[int]struct{}) // add a visited to not add twice a single value (09-Jul-2019).
fields := lookupFields(s.elemType, true, nil) fields := lookupFields(s.elemType, true, nil)
// for idx, val := range values { // for idx, val := range values {
@ -286,7 +286,7 @@ func (s *StructInjector) String() (trace string) {
} }
// Inject accepts a destination struct and any optional context value(s), // Inject accepts a destination struct and any optional context value(s),
// hero and mvc takes only one context value and this is the `context.Contex`. // hero and mvc takes only one context value and this is the `context.Context`.
// It applies the bindings to the "dest" struct. It calls the InjectElem. // It applies the bindings to the "dest" struct. It calls the InjectElem.
func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) { func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
if dest == nil { if dest == nil {
@ -301,6 +301,11 @@ func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) { func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) {
for _, f := range s.fields { for _, f := range s.fields {
f.Object.Assign(ctx, func(v reflect.Value) { f.Object.Assign(ctx, func(v reflect.Value) {
ff := destElem.FieldByIndex(f.FieldIndex)
if !v.Type().AssignableTo(ff.Type()) {
return
}
destElem.FieldByIndex(f.FieldIndex).Set(v) destElem.FieldByIndex(f.FieldIndex).Set(v)
}) })
} }

View File

@ -14,7 +14,7 @@ func NewValues() Values {
// Clone returns a copy of the current values. // Clone returns a copy of the current values.
func (bv Values) Clone() Values { func (bv Values) Clone() Values {
if n := len(bv); n > 0 { if n := len(bv); n > 0 {
values := make(Values, n, n) values := make(Values, n)
copy(values, bv) copy(values, bv)
return values return values
} }

View File

@ -321,7 +321,7 @@ func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values [
if statusCode < 400 { if statusCode < 400 {
statusCode = DefaultErrStatusCode statusCode = DefaultErrStatusCode
} }
break // break on first error, error should be in the end but we // break on first error, error should be in the end but we
// need to know break the dispatcher if any error. // need to know break the dispatcher if any error.
// at the end; we don't want to write anything to the response if error is not nil. // at the end; we don't want to write anything to the response if error is not nil.
@ -505,7 +505,7 @@ func (r View) Dispatch(ctx context.Context) { // r as Response view.
} }
} }
ctx.View(r.Name) _ = ctx.View(r.Name)
} }
} }

View File

@ -38,7 +38,7 @@ type testCustomResult struct {
// The only one required function to make that a custom Response dispatcher. // The only one required function to make that a custom Response dispatcher.
func (r testCustomResult) Dispatch(ctx context.Context) { func (r testCustomResult) Dispatch(ctx context.Context) {
ctx.HTML(r.HTML) _, _ = ctx.HTML(r.HTML)
} }
func GetCustomResponse() testCustomResult { func GetCustomResponse() testCustomResult {
@ -90,7 +90,7 @@ func (e err) Dispatch(ctx context.Context) {
// write the status code based on the err's StatusCode. // write the status code based on the err's StatusCode.
ctx.StatusCode(e.Status) ctx.StatusCode(e.Status)
// send to the client the whole object as json // send to the client the whole object as json
ctx.JSON(e) _, _ = ctx.JSON(e)
} }
func GetCustomErrorAsDispatcher() err { func GetCustomErrorAsDispatcher() err {

23
iris.go
View File

@ -41,7 +41,7 @@ import (
) )
// Version is the current version number of the Iris Web Framework. // Version is the current version number of the Iris Web Framework.
const Version = "12.1.4" const Version = "12.1.5"
// HTTP status codes as registered with IANA. // HTTP status codes as registered with IANA.
// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml. // See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.
@ -636,17 +636,9 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
// A shortcut for the `host#RegisterOnInterrupt`. // A shortcut for the `host#RegisterOnInterrupt`.
var RegisterOnInterrupt = host.RegisterOnInterrupt var RegisterOnInterrupt = host.RegisterOnInterrupt
// Shutdown gracefully terminates all the application's server hosts. // Shutdown gracefully terminates all the application's server hosts and any tunnels.
// Returns an error on the first failure, otherwise nil. // Returns an error on the first failure, otherwise nil.
func (app *Application) Shutdown(ctx stdContext.Context) error { func (app *Application) Shutdown(ctx stdContext.Context) error {
for _, t := range app.config.Tunneling.Tunnels {
if t.Name == "" {
continue
}
app.config.Tunneling.stopTunnel(t)
}
for i, su := range app.Hosts { for i, su := range app.Hosts {
app.logger.Debugf("Host[%d]: Shutdown now", i) app.logger.Debugf("Host[%d]: Shutdown now", i)
if err := su.Shutdown(ctx); err != nil { if err := su.Shutdown(ctx); err != nil {
@ -654,6 +646,17 @@ func (app *Application) Shutdown(ctx stdContext.Context) error {
return err return err
} }
} }
for _, t := range app.config.Tunneling.Tunnels {
if t.Name == "" {
continue
}
if err := app.config.Tunneling.stopTunnel(t); err != nil {
return err
}
}
return nil return nil
} }

View File

@ -83,19 +83,6 @@ const (
DefaultParamErrorCode = 404 DefaultParamErrorCode = 404
) )
// func parseParamFuncArg(t token.Token) (a ast.ParamFuncArg, err error) {
// if t.Type == token.INT {
// return ast.ParamFuncArgToInt(t.Literal)
// }
// // act all as strings here, because of int vs int64 vs uint64 and etc.
// return t.Literal, nil
// }
func parseParamFuncArg(t token.Token) (a string, err error) {
// act all as strings here, because of int vs int64 vs uint64 and etc.
return t.Literal, nil
}
func (p ParamParser) Error() error { func (p ParamParser) Error() error {
if len(p.errors) > 0 { if len(p.errors) > 0 {
return fmt.Errorf(strings.Join(p.errors, "\n")) return fmt.Errorf(strings.Join(p.errors, "\n"))
@ -180,7 +167,6 @@ func (p *ParamParser) Parse(paramTypes []ast.ParamType) (*ast.ParamStatement, er
if stmt.Name == "" { if stmt.Name == "" {
p.appendErr("[%d:%d] illegal token: }, forgot '{' ?", t.Start, t.End) p.appendErr("[%d:%d] illegal token: }, forgot '{' ?", t.Start, t.End)
} }
break
case token.ILLEGAL: case token.ILLEGAL:
p.appendErr("[%d:%d] illegal token: %s", t.Start, t.End, t.Literal) p.appendErr("[%d:%d] illegal token: %s", t.Start, t.End, t.Literal)
default: default:

View File

@ -37,8 +37,6 @@ const (
INT // 42 INT // 42
) )
const eof rune = 0
var keywords = map[string]Type{ var keywords = map[string]Type{
"else": ELSE, "else": ELSE,
} }

View File

@ -116,7 +116,7 @@ func (b *basicAuthMiddleware) Serve(ctx context.Context) {
} }
// all ok // all ok
if b.expireEnabled { if b.expireEnabled {
if auth.logged == false { if !auth.logged {
auth.expires = time.Now().Add(b.config.Expires) auth.expires = time.Now().Add(b.config.Expires)
auth.logged = true auth.logged = true
} }

View File

@ -108,7 +108,7 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) {
if l.config.Columns { if l.config.Columns {
endTimeFormatted := endTime.Format("2006/01/02 - 15:04:05") endTimeFormatted := endTime.Format("2006/01/02 - 15:04:05")
output := Columnize(endTimeFormatted, latency, status, ip, method, path, message, headerMessage) output := Columnize(endTimeFormatted, latency, status, ip, method, path, message, headerMessage)
ctx.Application().Logger().Printer.Output.Write([]byte(output)) _, _ = ctx.Application().Logger().Printer.Output.Write([]byte(output))
return return
} }
// no new line, the framework's logger is responsible how to render each log. // no new line, the framework's logger is responsible how to render each log.

View File

@ -39,7 +39,10 @@ func TestMethodOverrideWrapper(t *testing.T) {
}) })
app.Delete("/path2", func(ctx iris.Context) { app.Delete("/path2", func(ctx iris.Context) {
ctx.Writef("%s%s", expectedDelResponse, ctx.Request().Context().Value("_originalMethod")) _, err := ctx.Writef("%s%s", expectedDelResponse, ctx.Request().Context().Value("_originalMethod"))
if err != nil {
t.Fatal(err)
}
}) })
e := httptest.New(t, app) e := httptest.New(t, app)

View File

@ -12,8 +12,8 @@ import (
) )
const ( const (
responseFormValue = "g-recaptcha-response" // responseFormValue = "g-recaptcha-response"
apiURL = "https://www.google.com/recaptcha/api/siteverify" apiURL = "https://www.google.com/recaptcha/api/siteverify"
) )
// Response is the google's recaptcha response as JSON. // Response is the google's recaptcha response as JSON.

View File

@ -273,11 +273,7 @@ func (c *ControllerActivator) activate() {
} }
func (c *ControllerActivator) addErr(err error) bool { func (c *ControllerActivator) addErr(err error) bool {
if c.router.GetReporter().Err(err) != nil { return c.router.GetReporter().Err(err) != nil
return true
}
return false
} }
// register all available, exported methods to handlers if possible. // register all available, exported methods to handlers if possible.
@ -492,7 +488,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
ctxValue = reflect.ValueOf(ctx) ctxValue = reflect.ValueOf(ctx)
} }
in := make([]reflect.Value, n, n) in := make([]reflect.Value, n)
in[0] = ctrl in[0] = ctrl
funcInjector.Inject(&in, ctxValue) funcInjector.Inject(&in, ctxValue)

View File

@ -87,15 +87,6 @@ func (l *methodLexer) peekNext() (w string) {
return l.peek(l.cur + 1) return l.peek(l.cur + 1)
} }
func (l *methodLexer) peekPrev() (w string) {
if l.cur > 0 {
cur := l.cur - 1
w = l.words[cur]
}
return w
}
func genParamKey(argIdx int) string { func genParamKey(argIdx int) string {
return "param" + strconv.Itoa(argIdx) // param0, param1, param2... return "param" + strconv.Itoa(argIdx) // param0, param1, param2...
} }

View File

@ -537,3 +537,51 @@ func TestControllerNotCreateNewDueManuallySettingAllFields(t *testing.T) {
e.GET("/").Expect().Status(iris.StatusOK). e.GET("/").Expect().Status(iris.StatusOK).
Body().Equal("my title") Body().Equal("my title")
} }
type testControllerRequestScopedDependencies struct {
MyContext *testMyContext
CustomStruct *testCustomStruct
}
func (c *testControllerRequestScopedDependencies) Get() *testCustomStruct {
return c.CustomStruct
}
func (c *testControllerRequestScopedDependencies) GetCustomContext() string {
return c.MyContext.OtherField
}
func newRequestDep1(ctx context.Context) *testCustomStruct {
return &testCustomStruct{
Name: ctx.URLParam("name"),
Age: ctx.URLParamIntDefault("age", 0),
}
}
type testMyContext struct {
Context context.Context
OtherField string
}
func newRequestDep2(ctx context.Context) *testMyContext {
return &testMyContext{
Context: ctx,
OtherField: "test",
}
}
func TestControllerRequestScopedDependencies(t *testing.T) {
app := iris.New()
m := New(app)
m.Register(newRequestDep1)
m.Register(newRequestDep2)
m.Handle(new(testControllerRequestScopedDependencies))
e := httptest.New(t, app)
e.GET("/").WithQuery("name", "kataras").WithQuery("age", 27).
Expect().Status(httptest.StatusOK).JSON().Equal(&testCustomStruct{
Name: "kataras",
Age: 27,
})
e.GET("/custom/context").Expect().Status(httptest.StatusOK).Body().Equal("test")
}

View File

@ -39,7 +39,7 @@ func hasErrorOutArgs(fn reflect.Method) bool {
func getInputArgsFromFunc(funcTyp reflect.Type) []reflect.Type { func getInputArgsFromFunc(funcTyp reflect.Type) []reflect.Type {
n := funcTyp.NumIn() n := funcTyp.NumIn()
funcIn := make([]reflect.Type, n, n) funcIn := make([]reflect.Type, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
funcIn[i] = funcTyp.In(i) funcIn[i] = funcTyp.In(i)
} }

View File

@ -23,7 +23,7 @@ type (
// newProvider returns a new sessions provider // newProvider returns a new sessions provider
func newProvider() *provider { func newProvider() *provider {
return &provider{ return &provider{
sessions: make(map[string]*Session, 0), sessions: make(map[string]*Session),
db: newMemDB(), db: newMemDB(),
} }
} }

View File

@ -244,7 +244,11 @@ func (db *Database) Clear(sid string) {
defer iter.Close() defer iter.Close()
for iter.Rewind(); iter.ValidForPrefix(prefix); iter.Next() { for iter.Rewind(); iter.ValidForPrefix(prefix); iter.Next() {
txn.Delete(iter.Item().Key()) key := iter.Item().Key()
if err := txn.Delete(key); err != nil {
golog.Warnf("Database.Clear: %s: %v", key, err)
continue
}
} }
} }
@ -255,8 +259,12 @@ func (db *Database) Release(sid string) {
db.Clear(sid) db.Clear(sid)
// and remove the $sid. // and remove the $sid.
txn := db.Service.NewTransaction(true) txn := db.Service.NewTransaction(true)
txn.Delete([]byte(sid)) if err := txn.Delete([]byte(sid)); err != nil {
txn.Commit() golog.Warnf("Database.Release.Delete: %s: %v", sid, err)
}
if err := txn.Commit(); err != nil {
golog.Debugf("Database.Release.Commit: %s: %v", sid, err)
}
} }
// Close shutdowns the badger connection. // Close shutdowns the badger connection.

View File

@ -42,7 +42,7 @@ func New(path string, fileMode os.FileMode) (*Database, error) {
return nil, errPathMissing return nil, errPathMissing
} }
if fileMode <= 0 { if fileMode == 0 {
fileMode = os.FileMode(DefaultFileMode) fileMode = os.FileMode(DefaultFileMode)
} }
@ -67,11 +67,15 @@ func New(path string, fileMode os.FileMode) (*Database, error) {
func NewFromDB(service *bolt.DB, bucketName string) (*Database, error) { func NewFromDB(service *bolt.DB, bucketName string) (*Database, error) {
bucket := []byte(bucketName) bucket := []byte(bucketName)
service.Update(func(tx *bolt.Tx) (err error) { err := service.Update(func(tx *bolt.Tx) (err error) {
_, err = tx.CreateBucketIfNotExists(bucket) _, err = tx.CreateBucketIfNotExists(bucket)
return return
}) })
if err != nil {
return nil, err
}
db := &Database{table: bucket, Service: service} db := &Database{table: bucket, Service: service}
runtime.SetFinalizer(db, closeDB) runtime.SetFinalizer(db, closeDB)
@ -292,7 +296,7 @@ func (db *Database) Get(sid string, key string) (value interface{}) {
// Visit loops through all session keys and values. // Visit loops through all session keys and values.
func (db *Database) Visit(sid string, cb func(key string, value interface{})) { func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
db.Service.View(func(tx *bolt.Tx) error { err := db.Service.View(func(tx *bolt.Tx) error {
b := db.getBucketForSession(tx, sid) b := db.getBucketForSession(tx, sid)
if b == nil { if b == nil {
return nil return nil
@ -309,11 +313,15 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
return nil return nil
}) })
}) })
if err != nil {
golog.Debugf("Database.Visit: %s: %v", sid, err)
}
} }
// Len returns the length of the session's entries (keys). // Len returns the length of the session's entries (keys).
func (db *Database) Len(sid string) (n int) { func (db *Database) Len(sid string) (n int) {
db.Service.View(func(tx *bolt.Tx) error { err := db.Service.View(func(tx *bolt.Tx) error {
b := db.getBucketForSession(tx, sid) b := db.getBucketForSession(tx, sid)
if b == nil { if b == nil {
return nil return nil
@ -323,6 +331,10 @@ func (db *Database) Len(sid string) (n int) {
return nil return nil
}) })
if err != nil {
golog.Debugf("Database.Len: %s: %v", sid, err)
}
return return
} }
@ -342,7 +354,7 @@ func (db *Database) Delete(sid string, key string) (deleted bool) {
// Clear removes all session key values but it keeps the session entry. // Clear removes all session key values but it keeps the session entry.
func (db *Database) Clear(sid string) { func (db *Database) Clear(sid string) {
db.Service.Update(func(tx *bolt.Tx) error { err := db.Service.Update(func(tx *bolt.Tx) error {
b := db.getBucketForSession(tx, sid) b := db.getBucketForSession(tx, sid)
if b == nil { if b == nil {
return nil return nil
@ -352,20 +364,28 @@ func (db *Database) Clear(sid string) {
return b.Delete(k) return b.Delete(k)
}) })
}) })
if err != nil {
golog.Debugf("Database.Clear: %s: %v", sid, err)
}
} }
// Release destroys the session, it clears and removes the session entry, // Release destroys the session, it clears and removes the session entry,
// session manager will create a new session ID on the next request after this call. // session manager will create a new session ID on the next request after this call.
func (db *Database) Release(sid string) { func (db *Database) Release(sid string) {
db.Service.Update(func(tx *bolt.Tx) error { err := db.Service.Update(func(tx *bolt.Tx) error {
// delete the session bucket. // delete the session bucket.
b := db.getBucket(tx) b := db.getBucket(tx)
bsid := []byte(sid) bsid := []byte(sid)
// try to delete the associated expiration bucket, if exists, ignore error. // try to delete the associated expiration bucket, if exists, ignore error.
b.DeleteBucket(getExpirationBucketName(bsid)) _ = b.DeleteBucket(getExpirationBucketName(bsid))
return b.DeleteBucket(bsid) return b.DeleteBucket(bsid)
}) })
if err != nil {
golog.Debugf("Database.Release: %s: %v", sid, err)
}
} }
// Close shutdowns the BoltDB connection. // Close shutdowns the BoltDB connection.

View File

@ -239,7 +239,10 @@ func (db *Database) Release(sid string) {
// clear all $sid-$key. // clear all $sid-$key.
db.Clear(sid) db.Clear(sid)
// and remove the $sid. // and remove the $sid.
db.c.Driver.Delete(sid) err := db.c.Driver.Delete(sid)
if err != nil {
golog.Debugf("Database.Release.Driver.Delete: %s: %v", sid, err)
}
} }
// Close terminates the redis connection. // Close terminates the redis connection.

View File

@ -205,7 +205,7 @@ func (r *RedigoDriver) getKeysConn(c redis.Conn, cursor interface{}, prefix stri
if len(replies) == 2 { if len(replies) == 2 {
// take the second, it must contain the slice of keys. // take the second, it must contain the slice of keys.
if keysSliceAsBytes, ok := replies[1].([]interface{}); ok { if keysSliceAsBytes, ok := replies[1].([]interface{}); ok {
keys := make([]string, len(keysSliceAsBytes), len(keysSliceAsBytes)) keys := make([]string, len(keysSliceAsBytes))
for i, k := range keysSliceAsBytes { for i, k := range keysSliceAsBytes {
keys[i] = fmt.Sprintf("%s", k)[len(r.Config.Prefix):] keys[i] = fmt.Sprintf("%s", k)[len(r.Config.Prefix):]

View File

@ -58,7 +58,7 @@ func (s *Sessions) updateCookie(ctx context.Context, sid string, expires time.Du
} else { // > 0 } else { // > 0
cookie.Expires = time.Now().Add(expires) cookie.Expires = time.Now().Add(expires)
} }
cookie.MaxAge = int(cookie.Expires.Sub(time.Now()).Seconds()) cookie.MaxAge = int(time.Until(cookie.Expires).Seconds())
} }
// set the cookie to secure if this is a tls wrapped request // set the cookie to secure if this is a tls wrapped request

View File

@ -31,7 +31,10 @@ func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application)
s := sess.Start(ctx) s := sess.Start(ctx)
sessValues := s.GetAll() sessValues := s.GetAll()
ctx.JSON(sessValues) _, err := ctx.JSON(sessValues)
if err != nil {
t.Fatal(err)
}
} }
if testEnableSubdomain { if testEnableSubdomain {
@ -40,7 +43,7 @@ func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application)
app.Post("/set", func(ctx context.Context) { app.Post("/set", func(ctx context.Context) {
s := sess.Start(ctx) s := sess.Start(ctx)
vals := make(map[string]interface{}, 0) vals := make(map[string]interface{})
if err := ctx.ReadJSON(&vals); err != nil { if err := ctx.ReadJSON(&vals); err != nil {
t.Fatalf("Cannot read JSON. Trace %s", err.Error()) t.Fatalf("Cannot read JSON. Trace %s", err.Error())
} }
@ -74,7 +77,10 @@ func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application)
ctx.Next() ctx.Next()
}, func(ctx context.Context) { }, func(ctx context.Context) {
s := sess.Start(ctx) s := sess.Start(ctx)
ctx.Writef(s.GetString("key")) _, err := ctx.Writef(s.GetString("key"))
if err != nil {
t.Fatal(err)
}
}) })
e := httptest.New(t, app, httptest.URL("http://example.com")) e := httptest.New(t, app, httptest.URL("http://example.com"))
@ -123,7 +129,7 @@ func TestFlashMessages(t *testing.T) {
} }
app.Post("/set", func(ctx context.Context) { app.Post("/set", func(ctx context.Context) {
vals := make(map[string]interface{}, 0) vals := make(map[string]interface{})
if err := ctx.ReadJSON(&vals); err != nil { if err := ctx.ReadJSON(&vals); err != nil {
t.Fatalf("Cannot readjson. Trace %s", err.Error()) t.Fatalf("Cannot readjson. Trace %s", err.Error())
} }

View File

@ -26,7 +26,6 @@ Would be readily available to anyone who could intercept the HTTP request.
*/ */
import ( import (
"bufio" "bufio"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -47,8 +46,7 @@ type (
log func(format string, a ...interface{}) log func(format string, a ...interface{})
enabled bool // default true enabled bool // default true
// after alm started // after alm started
process *os.Process process *os.Process
debugOutput io.Writer
} }
) )
@ -113,11 +111,6 @@ func (e *Editor) GetDescription() string {
return "A bridge between iris and the alm-tools, the browser-based IDE." return "A bridge between iris and the alm-tools, the browser-based IDE."
} }
// we use that editorWriter to prefix the editor's output with "Editor Adaptor: "
type editorWriter struct {
underline io.Writer
}
// Run starts the editor's server. // Run starts the editor's server.
// //
// Developers should call the `Stop` to shutdown the editor's server when main server will be closed. // Developers should call the `Stop` to shutdown the editor's server when main server will be closed.

View File

@ -170,8 +170,8 @@ func (t *Typescript) hasTypescriptFiles() bool {
t.log("typescript error: directory '%s' couldn't be found,\nplease specify a valid path for your *.ts files", root) t.log("typescript error: directory '%s' couldn't be found,\nplease specify a valid path for your *.ts files", root)
return false return false
} }
// ignore error
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() { if fi.IsDir() {
return nil return nil
} }
@ -188,6 +188,9 @@ func (t *Typescript) hasTypescriptFiles() bool {
return nil return nil
}) })
if err != nil {
t.log("typescript: hasTypescriptFiles: %v", err)
}
return hasTs return hasTs
} }
@ -210,8 +213,7 @@ func (t *Typescript) getTypescriptProjects() []string {
root := t.Config.Dir root := t.Config.Dir
// t.logger.Printf("\nSearching for typescript projects in %s", root) // t.logger.Printf("\nSearching for typescript projects in %s", root)
// ignore error err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() { if fi.IsDir() {
return nil return nil
} }
@ -228,6 +230,10 @@ func (t *Typescript) getTypescriptProjects() []string {
return nil return nil
}) })
if err != nil {
t.log("typescript: getTypescriptProjects: %v", err)
}
return projects return projects
} }
@ -238,8 +244,7 @@ func (t *Typescript) getTypescriptFiles() []string {
root := t.Config.Dir root := t.Config.Dir
// ignore error err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() { if fi.IsDir() {
return nil return nil
} }
@ -256,5 +261,10 @@ func (t *Typescript) getTypescriptFiles() []string {
return nil return nil
}) })
if err != nil {
t.log("typescript: getTypescriptFiles: %v", err)
}
return files return files
} }

View File

@ -33,8 +33,8 @@ func Amber(directory, extension string) *AmberEngine {
s := &AmberEngine{ s := &AmberEngine{
directory: directory, directory: directory,
extension: extension, extension: extension,
templateCache: make(map[string]*template.Template, 0), templateCache: make(map[string]*template.Template),
funcs: make(map[string]interface{}, 0), funcs: make(map[string]interface{}),
} }
return s return s

View File

@ -113,9 +113,9 @@ func Django(directory, extension string) *DjangoEngine {
s := &DjangoEngine{ s := &DjangoEngine{
directory: directory, directory: directory,
extension: extension, extension: extension,
globals: make(map[string]interface{}, 0), globals: make(map[string]interface{}),
filters: make(map[string]FilterFunction, 0), filters: make(map[string]FilterFunction),
templateCache: make(map[string]*pongo2.Template, 0), templateCache: make(map[string]*pongo2.Template),
} }
return s return s
@ -235,7 +235,7 @@ func (s *DjangoEngine) loadDirectory() (templateErr error) {
defer s.mu.Unlock() defer s.mu.Unlock()
// Walk the supplied directory and compile any files that match our extension list. // Walk the supplied directory and compile any files that match our extension list.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html". // Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
// These dirs should be excluded as they are not valid golang templates, but files under // These dirs should be excluded as they are not valid golang templates, but files under
// them should be treat as normal. // them should be treat as normal.
@ -270,6 +270,10 @@ func (s *DjangoEngine) loadDirectory() (templateErr error) {
return nil return nil
}) })
if err != nil {
return err
}
return return
} }
@ -294,8 +298,6 @@ func (s *DjangoEngine) loadAssets() error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
var templateErr error
names := namesFn() names := namesFn()
for _, path := range names { for _, path := range names {
if !strings.HasPrefix(path, virtualDirectory) { if !strings.HasPrefix(path, virtualDirectory) {
@ -304,7 +306,6 @@ func (s *DjangoEngine) loadAssets() error {
rel, err := filepath.Rel(virtualDirectory, path) rel, err := filepath.Rel(virtualDirectory, path)
if err != nil { if err != nil {
templateErr = err
return err return err
} }
@ -313,18 +314,18 @@ func (s *DjangoEngine) loadAssets() error {
buf, err := assetFn(path) buf, err := assetFn(path)
if err != nil { if err != nil {
templateErr = err
return err return err
} }
name := filepath.ToSlash(rel) name := filepath.ToSlash(rel)
s.templateCache[name], err = set.FromString(string(buf)) s.templateCache[name], err = set.FromString(string(buf))
if err != nil { if err != nil {
templateErr = err
return err return err
} }
} }
} }
return templateErr
return nil
} }
// getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly // getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly

View File

@ -29,15 +29,3 @@ type Engine interface {
// Ext should return the final file extension which this view engine is responsible to render. // Ext should return the final file extension which this view engine is responsible to render.
Ext() string Ext() string
} }
type namedEngine interface {
String() string
}
func getEngineName(e Engine) string {
if n, ok := e.(namedEngine); ok {
return n.String()
}
return ""
}

View File

@ -8,11 +8,3 @@ type EngineFuncer interface {
// AddFunc should adds a function to the template's function map. // AddFunc should adds a function to the template's function map.
AddFunc(funcName string, funcBody interface{}) AddFunc(funcName string, funcBody interface{})
} }
// these will be added to all template engines used
// and completes the EngineFuncer interface.
//
// There are a lot of default functions but some of them are placed inside of each
// template engine because of the different behavior, i.e urlpath and url are inside framework itself,
// yield,partial,partial_r,current and render as inside html engine etc...
var defaultSharedFuncs = map[string]interface{}{}

View File

@ -34,8 +34,8 @@ func Handlebars(directory, extension string) *HandlebarsEngine {
s := &HandlebarsEngine{ s := &HandlebarsEngine{
directory: directory, directory: directory,
extension: extension, extension: extension,
templateCache: make(map[string]*raymond.Template, 0), templateCache: make(map[string]*raymond.Template),
helpers: make(map[string]interface{}, 0), helpers: make(map[string]interface{}),
} }
// register the render helper here // register the render helper here
@ -133,7 +133,7 @@ func (s *HandlebarsEngine) loadDirectory() error {
// instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error) // instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error)
var templateErr error var templateErr error
filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error { err := filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error {
if info == nil || info.IsDir() { if info == nil || info.IsDir() {
return nil return nil
} }
@ -165,6 +165,10 @@ func (s *HandlebarsEngine) loadDirectory() error {
return nil return nil
}) })
if err != nil {
return err
}
return templateErr return templateErr
} }
@ -186,7 +190,6 @@ func (s *HandlebarsEngine) loadAssets() error {
virtualDirectory = virtualDirectory[1:] virtualDirectory = virtualDirectory[1:]
} }
} }
var templateErr error
names := namesFn() names := namesFn()
for _, path := range names { for _, path := range names {
@ -198,28 +201,27 @@ func (s *HandlebarsEngine) loadAssets() error {
rel, err := filepath.Rel(virtualDirectory, path) rel, err := filepath.Rel(virtualDirectory, path)
if err != nil { if err != nil {
templateErr = err
return err return err
} }
buf, err := assetFn(path) buf, err := assetFn(path)
if err != nil { if err != nil {
templateErr = err
return err return err
} }
contents := string(buf) contents := string(buf)
name := filepath.ToSlash(rel) name := filepath.ToSlash(rel)
tmpl, err := raymond.Parse(contents) tmpl, err := raymond.Parse(contents)
if err != nil { if err != nil {
templateErr = err
return err return err
} }
s.templateCache[name] = tmpl s.templateCache[name] = tmpl
} }
} }
return templateErr
return nil
} }
func (s *HandlebarsEngine) fromCache(relativeName string) *raymond.Template { func (s *HandlebarsEngine) fromCache(relativeName string) *raymond.Template {

View File

@ -10,7 +10,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"time"
) )
// HTMLEngine contains the html view engine structure. // HTMLEngine contains the html view engine structure.
@ -71,8 +70,8 @@ func HTML(directory, extension string) *HTMLEngine {
left: "{{", left: "{{",
right: "}}", right: "}}",
layout: "", layout: "",
layoutFuncs: make(map[string]interface{}, 0), layoutFuncs: make(map[string]interface{}),
funcs: make(map[string]interface{}, 0), funcs: make(map[string]interface{}),
} }
return s return s
@ -244,7 +243,7 @@ func (s *HTMLEngine) loadDirectory() error {
s.Templates = template.New(dir) s.Templates = template.New(dir)
s.Templates.Delims(s.left, s.right) s.Templates.Delims(s.left, s.right)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() { if info == nil || info.IsDir() {
} else { } else {
rel, err := filepath.Rel(dir, path) rel, err := filepath.Rel(dir, path)
@ -288,6 +287,10 @@ func (s *HTMLEngine) loadDirectory() error {
return nil return nil
}) })
if err != nil {
return err
}
return templateErr return templateErr
} }
@ -374,7 +377,10 @@ func (s *HTMLEngine) loadAssets() error {
} }
// Add our funcmaps. // Add our funcmaps.
tmpl.Funcs(emptyFuncs).Funcs(s.funcs).Parse(contents) if _, err = tmpl.Funcs(emptyFuncs).Funcs(s.funcs).Parse(contents); err != nil {
templateErr = err
continue
}
} }
} }
return templateErr return templateErr
@ -455,8 +461,6 @@ func (s *HTMLEngine) runtimeFuncsFor(name string, binding interface{}) {
} }
} }
var zero = time.Time{}
// ExecuteWriter executes a template and writes its result to the w writer. // ExecuteWriter executes a template and writes its result to the w writer.
func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bindingData interface{}) error { func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bindingData interface{}) error {
// re-parse the templates if reload is enabled. // re-parse the templates if reload is enabled.