diff --git a/README_FR.md b/README_FR.md
index 6d8c7327e..0f7c7065b 100644
--- a/README_FR.md
+++ b/README_FR.md
@@ -1,6 +1,6 @@
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
@@ -57,7 +57,7 @@ Iris possède un **[wiki](https://www.iris-go.com/#ebookDonateForm)** complet et
-Pour une documentation encore plus complète vous pouvez visiter notre [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.9) (en Anglais). Et vous trouverez du code executable dans le dossier [\_examples](_examples/).
+Pour une documentation encore plus complète vous pouvez visiter notre [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v) (en Anglais). Et vous trouverez du code executable dans le dossier [\_examples](_examples/).
### Vous préférez une version PDF?
diff --git a/README_GR.md b/README_GR.md
index 3e3f4338b..29b196c95 100644
--- a/README_GR.md
+++ b/README_GR.md
@@ -1,6 +1,6 @@
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
Το Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go γλώσσα προγραμματισμού. Παρέχει ένα εκφραστικό και εύχρηστο υπόβαθρο για την επόμενη ιστοσελίδα σας.
@@ -53,7 +53,7 @@ $ go run example.go
Το Iris περιέχει εκτενείς και λεπτομερείς **[book](https://www.iris-go.com/#ebookDonateForm)** καθιστώντας το εύκολο στην εκμάθηση.
-Για λεπτομερέστερη τεχνική τεκμηρίωση μπορείτε να κατευθυνθείτε προς τα [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.9) μας. Και για εκτελέσιμο κώδικα μπορείτε πάντα να επισκέπτεστε τα [παραδείγματα](_examples/).
+Για λεπτομερέστερη τεχνική τεκμηρίωση μπορείτε να κατευθυνθείτε προς τα [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v) μας. Και για εκτελέσιμο κώδικα μπορείτε πάντα να επισκέπτεστε τα [παραδείγματα](_examples/).
### Σας αρέσει να διαβάζετε ενώ ταξιδεύετε;
diff --git a/README_JA.md b/README_JA.md
index 600c9953c..e7218e712 100644
--- a/README_JA.md
+++ b/README_JA.md
@@ -6,7 +6,7 @@ Try the official [Iris Command Line Interface](https://github.com/kataras/iris-c
#
Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
Irisは、高速でシンプルでありながら、十分な機能を備えた、非常に効率的なGo用Webフレームワークです。
@@ -219,7 +219,7 @@ Irisが提供する機能の一部:
$ mkdir myapp
$ cd myapp
$ go mod init myapp
-$ go get github.com/kataras/iris/v12@latest # or @v12.2.9
+$ go get github.com/kataras/iris/v12@latest # or @v
```
既存のプロジェクトにインストールする場合
diff --git a/README_KO.md b/README_KO.md
index 05c594d7b..d4be33216 100644
--- a/README_KO.md
+++ b/README_KO.md
@@ -1,6 +1,6 @@
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
Iris는 단순하고 빠르며 좋은 성능과 모든 기능을 갖춘 Go언어용 웹 프레임워크입니다. 당신의 웹사이트나 API를 위해서 아름답고 사용하기 쉬운 기반을 제공합니다.
@@ -49,7 +49,7 @@ $ go run example.go
Iris는 광범위하고 꼼꼼한 **[wiki](https://www.iris-go.com/#ebookDonateForm)** 를 가지고 있기 때문에 쉽게 프레임워크를 시작할 수 있습니다.
-더 자세한 기술문서를 보시려면 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.9)를 방문하세요. 그리고 실행가능한 예제코드는 [\_examples](_examples/) 하위 디렉토리에 있습니다.
+더 자세한 기술문서를 보시려면 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v)를 방문하세요. 그리고 실행가능한 예제코드는 [\_examples](_examples/) 하위 디렉토리에 있습니다.
### 여행하면서 독서를 즐기세요?
diff --git a/README_PT_BR.md b/README_PT_BR.md
index c605b19a1..e3ac80e5a 100644
--- a/README_PT_BR.md
+++ b/README_PT_BR.md
@@ -2,7 +2,7 @@
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-270-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-270-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
@@ -226,7 +226,7 @@ O único requisito é a [Linguagem de programação Go](https://go.dev/dl/).
$ mkdir myapp
$ cd myapp
$ go mod init myapp
-$ go get github.com/kataras/iris/v12@latest # or @v12.2.9
+$ go get github.com/kataras/iris/v12@latest # or @v
```
Instalar num projeto existente
diff --git a/README_RU.md b/README_RU.md
index 37766319d..41674d83d 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -1,6 +1,6 @@
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
Iris — это быстрый, простой, но полнофункциональный и эффективный веб-фреймворк для Go. Он обеспечивает красивую, выразительную и простую в использовании основу для вашего следующего веб-сайта или API.
Узнайте, что [говорят другие люди об Iris](https://iris-go.com/testimonials/) и поставьте **[звёздочку](https://github.com/kataras/iris/stargazers)** этому проекту с открытым исходным кодом, чтобы поддержать его потенциал.
@@ -50,7 +50,7 @@ $ go run example.go
-Для получения более подробной технической документации вы можете обратиться к нашему [godoc](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.9). А для живых примеров кода — вы всегда можете посетить [\_examples](_examples/) в поддиректории этого репозитория.
+Для получения более подробной технической документации вы можете обратиться к нашему [godoc](https://pkg.go.dev/github.com/kataras/iris/v12@v). А для живых примеров кода — вы всегда можете посетить [\_examples](_examples/) в поддиректории этого репозитория.
### Вы любите читать во время путешествий?
diff --git a/README_VN.md b/README_VN.md
index 5550319da..4dc792ad5 100644
--- a/README_VN.md
+++ b/README_VN.md
@@ -6,7 +6,7 @@ Try the official [Iris Command Line Interface](https://github.com/kataras/iris-c
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
Iris là một khung web nhanh, đơn giản nhưng đầy đủ tính năng và rất hiệu quả dành cho Go.
@@ -220,7 +220,7 @@ Yêu cầu duy nhất là [Ngôn ngữ lập trình Go](https://go.dev/dl/).
$ mkdir myapp
$ cd myapp
$ go mod init myapp
-$ go get github.com/kataras/iris/v12@latest # or @v12.2.9
+$ go get github.com/kataras/iris/v12@latest # or @v
```
Cài đặt trên dự án hiện có
diff --git a/README_ZH_HANS.md b/README_ZH_HANS.md
index 613845e9c..8046c1bb2 100644
--- a/README_ZH_HANS.md
+++ b/README_ZH_HANS.md
@@ -10,7 +10,7 @@
# Iris Web Framework
-[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-253-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
+[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-253-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
diff --git a/README_ZH_HANT.md b/README_ZH_HANT.md
index 52f238743..3c9aded28 100644
--- a/README_ZH_HANT.md
+++ b/README_ZH_HANT.md
@@ -12,7 +12,7 @@
# Iris Web 框架
-[![組建狀態](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![查看範例](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![聊天室](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![捐助](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
+[![組建狀態](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![查看範例](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![聊天室](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![捐助](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate)
@@ -237,7 +237,7 @@ Iris 提供了至少這些功能:
$ mkdir myapp
$ cd myapp
$ go mod init myapp
-$ go get github.com/kataras/iris/v12@latest # 或 @v12.2.9
+$ go get github.com/kataras/iris/v12@latest # 或 @v
```
在現有專案安裝
diff --git a/_examples/file-server/embedding-files-into-app-bindata/main_test.go b/_examples/file-server/embedding-files-into-app-bindata/main_test.go
index fc546e557..03e32d338 100644
--- a/_examples/file-server/embedding-files-into-app-bindata/main_test.go
+++ b/_examples/file-server/embedding-files-into-app-bindata/main_test.go
@@ -50,11 +50,11 @@ func (r resource) loadFromBase(dir string) string {
}
result := string(b)
-
if runtime.GOOS != "windows" {
result = strings.ReplaceAll(result, "\n", "\r\n")
result = strings.ReplaceAll(result, "\r\r", "")
}
+
return result
}
diff --git a/_examples/file-server/embedding-files-into-app/assets/favicon.ico b/_examples/file-server/embedding-files-into-app/assets/favicon.ico
new file mode 100644
index 000000000..c370da518
Binary files /dev/null and b/_examples/file-server/embedding-files-into-app/assets/favicon.ico differ
diff --git a/_examples/file-server/embedding-files-into-app/main_test.go b/_examples/file-server/embedding-files-into-app/main_test.go
index ee3978906..03e32d338 100644
--- a/_examples/file-server/embedding-files-into-app/main_test.go
+++ b/_examples/file-server/embedding-files-into-app/main_test.go
@@ -50,6 +50,11 @@ func (r resource) loadFromBase(dir string) string {
}
result := string(b)
+ if runtime.GOOS != "windows" {
+ result = strings.ReplaceAll(result, "\n", "\r\n")
+ result = strings.ReplaceAll(result, "\r\r", "")
+ }
+
return result
}
diff --git a/_examples/logging/json-logger/main_test.go b/_examples/logging/json-logger/main_test.go
index 74c66cb38..f55376c3e 100644
--- a/_examples/logging/json-logger/main_test.go
+++ b/_examples/logging/json-logger/main_test.go
@@ -3,6 +3,9 @@ package main
import (
"bytes"
"encoding/json"
+ "fmt"
+ "path"
+ "runtime"
"strings"
"sync"
"testing"
@@ -30,7 +33,8 @@ func TestJSONLogger(t *testing.T) {
app.Get("/ping", ping)
- const expectedLogStr = `{"level":"debug","message":"Request path: /ping","fields":{"request_id":null},"stacktrace":[{"function":"json-logger/ping","source":"/home/runner/work/iris/iris/_examples/logging/json-logger/main.go:78"}]}` // gh actions-specific.
+ expectedSourceDir := getSourceDirPath()
+ expectedLogStr := fmt.Sprintf(`{"level":"debug","message":"Request path: /ping","fields":{"request_id":null},"stacktrace":[{"function":"json-logger/ping","source":"%s/main.go:78"}]}`, expectedSourceDir) // gh actions-specific.
e := httptest.New(t, app, httptest.LogLevel("debug"))
wg := new(sync.WaitGroup)
wg.Add(iters)
@@ -57,3 +61,12 @@ func TestJSONLogger(t *testing.T) {
}
}
}
+
+func getSourceDirPath() string {
+ _, file, _, ok := runtime.Caller(1) // get the caller's file.
+ if !ok {
+ return "unknown source"
+ }
+
+ return path.Dir(file) // get the directory of the file (delimiter: /).
+}
diff --git a/configuration.go b/configuration.go
index 0f2ee4cda..848354d93 100644
--- a/configuration.go
+++ b/configuration.go
@@ -7,7 +7,6 @@ import (
"path/filepath"
"runtime"
"strings"
- "sync"
"time"
"github.com/kataras/iris/v12/context"
@@ -977,25 +976,19 @@ type Configuration struct {
//
// Defaults to empty map.
Other map[string]interface{} `ini:"other" json:"other,omitempty" yaml:"Other" toml:"Other"`
-
- mu sync.RWMutex // mutex for some of the configuration fields that may change during parallel jobs (see Application.NonBlocking & Wait).
}
var _ context.ConfigurationReadOnly = (*Configuration)(nil)
-// GetVHost returns the non-exported VHost config field.
+// GetVHost returns the VHost config field.
func (c *Configuration) GetVHost() string {
- c.mu.RLock()
vhost := c.VHost
- c.mu.RUnlock()
return vhost
}
-// SetVHost sets the non-exported VHost config field.
+// SetVHost sets the VHost config field.
func (c *Configuration) SetVHost(s string) {
- c.mu.Lock()
c.VHost = s
- c.mu.Unlock()
}
// GetLogLevel returns the LogLevel field.
diff --git a/configuration_test.go b/configuration_test.go
index 213d0fb79..929b23a25 100644
--- a/configuration_test.go
+++ b/configuration_test.go
@@ -18,7 +18,7 @@ func TestConfigurationStatic(t *testing.T) {
afterNew := *app.config
if !reflect.DeepEqual(def, afterNew) {
- t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, afterNew)
+ t.Fatalf("Default configuration is not the same after New expected:\n %#v \ngot:\n %#v", def, afterNew)
}
afterNew.Charset = "changed"
@@ -37,7 +37,7 @@ func TestConfigurationStatic(t *testing.T) {
app = New() // empty , means defaults so
if !reflect.DeepEqual(def, *app.config) {
- t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, *app.config)
+ t.Fatalf("Default configuration is not the same after New expected:\n %#v \ngot:\n %#v", def, *app.config)
}
}
diff --git a/context/handler.go b/context/handler.go
index 42d4caa37..5939b3cec 100644
--- a/context/handler.go
+++ b/context/handler.go
@@ -12,7 +12,7 @@ import (
var (
// PackageName is the Iris Go module package name.
- PackageName = strings.TrimSuffix(reflect.TypeOf(Handlers{}).PkgPath(), "/context")
+ PackageName = strings.TrimSuffix(reflect.TypeOf(Context{}).PkgPath(), "/context")
// WorkingDir is the (initial) current directory.
WorkingDir, _ = os.Getwd()
diff --git a/core/host/proxy_test.go b/core/host/proxy_test.go
index 4b8ba34fb..258f33fcc 100644
--- a/core/host/proxy_test.go
+++ b/core/host/proxy_test.go
@@ -2,6 +2,7 @@
package host_test
import (
+ stdContext "context"
"crypto/tls"
"net"
"net/url"
@@ -30,6 +31,7 @@ func TestProxy(t *testing.T) {
MinVersion: tls.VersionTLS13,
}
proxy := host.NewProxy("", u, config)
+ proxy.Configure(host.NonBlocking())
addr := &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
@@ -41,10 +43,15 @@ func TestProxy(t *testing.T) {
t.Fatalf("%v while creating listener", err)
}
- go proxy.Serve(listener) // should be localhost/127.0.0.1:80 but travis throws permission denied.
+ // non-blocking (see above).
+ proxy.Serve(listener)
+
+ ctx, cancelFunc := stdContext.WithTimeout(stdContext.Background(), 15*time.Second)
+ defer cancelFunc()
+
+ // Wait for up to 15 seconds or until the proxy is ready to serve or a serve failure.
+ proxy.Wait(ctx)
- t.Log(listener.Addr().String())
- <-time.After(time.Second)
t.Log(listener.Addr().String())
app := iris.New()
@@ -60,7 +67,7 @@ func TestProxy(t *testing.T) {
ctx.WriteString(unexpectedRoute)
})
- l, err := net.Listen("tcp", "localhost:4444") // should be localhost/127.0.0.1:443 but travis throws permission denied.
+ l, err := net.Listen("tcp", "localhost:4444")
if err != nil {
t.Fatalf("%v while creating tcp4 listener for new tls local test listener", err)
}
diff --git a/core/host/supervisor.go b/core/host/supervisor.go
index e8ea77a67..93d6cf34e 100644
--- a/core/host/supervisor.go
+++ b/core/host/supervisor.go
@@ -25,6 +25,13 @@ import (
// Look the `Configure` func for more.
type Configurator func(su *Supervisor)
+// NonBlocking sets the server to non-blocking mode. Use its `Wait` method to wait for server to be up and running.
+func NonBlocking() Configurator {
+ return func(su *Supervisor) {
+ su.nonBlocking = true
+ }
+}
+
// Supervisor is the wrapper and the manager for a compatible server
// and it's relative actions, called Tasks.
//
@@ -34,14 +41,12 @@ type Supervisor struct {
// FriendlyAddr can be set to customize the "Now Listening on: {FriendlyAddr}".
FriendlyAddr string // e.g mydomain.com instead of :443 when AutoTLS is used, see `WriteStartupLogOnServe` task.
disableHTTP1ToHTTP2Redirection bool
- closedManually uint32 // future use, accessed atomically (non-zero means we've called the Shutdown)
- closedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose.
- manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin.
- autoTLS bool
- shouldWait int32 // non-zero means that the host should wait for unblocking
- unblockChan chan struct{}
- mu sync.Mutex
+ closedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose.
+ manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin.
+ autoTLS bool
+
+ mu sync.RWMutex
onServe []func(TaskHost)
// IgnoreErrors should contains the errors that should be ignored
@@ -73,6 +78,10 @@ type Supervisor struct {
// If more than zero then tcp keep alive listener is attached instead of the simple TCP listener.
// See `iris.Configuration.KeepAlive`
KeepAlive time.Duration
+
+ address string
+ nonBlocking bool
+ waiter *Waiter
}
// New returns a new host supervisor
@@ -83,10 +92,12 @@ type Supervisor struct {
// It has its own flow, which means that you can prevent
// to return and exit and restore the flow too.
func New(srv *http.Server) *Supervisor {
- return &Supervisor{
- Server: srv,
- unblockChan: make(chan struct{}, 1),
+ su := &Supervisor{
+ Server: srv,
}
+
+ su.waiter = NewWaiter(7, su.getAddress)
+ return su
}
// Configure accepts one or more `Configurator`.
@@ -113,36 +124,6 @@ func (su *Supervisor) NoRedirect() {
su.disableHTTP1ToHTTP2Redirection = true
}
-// DeferFlow defers the flow of the exeuction,
-// i.e: when server should return error and exit
-// from app, a DeferFlow call inside a Task
-// can wait for a `RestoreFlow` to exit or not exit if
-// host's server is "fixed".
-//
-// See `RestoreFlow` too.
-func (su *Supervisor) DeferFlow() {
- atomic.StoreInt32(&su.shouldWait, 1)
-}
-
-// RestoreFlow restores the flow of the execution,
-// if called without a `DeferFlow` call before
-// then it does nothing.
-// See tests to understand how that can be useful on specific cases.
-//
-// See `DeferFlow` too.
-func (su *Supervisor) RestoreFlow() {
- if su.isWaiting() {
- atomic.StoreInt32(&su.shouldWait, 0)
- su.mu.Lock()
- su.unblockChan <- struct{}{}
- su.mu.Unlock()
- }
-}
-
-func (su *Supervisor) isWaiting() bool {
- return atomic.LoadInt32(&su.shouldWait) != 0
-}
-
func (su *Supervisor) newListener() (net.Listener, error) {
var (
l net.Listener
@@ -198,14 +179,28 @@ func (su *Supervisor) validateErr(err error) error {
}
func (su *Supervisor) notifyErr(err error) {
- err = su.validateErr(err)
- if err != nil {
- su.mu.Lock()
- for _, f := range su.onErr {
- go f(err)
- }
- su.mu.Unlock()
+ if err == nil {
+ return
+ }
+
+ su.mu.Lock()
+ for _, f := range su.onErr {
+ go f(err)
}
+ su.mu.Unlock()
+}
+
+func (su *Supervisor) getAddress() string {
+ su.mu.RLock()
+ addr := su.address
+ su.mu.RUnlock()
+ return addr
+}
+
+func (su *Supervisor) setAddress(addr string) {
+ su.mu.Lock()
+ su.address = addr
+ su.mu.Unlock()
}
// RegisterOnServe registers a function to call on
@@ -224,27 +219,36 @@ func (su *Supervisor) notifyServe(host TaskHost) {
su.mu.Unlock()
}
-// Remove all channels, do it with events
-// or with channels but with a different channel on each task proc
-// I don't know channels are not so safe, when go func and race risk..
-// so better with callbacks....
func (su *Supervisor) supervise(blockFunc func() error) error {
host := createTaskHost(su)
su.notifyServe(host)
atomic.StoreUint32(&su.closedByInterruptHandler, 0)
- atomic.StoreUint32(&su.closedManually, 0)
+
+ if su.nonBlocking {
+ go func() {
+ err := blockFunc()
+ if err != nil {
+ su.waiter.Fail(err)
+ }
+
+ err = su.validateErr(err)
+ su.notifyErr(err)
+ }()
+
+ return nil
+ }
err := blockFunc()
+ err = su.validateErr(err)
su.notifyErr(err)
- if su.isWaiting() {
- for range su.unblockChan {
- break
- }
- }
+ return err
+}
- return su.validateErr(err)
+// Wait blocks until server is up and running or a serve failure.
+func (su *Supervisor) Wait(ctx context.Context) error {
+ return su.waiter.Wait(ctx)
}
// Serve accepts incoming connections on the Listener l, creating a
@@ -259,7 +263,11 @@ func (su *Supervisor) supervise(blockFunc func() error) error {
// Serve always returns a non-nil error. After Shutdown or Close, the
// returned error is http.ErrServerClosed.
func (su *Supervisor) Serve(l net.Listener) error {
- return su.supervise(func() error { return su.Server.Serve(l) })
+ su.setAddress(l.Addr().String())
+
+ return su.supervise(func() error {
+ return su.Server.Serve(l)
+ })
}
// ListenAndServe listens on the TCP network address addr
@@ -502,7 +510,6 @@ func (su *Supervisor) RegisterOnShutdown(cb func()) {
// separately notify such long-lived connections of shutdown and wait
// for them to close, if desired.
func (su *Supervisor) Shutdown(ctx context.Context) error {
- atomic.StoreUint32(&su.closedManually, 1) // future-use
if ctx == nil {
ctx = context.Background()
}
diff --git a/core/host/supervisor_task_example_test.go b/core/host/supervisor_task_example_test.go
index e2e1d127b..27eb63a67 100644
--- a/core/host/supervisor_task_example_test.go
+++ b/core/host/supervisor_task_example_test.go
@@ -4,10 +4,7 @@ package host
import (
"context"
"fmt"
- "log"
- "net"
"net/http"
- "os"
"time"
)
@@ -38,75 +35,3 @@ func ExampleSupervisor_RegisterOnError() {
// http: Server closed
// http: Server closed
}
-
-type myTestTask struct {
- restartEvery time.Duration
- maxRestarts int
- logger *log.Logger
-}
-
-func (m myTestTask) OnServe(host TaskHost) {
- host.Supervisor.DeferFlow() // don't exit on underline server's Shutdown.
-
- ticker := time.NewTicker(m.restartEvery)
- defer ticker.Stop()
- rans := 0
- for range ticker.C {
- exitAfterXRestarts := m.maxRestarts
- if rans == exitAfterXRestarts {
- m.logger.Println("exit")
- ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
- _ = host.Supervisor.Shutdown(ctx) // total shutdown
- host.Supervisor.RestoreFlow() // free to exit (if shutdown)
- cancel()
- 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)
- }
- })
-
- }
-}
-
-func ExampleSupervisor_RegisterOnServe() {
- h := New(&http.Server{
- Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- }),
- })
-
- logger := log.New(os.Stdout, "Supervisor: ", 0)
-
- mytask := myTestTask{
- restartEvery: 3 * time.Second,
- maxRestarts: 2,
- logger: logger,
- }
-
- h.RegisterOnServe(mytask.OnServe)
-
- ln, err := net.Listen("tcp4", ":9394")
- if err != nil {
- panic(err.Error())
- }
-
- logger.Println("server started...")
- h.Serve(ln)
-
- // Output:
- // Supervisor: server started...
- // Supervisor: closed 1 times
- // Supervisor: restart
- // Supervisor: closed 2 times
- // Supervisor: restart
- // Supervisor: exit
-}
diff --git a/core/host/supervisor_test.go b/core/host/supervisor_test.go
index c8b3f82b2..9f938efc6 100644
--- a/core/host/supervisor_test.go
+++ b/core/host/supervisor_test.go
@@ -35,7 +35,7 @@ func newTester(t *testing.T, baseURL string, handler http.Handler) *httpexpect.E
BaseURL: baseURL,
Client: &http.Client{
Transport: transporter,
- Jar: httpexpect.NewJar(),
+ Jar: httpexpect.NewCookieJar(),
},
Reporter: httpexpect.NewAssertReporter(t),
}
diff --git a/core/host/task.go b/core/host/task.go
index a7ea30516..b50f9783b 100644
--- a/core/host/task.go
+++ b/core/host/task.go
@@ -94,10 +94,10 @@ func WriteStartupLogOnServe(w io.Writer) func(TaskHost) {
// This function should be registered on Interrupt.
func ShutdownOnInterrupt(su *Supervisor, shutdownTimeout time.Duration) func() {
return func() {
- ctx, cancel := context.WithTimeout(context.TODO(), shutdownTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
+
su.shutdownOnInterrupt(ctx)
- su.RestoreFlow()
}
}
diff --git a/core/host/waiter.go b/core/host/waiter.go
new file mode 100644
index 000000000..85b4ec8f9
--- /dev/null
+++ b/core/host/waiter.go
@@ -0,0 +1,134 @@
+package host
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "net"
+ "sync"
+ "time"
+)
+
+// Waiter is a helper for waiting for a server to be up and running.
+type Waiter struct {
+ defaultMaxRetries int
+ addressFunc func() string
+
+ failure error // or runError for app.Run.
+ mu sync.RWMutex
+}
+
+// NewWaiter returns a new Waiter.
+func NewWaiter(defaultMaxRetries int, addressFunc func() string) *Waiter {
+ if defaultMaxRetries <= 0 {
+ defaultMaxRetries = 7 // 256 seconds max.
+ }
+
+ return &Waiter{
+ defaultMaxRetries: defaultMaxRetries,
+ addressFunc: addressFunc,
+ }
+}
+
+// Wait blocks the main goroutine until the application is up and running.
+func (w *Waiter) Wait(ctx context.Context) error {
+ // First check if there is an error already from Done.
+ if err := w.getFailure(); err != nil {
+ return err
+ }
+
+ // Set the base for exponential backoff.
+ base := 2.0
+
+ // Get the maximum number of retries by context or force to default max retries (e.g. 7).
+ var maxRetries int
+ // Get the deadline of the context.
+ if deadline, ok := ctx.Deadline(); ok {
+ now := time.Now()
+ timeout := deadline.Sub(now)
+
+ maxRetries = getMaxRetries(timeout, base)
+ } else {
+ maxRetries = w.defaultMaxRetries
+ }
+
+ // Set the initial retry interval.
+ retryInterval := time.Second
+
+ return w.tryConnect(ctx, w.addressFunc, maxRetries, retryInterval, base)
+}
+
+// getMaxRetries calculates the maximum number of retries from the retry interval and the base.
+func getMaxRetries(retryInterval time.Duration, base float64) int {
+ // Convert the retry interval to seconds.
+ seconds := retryInterval.Seconds()
+ // Apply the inverse formula.
+ retries := math.Log(seconds)/math.Log(base) - 1
+ return int(math.Round(retries))
+}
+
+// tryConnect tries to connect to the server with the given context and retry parameters.
+func (w *Waiter) tryConnect(ctx context.Context, addressFunc func() string, maxRetries int, retryInterval time.Duration, base float64) error {
+ // Try to connect to the server in a loop.
+ for i := 0; i < maxRetries; i++ {
+ // Check the context before each attempt.
+ select {
+ case <-ctx.Done():
+ // Context is canceled, return the context error.
+ return ctx.Err()
+ default:
+ address := addressFunc() // Get this server's listening address.
+ if address == "" {
+ i-- // Note that this may be modified at another go routine of the serve method. So it may be empty at first chance. So retry fetching the VHost every 1 second.
+ time.Sleep(time.Second)
+ continue
+ }
+
+ // Context is not canceled, proceed with the attempt.
+ conn, err := net.Dial("tcp", address)
+ if err == nil {
+ // Connection successful, close the connection and return nil.
+ conn.Close()
+ return nil // exit.
+ } // ignore error.
+
+ // Connection failed, wait for the retry interval and try again.
+ time.Sleep(retryInterval)
+ // After each failed attempt, check the server Run's error again.
+ if err := w.getFailure(); err != nil {
+ return err
+ }
+
+ // Increase the retry interval by the base raised to the power of the number of attempts.
+ /*
+ 0 2 seconds
+ 1 4 seconds
+ 2 8 seconds
+ 3 ~16 seconds
+ 4 ~32 seconds
+ 5 ~64 seconds
+ 6 ~128 seconds
+ 7 ~256 seconds
+ 8 ~512 seconds
+ ...
+ */
+ retryInterval = time.Duration(math.Pow(base, float64(i+1))) * time.Second
+ }
+ }
+ // All attempts failed, return an error.
+ return fmt.Errorf("failed to connect to the server after %d retries", maxRetries)
+}
+
+// Fail is called by the server's Run method when the server failed to start.
+func (w *Waiter) Fail(err error) {
+ w.mu.Lock()
+ w.failure = err
+ w.mu.Unlock()
+}
+
+func (w *Waiter) getFailure() error {
+ w.mu.RLock()
+ err := w.failure
+ w.mu.RUnlock()
+ return err
+}
diff --git a/doc.go b/doc.go
index b4b2c43cc..c7f64f167 100644
--- a/doc.go
+++ b/doc.go
@@ -10,7 +10,7 @@ Source code and other details for the project are available at GitHub:
# Current Version
-12.2.9
+12.2.10
# Installation
diff --git a/go.mod b/go.mod
index 1dfaaf59b..1f04f0c76 100644
--- a/go.mod
+++ b/go.mod
@@ -35,12 +35,12 @@ require (
github.com/redis/go-redis/v9 v9.4.0
github.com/schollz/closestmatch v2.1.0+incompatible
github.com/shirou/gopsutil/v3 v3.23.12
- github.com/tdewolff/minify/v2 v2.20.13
+ github.com/tdewolff/minify/v2 v2.20.14
github.com/vmihailenco/msgpack/v5 v5.4.1
github.com/yosssi/ace v0.0.5
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.18.0
- golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e
+ golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3
golang.org/x/net v0.20.0
golang.org/x/sys v0.16.0
golang.org/x/text v0.14.0
diff --git a/go.sum b/go.sum
index 8d2a74f35..1a417323e 100644
--- a/go.sum
+++ b/go.sum
@@ -116,6 +116,8 @@ github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20=
github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A=
github.com/kataras/jwt v0.1.12 h1:FHPgTTj5UqjlBye4PA4/oxknCY+kQ9K34XAi8d37glA=
github.com/kataras/jwt v0.1.12/go.mod h1:xkimAtDhU/aGlQqjwvgtg+VyuPwMiyZHaY8LJRh0mYo=
+github.com/kataras/neffos v0.0.23 h1:Jlbn7aK+pl/U/4vfDs1508+tlIdcjE5BdKFtzePsrBI=
+github.com/kataras/neffos v0.0.23/go.mod h1:3pzx6A5QUvwavU1RF0uIl5/TNbwyYswmfnfG5z8VBm8=
github.com/kataras/neffos v0.0.24-0.20240110215151-1db32f4ef9ed h1:iCsmmi2n7vUyWJ0NS1Wsu8N5oIS+C05fyIo3YTBQHMQ=
github.com/kataras/neffos v0.0.24-0.20240110215151-1db32f4ef9ed/go.mod h1:VLyfPmZh2Tbxjc2XKe1J0xwwKqiUD2zTA36Xcp+UIDs=
github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=
@@ -158,6 +160,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
+github.com/nats-io/nkeys v0.4.5 h1:Zdz2BUlFm4fJlierwvGK+yl20IAKUm7eV6AAZXEhkPk=
+github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
@@ -219,8 +223,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
-github.com/tdewolff/minify/v2 v2.20.13 h1:TDWS1orkBJjq6Sz9NjEvHEeUnAvlfU7jgStGQBwBPGM=
-github.com/tdewolff/minify/v2 v2.20.13/go.mod h1:qnIJbnG2dSzk7LIa/UUwgN2OjS8ir6RRlqc0T/1q2xY=
+github.com/tdewolff/minify/v2 v2.20.14 h1:sktSuVixRwk0ryQjqvKBu/uYS+MWmkwEFMEWtFZ+TdE=
+github.com/tdewolff/minify/v2 v2.20.14/go.mod h1:qnIJbnG2dSzk7LIa/UUwgN2OjS8ir6RRlqc0T/1q2xY=
github.com/tdewolff/parse/v2 v2.7.8 h1:1cnVqa8L63xFkc2vfRsZTM6Qy35nJpTvQ2Uvdv3vbvs=
github.com/tdewolff/parse/v2 v2.7.8/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
@@ -266,8 +270,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
-golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE=
-golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
+golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
+golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
diff --git a/httptest/httptest.go b/httptest/httptest.go
index 2ef74124c..1d6b302cd 100644
--- a/httptest/httptest.go
+++ b/httptest/httptest.go
@@ -134,7 +134,7 @@ func New(t IrisTesty, app *iris.Application, setters ...OptionSetter) *httpexpec
BaseURL: conf.URL,
Client: &http.Client{
Transport: httpexpect.NewBinder(app),
- Jar: httpexpect.NewJar(),
+ Jar: httpexpect.NewCookieJar(),
},
Reporter: reporter,
}
@@ -163,7 +163,7 @@ func NewInsecure(t IrisTesty, setters ...OptionSetter) *httpexpect.Expect {
BaseURL: conf.URL,
Client: &http.Client{
Transport: transport,
- Jar: httpexpect.NewJar(),
+ Jar: httpexpect.NewCookieJar(),
},
Reporter: httpexpect.NewAssertReporter(t),
}
diff --git a/iris.go b/iris.go
index 39c2b69a7..a045612e2 100644
--- a/iris.go
+++ b/iris.go
@@ -39,7 +39,7 @@ import (
)
// Version is the current version of the Iris Web Framework.
-const Version = "12.2.9"
+const Version = "12.2.10"
// Byte unit helpers.
const (
@@ -95,7 +95,7 @@ type Application struct {
// Users can wrap it to accept more events.
OnBuild func() error
- mu sync.Mutex
+ mu sync.RWMutex
// name is the application name and the log prefix for
// that Application instance's Logger. See `SetName` and `String`.
// Defaults to IRIS_APP_NAME envrinoment variable otherwise empty.
@@ -517,10 +517,38 @@ func (l *customHostServerLogger) Write(p []byte) (int, error) {
return l.parent.Write(p)
}
+// this may change during parallel jobs (see Application.NonBlocking & Wait).
+func (app *Application) getVHost() string {
+ app.mu.RLock()
+ vhost := app.config.VHost
+ app.mu.RUnlock()
+ return vhost
+}
+
+func (app *Application) setVHost(vhost string) {
+ app.mu.Lock()
+ app.config.VHost = vhost
+ app.mu.Unlock()
+}
+
// NewHost accepts a standard *http.Server object,
// completes the necessary missing parts of that "srv"
// and returns a new, ready-to-use, host (supervisor).
func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
+ if app.getVHost() == "" { // vhost now is useful for router subdomain on wildcard subdomains,
+ // in order to correct decide what to do on:
+ // mydomain.com -> invalid
+ // localhost -> invalid
+ // sub.mydomain.com -> valid
+ // sub.localhost -> valid
+ // we need the host (without port if 80 or 443) in order to validate these, so:
+ app.setVHost(netutil.ResolveVHost(srv.Addr))
+ } else {
+ context.GetDomain = func(_ string) string { // #1886
+ return app.config.VHost // GetVHost: here we don't need mutex protection as it's request-time and all modifications are already made.
+ }
+ } // before lock.
+
app.mu.Lock()
defer app.mu.Unlock()
@@ -551,21 +579,6 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
// create the new host supervisor
// bind the constructed server and return it
su := host.New(srv)
-
- if app.config.GetVHost() == "" { // vhost now is useful for router subdomain on wildcard subdomains,
- // in order to correct decide what to do on:
- // mydomain.com -> invalid
- // localhost -> invalid
- // sub.mydomain.com -> valid
- // sub.localhost -> valid
- // we need the host (without port if 80 or 443) in order to validate these, so:
- app.config.SetVHost(netutil.ResolveVHost(srv.Addr))
- } else {
- context.GetDomain = func(_ string) string { // #1886
- return app.config.VHost // GetVHost: here we don't need mutex protection as it's request-time and all modifications are already made.
- }
- }
-
// app.logger.Debugf("Host: virtual host is %s", app.config.VHost)
// the below schedules some tasks that will run among the server
@@ -1142,7 +1155,7 @@ func (app *Application) tryConnect(ctx stdContext.Context, maxRetries int, retry
// Context is canceled, return the context error.
return ctx.Err()
default:
- address := app.config.GetVHost() // Get this server's listening address.
+ address := app.getVHost() // Get this server's listening address.
if address == "" {
i-- // Note that this may be modified at another go routine of the serve method. So it may be empty at first chance. So retry fetching the VHost every 1 second.
time.Sleep(time.Second)
@@ -1200,7 +1213,7 @@ func (app *Application) tryStartTunneling() {
publicAddr := publicAddrs[0]
// to make subdomains resolution still based on this new remote, public addresses.
- app.config.SetVHost(publicAddr[strings.Index(publicAddr, "://")+3:])
+ app.setVHost(publicAddr[strings.Index(publicAddr, "://")+3:])
directLog := []byte(fmt.Sprintf("• Public Address: %s\n", publicAddr))
app.logger.Printer.Write(directLog) // nolint:errcheck