Merge pull request #1 from kataras/master

merge main
This commit is contained in:
Phước Trung 2022-08-29 17:04:45 +07:00 committed by GitHub
commit d8d170b775
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 348 additions and 48 deletions

View File

@ -17,7 +17,7 @@ jobs:
strategy: strategy:
matrix: matrix:
go_version: [1.18.x] go_version: [1.19.x]
steps: steps:
- name: Set up Go 1.x - name: Set up Go 1.x

View File

@ -28,6 +28,9 @@ The codebase for Dependency Injection, Internationalization and localization and
## Fixes and Improvements ## Fixes and Improvements
- Enable setting a custom "go-redis" client through `SetClient` go redis driver method or `Client` struct field on sessions/database/redis driver as requested at [chat](https://chat.iris-go.com).
- Ignore `"csrf.token"` form data key when missing on `ctx.ReadForm` by default as requested at [#1941](https://github.com/kataras/iris/issues/1941).
- Fix [CVE-2020-5398](https://github.com/advisories/GHSA-8wx2-9q48-vm9r). - Fix [CVE-2020-5398](https://github.com/advisories/GHSA-8wx2-9q48-vm9r).
- New `{x:weekday}` path parameter type, example code: - New `{x:weekday}` path parameter type, example code:

View File

@ -246,6 +246,7 @@ With your help, we can improve Open Source web development for everyone!
<a href="https://github.com/lexrus"><img src="https://avatars1.githubusercontent.com/u/219689?v=4" alt="lexrus" title="lexrus" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/lexrus"><img src="https://avatars1.githubusercontent.com/u/219689?v=4" alt="lexrus" title="lexrus" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/li3p"><img src="https://avatars1.githubusercontent.com/u/55519?v=4" alt="li3p" title="li3p" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/li3p"><img src="https://avatars1.githubusercontent.com/u/55519?v=4" alt="li3p" title="li3p" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/se77en"><img src="https://avatars1.githubusercontent.com/u/1468284?v=4" alt="se77en" title="se77en" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/se77en"><img src="https://avatars1.githubusercontent.com/u/1468284?v=4" alt="se77en" title="se77en" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/simpleittools"><img src="https://avatars1.githubusercontent.com/u/42871067?v=4" alt="simpleittools" title="simpleittools" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/sumjoe"><img src="https://avatars1.githubusercontent.com/u/32655210?v=4" alt="sumjoe" title="sumjoe" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/sumjoe"><img src="https://avatars1.githubusercontent.com/u/32655210?v=4" alt="sumjoe" title="sumjoe" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/vincent-li"><img src="https://avatars1.githubusercontent.com/u/765470?v=4" alt="vincent-li" title="vincent-li" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/vincent-li"><img src="https://avatars1.githubusercontent.com/u/765470?v=4" alt="vincent-li" title="vincent-li" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/sascha11110"><img src="https://avatars1.githubusercontent.com/u/15168372?v=4" alt="sascha11110" title="sascha11110" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/sascha11110"><img src="https://avatars1.githubusercontent.com/u/15168372?v=4" alt="sascha11110" title="sascha11110" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
@ -337,6 +338,7 @@ With your help, we can improve Open Source web development for everyone!
<a href="https://github.com/edwindna2"><img src="https://avatars1.githubusercontent.com/u/5441354?v=4" alt="edwindna2" title="edwindna2" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/edwindna2"><img src="https://avatars1.githubusercontent.com/u/5441354?v=4" alt="edwindna2" title="edwindna2" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/fenriz07"><img src="https://avatars1.githubusercontent.com/u/9199380?v=4" alt="fenriz07" title="fenriz07" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/fenriz07"><img src="https://avatars1.githubusercontent.com/u/9199380?v=4" alt="fenriz07" title="fenriz07" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/ffelipelimao"><img src="https://avatars1.githubusercontent.com/u/28612817?v=4" alt="ffelipelimao" title="ffelipelimao" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/ffelipelimao"><img src="https://avatars1.githubusercontent.com/u/28612817?v=4" alt="ffelipelimao" title="ffelipelimao" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/geGao123"><img src="https://avatars1.githubusercontent.com/u/6398228?v=4" alt="geGao123" title="geGao123" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/gnosthi"><img src="https://avatars1.githubusercontent.com/u/17650528?v=4" alt="gnosthi" title="gnosthi" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/gnosthi"><img src="https://avatars1.githubusercontent.com/u/17650528?v=4" alt="gnosthi" title="gnosthi" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/goten002"><img src="https://avatars1.githubusercontent.com/u/5025060?v=4" alt="goten002" title="goten002" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/goten002"><img src="https://avatars1.githubusercontent.com/u/5025060?v=4" alt="goten002" title="goten002" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/guanzi008"><img src="https://avatars1.githubusercontent.com/u/20619190?v=4" alt="guanzi008" title="guanzi008" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/guanzi008"><img src="https://avatars1.githubusercontent.com/u/20619190?v=4" alt="guanzi008" title="guanzi008" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
@ -368,6 +370,7 @@ With your help, we can improve Open Source web development for everyone!
<a href="https://github.com/ozfive"><img src="https://avatars1.githubusercontent.com/u/4494266?v=4" alt="ozfive" title="ozfive" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/ozfive"><img src="https://avatars1.githubusercontent.com/u/4494266?v=4" alt="ozfive" title="ozfive" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/paulxu21"><img src="https://avatars1.githubusercontent.com/u/6261758?v=4" alt="paulxu21" title="paulxu21" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/paulxu21"><img src="https://avatars1.githubusercontent.com/u/6261758?v=4" alt="paulxu21" title="paulxu21" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/pitt134"><img src="https://avatars1.githubusercontent.com/u/13091629?v=4" alt="pitt134" title="pitt134" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/pitt134"><img src="https://avatars1.githubusercontent.com/u/13091629?v=4" alt="pitt134" title="pitt134" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/qiepeipei"><img src="https://avatars1.githubusercontent.com/u/16110628?v=4" alt="qiepeipei" title="qiepeipei" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/qiuzhanghua"><img src="https://avatars1.githubusercontent.com/u/478393?v=4" alt="qiuzhanghua" title="qiuzhanghua" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/qiuzhanghua"><img src="https://avatars1.githubusercontent.com/u/478393?v=4" alt="qiuzhanghua" title="qiuzhanghua" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/rapita"><img src="https://avatars1.githubusercontent.com/u/22305375?v=4" alt="rapita" title="rapita" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/rapita"><img src="https://avatars1.githubusercontent.com/u/22305375?v=4" alt="rapita" title="rapita" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/relaera"><img src="https://avatars1.githubusercontent.com/u/26012106?v=4" alt="relaera" title="relaera" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/relaera"><img src="https://avatars1.githubusercontent.com/u/26012106?v=4" alt="relaera" title="relaera" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
@ -380,6 +383,7 @@ With your help, we can improve Open Source web development for everyone!
<a href="https://github.com/sbenimeli"><img src="https://avatars1.githubusercontent.com/u/46652122?v=4" alt="sbenimeli" title="sbenimeli" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/sbenimeli"><img src="https://avatars1.githubusercontent.com/u/46652122?v=4" alt="sbenimeli" title="sbenimeli" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/sebyno"><img src="https://avatars1.githubusercontent.com/u/15988169?v=4" alt="sebyno" title="sebyno" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/sebyno"><img src="https://avatars1.githubusercontent.com/u/15988169?v=4" alt="sebyno" title="sebyno" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/seun-otosho"><img src="https://avatars1.githubusercontent.com/u/74518370?v=4" alt="seun-otosho" title="seun-otosho" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/seun-otosho"><img src="https://avatars1.githubusercontent.com/u/74518370?v=4" alt="seun-otosho" title="seun-otosho" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/su1gen"><img src="https://avatars1.githubusercontent.com/u/86298730?v=4" alt="su1gen" title="su1gen" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/svirmi"><img src="https://avatars1.githubusercontent.com/u/52601346?v=4" alt="svirmi" title="svirmi" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/svirmi"><img src="https://avatars1.githubusercontent.com/u/52601346?v=4" alt="svirmi" title="svirmi" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/unixedia"><img src="https://avatars1.githubusercontent.com/u/70646128?v=4" alt="unixedia" title="unixedia" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/unixedia"><img src="https://avatars1.githubusercontent.com/u/70646128?v=4" alt="unixedia" title="unixedia" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/vguhesan"><img src="https://avatars1.githubusercontent.com/u/193960?v=4" alt="vguhesan" title="vguhesan" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/vguhesan"><img src="https://avatars1.githubusercontent.com/u/193960?v=4" alt="vguhesan" title="vguhesan" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
@ -393,6 +397,7 @@ With your help, we can improve Open Source web development for everyone!
<a href="https://github.com/mdamschen"><img src="https://avatars1.githubusercontent.com/u/40914728?v=4" alt="mdamschen" title="mdamschen" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/mdamschen"><img src="https://avatars1.githubusercontent.com/u/40914728?v=4" alt="mdamschen" title="mdamschen" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/mtrense"><img src="https://avatars1.githubusercontent.com/u/1008285?v=4" alt="mtrense" title="mtrense" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/mtrense"><img src="https://avatars1.githubusercontent.com/u/1008285?v=4" alt="mtrense" title="mtrense" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/netbaalzovf"><img src="https://avatars1.githubusercontent.com/u/98529711?v=4" alt="netbaalzovf" title="netbaalzovf" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/netbaalzovf"><img src="https://avatars1.githubusercontent.com/u/98529711?v=4" alt="netbaalzovf" title="netbaalzovf" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/oliverjosefzimmer"><img src="https://avatars1.githubusercontent.com/u/24566297?v=4" alt="oliverjosefzimmer" title="oliverjosefzimmer" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/lfaynman"><img src="https://avatars1.githubusercontent.com/u/16815068?v=4" alt="lfaynman" title="lfaynman" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/lfaynman"><img src="https://avatars1.githubusercontent.com/u/16815068?v=4" alt="lfaynman" title="lfaynman" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/ArturWierzbicki"><img src="https://avatars1.githubusercontent.com/u/23451458?v=4" alt="ArturWierzbicki" title="ArturWierzbicki" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/ArturWierzbicki"><img src="https://avatars1.githubusercontent.com/u/23451458?v=4" alt="ArturWierzbicki" title="ArturWierzbicki" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
<a href="https://github.com/NA"><img src="https://avatars1.githubusercontent.com/u/1600?v=4" alt="NA" title="NA" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a> <a href="https://github.com/NA"><img src="https://avatars1.githubusercontent.com/u/1600?v=4" alt="NA" title="NA" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
@ -470,7 +475,7 @@ $ go get github.com/kataras/iris/v12@master
**Run** **Run**
```sh ```sh
$ go mod tidy -compat=1.18 $ go mod tidy -compat=1.19
$ go run . $ go run .
``` ```

View File

@ -261,7 +261,7 @@ $ go get github.com/kataras/iris/v12@master
```txt ```txt
module myapp module myapp
go 1.18 go 1.19
require github.com/kataras/iris/v12 master require github.com/kataras/iris/v12 master
``` ```

View File

@ -55,6 +55,7 @@
* [Not Found - Intelligence](routing/intelligence/main.go) * [Not Found - Intelligence](routing/intelligence/main.go)
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go) * [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
* [Dynamic Path](routing/dynamic-path/main.go) * [Dynamic Path](routing/dynamic-path/main.go)
* [At-username](routing/dynamic-path/at-username/main.go)
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go) * [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
* [Implement a Parameter Type](routing/macros/main.go) * [Implement a Parameter Type](routing/macros/main.go)
* [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go) * [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go)

View File

@ -0,0 +1,30 @@
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.Writef("Hello %s", "world")
})
// This is an Iris-only feature across all web frameworks
// in every programming language for years.
// Dynamic Route Path Parameters Functions.
// Set min length characters to 2.
// Prefix of the username is '@'
// Otherwise 404.
//
// You can also use the regexp(...) function for more advanced expressions.
app.Get("/{username:string min(2) prefix(@)}", func(ctx iris.Context) {
username := ctx.Params().Get("username")[1:]
ctx.Writef("Username is %s", username)
})
// http://localhost:8080 -> FOUND (Hello world)
// http://localhost:8080/other -> NOT FOUND
// http://localhost:8080/@ -> NOT FOUND
// http://localhost:8080/@kataras -> FOUND (username is kataras)
app.Listen(":8080")
}

View File

@ -30,11 +30,16 @@ func main() {
Password: "", Password: "",
Database: "", Database: "",
Prefix: "myapp-", Prefix: "myapp-",
Driver: redis.GoRedis(), // defaults. Driver: redis.GoRedis(), // defaults to this driver.
// To set a custom, existing go-redis client, use the "SetClient" method:
// Driver: redis.GoRedis().SetClient(customGoRedisClient)
}) })
// Optionally configure the underline driver: // Optionally configure the underline driver:
// driver := redis.GoRedis() // driver := redis.GoRedis()
// // To set a custom client:
// driver.SetClient(customGoRedisClient)
// OR:
// driver.ClientOptions = redis.Options{...} // driver.ClientOptions = redis.Options{...}
// driver.ClusterOptions = redis.ClusterOptions{...} // driver.ClusterOptions = redis.ClusterOptions{...}
// redis.New(redis.Config{Driver: driver, ...}) // redis.New(redis.Config{Driver: driver, ...})

View File

@ -1012,6 +1012,10 @@ var GetDomain = func(hostport string) string {
// loopback. // loopback.
return "localhost" return "localhost"
default: default:
if net.ParseIP(host) != nil { // if it's an IP, see #1945.
return host
}
if domain, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil { if domain, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil {
host = domain host = domain
} }
@ -2575,8 +2579,12 @@ func (ctx *Context) RecordRequestBody(b bool) {
// IsRecordingBody reports whether the request body can be readen multiple times. // IsRecordingBody reports whether the request body can be readen multiple times.
func (ctx *Context) IsRecordingBody() bool { func (ctx *Context) IsRecordingBody() bool {
return ctx.values.GetBoolDefault(disableRequestBodyConsumptionContextKey, if ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal() {
ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal()) return true
}
value, _ := ctx.values.GetBool(disableRequestBodyConsumptionContextKey)
return value
} }
// GetBody reads and returns the request body. // GetBody reads and returns the request body.
@ -2672,7 +2680,20 @@ type JSONReader struct { // Note(@kataras): struct instead of optional funcs to
} }
var ReadJSON = func(ctx *Context, outPtr interface{}, opts ...JSONReader) error { var ReadJSON = func(ctx *Context, outPtr interface{}, opts ...JSONReader) error {
decoder := json.NewDecoder(ctx.request.Body) var body io.Reader
if ctx.IsRecordingBody() {
data, err := io.ReadAll(ctx.request.Body)
if err != nil {
return err
}
setBody(ctx.request, data)
body = bytes.NewReader(data)
} else {
body = ctx.request.Body
}
decoder := json.NewDecoder(body)
// decoder := gojson.NewDecoder(ctx.Request().Body) // decoder := gojson.NewDecoder(ctx.Request().Body)
if len(opts) > 0 { if len(opts) > 0 {
options := opts[0] options := opts[0]
@ -2778,6 +2799,24 @@ var (
// A shortcut for the `schema#IsErrPath`. // A shortcut for the `schema#IsErrPath`.
IsErrPath = schema.IsErrPath IsErrPath = schema.IsErrPath
// IsErrPathCRSFToken reports whether the given "err" is caused
// by unknown key error on "csrf.token". See `context#ReadForm` for more.
IsErrPathCRSFToken = func(err error) bool {
if err == nil || CSRFTokenFormKey == "" {
return false
}
if m, ok := err.(schema.MultiError); ok {
if csrfErr, hasCSRFToken := m[CSRFTokenFormKey]; hasCSRFToken {
_, is := csrfErr.(schema.UnknownKeyError)
return is
}
}
return false
}
// ErrEmptyForm is returned by // ErrEmptyForm is returned by
// - `context#ReadForm` // - `context#ReadForm`
// - `context#ReadQuery` // - `context#ReadQuery`
@ -2820,6 +2859,11 @@ var (
} }
) )
// CSRFTokenFormKey the CSRF token key of the form data.
//
// See ReadForm method for more.
const CSRFTokenFormKey = "csrf.token"
// ReadForm binds the request body of a form to the "formObject". // ReadForm binds the request body of a form to the "formObject".
// It supports any kind of type, including custom structs. // It supports any kind of type, including custom structs.
// It will return nothing if request data are empty. // It will return nothing if request data are empty.
@ -2831,6 +2875,9 @@ var (
// If a client sent an unknown field, this method will return an error, // If a client sent an unknown field, this method will return an error,
// in order to ignore that error use the `err != nil && !iris.IsErrPath(err)`. // in order to ignore that error use the `err != nil && !iris.IsErrPath(err)`.
// //
// As of 15 Aug 2022, ReadForm does not return an error over unknown CSRF token form key,
// to change this behavior globally, set the `context.CSRFTokenFormKey` to an empty value.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-form/main.go // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-form/main.go
func (ctx *Context) ReadForm(formObject interface{}) error { func (ctx *Context) ReadForm(formObject interface{}) error {
values := ctx.FormValues() values := ctx.FormValues()
@ -2842,7 +2889,7 @@ func (ctx *Context) ReadForm(formObject interface{}) error {
} }
err := schema.DecodeForm(values, formObject) err := schema.DecodeForm(values, formObject)
if err != nil { if err != nil && !IsErrPathCRSFToken(err) {
return err return err
} }

View File

@ -147,7 +147,7 @@ func (e *Error) As(target interface{}) bool {
} }
} }
return errors.As(e.Err, &te.Err) return errors.As(te.Err, &e)
} }
return ok return ok

View File

@ -32,13 +32,22 @@ func TestErrorIs(t *testing.T) {
} }
} }
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
func TestErrorAs(t *testing.T) { func TestErrorAs(t *testing.T) {
testErr := errors.New("as") testErr := &errorString{"as"}
err := &Error{Err: testErr} err := &Error{Err: testErr}
if expected, got := true, errors.As(err, &testErr); expected != got { if expected, got := true, errors.As(err, &testErr); expected != got {
t.Fatalf("[testErr as err] expected %v but got %v", expected, got) t.Fatalf("[testErr as err] expected %v but got %v", expected, got)
} }
if expected, got := true, errors.As(testErr, &err); expected != got { if expected, got := false, errors.As(testErr, &err); expected != got /* errorString does not implemeny As, so the std/default functionality will be applied */ {
t.Fatalf("[err as testErr] expected %v but got %v", expected, got) t.Fatalf("[err as testErr] expected %v but got %v", expected, got)
} }
} }

14
go.mod
View File

@ -1,13 +1,13 @@
module github.com/kataras/iris/v12 module github.com/kataras/iris/v12
go 1.18 go 1.19
// retract v12.1.8 // please update to @master // retract v12.1.8 // please update to @master
require ( require (
github.com/BurntSushi/toml v1.2.0 github.com/BurntSushi/toml v1.2.0
github.com/CloudyKit/jet/v6 v6.1.0 github.com/CloudyKit/jet/v6 v6.1.0
github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06
github.com/andybalholm/brotli v1.0.4 github.com/andybalholm/brotli v1.0.4
github.com/blang/semver/v4 v4.0.0 github.com/blang/semver/v4 v4.0.0
github.com/dgraph-io/badger/v2 v2.2007.4 github.com/dgraph-io/badger/v2 v2.2007.4
@ -40,11 +40,11 @@ require (
github.com/vmihailenco/msgpack/v5 v5.3.5 github.com/vmihailenco/msgpack/v5 v5.3.5
github.com/yosssi/ace v0.0.5 github.com/yosssi/ace v0.0.5
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/net v0.0.0-20220812174116-3211cb980234
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20220411224347-583f2d630306 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
google.golang.org/protobuf v1.28.1 google.golang.org/protobuf v1.28.1
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@ -81,7 +81,7 @@ require (
github.com/minio/highwayhash v1.0.2 // indirect github.com/minio/highwayhash v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nats-io/jwt/v2 v2.2.0 // indirect github.com/nats-io/jwt/v2 v2.3.0 // indirect
github.com/nats-io/nats.go v1.16.0 // indirect github.com/nats-io/nats.go v1.16.0 // indirect
github.com/nats-io/nkeys v0.3.0 // indirect github.com/nats-io/nkeys v0.3.0 // indirect
github.com/nats-io/nuid v1.0.1 // indirect github.com/nats-io/nuid v1.0.1 // indirect

23
go.sum generated
View File

@ -9,8 +9,8 @@ github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f h1:XeOBnoBP7K19tMBEKeUo1NOxOO+h5FFi2HGzQvvkb44= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@ -150,8 +150,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nats-io/jwt/v2 v2.2.0 h1:Yg/4WFK6vsqMudRg91eBb7Dh6XeVcDMPHycDE8CfltE= github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
github.com/nats-io/jwt/v2 v2.2.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4= github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g= github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
@ -245,13 +245,13 @@ go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -266,15 +266,16 @@ golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -47,7 +47,14 @@ func (g GRPC) Apply(c *ControllerActivator) {
return return
} }
// If strict was false, allow common HTTP clients, consumes and produces JSON. // If strict was true fires 404 on common HTTP clients.
if g.Strict {
ctx.NotFound()
ctx.StopExecution()
return
}
// Allow common HTTP clients, consumes and produces JSON.
ctx.Next() ctx.Next()
} }

View File

@ -30,6 +30,7 @@ type Config struct {
Addr string Addr string
// Clusters a list of network addresses for clusters. // Clusters a list of network addresses for clusters.
// If not empty "Addr" is ignored and Redis clusters feature is used instead. // If not empty "Addr" is ignored and Redis clusters feature is used instead.
// Note that this field is ignored when setgging a custom `GoRedisClient`.
Clusters []string Clusters []string
// Use the specified Username to authenticate the current connection // Use the specified Username to authenticate the current connection
// with one of the connections defined in the ACL list when connecting // with one of the connections defined in the ACL list when connecting
@ -267,7 +268,8 @@ 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) error { func (db *Database) Clear(sid string) error {
keys := db.keys(db.makeSID(sid)) sid = db.makeSID(sid)
keys := db.keys(sid)
for _, key := range keys { for _, key := range keys {
if key == SessionIDKey { if key == SessionIDKey {
continue continue

View File

@ -27,7 +27,9 @@ type GoRedisClient interface {
// for the go-redis redis driver. See driver.go file. // for the go-redis redis driver. See driver.go file.
type GoRedisDriver struct { type GoRedisDriver struct {
// Both Client and ClusterClient implements this interface. // Both Client and ClusterClient implements this interface.
client GoRedisClient // Custom one can be directly passed but if so, the
// Connect method does nothing (so all connection and client settings are ignored).
Client GoRedisClient
// Customize any go-redis fields manually // Customize any go-redis fields manually
// before Connect. // before Connect.
ClientOptions Options ClientOptions Options
@ -111,12 +113,24 @@ func (r *GoRedisDriver) mergeClusterOptions(c Config) *ClusterOptions {
return &opts return &opts
} }
// SetClient sets an existing go redis client to the sessions redis driver.
//
// Returns itself.
func (r *GoRedisDriver) SetClient(goRedisClient GoRedisClient) *GoRedisDriver {
r.Client = goRedisClient
return r
}
// Connect initializes the redis client. // Connect initializes the redis client.
func (r *GoRedisDriver) Connect(c Config) error { func (r *GoRedisDriver) Connect(c Config) error {
if r.Client != nil { // if a custom one was given through SetClient.
return nil
}
if len(c.Clusters) > 0 { if len(c.Clusters) > 0 {
r.client = redis.NewClusterClient(r.mergeClusterOptions(c)) r.Client = redis.NewClusterClient(r.mergeClusterOptions(c))
} else { } else {
r.client = redis.NewClient(r.mergeClientOptions(c)) r.Client = redis.NewClient(r.mergeClientOptions(c))
} }
return nil return nil
@ -125,29 +139,29 @@ func (r *GoRedisDriver) Connect(c Config) error {
// PingPong sends a ping message and reports whether // PingPong sends a ping message and reports whether
// the PONG message received successfully. // the PONG message received successfully.
func (r *GoRedisDriver) PingPong() (bool, error) { func (r *GoRedisDriver) PingPong() (bool, error) {
pong, err := r.client.Ping(defaultContext).Result() pong, err := r.Client.Ping(defaultContext).Result()
return pong == "PONG", err return pong == "PONG", err
} }
// CloseConnection terminates the underline redis connection. // CloseConnection terminates the underline redis connection.
func (r *GoRedisDriver) CloseConnection() error { func (r *GoRedisDriver) CloseConnection() error {
return r.client.Close() return r.Client.Close()
} }
// Set stores a "value" based on the session's "key". // Set stores a "value" based on the session's "key".
// The value should be type of []byte, so unmarshal can happen. // The value should be type of []byte, so unmarshal can happen.
func (r *GoRedisDriver) Set(sid, key string, value interface{}) error { func (r *GoRedisDriver) Set(sid, key string, value interface{}) error {
return r.client.HSet(defaultContext, sid, key, value).Err() return r.Client.HSet(defaultContext, sid, key, value).Err()
} }
// Get returns the associated value of the session's given "key". // Get returns the associated value of the session's given "key".
func (r *GoRedisDriver) Get(sid, key string) (interface{}, error) { func (r *GoRedisDriver) Get(sid, key string) (interface{}, error) {
return r.client.HGet(defaultContext, sid, key).Bytes() return r.Client.HGet(defaultContext, sid, key).Bytes()
} }
// Exists reports whether a session exists or not. // Exists reports whether a session exists or not.
func (r *GoRedisDriver) Exists(sid string) bool { func (r *GoRedisDriver) Exists(sid string) bool {
n, err := r.client.Exists(defaultContext, sid).Result() n, err := r.Client.Exists(defaultContext, sid).Result()
if err != nil { if err != nil {
return false return false
} }
@ -157,7 +171,7 @@ func (r *GoRedisDriver) Exists(sid string) bool {
// TTL returns any TTL value of the session. // TTL returns any TTL value of the session.
func (r *GoRedisDriver) TTL(sid string) time.Duration { func (r *GoRedisDriver) TTL(sid string) time.Duration {
dur, err := r.client.TTL(defaultContext, sid).Result() dur, err := r.Client.TTL(defaultContext, sid).Result()
if err != nil { if err != nil {
return 0 return 0
} }
@ -167,29 +181,29 @@ func (r *GoRedisDriver) TTL(sid string) time.Duration {
// UpdateTTL sets expiration duration of the session. // UpdateTTL sets expiration duration of the session.
func (r *GoRedisDriver) UpdateTTL(sid string, newLifetime time.Duration) error { func (r *GoRedisDriver) UpdateTTL(sid string, newLifetime time.Duration) error {
_, err := r.client.Expire(defaultContext, sid, newLifetime).Result() _, err := r.Client.Expire(defaultContext, sid, newLifetime).Result()
return err return err
} }
// GetAll returns all the key values under the session. // GetAll returns all the key values under the session.
func (r *GoRedisDriver) GetAll(sid string) (map[string]string, error) { func (r *GoRedisDriver) GetAll(sid string) (map[string]string, error) {
return r.client.HGetAll(defaultContext, sid).Result() return r.Client.HGetAll(defaultContext, sid).Result()
} }
// GetKeys returns all keys under the session. // GetKeys returns all keys under the session.
func (r *GoRedisDriver) GetKeys(sid string) ([]string, error) { func (r *GoRedisDriver) GetKeys(sid string) ([]string, error) {
return r.client.HKeys(defaultContext, sid).Result() return r.Client.HKeys(defaultContext, sid).Result()
} }
// Len returns the total length of key-values of the session. // Len returns the total length of key-values of the session.
func (r *GoRedisDriver) Len(sid string) int { func (r *GoRedisDriver) Len(sid string) int {
return int(r.client.HLen(defaultContext, sid).Val()) return int(r.Client.HLen(defaultContext, sid).Val())
} }
// Delete removes a value from the redis store. // Delete removes a value from the redis store.
func (r *GoRedisDriver) Delete(sid, key string) error { func (r *GoRedisDriver) Delete(sid, key string) error {
if key == "" { if key == "" {
return r.client.Del(defaultContext, sid).Err() return r.Client.Del(defaultContext, sid).Err()
} }
return r.client.HDel(defaultContext, sid, key).Err() return r.Client.HDel(defaultContext, sid, key).Err()
} }

View File

@ -1,18 +1,34 @@
package jsonx package jsonx
import ( import (
"database/sql/driver"
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
) )
var fixedEastUTCLocations = make(map[int]*time.Location)
func registerFixedEastUTCLocation(name string, secondsFromUTC int) {
loc := time.FixedZone(name, secondsFromUTC)
fixedEastUTCLocations[secondsFromUTC] = loc
}
func init() {
registerFixedEastUTCLocation("EEST", 3*60*60) // + 3 hours.
}
const ( const (
// ISO8601Layout holds the time layout for the the javascript iso time. // ISO8601Layout holds the time layout for the the javascript iso time.
// Read more at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString. // Read more at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString.
ISO8601Layout = "2006-01-02T15:04:05" ISO8601Layout = "2006-01-02T15:04:05"
// ISO8601ZLayout same as ISO8601Layout but with the timezone suffix. // ISO8601ZLayout same as ISO8601Layout but with the timezone suffix.
ISO8601ZLayout = "2006-01-02T15:04:05Z" ISO8601ZLayout = "2006-01-02T15:04:05Z"
// ISO8601ZUTCOffsetLayout ISO 8601 format, with full time and zone with UTC offset.
// Example: 2022-08-10T03:21:00.000000+03:00, 2022-08-09T00:00:00.000000.
ISO8601ZUTCOffsetLayout = "2006-01-02T15:04:05.999999Z07:00"
) )
// ISO8601 describes a time compatible with javascript time format. // ISO8601 describes a time compatible with javascript time format.
@ -29,7 +45,28 @@ func ParseISO8601(s string) (ISO8601, error) {
err error err error
) )
if s[len(s)-1] == 'Z' { if idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 20 /* should have some distance, e.g. 26 */ {
length := parseSignedOffset(s[idx:])
if idx+1 > idx+length || len(s) <= idx+length+1 {
return ISO8601{}, fmt.Errorf("ISO8601: invalid timezone format: %s", s[idx:])
}
offsetText := s[idx+1 : idx+length]
offset, parseErr := strconv.Atoi(offsetText)
if parseErr != nil {
return ISO8601{}, err
}
// E.g. offset of +0300 is returned as 10800 which is - (3 * 60 * 60).
secondsEastUTC := offset * 60 * 60
if loc, ok := fixedEastUTCLocations[secondsEastUTC]; ok { // Specific (fixed) zone.
tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayout, s, loc)
} else { // Local or UTC.
tt, err = time.Parse(ISO8601ZUTCOffsetLayout, s)
}
} else if s[len(s)-1] == 'Z' {
tt, err = time.Parse(ISO8601ZLayout, s) tt, err = time.Parse(ISO8601ZLayout, s)
} else { } else {
tt, err = time.Parse(ISO8601Layout, s) tt, err = time.Parse(ISO8601Layout, s)
@ -39,7 +76,7 @@ func ParseISO8601(s string) (ISO8601, error) {
return ISO8601{}, err return ISO8601{}, err
} }
return ISO8601(tt.UTC()), nil return ISO8601(tt), nil
} }
// UnmarshalJSON parses the "b" into ISO8601 time. // UnmarshalJSON parses the "b" into ISO8601 time.
@ -90,6 +127,11 @@ func (t ISO8601) String() string {
return tt.Format(ISO8601Layout) return tt.Format(ISO8601Layout)
} }
// Value returns the database value of time.Time.
func (t ISO8601) Value() (driver.Value, error) {
return time.Time(t), nil
}
// Scan completes the sql driver.Scanner interface. // Scan completes the sql driver.Scanner interface.
func (t *ISO8601) Scan(src interface{}) error { func (t *ISO8601) Scan(src interface{}) error {
switch v := src.(type) { switch v := src.(type) {
@ -104,6 +146,8 @@ func (t *ISO8601) Scan(src interface{}) error {
return err return err
} }
*t = tt *t = tt
case []byte:
return t.Scan(string(v))
case nil: case nil:
*t = ISO8601(time.Time{}) *t = ISO8601(time.Time{})
default: default:
@ -112,3 +156,54 @@ func (t *ISO8601) Scan(src interface{}) error {
return nil return nil
} }
// parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").
// The function checks for a signed number in the range -23 through +23 excluding zero.
// Returns length of the found offset string or 0 otherwise.
//
// Language internal function.
func parseSignedOffset(value string) int {
sign := value[0]
if sign != '-' && sign != '+' {
return 0
}
x, rem, err := leadingInt(value[1:])
// fail if nothing consumed by leadingInt
if err != nil || value[1:] == rem {
return 0
}
if x > 23 {
return 0
}
return len(value) - len(rem)
}
var errLeadingInt = errors.New("ISO8601: time: bad [0-9]*") // never printed.
// leadingInt consumes the leading [0-9]* from s.
//
// Language internal function.
func leadingInt(s string) (x uint64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x > 1<<63/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + uint64(c) - '0'
if x > 1<<63 {
// overflow
return 0, "", errLeadingInt
}
}
return x, s[i:], nil
}
func startUTCOffsetIndexFunc(char rune) bool {
return char == '+' || char == '-'
}

81
x/jsonx/iso8601_test.go Normal file
View File

@ -0,0 +1,81 @@
package jsonx
import (
"encoding/json"
"testing"
"time"
)
func TestISO8601(t *testing.T) {
data := `{"start": "2021-08-20T10:05:01", "end": "2021-12-01T17:05:06", "nothing": null, "empty": ""}`
v := struct {
Start ISO8601 `json:"start"`
End ISO8601 `json:"end"`
Nothing ISO8601 `json:"nothing"`
Empty ISO8601 `json:"empty"`
}{}
err := json.Unmarshal([]byte(data), &v)
if err != nil {
t.Fatal(err)
}
if !v.Nothing.IsZero() {
t.Fatalf("expected 'nothing' to be zero but got: %v", v.Nothing)
}
if !v.Empty.IsZero() {
t.Fatalf("expected 'empty' to be zero but got: %v", v.Empty)
}
loc := time.UTC
if expected, got := time.Date(2021, time.August, 20, 10, 5, 1, 0, loc), v.Start.ToTime(); expected != got {
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
}
if expected, got := time.Date(2021, time.December, 1, 17, 5, 6, 0, loc), v.End.ToTime(); expected != got {
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
}
}
func TestISO8601WithZoneUTCOffset(t *testing.T) {
data := `{"start": "2022-08-10T03:21:00.000000+03:00", "end": "2022-08-10T09:49:00.000000+03:00", "nothing": null, "empty": ""}`
v := struct {
Start ISO8601 `json:"start"`
End ISO8601 `json:"end"`
Nothing ISO8601 `json:"nothing"`
Empty ISO8601 `json:"empty"`
}{}
err := json.Unmarshal([]byte(data), &v)
if err != nil {
t.Fatalf("unmarshal: %v", err)
}
// t.Logf("Start: %s, location: %s\n", v.Start.String(), v.Start.ToTime().Location().String())
if !v.Nothing.IsZero() {
t.Fatalf("expected 'nothing' to be zero but got: %v", v.Nothing)
}
if !v.Empty.IsZero() {
t.Fatalf("expected 'empty' to be zero but got: %v", v.Empty)
}
loc := time.FixedZone("EEST", 10800)
if expected, got := time.Date(2022, time.August, 10, 3, 21, 0, 0, loc).String(), v.Start.ToTime().String(); expected != got {
t.Fatalf("expected 'start' string to be: %v but got: %v", expected, got)
}
if expected, got := time.Date(2022, time.August, 10, 9, 49, 0, 0, loc).String(), v.End.ToTime().String(); expected != got {
t.Fatalf("expected 'start' string to be: %v but got: %v", expected, got)
}
if expected, got := time.Date(2022, time.August, 10, 3, 21, 0, 0, loc), v.Start.ToTime().In(loc); expected != got {
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
}
if expected, got := time.Date(2022, time.August, 10, 9, 49, 0, 0, loc), v.End.ToTime().In(loc); expected != got {
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
}
}