mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
version 12.1.5
Former-commit-id: cda69f08955cb0d594e98bf26197ee573cbba4b2
This commit is contained in:
parent
e04ea83c04
commit
3093d65363
|
@ -11,19 +11,19 @@ env:
|
|||
install:
|
||||
- go get ./...
|
||||
script:
|
||||
- go test -v -cover ./...
|
||||
- go test -count=1 -v -cover ./...
|
||||
after_script:
|
||||
# examples
|
||||
- cd ./_examples
|
||||
- go get ./...
|
||||
- go test -v -cover ./...
|
||||
- go test -count=1 -v -cover ./...
|
||||
- cd ../
|
||||
# typescript examples
|
||||
- cd ./typescript/_examples
|
||||
- go get ./...
|
||||
- go test -v -cover ./...
|
||||
- go test -count=1 -v -cover ./...
|
||||
- cd ../../
|
||||
# make sure that the _benchmarks code is working
|
||||
- cd ./_benchmarks
|
||||
- go get ./...
|
||||
- go test -v -cover ./...
|
||||
- go test -count=1 -v -cover ./...
|
|
@ -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`.
|
||||
|
||||
# Su, 02 February 2020 | v12.1.5
|
||||
|
||||
Various improvements and linting.
|
||||
|
||||
# Su, 29 December 2019 | v12.1.4
|
||||
|
||||
Minor fix on serving [embedded files](https://github.com/kataras/iris/wiki/File-server).
|
||||
|
|
|
@ -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`.
|
||||
|
||||
# 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
|
||||
|
||||
|
|
|
@ -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!
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -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
|
|
@ -4,5 +4,5 @@ go 1.13
|
|||
|
||||
require (
|
||||
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
|
||||
)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
just a text.
|
|
@ -0,0 +1 @@
|
|||
<h1>Hello App2App3 index</h1>
|
1
_examples/file-server/subdomain/assets/app2/index.html
Normal file
1
_examples/file-server/subdomain/assets/app2/index.html
Normal file
|
@ -0,0 +1 @@
|
|||
<h1>Hello App2 index</h1>
|
3
_examples/file-server/subdomain/assets/css/main.css
Normal file
3
_examples/file-server/subdomain/assets/css/main.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
background-color: black;
|
||||
}
|
BIN
_examples/file-server/subdomain/assets/favicon.ico
Normal file
BIN
_examples/file-server/subdomain/assets/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
1
_examples/file-server/subdomain/assets/index.html
Normal file
1
_examples/file-server/subdomain/assets/index.html
Normal file
|
@ -0,0 +1 @@
|
|||
<h1>Hello index</h1>
|
9190
_examples/file-server/subdomain/assets/js/jquery-2.1.1.js
vendored
Normal file
9190
_examples/file-server/subdomain/assets/js/jquery-2.1.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
_examples/file-server/subdomain/hosts
Normal file
2
_examples/file-server/subdomain/hosts
Normal file
|
@ -0,0 +1,2 @@
|
|||
127.0.0.1 examle.com
|
||||
127.0.0.1 v1.examle.com
|
29
_examples/file-server/subdomain/main.go
Normal file
29
_examples/file-server/subdomain/main.go
Normal 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))
|
||||
}
|
81
_examples/file-server/subdomain/main_test.go
Normal file
81
_examples/file-server/subdomain/main_test.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -4,5 +4,5 @@ go 1.13
|
|||
|
||||
require (
|
||||
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
12
cache/cache_test.go
vendored
|
@ -165,10 +165,8 @@ func TestCacheValidator(t *testing.T) {
|
|||
managedCache := cache.Cache(cacheDuration)
|
||||
managedCache.AddRule(rule.Validator([]rule.PreValidator{
|
||||
func(ctx context.Context) bool {
|
||||
if ctx.Request().URL.Path == "/invalid" {
|
||||
return false // should always invalid for cache, don't bother to go to try to get or set cache
|
||||
}
|
||||
return true
|
||||
// should always invalid for cache, don't bother to go to try to get or set cache
|
||||
return ctx.Request().URL.Path != "/invalid"
|
||||
},
|
||||
}, nil))
|
||||
|
||||
|
@ -176,10 +174,8 @@ func TestCacheValidator(t *testing.T) {
|
|||
managedCache2.AddRule(rule.Validator(nil,
|
||||
[]rule.PostValidator{
|
||||
func(ctx context.Context) bool {
|
||||
if ctx.ResponseWriter().Header().Get("DONT") != "" {
|
||||
return false // it's passed the Claim and now Valid checks if the response contains a header of "DONT"
|
||||
}
|
||||
return true
|
||||
// it's passed the Claim and now Valid checks if the response contains a header of "DONT"
|
||||
return ctx.ResponseWriter().Header().Get("DONT") == ""
|
||||
},
|
||||
},
|
||||
))
|
||||
|
|
8
cache/client/client.go
vendored
8
cache/client/client.go
vendored
|
@ -153,7 +153,10 @@ func (h *ClientHandler) ServeHTTP(ctx context.Context) {
|
|||
return
|
||||
}
|
||||
// go Client.Do(request)
|
||||
Client.Do(request)
|
||||
_, err = Client.Do(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// get the status code , content type and the write the response body
|
||||
ctx.ContentType(response.Header.Get(cfg.ContentTypeHeader))
|
||||
|
@ -163,7 +166,6 @@ func (h *ClientHandler) ServeHTTP(ctx context.Context) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
ctx.Write(responseBody)
|
||||
|
||||
_, _ = ctx.Write(responseBody)
|
||||
}
|
||||
}
|
||||
|
|
2
cache/client/handler.go
vendored
2
cache/client/handler.go
vendored
|
@ -30,7 +30,7 @@ func NewHandler(expiration time.Duration) *Handler {
|
|||
return &Handler{
|
||||
rule: DefaultRuleSet,
|
||||
expiration: expiration,
|
||||
entries: make(map[string]*entry.Entry, 0),
|
||||
entries: make(map[string]*entry.Entry),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
cache/client/rule/conditional.go
vendored
10
cache/client/rule/conditional.go
vendored
|
@ -34,16 +34,10 @@ func Conditional(claimPredicate func() bool, validPredicate func() bool) Rule {
|
|||
|
||||
// Claim validator
|
||||
func (c *conditionalRule) Claim(ctx context.Context) bool {
|
||||
if !c.claimPredicate() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return c.claimPredicate()
|
||||
}
|
||||
|
||||
// Valid validator
|
||||
func (c *conditionalRule) Valid(ctx context.Context) bool {
|
||||
if !c.validPredicate() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return c.validPredicate()
|
||||
}
|
||||
|
|
|
@ -355,6 +355,8 @@ type Context interface {
|
|||
//
|
||||
// Keep note that this checks the "User-Agent" request header.
|
||||
IsMobile() bool
|
||||
// IsScript reports whether a client is a script.
|
||||
IsScript() bool
|
||||
// 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
|
||||
// or by the URL query parameter "referer".
|
||||
|
@ -1700,7 +1702,7 @@ func (ctx *context) IsAjax() bool {
|
|||
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.
|
||||
// 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.
|
||||
func (ctx *context) IsMobile() bool {
|
||||
s := ctx.GetHeader("User-Agent")
|
||||
s := strings.ToLower(ctx.GetHeader("User-Agent"))
|
||||
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 (
|
||||
// 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.
|
||||
func WriteJSONP(writer io.Writer, v interface{}, options JSONP, enableOptimization ...bool) (int, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -3342,7 +3355,10 @@ func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|||
}
|
||||
|
||||
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())
|
||||
|
@ -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.
|
||||
func WriteXML(writer io.Writer, v interface{}, options XML) (int, error) {
|
||||
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 != "" {
|
||||
|
|
|
@ -186,7 +186,7 @@ func AddGzipHeaders(w ResponseWriter) {
|
|||
// FlushResponse validates the response headers in order to be compatible with the gzip written data
|
||||
// and writes the data to the underline ResponseWriter.
|
||||
func (w *GzipResponseWriter) FlushResponse() {
|
||||
w.WriteNow(w.chunks)
|
||||
_, _ = w.WriteNow(w.chunks)
|
||||
w.ResponseWriter.FlushResponse()
|
||||
}
|
||||
|
||||
|
|
|
@ -223,7 +223,10 @@ func (p Problem) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|||
|
||||
for k, v := range p {
|
||||
// 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())
|
||||
|
|
|
@ -24,6 +24,8 @@ type ResponseWriter interface {
|
|||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
// Note:
|
||||
// The http.CloseNotifier interface is deprecated. New code should use Request.Context instead.
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
|
||||
|
@ -99,6 +101,7 @@ type ResponseWriter interface {
|
|||
Flusher() (http.Flusher, bool)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ const delim = "\n"
|
|||
|
||||
func (g *Group) Error() (s string) {
|
||||
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 {
|
||||
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 {
|
||||
if g, ok := err.(*Group); ok {
|
||||
if len(g.Errors) > 0 {
|
||||
|
|
|
@ -136,7 +136,7 @@ func TestGroup(t *testing.T) {
|
|||
|
||||
t.Run("Walk", func(t *testing.T) {
|
||||
expectedEntries := 4
|
||||
Walk(g, func(typ interface{}, err error) {
|
||||
_ = Walk(g, func(typ interface{}, err error) {
|
||||
g.IncludeChildren = false
|
||||
childAPIErrorsGroup.IncludeChildren = false
|
||||
childAPIErrorsGroup2.IncludeChildren = false
|
||||
|
|
|
@ -14,13 +14,13 @@ import (
|
|||
// .FromStd(func(w http.ResponseWriter, r *http.Request))
|
||||
// .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))
|
||||
func FromStd(handler interface{}) context.Handler {
|
||||
switch handler.(type) {
|
||||
switch h := handler.(type) {
|
||||
case context.Handler:
|
||||
{
|
||||
//
|
||||
// it's already a iris handler
|
||||
//
|
||||
return handler.(context.Handler)
|
||||
return h
|
||||
}
|
||||
|
||||
case http.Handler:
|
||||
|
@ -28,7 +28,6 @@ func FromStd(handler interface{}) context.Handler {
|
|||
// handlerFunc.ServeHTTP(w,r)
|
||||
//
|
||||
{
|
||||
h := handler.(http.Handler)
|
||||
return func(ctx context.Context) {
|
||||
h.ServeHTTP(ctx.ResponseWriter(), ctx.Request())
|
||||
}
|
||||
|
@ -39,7 +38,7 @@ func FromStd(handler interface{}) context.Handler {
|
|||
//
|
||||
// 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):
|
||||
|
@ -47,7 +46,7 @@ func FromStd(handler interface{}) context.Handler {
|
|||
//
|
||||
// handlerFunc(w,r, http.HandlerFunc)
|
||||
//
|
||||
return FromStdWithNext(handler.(func(http.ResponseWriter, *http.Request, http.HandlerFunc)))
|
||||
return FromStdWithNext(h)
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
@ -15,7 +15,10 @@ import (
|
|||
func TestFromStd(t *testing.T) {
|
||||
expected := "ok"
|
||||
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))
|
||||
|
|
|
@ -64,13 +64,11 @@ func (i *interruptListener) notifyAndFire() {
|
|||
os.Interrupt,
|
||||
syscall.SIGINT, // register that too, it should be ok
|
||||
// os.Kill is equivalent with the syscall.SIGKILL
|
||||
os.Kill,
|
||||
syscall.SIGKILL, // register that too, it should be ok
|
||||
// os.Kill,
|
||||
// syscall.SIGKILL, // register that too, it should be ok
|
||||
// kill -SIGTERM XXXX
|
||||
syscall.SIGTERM,
|
||||
)
|
||||
select {
|
||||
case <-ch:
|
||||
i.FireNow()
|
||||
}
|
||||
<-ch
|
||||
i.FireNow()
|
||||
}
|
||||
|
|
|
@ -194,12 +194,8 @@ func (su *Supervisor) supervise(blockFunc func() error) error {
|
|||
su.notifyErr(err)
|
||||
|
||||
if su.isWaiting() {
|
||||
blockStatement:
|
||||
for {
|
||||
select {
|
||||
case <-su.unblockChan:
|
||||
break blockStatement
|
||||
}
|
||||
for range su.unblockChan {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,9 @@ func ExampleSupervisor_RegisterOnError() {
|
|||
|
||||
go su.ListenAndServe()
|
||||
time.Sleep(1 * time.Second)
|
||||
su.Shutdown(context.TODO())
|
||||
if err := su.Shutdown(context.TODO()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Output:
|
||||
|
@ -49,37 +51,30 @@ func (m myTestTask) OnServe(host TaskHost) {
|
|||
ticker := time.NewTicker(m.restartEvery)
|
||||
defer ticker.Stop()
|
||||
rans := 0
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-ticker.C:
|
||||
{
|
||||
if !ok {
|
||||
m.logger.Println("ticker issue, closed channel, exiting from this task...")
|
||||
return
|
||||
}
|
||||
exitAfterXRestarts := m.maxRestarts
|
||||
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
|
||||
})
|
||||
|
||||
}
|
||||
for range ticker.C {
|
||||
exitAfterXRestarts := m.maxRestarts
|
||||
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")
|
||||
if err := host.Serve(); err != nil { // restart
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,10 @@ func testSupervisor(t *testing.T, creator func(*http.Server, []func(TaskHost)) *
|
|||
|
||||
mux := http.NewServeMux()
|
||||
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
|
||||
|
|
|
@ -27,7 +27,7 @@ func WriteStartupLogOnServe(w io.Writer) func(TaskHost) {
|
|||
if runtime.GOOS == "darwin" {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -711,7 +711,7 @@ func (r *Store) Save(key string, value interface{}, immutable bool) (Entry, bool
|
|||
// we should allow this
|
||||
kv.ValueRaw = value
|
||||
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`
|
||||
kv.ValueRaw = value
|
||||
kv.immutable = immutable
|
||||
|
|
|
@ -91,13 +91,8 @@ var IsLoopbackHost = func(requestHost string) bool {
|
|||
const (
|
||||
// defaultServerHostname returns the default hostname which is "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
|
||||
func ResolveAddr(addr string) string {
|
||||
// check if addr has :port, if not do it +:80 ,we need the hostname for many cases
|
||||
|
|
|
@ -23,13 +23,17 @@ type tcpKeepAliveListener struct {
|
|||
}
|
||||
|
||||
// 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()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -42,66 +41,6 @@ type repository struct {
|
|||
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 {
|
||||
for _, r := range repo.routes {
|
||||
if r.Name == routeName {
|
||||
|
@ -160,15 +99,6 @@ func (repo *repository) register(route *Route) {
|
|||
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
|
||||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
|
@ -179,9 +109,6 @@ type APIBuilder struct {
|
|||
// the api builder global 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 list of possible errors that can be
|
||||
// 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.
|
||||
methods = append(api.allowMethods, methods...)
|
||||
|
||||
routes := make([]*Route, len(methods), len(methods))
|
||||
routes := make([]*Route, len(methods))
|
||||
|
||||
for i, m := range methods {
|
||||
route, err := NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros)
|
||||
|
@ -492,11 +419,22 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
|
|||
continue
|
||||
}
|
||||
|
||||
slashIdx := strings.IndexByte(s.RequestPath, '/')
|
||||
if slashIdx == -1 {
|
||||
slashIdx = 0
|
||||
if n := len(api.relativePath); n > 0 && api.relativePath[n-1] == SubdomainPrefix[0] {
|
||||
// this api is a subdomain-based.
|
||||
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)...)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// accepts 2 parameters, second is optional
|
||||
// favPath (string), declare the system directory path of the __.ico
|
||||
|
|
|
@ -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() })
|
||||
|
||||
ctx.ContentType(context.ContentHTMLHeaderValue)
|
||||
ctx.WriteString("<pre>\n")
|
||||
_, err = ctx.WriteString("<pre>\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, d := range dirs {
|
||||
name := d.Name()
|
||||
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
|
||||
// string or fragment.
|
||||
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")
|
||||
return nil
|
||||
_, err = ctx.WriteString("</pre>\n")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,18 +21,6 @@ type ExecutionRules struct {
|
|||
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) {
|
||||
if !rules.Begin.Force && !rules.Done.Force && !rules.Main.Force {
|
||||
return // do not proceed and spend buld-time here if nothing changed.
|
||||
|
|
|
@ -542,7 +542,7 @@ var types = map[string]string{
|
|||
func init() {
|
||||
for ext, typ := range types {
|
||||
// skip errors
|
||||
mime.AddExtensionType(ext, typ)
|
||||
_ = mime.AddExtensionType(ext, typ)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,13 +62,6 @@ func prefix(s string, prefix string) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func suffix(s string, suffix string) string {
|
||||
if !strings.HasSuffix(s, suffix) {
|
||||
return s + suffix
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func splitMethod(methodMany string) []string {
|
||||
methodMany = strings.Trim(methodMany, " ")
|
||||
return strings.Split(methodMany, " ")
|
||||
|
@ -348,7 +341,7 @@ func toStringSlice(args []interface{}) (argsString []string) {
|
|||
return
|
||||
}
|
||||
|
||||
argsString = make([]string, argsSize, argsSize)
|
||||
argsString = make([]string, argsSize)
|
||||
for i, v := range args {
|
||||
if s, ok := v.(string); ok {
|
||||
argsString[i] = s
|
||||
|
|
|
@ -95,12 +95,6 @@ type trie struct {
|
|||
subdomain string
|
||||
}
|
||||
|
||||
func newTrie() *trie {
|
||||
return &trie{
|
||||
root: newTrieNode(),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
pathSep = "/"
|
||||
pathSepB = '/'
|
||||
|
|
2
doc.go
2
doc.go
|
@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
|
|||
|
||||
Current Version
|
||||
|
||||
12.1.4
|
||||
12.1.5
|
||||
|
||||
Installation
|
||||
|
||||
|
|
|
@ -27,8 +27,6 @@ type (
|
|||
// performance reasons.
|
||||
Has bool
|
||||
|
||||
trace string // for debug info.
|
||||
|
||||
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,
|
||||
// then the `Inject` method should be used instead.
|
||||
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...)
|
||||
return s.fn.Call(in)
|
||||
}
|
||||
|
|
|
@ -146,7 +146,9 @@ func equalTypes(got reflect.Type, expected reflect.Type) bool {
|
|||
// implement this "expected" user handler's input argument.
|
||||
if expected.Kind() == reflect.Interface {
|
||||
// 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 {}" {
|
||||
|
|
|
@ -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)
|
||||
|
||||
// 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),
|
||||
// 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.
|
||||
func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
|
||||
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) {
|
||||
for _, f := range s.fields {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func NewValues() Values {
|
|||
// Clone returns a copy of the current values.
|
||||
func (bv Values) Clone() Values {
|
||||
if n := len(bv); n > 0 {
|
||||
values := make(Values, n, n)
|
||||
values := make(Values, n)
|
||||
copy(values, bv)
|
||||
return values
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values [
|
|||
if statusCode < 400 {
|
||||
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.
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ type testCustomResult struct {
|
|||
|
||||
// The only one required function to make that a custom Response dispatcher.
|
||||
func (r testCustomResult) Dispatch(ctx context.Context) {
|
||||
ctx.HTML(r.HTML)
|
||||
_, _ = ctx.HTML(r.HTML)
|
||||
}
|
||||
|
||||
func GetCustomResponse() testCustomResult {
|
||||
|
@ -90,7 +90,7 @@ func (e err) Dispatch(ctx context.Context) {
|
|||
// write the status code based on the err's StatusCode.
|
||||
ctx.StatusCode(e.Status)
|
||||
// send to the client the whole object as json
|
||||
ctx.JSON(e)
|
||||
_, _ = ctx.JSON(e)
|
||||
}
|
||||
|
||||
func GetCustomErrorAsDispatcher() err {
|
||||
|
|
23
iris.go
23
iris.go
|
@ -41,7 +41,7 @@ import (
|
|||
)
|
||||
|
||||
// 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.
|
||||
// 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`.
|
||||
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.
|
||||
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 {
|
||||
app.logger.Debugf("Host[%d]: Shutdown now", i)
|
||||
if err := su.Shutdown(ctx); err != nil {
|
||||
|
@ -654,6 +646,17 @@ func (app *Application) Shutdown(ctx stdContext.Context) error {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -83,19 +83,6 @@ const (
|
|||
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 {
|
||||
if len(p.errors) > 0 {
|
||||
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 == "" {
|
||||
p.appendErr("[%d:%d] illegal token: }, forgot '{' ?", t.Start, t.End)
|
||||
}
|
||||
break
|
||||
case token.ILLEGAL:
|
||||
p.appendErr("[%d:%d] illegal token: %s", t.Start, t.End, t.Literal)
|
||||
default:
|
||||
|
|
|
@ -37,8 +37,6 @@ const (
|
|||
INT // 42
|
||||
)
|
||||
|
||||
const eof rune = 0
|
||||
|
||||
var keywords = map[string]Type{
|
||||
"else": ELSE,
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ func (b *basicAuthMiddleware) Serve(ctx context.Context) {
|
|||
}
|
||||
// all ok
|
||||
if b.expireEnabled {
|
||||
if auth.logged == false {
|
||||
if !auth.logged {
|
||||
auth.expires = time.Now().Add(b.config.Expires)
|
||||
auth.logged = true
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) {
|
|||
if l.config.Columns {
|
||||
endTimeFormatted := endTime.Format("2006/01/02 - 15:04:05")
|
||||
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
|
||||
}
|
||||
// no new line, the framework's logger is responsible how to render each log.
|
||||
|
|
|
@ -39,7 +39,10 @@ func TestMethodOverrideWrapper(t *testing.T) {
|
|||
})
|
||||
|
||||
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)
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
responseFormValue = "g-recaptcha-response"
|
||||
apiURL = "https://www.google.com/recaptcha/api/siteverify"
|
||||
// responseFormValue = "g-recaptcha-response"
|
||||
apiURL = "https://www.google.com/recaptcha/api/siteverify"
|
||||
)
|
||||
|
||||
// Response is the google's recaptcha response as JSON.
|
||||
|
|
|
@ -273,11 +273,7 @@ func (c *ControllerActivator) activate() {
|
|||
}
|
||||
|
||||
func (c *ControllerActivator) addErr(err error) bool {
|
||||
if c.router.GetReporter().Err(err) != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return c.router.GetReporter().Err(err) != nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
in := make([]reflect.Value, n, n)
|
||||
in := make([]reflect.Value, n)
|
||||
in[0] = ctrl
|
||||
funcInjector.Inject(&in, ctxValue)
|
||||
|
||||
|
|
|
@ -87,15 +87,6 @@ func (l *methodLexer) peekNext() (w string) {
|
|||
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 {
|
||||
return "param" + strconv.Itoa(argIdx) // param0, param1, param2...
|
||||
}
|
||||
|
|
|
@ -537,3 +537,51 @@ func TestControllerNotCreateNewDueManuallySettingAllFields(t *testing.T) {
|
|||
e.GET("/").Expect().Status(iris.StatusOK).
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func hasErrorOutArgs(fn reflect.Method) bool {
|
|||
|
||||
func getInputArgsFromFunc(funcTyp reflect.Type) []reflect.Type {
|
||||
n := funcTyp.NumIn()
|
||||
funcIn := make([]reflect.Type, n, n)
|
||||
funcIn := make([]reflect.Type, n)
|
||||
for i := 0; i < n; i++ {
|
||||
funcIn[i] = funcTyp.In(i)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ type (
|
|||
// newProvider returns a new sessions provider
|
||||
func newProvider() *provider {
|
||||
return &provider{
|
||||
sessions: make(map[string]*Session, 0),
|
||||
sessions: make(map[string]*Session),
|
||||
db: newMemDB(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,7 +244,11 @@ func (db *Database) Clear(sid string) {
|
|||
defer iter.Close()
|
||||
|
||||
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)
|
||||
// and remove the $sid.
|
||||
txn := db.Service.NewTransaction(true)
|
||||
txn.Delete([]byte(sid))
|
||||
txn.Commit()
|
||||
if err := txn.Delete([]byte(sid)); err != nil {
|
||||
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.
|
||||
|
|
|
@ -42,7 +42,7 @@ func New(path string, fileMode os.FileMode) (*Database, error) {
|
|||
return nil, errPathMissing
|
||||
}
|
||||
|
||||
if fileMode <= 0 {
|
||||
if fileMode == 0 {
|
||||
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) {
|
||||
bucket := []byte(bucketName)
|
||||
|
||||
service.Update(func(tx *bolt.Tx) (err error) {
|
||||
err := service.Update(func(tx *bolt.Tx) (err error) {
|
||||
_, err = tx.CreateBucketIfNotExists(bucket)
|
||||
return
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := &Database{table: bucket, Service: service}
|
||||
|
||||
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.
|
||||
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)
|
||||
if b == nil {
|
||||
return nil
|
||||
|
@ -309,11 +313,15 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
|
|||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
golog.Debugf("Database.Visit: %s: %v", sid, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the length of the session's entries (keys).
|
||||
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)
|
||||
if b == nil {
|
||||
return nil
|
||||
|
@ -323,6 +331,10 @@ func (db *Database) Len(sid string) (n int) {
|
|||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
golog.Debugf("Database.Len: %s: %v", sid, err)
|
||||
}
|
||||
|
||||
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.
|
||||
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)
|
||||
if b == nil {
|
||||
return nil
|
||||
|
@ -352,20 +364,28 @@ func (db *Database) Clear(sid string) {
|
|||
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,
|
||||
// session manager will create a new session ID on the next request after this call.
|
||||
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.
|
||||
b := db.getBucket(tx)
|
||||
bsid := []byte(sid)
|
||||
// try to delete the associated expiration bucket, if exists, ignore error.
|
||||
b.DeleteBucket(getExpirationBucketName(bsid))
|
||||
_ = b.DeleteBucket(getExpirationBucketName(bsid))
|
||||
|
||||
return b.DeleteBucket(bsid)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
golog.Debugf("Database.Release: %s: %v", sid, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close shutdowns the BoltDB connection.
|
||||
|
|
|
@ -239,7 +239,10 @@ func (db *Database) Release(sid string) {
|
|||
// clear all $sid-$key.
|
||||
db.Clear(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.
|
||||
|
|
|
@ -205,7 +205,7 @@ func (r *RedigoDriver) getKeysConn(c redis.Conn, cursor interface{}, prefix stri
|
|||
if len(replies) == 2 {
|
||||
// take the second, it must contain the slice of keys.
|
||||
if keysSliceAsBytes, ok := replies[1].([]interface{}); ok {
|
||||
keys := make([]string, len(keysSliceAsBytes), len(keysSliceAsBytes))
|
||||
keys := make([]string, len(keysSliceAsBytes))
|
||||
|
||||
for i, k := range keysSliceAsBytes {
|
||||
keys[i] = fmt.Sprintf("%s", k)[len(r.Config.Prefix):]
|
||||
|
|
|
@ -58,7 +58,7 @@ func (s *Sessions) updateCookie(ctx context.Context, sid string, expires time.Du
|
|||
} else { // > 0
|
||||
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
|
||||
|
|
|
@ -31,7 +31,10 @@ func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application)
|
|||
s := sess.Start(ctx)
|
||||
sessValues := s.GetAll()
|
||||
|
||||
ctx.JSON(sessValues)
|
||||
_, err := ctx.JSON(sessValues)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if testEnableSubdomain {
|
||||
|
@ -40,7 +43,7 @@ func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application)
|
|||
|
||||
app.Post("/set", func(ctx context.Context) {
|
||||
s := sess.Start(ctx)
|
||||
vals := make(map[string]interface{}, 0)
|
||||
vals := make(map[string]interface{})
|
||||
if err := ctx.ReadJSON(&vals); err != nil {
|
||||
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()
|
||||
}, func(ctx context.Context) {
|
||||
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"))
|
||||
|
@ -123,7 +129,7 @@ func TestFlashMessages(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
t.Fatalf("Cannot readjson. Trace %s", err.Error())
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ Would be readily available to anyone who could intercept the HTTP request.
|
|||
*/
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
@ -47,8 +46,7 @@ type (
|
|||
log func(format string, a ...interface{})
|
||||
enabled bool // default true
|
||||
// after alm started
|
||||
process *os.Process
|
||||
debugOutput io.Writer
|
||||
process *os.Process
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -113,11 +111,6 @@ func (e *Editor) GetDescription() string {
|
|||
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.
|
||||
//
|
||||
// Developers should call the `Stop` to shutdown the editor's server when main server will be closed.
|
||||
|
|
|
@ -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)
|
||||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -188,6 +188,9 @@ func (t *Typescript) hasTypescriptFiles() bool {
|
|||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.log("typescript: hasTypescriptFiles: %v", err)
|
||||
}
|
||||
return hasTs
|
||||
}
|
||||
|
||||
|
@ -210,8 +213,7 @@ func (t *Typescript) getTypescriptProjects() []string {
|
|||
root := t.Config.Dir
|
||||
// t.logger.Printf("\nSearching for typescript projects in %s", root)
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -228,6 +230,10 @@ func (t *Typescript) getTypescriptProjects() []string {
|
|||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.log("typescript: getTypescriptProjects: %v", err)
|
||||
}
|
||||
return projects
|
||||
}
|
||||
|
||||
|
@ -238,8 +244,7 @@ func (t *Typescript) getTypescriptFiles() []string {
|
|||
|
||||
root := t.Config.Dir
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -256,5 +261,10 @@ func (t *Typescript) getTypescriptFiles() []string {
|
|||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.log("typescript: getTypescriptFiles: %v", err)
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ func Amber(directory, extension string) *AmberEngine {
|
|||
s := &AmberEngine{
|
||||
directory: directory,
|
||||
extension: extension,
|
||||
templateCache: make(map[string]*template.Template, 0),
|
||||
funcs: make(map[string]interface{}, 0),
|
||||
templateCache: make(map[string]*template.Template),
|
||||
funcs: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
return s
|
||||
|
|
|
@ -113,9 +113,9 @@ func Django(directory, extension string) *DjangoEngine {
|
|||
s := &DjangoEngine{
|
||||
directory: directory,
|
||||
extension: extension,
|
||||
globals: make(map[string]interface{}, 0),
|
||||
filters: make(map[string]FilterFunction, 0),
|
||||
templateCache: make(map[string]*pongo2.Template, 0),
|
||||
globals: make(map[string]interface{}),
|
||||
filters: make(map[string]FilterFunction),
|
||||
templateCache: make(map[string]*pongo2.Template),
|
||||
}
|
||||
|
||||
return s
|
||||
|
@ -235,7 +235,7 @@ func (s *DjangoEngine) loadDirectory() (templateErr error) {
|
|||
defer s.mu.Unlock()
|
||||
|
||||
// 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".
|
||||
// These dirs should be excluded as they are not valid golang templates, but files under
|
||||
// them should be treat as normal.
|
||||
|
@ -270,6 +270,10 @@ func (s *DjangoEngine) loadDirectory() (templateErr error) {
|
|||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -294,8 +298,6 @@ func (s *DjangoEngine) loadAssets() error {
|
|||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var templateErr error
|
||||
|
||||
names := namesFn()
|
||||
for _, path := range names {
|
||||
if !strings.HasPrefix(path, virtualDirectory) {
|
||||
|
@ -304,7 +306,6 @@ func (s *DjangoEngine) loadAssets() error {
|
|||
|
||||
rel, err := filepath.Rel(virtualDirectory, path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -313,18 +314,18 @@ func (s *DjangoEngine) loadAssets() error {
|
|||
|
||||
buf, err := assetFn(path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(rel)
|
||||
s.templateCache[name], err = set.FromString(string(buf))
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateErr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly
|
||||
|
|
|
@ -29,15 +29,3 @@ type Engine interface {
|
|||
// Ext should return the final file extension which this view engine is responsible to render.
|
||||
Ext() string
|
||||
}
|
||||
|
||||
type namedEngine interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
func getEngineName(e Engine) string {
|
||||
if n, ok := e.(namedEngine); ok {
|
||||
return n.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -8,11 +8,3 @@ type EngineFuncer interface {
|
|||
// AddFunc should adds a function to the template's function map.
|
||||
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{}{}
|
||||
|
|
|
@ -34,8 +34,8 @@ func Handlebars(directory, extension string) *HandlebarsEngine {
|
|||
s := &HandlebarsEngine{
|
||||
directory: directory,
|
||||
extension: extension,
|
||||
templateCache: make(map[string]*raymond.Template, 0),
|
||||
helpers: make(map[string]interface{}, 0),
|
||||
templateCache: make(map[string]*raymond.Template),
|
||||
helpers: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -165,6 +165,10 @@ func (s *HandlebarsEngine) loadDirectory() error {
|
|||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return templateErr
|
||||
}
|
||||
|
||||
|
@ -186,7 +190,6 @@ func (s *HandlebarsEngine) loadAssets() error {
|
|||
virtualDirectory = virtualDirectory[1:]
|
||||
}
|
||||
}
|
||||
var templateErr error
|
||||
|
||||
names := namesFn()
|
||||
for _, path := range names {
|
||||
|
@ -198,28 +201,27 @@ func (s *HandlebarsEngine) loadAssets() error {
|
|||
|
||||
rel, err := filepath.Rel(virtualDirectory, path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := assetFn(path)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
contents := string(buf)
|
||||
name := filepath.ToSlash(rel)
|
||||
|
||||
tmpl, err := raymond.Parse(contents)
|
||||
if err != nil {
|
||||
templateErr = err
|
||||
return err
|
||||
}
|
||||
s.templateCache[name] = tmpl
|
||||
|
||||
}
|
||||
}
|
||||
return templateErr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HandlebarsEngine) fromCache(relativeName string) *raymond.Template {
|
||||
|
|
18
view/html.go
18
view/html.go
|
@ -10,7 +10,6 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTMLEngine contains the html view engine structure.
|
||||
|
@ -71,8 +70,8 @@ func HTML(directory, extension string) *HTMLEngine {
|
|||
left: "{{",
|
||||
right: "}}",
|
||||
layout: "",
|
||||
layoutFuncs: make(map[string]interface{}, 0),
|
||||
funcs: make(map[string]interface{}, 0),
|
||||
layoutFuncs: make(map[string]interface{}),
|
||||
funcs: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
return s
|
||||
|
@ -244,7 +243,7 @@ func (s *HTMLEngine) loadDirectory() error {
|
|||
s.Templates = template.New(dir)
|
||||
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() {
|
||||
} else {
|
||||
rel, err := filepath.Rel(dir, path)
|
||||
|
@ -288,6 +287,10 @@ func (s *HTMLEngine) loadDirectory() error {
|
|||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return templateErr
|
||||
}
|
||||
|
||||
|
@ -374,7 +377,10 @@ func (s *HTMLEngine) loadAssets() error {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
@ -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.
|
||||
func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bindingData interface{}) error {
|
||||
// re-parse the templates if reload is enabled.
|
||||
|
|
Loading…
Reference in New Issue
Block a user