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:
|
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 ./...
|
|
@ -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).
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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!
|
||||||
|
|
||||||
|
|
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 (
|
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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 (
|
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
12
cache/cache_test.go
vendored
|
@ -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
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|
8
cache/client/client.go
vendored
8
cache/client/client.go
vendored
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
cache/client/handler.go
vendored
2
cache/client/handler.go
vendored
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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
|
// 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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 != "" {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
2
doc.go
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {}" {
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
23
iris.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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...
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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):]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ""
|
|
||||||
}
|
|
||||||
|
|
|
@ -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{}{}
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
18
view/html.go
18
view/html.go
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user