diff --git a/HISTORY.md b/HISTORY.md index 74f6b150..6b331b2d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -28,6 +28,7 @@ The codebase for Dependency Injection, Internationalization and localization and ## Fixes and Improvements +- Make the `Context.JSON` method customizable by modifying the `context.WriteJSON` package-level function. - Add new `iris.NewGuide` which helps you build a simple and nice JSON API with services as dependencies and better design pattern. - Make `Context.Domain()` customizable by letting developers to modify the `Context.GetDomain` package-level function. - Remove Request Context-based Transaction feature as its usage can be replaced with just the Iris Context (as of go1.7+) and better [project](_examples/project) structure. diff --git a/LICENSE b/LICENSE index 1bc2959b..24efccb8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,7 @@ -Copyright (c) 2017-2022 The Iris Authors. All rights reserved. +Copyright (c) 2017-2022, The Iris Authors. All rights reserved. + +The Iris Authors: + * Gerasimos (Makis) Maropoulos Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/README.md b/README.md index 9956bbfb..997ac5f2 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ Iris is a fast, simple yet fully featured and very efficient web framework for Go. -It provides a [beautifully](iris_guide.go#L31-L44) expressive and easy to use foundation for your next website or API. +It provides a beautifully expressive and easy to use foundation for your next website or API. + ```go package main @@ -27,18 +28,18 @@ package main import "github.com/kataras/iris/v12" func main() { - app := iris.New() - app.Use(iris.Compression) + app := iris.New() + app.Use(iris.Compression) - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Hello %s!", "World") - }) + app.Get("/", func(ctx iris.Context) { + ctx.HTML("Hello %s!", "World") + }) - app.Listen(":8080") + app.Listen(":8080") } ``` -
More with simple Handler + + +As one [Go developer](https://twitter.com/dkuye/status/1532087942696554497) once said, **Iris got you covered all-round and standing strong over the years**. + +Some of the features Iris offers: + +* HTTP/2 (Push, even Embedded data) +* Middleware (Accesslog, Basicauth, CORS, gRPC, Anti-Bot hCaptcha, JWT, MethodOverride, ModRevision, Monitor, PPROF, Ratelimit, Anti-Bot reCaptcha, Recovery, RequestID, Rewrite) +* API Versioning +* Model-View-Controller +* Websockets +* gRPC +* Auto-HTTPS +* Builtin support for ngrok to put your app on the internet, the fastest way +* Unique Router with dynamic path as parameter with standard types like :uuid, :string, :int... and the ability to create your own +* Compression +* View Engines (HTML, Django, Amber, Handlebars, Pug/Jade and more) +* Create your own File Server and host your own WebDAV server +* Cache +* Localization (i18n, sitemap) +* Sessions +* Rich Responses (HTML, Text, Markdown, XML, YAML, Binary, JSON, JSONP, Protocol Buffers, MessagePack, Content Negotiation, Streaming, Server-Sent Events and more) +* Response Compression (gzip, deflate, brotli, snappy, s2) +* Rich Requests (Bind URL Query, Headers, Form, Text, XML, YAML, Binary, JSON, Validation, Protocol Buffers, MessagePack and more) +* Dependency Injection (MVC, Handlers, API Routers) +* Testing Suite +* And the most important... you get fast answers and support from the 1st day until now - that's six full years! + Learn what [others saying about Iris](https://www.iris-go.com/#review) and **[star](https://github.com/kataras/iris/stargazers)** this open-source project to support its potentials. [![](https://iris-go.com/images/reviews.gif)](https://iris-go.com/testimonials/) diff --git a/_examples/auth/jwt/tutorial/go-client/client.go b/_examples/auth/jwt/tutorial/go-client/client.go index e96b769f..9ca27c9d 100644 --- a/_examples/auth/jwt/tutorial/go-client/client.go +++ b/_examples/auth/jwt/tutorial/go-client/client.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "strconv" @@ -105,5 +104,5 @@ func BindResponse(resp *http.Response, dest interface{}) error { func RawResponse(resp *http.Response) ([]byte, error) { defer resp.Body.Close() - return ioutil.ReadAll(resp.Body) + return io.ReadAll(resp.Body) } diff --git a/_examples/compression/client-using-iris/main.go b/_examples/compression/client-using-iris/main.go index 65d97131..0083b571 100644 --- a/_examples/compression/client-using-iris/main.go +++ b/_examples/compression/client-using-iris/main.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "github.com/kataras/iris/v12/context" @@ -51,7 +51,7 @@ func getExample() { } defer cr.Close() - body, err := ioutil.ReadAll(cr) + body, err := io.ReadAll(cr) if err != nil { panic(err) } @@ -103,7 +103,7 @@ func postExample() { } defer cr.Close() - body, err := ioutil.ReadAll(cr) + body, err := io.ReadAll(cr) if err != nil { panic(err) } diff --git a/_examples/compression/client/main.go b/_examples/compression/client/main.go index 6c39294a..0ee40aa3 100644 --- a/_examples/compression/client/main.go +++ b/_examples/compression/client/main.go @@ -5,7 +5,7 @@ import ( "compress/gzip" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" ) @@ -42,7 +42,7 @@ func getExample() { } defer r.Close() - body, err := ioutil.ReadAll(r) + body, err := io.ReadAll(r) if err != nil { panic(err) } @@ -93,7 +93,7 @@ func postExample() { } defer r.Close() - body, err := ioutil.ReadAll(r) + body, err := io.ReadAll(r) if err != nil { panic(err) } diff --git a/_examples/desktop/blink/main.go b/_examples/desktop/blink/main.go index e0c3fca6..866bdd7b 100644 --- a/_examples/desktop/blink/main.go +++ b/_examples/desktop/blink/main.go @@ -11,8 +11,8 @@ import ( const addr = "127.0.0.1:8080" /* - $ go build -mod=mod -ldflags -H=windowsgui -o myapp.exe - $ ./myapp.exe # run the app +$ go build -mod=mod -ldflags -H=windowsgui -o myapp.exe +$ ./myapp.exe # run the app */ func main() { go runServer() diff --git a/_examples/desktop/lorca/main.go b/_examples/desktop/lorca/main.go index dfd001dc..a59b9623 100644 --- a/_examples/desktop/lorca/main.go +++ b/_examples/desktop/lorca/main.go @@ -8,8 +8,8 @@ import ( const addr = "127.0.0.1:8080" /* - $ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows - $ ./myapp.exe # run +$ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows +$ ./myapp.exe # run */ func main() { go runServer() diff --git a/_examples/desktop/webview/main.go b/_examples/desktop/webview/main.go index a8bab602..6272860b 100644 --- a/_examples/desktop/webview/main.go +++ b/_examples/desktop/webview/main.go @@ -8,21 +8,21 @@ import ( const addr = "127.0.0.1:8080" /* - # Windows requires special linker flags for GUI apps. - # It's also recommended to use TDM-GCC-64 compiler for CGo. - # http://tdm-gcc.tdragon.net/download - # - # - $ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows - $ ./myapp.exe # run - # - # MacOS uses app bundles for GUI apps - $ mkdir -p example.app/Contents/MacOS - $ go build -o example.app/Contents/MacOS/example - $ open example.app # Or click on the app in Finder - # - # Note: if you see "use option -std=c99 or -std=gnu99 to compile your code" - # please refer to: https://github.com/webview/webview/issues/188 +# Windows requires special linker flags for GUI apps. +# It's also recommended to use TDM-GCC-64 compiler for CGo. +# http://tdm-gcc.tdragon.net/download +# +# +$ go build -mod=mod -ldflags="-H windowsgui" -o myapp.exe # build for windows +$ ./myapp.exe # run +# +# MacOS uses app bundles for GUI apps +$ mkdir -p example.app/Contents/MacOS +$ go build -o example.app/Contents/MacOS/example +$ open example.app # Or click on the app in Finder +# +# Note: if you see "use option -std=c99 or -std=gnu99 to compile your code" +# please refer to: https://github.com/webview/webview/issues/188 */ func main() { go runServer() diff --git a/_examples/file-server/basic/main_test.go b/_examples/file-server/basic/main_test.go index 23db8229..a27067bb 100644 --- a/_examples/file-server/basic/main_test.go +++ b/_examples/file-server/basic/main_test.go @@ -1,7 +1,7 @@ package main import ( - "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -47,7 +47,7 @@ func (r resource) loadFromBase(dir string, strip string) string { fullpath := filepath.Join(dir, filename) - b, err := ioutil.ReadFile(fullpath) + b, err := os.ReadFile(fullpath) if err != nil { panic(fullpath + " failed with error: " + err.Error()) } diff --git a/_examples/file-server/embedding-files-into-app/bindata.go b/_examples/file-server/embedding-files-into-app/bindata.go index b6f638f4..80b96bf4 100644 --- a/_examples/file-server/embedding-files-into-app/bindata.go +++ b/_examples/file-server/embedding-files-into-app/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package main generated by go-bindata.// sources: +// Package main generated by go-bindata.// sources: // assets/css/main.css // assets/favicon.ico // assets/js/main.js @@ -11,7 +11,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -288,11 +287,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -348,7 +349,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } 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 94745c11..aa6eda89 100644 --- a/_examples/file-server/embedding-files-into-app/main_test.go +++ b/_examples/file-server/embedding-files-into-app/main_test.go @@ -1,7 +1,7 @@ package main import ( - "io/ioutil" + "os" "path/filepath" "runtime" "strings" @@ -46,7 +46,7 @@ func (r resource) loadFromBase(dir string) string { fullpath := filepath.Join(dir, filename) - b, err := ioutil.ReadFile(fullpath) + b, err := os.ReadFile(fullpath) if err != nil { panic(fullpath + " failed with error: " + err.Error()) } diff --git a/_examples/file-server/embedding-gzipped-files-into-app/bindata.go b/_examples/file-server/embedding-gzipped-files-into-app/bindata.go index 1c5b2f45..4c8c93b4 100644 --- a/_examples/file-server/embedding-gzipped-files-into-app/bindata.go +++ b/_examples/file-server/embedding-gzipped-files-into-app/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package main generated by go-bindata.// sources: +// Package main generated by go-bindata.// sources: // ../embedding-files-into-app/assets/css/main.css // ../embedding-files-into-app/assets/favicon.ico // ../embedding-files-into-app/assets/js/main.js @@ -11,7 +11,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -288,11 +287,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -348,7 +349,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/file-server/embedding-gzipped-files-into-app/main_test.go b/_examples/file-server/embedding-gzipped-files-into-app/main_test.go index cf92338b..0e678f0d 100644 --- a/_examples/file-server/embedding-gzipped-files-into-app/main_test.go +++ b/_examples/file-server/embedding-gzipped-files-into-app/main_test.go @@ -2,7 +2,7 @@ package main import ( "bytes" - "io/ioutil" + "os" "path/filepath" "runtime" "strings" @@ -48,7 +48,7 @@ func (r resource) loadFromBase(dir string) string { fullpath := filepath.Join(dir, filename) - b, err := ioutil.ReadFile(fullpath) + b, err := os.ReadFile(fullpath) if err != nil { panic(fullpath + " failed with error: " + err.Error()) } diff --git a/_examples/file-server/http2push-embedded-gzipped/bindata.go b/_examples/file-server/http2push-embedded-gzipped/bindata.go index 78976fd8..dbea0bd3 100644 --- a/_examples/file-server/http2push-embedded-gzipped/bindata.go +++ b/_examples/file-server/http2push-embedded-gzipped/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package main generated by go-bindata.// sources: +// Package main generated by go-bindata.// sources: // ../http2push/assets/app2/app2app3/css/main.css // ../http2push/assets/app2/app2app3/dirs/dir1/text.txt // ../http2push/assets/app2/app2app3/dirs/dir2/text.txt @@ -19,7 +19,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -464,11 +463,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -546,7 +547,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/file-server/http2push-embedded/bindata.go b/_examples/file-server/http2push-embedded/bindata.go index 78976fd8..dbea0bd3 100644 --- a/_examples/file-server/http2push-embedded/bindata.go +++ b/_examples/file-server/http2push-embedded/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package main generated by go-bindata.// sources: +// Package main generated by go-bindata.// sources: // ../http2push/assets/app2/app2app3/css/main.css // ../http2push/assets/app2/app2app3/dirs/dir1/text.txt // ../http2push/assets/app2/app2app3/dirs/dir2/text.txt @@ -19,7 +19,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -464,11 +463,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -546,7 +547,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go index 0fc5312b..e9ad65da 100644 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package main generated by go-bindata.// sources: +// Package main generated by go-bindata.// sources: // public/app.js // public/css/main.css // public/index.html @@ -11,7 +11,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -288,11 +287,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -346,7 +347,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go index 39ca707a..53ccc9e2 100644 --- a/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go @@ -12,7 +12,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -310,11 +309,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error @@ -377,7 +378,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go b/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go index d603c81f..d1b64147 100644 --- a/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go @@ -1,7 +1,7 @@ package main import ( - "io/ioutil" + "os" "path/filepath" "runtime" "strings" @@ -41,7 +41,7 @@ func (r resource) loadFromBase(dir string) string { fullpath := filepath.Join(dir, filename) - b, err := ioutil.ReadFile(fullpath) + b, err := os.ReadFile(fullpath) if err != nil { panic(fullpath + " failed with error: " + err.Error()) } diff --git a/_examples/file-server/subdomain/main_test.go b/_examples/file-server/subdomain/main_test.go index 96eb882d..8674dd39 100644 --- a/_examples/file-server/subdomain/main_test.go +++ b/_examples/file-server/subdomain/main_test.go @@ -1,8 +1,8 @@ package main import ( - "io/ioutil" "net" + "os" "path/filepath" "testing" @@ -40,7 +40,7 @@ func (r resource) loadFromBase(dir string) string { fullpath := filepath.Join(dir, filename) - b, err := ioutil.ReadFile(fullpath) + b, err := os.ReadFile(fullpath) if err != nil { panic(fullpath + " failed with error: " + err.Error()) } diff --git a/_examples/logging/request-logger/accesslog-proxy/main.go b/_examples/logging/request-logger/accesslog-proxy/main.go index 810c1397..51325504 100644 --- a/_examples/logging/request-logger/accesslog-proxy/main.go +++ b/_examples/logging/request-logger/accesslog-proxy/main.go @@ -1,10 +1,12 @@ -/*Package main is a proxy + accesslog example. +/* +Package main is a proxy + accesslog example. In this example we will make a small proxy which listens requests on "/proxy/+path". With two accesslog instances, one for the main application and one for the /proxy/ requests. Of cource, you could a single accesslog for the whole application, but for the sake of the example let's log them separately. -We will make use of iris.StripPrefix and host.ProxyHandler.*/ +We will make use of iris.StripPrefix and host.ProxyHandler. +*/ package main import ( diff --git a/_examples/mvc/grpc-compatible/http-client/main.go b/_examples/mvc/grpc-compatible/http-client/main.go index f198d398..4766f1fb 100644 --- a/_examples/mvc/grpc-compatible/http-client/main.go +++ b/_examples/mvc/grpc-compatible/http-client/main.go @@ -5,15 +5,15 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" - "io/ioutil" "log" "net/http" + "os" pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld" ) func main() { - b, err := ioutil.ReadFile("../server.crt") + b, err := os.ReadFile("../server.crt") if err != nil { log.Fatal(err) } diff --git a/_examples/mvc/login/web/controllers/users_controller.go b/_examples/mvc/login/web/controllers/users_controller.go index 02715c48..24720b41 100644 --- a/_examples/mvc/login/web/controllers/users_controller.go +++ b/_examples/mvc/login/web/controllers/users_controller.go @@ -30,14 +30,16 @@ type UsersController struct { // curl -i -u admin:password http://localhost:8080/users // // The correct way if you have sensitive data: -// func (c *UsersController) Get() (results []viewmodels.User) { -// data := c.Service.GetAll() // -// for _, user := range data { -// results = append(results, viewmodels.User{user}) -// } -// return -// } +// func (c *UsersController) Get() (results []viewmodels.User) { +// data := c.Service.GetAll() +// +// for _, user := range data { +// results = append(results, viewmodels.User{user}) +// } +// return +// } +// // otherwise just return the datamodels. func (c *UsersController) Get() (results []datamodels.User) { return c.Service.GetAll() diff --git a/_examples/mvc/middleware/per-method/main.go b/_examples/mvc/middleware/per-method/main.go index f4e13560..83392a23 100644 --- a/_examples/mvc/middleware/per-method/main.go +++ b/_examples/mvc/middleware/per-method/main.go @@ -9,9 +9,11 @@ else as well, otherwise I am going with the first one: ```go // 1 -mvc.Configure(app.Party("/user"), func(m *mvc.Application) { - m.Router.Use(cache.Handler(10*time.Second)) -}) + + mvc.Configure(app.Party("/user"), func(m *mvc.Application) { + m.Router.Use(cache.Handler(10*time.Second)) + }) + ``` ```go @@ -32,10 +34,12 @@ mvc.Configure(userRouter, ...) ```go // 4 // same: -app.PartyFunc("/user", func(r iris.Party){ - r.Use(cache.Handler(10*time.Second)) - mvc.Configure(r, ...) -}) + + app.PartyFunc("/user", func(r iris.Party){ + r.Use(cache.Handler(10*time.Second)) + mvc.Configure(r, ...) + }) + ``` If you want to use a middleware for a single route, @@ -48,16 +52,18 @@ then you just call it on the method: var myMiddleware := myMiddleware.New(...) // this should return an iris/context.Handler type UserController struct{} -func (c *UserController) GetSomething(ctx iris.Context) { - // ctx.Proceed checks if myMiddleware called `ctx.Next()` - // inside it and returns true if so, otherwise false. - nextCalled := ctx.Proceed(myMiddleware) - if !nextCalled { - return - } - // else do the job here, it's allowed -} + func (c *UserController) GetSomething(ctx iris.Context) { + // ctx.Proceed checks if myMiddleware called `ctx.Next()` + // inside it and returns true if so, otherwise false. + nextCalled := ctx.Proceed(myMiddleware) + if !nextCalled { + return + } + + // else do the job here, it's allowed + } + ``` And last, if you want to add a middleware on a specific method diff --git a/_examples/mvc/middleware/without-ctx-next/main.go b/_examples/mvc/middleware/without-ctx-next/main.go index 1473880e..aae02aeb 100644 --- a/_examples/mvc/middleware/without-ctx-next/main.go +++ b/_examples/mvc/middleware/without-ctx-next/main.go @@ -1,8 +1,10 @@ -/*Package main shows how to add done handlers in an MVC application without +/* +Package main shows how to add done handlers in an MVC application without the necessity of `ctx.Next()` inside the controller's methods. When we want the `Done` handlers of that specific mvc app's `Party` -to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`*/ +to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest` +*/ package main import ( diff --git a/_examples/mvc/overview/main.go b/_examples/mvc/overview/main.go index 0e1449cf..26c2eb81 100644 --- a/_examples/mvc/overview/main.go +++ b/_examples/mvc/overview/main.go @@ -28,42 +28,45 @@ func pong(ctx iris.Context) { } /* - +-------------------+ - | Env (DEV, PROD) | - +---------+---------+ - | | | - | | | - | | | - DEV | | | PROD + +-------------------+ + | Env (DEV, PROD) | + +---------+---------+ + | | | + | | | + | | | + DEV | | | PROD + -------------------+---------------------+ | +----------------------+------------------- - | | | - | | | - +---+-----+ +----------------v------------------+ +----+----+ - | sqlite | | NewDB(Env) DB | | mysql | - +---+-----+ +----------------+---+--------------+ +----+----+ - | | | | - | | | | - | | | | - +--------------+-----+ +-------------------v---v-----------------+ +----+------+ - | greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter | - +--------------+-----+ +---------------------------+-------------+ +----+------+ - | | | - | | | - | +-----------------------------------------+ | - | | GreetController | | | - | | | | | - | | - Service GreetService <-- | | - | | | | - | +-------------------+---------------------+ | - | | | - | | | - | | | - | +-----------+-----------+ | - | | HTTP Request | | - | +-----------------------+ | - | | /greet?name=kataras | | - | +-----------+-----------+ | - | | | + + | | | + | | | + +---+-----+ +----------------v------------------+ +----+----+ + | sqlite | | NewDB(Env) DB | | mysql | + +---+-----+ +----------------+---+--------------+ +----+----+ + | | | | + | | | | + | | | | + +--------------+-----+ +-------------------v---v-----------------+ +----+------+ + | greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter | + +--------------+-----+ +---------------------------+-------------+ +----+------+ + | | | + | | | + | +-----------------------------------------+ | + | | GreetController | | | + | | | | | + | | - Service GreetService <-- | | + | | | | + | +-------------------+---------------------+ | + | | | + | | | + | | | + | +-----------+-----------+ | + | | HTTP Request | | + | +-----------------------+ | + | | /greet?name=kataras | | + | +-----------+-----------+ | + | | | + +------------------+--------+ +------------+------------+ +-------+------------------+ | model.Response (JSON) | | Response (JSON, error) | | Bad Request | +---------------------------+ +-------------------------+ +--------------------------+ diff --git a/_examples/mvc/repository/web/controllers/movie_controller.go b/_examples/mvc/repository/web/controllers/movie_controller.go index a800d959..9f1aec89 100644 --- a/_examples/mvc/repository/web/controllers/movie_controller.go +++ b/_examples/mvc/repository/web/controllers/movie_controller.go @@ -23,14 +23,16 @@ type MovieController struct { // curl -i http://localhost:8080/movies // // The correct way if you have sensitive data: -// func (c *MovieController) Get() (results []viewmodels.Movie) { -// data := c.Service.GetAll() // -// for _, movie := range data { -// results = append(results, viewmodels.Movie{movie}) -// } -// return -// } +// func (c *MovieController) Get() (results []viewmodels.Movie) { +// data := c.Service.GetAll() +// +// for _, movie := range data { +// results = append(results, viewmodels.Movie{movie}) +// } +// return +// } +// // otherwise just return the datamodels. func (c *MovieController) Get() (results []datamodels.Movie) { return c.Service.GetAll() diff --git a/_examples/project/api/router.go b/_examples/project/api/router.go index c5668b7f..5b3d718a 100644 --- a/_examples/project/api/router.go +++ b/_examples/project/api/router.go @@ -18,7 +18,7 @@ func (srv *Server) buildRouter() { ServerName: srv.config.ServerName, Env: srv.config.Env, Developer: "kataras", - TimeLocation: time.FixedZone("Greece/Athens", 10800), + TimeLocation: time.FixedZone("Greece/Athens", 7200), })) api := srv.Party("/api") diff --git a/_examples/request-body/read-many/main.go b/_examples/request-body/read-many/main.go index cc22d775..af970968 100644 --- a/_examples/request-body/read-many/main.go +++ b/_examples/request-body/read-many/main.go @@ -8,7 +8,7 @@ func main() { app := iris.New() app.Post("/", logAllBody, logJSON, logFormValues, func(ctx iris.Context) { - // body, err := ioutil.ReadAll(ctx.Request().Body) once or + // body, err := io.ReadAll(ctx.Request().Body) once or body, err := ctx.GetBody() // as many times as you need. if err != nil { ctx.StopWithError(iris.StatusInternalServerError, err) diff --git a/_examples/response-writer/content-negotiation/main_test.go b/_examples/response-writer/content-negotiation/main_test.go index 08c5e5a6..acb5bf9d 100644 --- a/_examples/response-writer/content-negotiation/main_test.go +++ b/_examples/response-writer/content-negotiation/main_test.go @@ -4,7 +4,7 @@ import ( "bytes" "compress/gzip" "encoding/xml" - "io/ioutil" + "io" "testing" "github.com/kataras/iris/v12" @@ -69,7 +69,7 @@ func TestContentNegotiation(t *testing.T) { t.Fatal(err) } - rawResponse, err := ioutil.ReadAll(zr) + rawResponse, err := io.ReadAll(zr) if err != nil { t.Fatal(err) } diff --git a/_examples/routing/custom-router/main.go b/_examples/routing/custom-router/main.go index 3ec24fe2..aba16974 100644 --- a/_examples/routing/custom-router/main.go +++ b/_examples/routing/custom-router/main.go @@ -7,14 +7,15 @@ import ( "github.com/kataras/iris/v12/core/router" ) -/* A Router should contain all three of the following methods: - - HandleRequest should handle the request based on the Context. - HandleRequest(ctx iris.Context) - - Build should builds the handler, it's being called on router's BuildRouter. - Build(provider router.RoutesProvider) error - - RouteExists reports whether a particular route exists. - RouteExists(ctx iris.Context, method, path string) bool - - FireErrorCode(ctx iris.Context) should handle the given ctx.GetStatusCode(). +/* + A Router should contain all three of the following methods: + - HandleRequest should handle the request based on the Context. + HandleRequest(ctx iris.Context) + - Build should builds the handler, it's being called on router's BuildRouter. + Build(provider router.RoutesProvider) error + - RouteExists reports whether a particular route exists. + RouteExists(ctx iris.Context, method, path string) bool + - FireErrorCode(ctx iris.Context) should handle the given ctx.GetStatusCode(). For a more detailed, complete and useful example you can take a look at the iris' router itself which is located at: diff --git a/_examples/routing/custom-wrapper/main_test.go b/_examples/routing/custom-wrapper/main_test.go index 540f0435..638cab5b 100644 --- a/_examples/routing/custom-wrapper/main_test.go +++ b/_examples/routing/custom-wrapper/main_test.go @@ -1,7 +1,7 @@ package main import ( - "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -29,7 +29,7 @@ func (r resource) loadFromBase(dir string) string { fullpath := filepath.Join(dir, filename) - b, err := ioutil.ReadFile(fullpath) + b, err := os.ReadFile(fullpath) if err != nil { panic(fullpath + " failed with error: " + err.Error()) } diff --git a/_examples/routing/route-handlers-execution-rules/main.go b/_examples/routing/route-handlers-execution-rules/main.go index 5b503b7e..73675729 100644 --- a/_examples/routing/route-handlers-execution-rules/main.go +++ b/_examples/routing/route-handlers-execution-rules/main.go @@ -1,4 +1,5 @@ -/*Package main is a simple example of the behavior change of the execution flow of the handlers, +/* +Package main is a simple example of the behavior change of the execution flow of the handlers, normally we need the `ctx.Next()` to call the next handler in a route's handler chain, but with the `ExecutionRules` we can change this default behavior. Please read below before continue. @@ -8,11 +9,11 @@ The `Party#SetExecutionRules` alters the execution flow of the route handlers. For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what, even if no `ctx.Next()` is called in the previous handlers: -app.SetExecutionRules(iris.ExecutionRules { - Begin: iris.ExecutionOptions{Force: true}, # begin handlers(.Use) - Main: iris.ExecutionOptions{Force: true}, # main handler (.Handle/Get...) - Done: iris.ExecutionOptions{Force: true}, # done handlers (.Done) -}) + app.SetExecutionRules(iris.ExecutionRules { + Begin: iris.ExecutionOptions{Force: true}, # begin handlers(.Use) + Main: iris.ExecutionOptions{Force: true}, # main handler (.Handle/Get...) + Done: iris.ExecutionOptions{Force: true}, # done handlers (.Done) + }) Note that if `true` then the only remained way to "break" the handler chain is by calling the `ctx.StopExecution()` (now that `ctx.Next()` doesn't even matter). @@ -22,7 +23,6 @@ the same rules will be applied to that as well. Reset of these rules to their defaults (before `Party#Handle`) can be done with `Party#SetExecutionRules(iris.ExecutionRules{})`. - */ package main diff --git a/_examples/url-shortener/main_test.go b/_examples/url-shortener/main_test.go index 46d1a90d..1c6d38a1 100644 --- a/_examples/url-shortener/main_test.go +++ b/_examples/url-shortener/main_test.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "testing" "time" @@ -14,7 +13,7 @@ import ( // The rest possible checks is up to you, take it as as an exercise! func TestURLShortener(t *testing.T) { // temp db file - f, err := ioutil.TempFile("", "shortener") + f, err := os.CreateTemp("", "shortener") if err != nil { t.Fatalf("creating temp file for database failed: %v", err) } diff --git a/_examples/view/embedding-templates-into-app/bindata.go b/_examples/view/embedding-templates-into-app/bindata.go index 4075f21a..2df28740 100644 --- a/_examples/view/embedding-templates-into-app/bindata.go +++ b/_examples/view/embedding-templates-into-app/bindata.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. (@generated) DO NOT EDIT. -//Package main generated by go-bindata.// sources: +// Package main generated by go-bindata.// sources: // templates/layouts/layout.html // templates/layouts/mylayout.html // templates/page1.html @@ -12,7 +12,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -310,11 +309,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -371,7 +372,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/view/template_amber_1_embedded/bindata.go b/_examples/view/template_amber_1_embedded/bindata.go index 1a797f84..d92c4889 100644 --- a/_examples/view/template_amber_1_embedded/bindata.go +++ b/_examples/view/template_amber_1_embedded/bindata.go @@ -10,7 +10,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -266,11 +265,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error @@ -323,7 +324,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/view/template_blocks_1_embedded/bindata.go b/_examples/view/template_blocks_1_embedded/bindata.go index 2c284465..1d3155a2 100644 --- a/_examples/view/template_blocks_1_embedded/bindata.go +++ b/_examples/view/template_blocks_1_embedded/bindata.go @@ -13,7 +13,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -332,11 +331,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error @@ -394,7 +395,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/view/template_jet_1_embedded/bindata.go b/_examples/view/template_jet_1_embedded/bindata.go index 60528aff..b80211e4 100644 --- a/_examples/view/template_jet_1_embedded/bindata.go +++ b/_examples/view/template_jet_1_embedded/bindata.go @@ -12,7 +12,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -310,11 +309,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error @@ -371,7 +372,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/view/template_pug_2_embedded/bindata.go b/_examples/view/template_pug_2_embedded/bindata.go index 1547e982..99d59246 100644 --- a/_examples/view/template_pug_2_embedded/bindata.go +++ b/_examples/view/template_pug_2_embedded/bindata.go @@ -10,7 +10,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -266,11 +265,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error @@ -321,7 +322,7 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + err = os.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } diff --git a/_examples/websocket/gorilla-filewatch/main.go b/_examples/websocket/gorilla-filewatch/main.go index e740c707..00583eb3 100644 --- a/_examples/websocket/gorilla-filewatch/main.go +++ b/_examples/websocket/gorilla-filewatch/main.go @@ -12,7 +12,6 @@ package main import ( "flag" - "io/ioutil" "log" "os" "strconv" @@ -53,7 +52,7 @@ func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) { if !fi.ModTime().After(lastMod) { return nil, lastMod, nil } - p, err := ioutil.ReadFile(filename) + p, err := os.ReadFile(filename) if err != nil { return nil, fi.ModTime(), err } diff --git a/apps/switch.go b/apps/switch.go index 56952477..d3d2dba0 100644 --- a/apps/switch.go +++ b/apps/switch.go @@ -13,12 +13,13 @@ import ( // The cases are filtered in order of their registration. // // Example Code: -// switcher := Switch(Hosts{ -// "mydomain.com": app, -// "test.mydomain.com": testSubdomainApp, -// "otherdomain.com": "appName", -// }) -// switcher.Listen(":80") +// +// switcher := Switch(Hosts{ +// "mydomain.com": app, +// "test.mydomain.com": testSubdomainApp, +// "otherdomain.com": "appName", +// }) +// switcher.Listen(":80") // // Note that this is NOT an alternative for a load balancer. // The filters are executed by registration order and a matched Application diff --git a/auth/auth.go b/auth/auth.go index a39cd51b..ce80ce88 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -99,6 +99,7 @@ func MustLoad[T User](filename string) *Auth[T] { // Must is a helper that wraps a call to a function returning (*Auth[T], error) // and panics if the error is non-nil. It is intended for use in variable // initializations such as +// // var s = auth.Must(auth.New[MyUser](config)) func Must[T User](s *Auth[T], err error) *Auth[T] { if err != nil { @@ -142,12 +143,13 @@ func New[T User](config Configuration) (*Auth[T], error) { // method when a Provider of T and ErrorHandler is available through the registered Party's dependencies. // // Usage Example: -// api := app.Party("/api") -// api.EnsureStaticBindings().RegisterDependency( -// NewAuthProviderErrorHandler(), -// NewAuthCustomerProvider, -// auth.Must(auth.New[Customer](authConfig)).WithProviderAndErrorHandler, -// ) +// +// api := app.Party("/api") +// api.EnsureStaticBindings().RegisterDependency( +// NewAuthProviderErrorHandler(), +// NewAuthCustomerProvider, +// auth.Must(auth.New[Customer](authConfig)).WithProviderAndErrorHandler, +// ) func (s *Auth[T]) WithProviderAndErrorHandler(provider Provider[T], errHandler ErrorHandler) *Auth[T] { if provider != nil { for i := range s.providers { diff --git a/cache/browser.go b/cache/browser.go index 9462cd5f..c0fe70b1 100644 --- a/cache/browser.go +++ b/cache/browser.go @@ -49,10 +49,11 @@ var NoCache = func(ctx *context.Context) { // Usage: `app.Use(cache.StaticCache(24 * time.Hour))` or `app.Use(cache.Staticcache(-1))`. // A middleware, which is a simple Handler can be called inside another handler as well, example: // cacheMiddleware := cache.StaticCache(...) -// func(ctx iris.Context){ -// cacheMiddleware(ctx) -// [...] -// } +// +// func(ctx iris.Context){ +// cacheMiddleware(ctx) +// [...] +// } var StaticCache = func(cacheDur time.Duration) context.Handler { if int64(cacheDur) <= 0 { return NoCache diff --git a/cache/client/client.go b/cache/client/client.go index 5d89c429..13d2fb20 100644 --- a/cache/client/client.go +++ b/cache/client/client.go @@ -2,7 +2,7 @@ package client import ( "bytes" - "io/ioutil" + "io" "net/http" "time" @@ -17,8 +17,8 @@ import ( // register one client handler per route. // // it's just calls a remote cache service server/handler, -// which lives on other, external machine. // +// which lives on other, external machine. type ClientHandler struct { // bodyHandler the original route's handler bodyHandler context.Handler @@ -162,7 +162,7 @@ func (h *ClientHandler) ServeHTTP(ctx *context.Context) { // get the status code , content type and the write the response body ctx.ContentType(response.Header.Get(cfg.ContentTypeHeader)) ctx.StatusCode(response.StatusCode) - responseBody, err := ioutil.ReadAll(response.Body) + responseBody, err := io.ReadAll(response.Body) response.Body.Close() if err != nil { return diff --git a/cache/client/rule/validator.go b/cache/client/rule/validator.go index 1f8ca36c..45cf03df 100644 --- a/cache/client/rule/validator.go +++ b/cache/client/rule/validator.go @@ -22,12 +22,13 @@ type PreValidator func(*context.Context) bool // // Q: What's the difference between this and a PreValidator? // A: PreValidator runs BEFORE trying to get the cache, it cares only for the request -// and if at least one PreValidator returns false then it just runs the original handler and stop there, at the other hand -// a PostValidator runs if all PreValidators returns true and original handler is executed but with a response recorder, -// also the PostValidator should return true to store the cached response. -// Last, a PostValidator accepts a context -// in order to be able to catch the original handler's response, -// the PreValidator checks only for request. +// +// and if at least one PreValidator returns false then it just runs the original handler and stop there, at the other hand +// a PostValidator runs if all PreValidators returns true and original handler is executed but with a response recorder, +// also the PostValidator should return true to store the cached response. +// Last, a PostValidator accepts a context +// in order to be able to catch the original handler's response, +// the PreValidator checks only for request. // // If a function of type of PostValidator returns true then the (shared-always) cache is allowed to be stored. type PostValidator func(*context.Context) bool diff --git a/configuration.go b/configuration.go index 49cab717..27e332dd 100644 --- a/configuration.go +++ b/configuration.go @@ -2,7 +2,6 @@ package iris import ( "fmt" - "io/ioutil" "os" "os/user" "path/filepath" @@ -65,7 +64,7 @@ func parseYAML(filename string) (Configuration, error) { } // read the raw contents of the file - data, err := ioutil.ReadFile(yamlAbsPath) + data, err := os.ReadFile(yamlAbsPath) if err != nil { return c, fmt.Errorf("parse yaml: %w", err) } @@ -112,7 +111,6 @@ func YAML(filename string) Configuration { // Read more about toml's implementation at: // https://github.com/toml-lang/toml // -// // Accepts the absolute path of the configuration file. // An error will be shown to the user via panic with the error message. // Error may occur when the file does not exist or is not formatted correctly. @@ -144,7 +142,7 @@ func TOML(filename string) Configuration { } // read the raw contents of the file - data, err := ioutil.ReadFile(tomlAbsPath) + data, err := os.ReadFile(tomlAbsPath) if err != nil { panic(fmt.Errorf("toml :%w", err)) } @@ -754,6 +752,7 @@ type Configuration struct { // then the application tries to optimize for the best performance where is possible. // // Defaults to false. + // Deprecated. As of version 12.2.x this field does nothing. EnableOptimizations bool `ini:"enable_optimizations" json:"enableOptimizations,omitempty" yaml:"EnableOptimizations" toml:"EnableOptimizations"` // EnableProtoJSON when this field is true // enables the proto marshaler on given proto messages when calling the Context.JSON method. diff --git a/configuration_test.go b/configuration_test.go index ae0e3edf..213d0fb7 100644 --- a/configuration_test.go +++ b/configuration_test.go @@ -1,7 +1,6 @@ package iris import ( - "io/ioutil" "os" "reflect" "testing" @@ -103,7 +102,7 @@ func createGlobalConfiguration(t *testing.T) { out, err := yaml.Marshal(&c) if err == nil { - err = ioutil.WriteFile(filename, out, os.FileMode(0666)) + err = os.WriteFile(filename, out, os.FileMode(0666)) } if err != nil { t.Fatalf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err) @@ -131,7 +130,7 @@ func testConfigurationGlobal(t *testing.T, c Configurator) { } func TestConfigurationYAML(t *testing.T) { - yamlFile, ferr := ioutil.TempFile("", "configuration.yml") + yamlFile, ferr := os.CreateTemp("", "configuration.yml") if ferr != nil { t.Fatal(ferr) @@ -264,7 +263,7 @@ Other: } func TestConfigurationTOML(t *testing.T) { - tomlFile, ferr := ioutil.TempFile("", "configuration.toml") + tomlFile, ferr := os.CreateTemp("", "configuration.toml") if ferr != nil { t.Fatal(ferr) diff --git a/context/context.go b/context/context.go index 8117afa8..a5f87f94 100644 --- a/context/context.go +++ b/context/context.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime" "mime/multipart" "net" @@ -31,7 +30,6 @@ import ( "github.com/Shopify/goreferrer" "github.com/fatih/structs" - gojson "github.com/goccy/go-json" "github.com/iris-contrib/schema" "github.com/mailru/easyjson" "github.com/mailru/easyjson/jwriter" @@ -100,7 +98,7 @@ type ( // is terminated and the error is received by the ReadJSONStream method, // otherwise it continues to read the next available object. // Look the `Context.ReadJSONStream` method. - DecodeFunc func(ctx stdContext.Context, outPtr interface{}) error + DecodeFunc func(outPtr interface{}) error ) // Unmarshal parses the X-encoded data and stores the result in the value pointed to by v. @@ -539,7 +537,6 @@ func (ctx *Context) Do(handlers Handlers) { // Router is calling this function to add the route's handler. // If AddHandler called then the handlers will be inserted // to the end of the already-defined route's handler. -// func (ctx *Context) AddHandler(handlers ...Handler) { ctx.handlers = append(ctx.handlers, handlers...) } @@ -590,23 +587,26 @@ func (ctx *Context) HandlerIndex(n int) (currentIndex int) { // // That said let's see an example of `ctx.Proceed`: // -// var authMiddleware = basicauth.New(basicauth.Config{ -// Users: map[string]string{ -// "admin": "password", -// }, -// }) +// var authMiddleware = basicauth.New(basicauth.Config{ +// Users: map[string]string{ +// "admin": "password", +// }, +// }) +// +// func (c *UsersController) BeginRequest(ctx iris.Context) { +// if !ctx.Proceed(authMiddleware) { +// ctx.StopExecution() +// } +// } // -// func (c *UsersController) BeginRequest(ctx iris.Context) { -// if !ctx.Proceed(authMiddleware) { -// ctx.StopExecution() -// } -// } // This Get() will be executed in the same handler as `BeginRequest`, // internally controller checks for `ctx.StopExecution`. // So it will not be fired if BeginRequest called the `StopExecution`. -// func(c *UsersController) Get() []models.User { -// return c.Service.GetAll() -//} +// +// func(c *UsersController) Get() []models.User { +// return c.Service.GetAll() +// } +// // Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure. func (ctx *Context) Proceed(h Handler) bool { _, ok := ctx.ProceedAndReportIfStopped(h) @@ -841,8 +841,7 @@ func (ctx *Context) StopWithPlainError(statusCode int, err error) { // it will also fire the specified error code handler. func (ctx *Context) StopWithJSON(statusCode int, jsonObject interface{}) error { ctx.StopWithStatus(statusCode) - _, err := ctx.writeJSON(jsonObject, nil) // do not modify - see errors.DefaultContextErrorHandler. - return err + return ctx.writeJSON(jsonObject, nil) // do not modify - see errors.DefaultContextErrorHandler. } // StopWithProblem stops the handlers chain, writes the status code @@ -854,8 +853,7 @@ func (ctx *Context) StopWithJSON(statusCode int, jsonObject interface{}) error { func (ctx *Context) StopWithProblem(statusCode int, problem Problem) error { ctx.StopWithStatus(statusCode) problem.Status(statusCode) - _, err := ctx.Problem(problem) - return err + return ctx.Problem(problem) } // +------------------------------------------------------------+ @@ -939,7 +937,7 @@ func (ctx *Context) RequestPath(escape bool) string { const sufscheme = "://" -// GetScheme returns the full scheme of the request URL (https://, http:// or ws:// and e.t.c.``). +// GetScheme returns the full scheme of the request URL (https://, http:// or ws:// and e.t.c.). func GetScheme(r *http.Request) string { scheme := r.URL.Scheme @@ -1111,10 +1109,11 @@ func (ctx *Context) FullRequestURI() string { // even if it's part of a private network. // // Look `Configuration.RemoteAddrHeaders`, -// `Configuration.RemoteAddrHeadersForce`, -// `Configuration.WithRemoteAddrHeader(...)`, -// `Configuration.WithoutRemoteAddrHeader(...)` and -// `Configuration.RemoteAddrPrivateSubnets` for more. +// +// Configuration.RemoteAddrHeadersForce, +// Configuration.WithRemoteAddrHeader(...), +// Configuration.WithoutRemoteAddrHeader(...) and +// Configuration.RemoteAddrPrivateSubnetsW for more. func (ctx *Context) RemoteAddr() string { if remoteHeaders := ctx.app.ConfigurationReadOnly().GetRemoteAddrHeaders(); len(remoteHeaders) > 0 { privateSubnets := ctx.app.ConfigurationReadOnly().GetRemoteAddrPrivateSubnets() @@ -1176,7 +1175,7 @@ func (ctx *Context) GetHeader(name string) string { // try to find another way of detecting the type(i.e, content type), // there are many blogs that describe these problems and provide different kind of solutions, // it's always depending on the application you're building, -// this is the reason why this `IsAjax`` is simple enough for general purpose use. +// this is the reason why this `IsAjax` is simple enough for general purpose use. // // Read more at: https://developer.mozilla.org/en-US/docs/AJAX // and https://xhr.spec.whatwg.org/ @@ -1557,9 +1556,11 @@ func (ctx *Context) URLParamEscape(name string) string { // Example: // // n, err := context.URLParamInt("url_query_param_name") -// if errors.Is(err, context.ErrNotFound) { -// // [handle error...] -// } +// +// if errors.Is(err, context.ErrNotFound) { +// // [handle error...] +// } +// // Another usage would be `err == context.ErrNotFound` // HOWEVER prefer use the new `errors.Is` as API details may change in the future. var ErrNotFound = errors.New("not found") @@ -1816,7 +1817,7 @@ func GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[str setBody(r, body) // so the ctx.request.Body works defer restoreBody() // so the next GetForm calls work. - // r.Body = ioutil.NopCloser(io.TeeReader(r.Body, buf)) + // r.Body = io.NopCloser(io.TeeReader(r.Body, buf)) } else { resetBody = false } @@ -1828,7 +1829,7 @@ func GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[str // subsequent calls have no effect, are idempotent. err := r.ParseMultipartForm(postMaxMemory) // if resetBody { - // r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyCopy)) + // r.Body = io.NopCloser(bytes.NewBuffer(bodyCopy)) // } if err != nil && err != http.ErrNotMultipart { return nil, false @@ -2037,7 +2038,6 @@ func (ctx *Context) PostValueBool(name string) (bool, error) { // FormFile returns the first uploaded file that received from the client. // -// // The default form's memory maximum size is 32MB, it can be changed by the // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument. // @@ -2284,7 +2284,7 @@ var emptyFunc = func() {} // GetBody reads and returns the request body. func GetBody(r *http.Request, resetBody bool) ([]byte, func(), error) { - data, err := ioutil.ReadAll(r.Body) + data, err := io.ReadAll(r.Body) if err != nil { return nil, nil, err } @@ -2301,7 +2301,7 @@ func GetBody(r *http.Request, resetBody bool) ([]byte, func(), error) { } func setBody(r *http.Request, data []byte) { - r.Body = ioutil.NopCloser(bytes.NewBuffer(data)) + r.Body = io.NopCloser(bytes.NewBuffer(data)) } const disableRequestBodyConsumptionContextKey = "iris.request.body.record" @@ -2385,10 +2385,6 @@ func (ctx *Context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) e return ctx.app.Validate(outPtr) } -func (ctx *Context) shouldOptimize() bool { - return ctx.app.ConfigurationReadOnly().GetEnableOptimizations() -} - // JSONReader holds the JSON decode options of the `Context.ReadJSON, ReadBody` methods. type JSONReader struct { // Note(@kataras): struct instead of optional funcs to keep consistently with the encoder options. // DisallowUnknownFields causes the json decoder to return an error when the destination @@ -2413,123 +2409,31 @@ type JSONReader struct { // Note(@kataras): struct instead of optional funcs to // {"username": "makis"} // {"username": "george"} ArrayStream bool - - // Optional context cancelation of decoder when Optimize field is enabled. - // On ReadJSON method this is automatically binded to the request context. - Context stdContext.Context } -type internalJSONDecoder interface { - Token() (json.Token, error) // gojson.Token is an alias of this, so we are ok. - More() bool - DisallowUnknownFields() -} +var ReadJSON = func(ctx *Context, outPtr interface{}, opts ...JSONReader) error { + decoder := json.NewDecoder(ctx.request.Body) + // decoder := gojson.NewDecoder(ctx.Request().Body) + if len(opts) > 0 { + options := opts[0] -type unmarshalerContext interface { - // UnmarshalJSON unmarshal json with context support. - UnmarshalJSON(stdContext.Context, []byte) error //lint:ignore stdmethods external pkg. -} - -func wrapDecodeFunc(decodeFunc func(interface{}) error) DecodeFunc { - return func(_ stdContext.Context, outPtr interface{}) error { - return decodeFunc(outPtr) - } -} - -func (options JSONReader) unmarshal(ctx stdContext.Context, body []byte, outPtr interface{}) error { - if options.Optimize { - if outPtr != nil { - if _, supportsContext := outPtr.(unmarshalerContext); !supportsContext { - return gojson.Unmarshal(body, outPtr) - } + if options.DisallowUnknownFields { + decoder.DisallowUnknownFields() } - - return gojson.UnmarshalContext(ctx, body, outPtr) } - return json.Unmarshal(body, outPtr) -} - -func (options JSONReader) getDecoder(r io.Reader, outPtr interface{}) (internalJSONDecoder, DecodeFunc) { - var ( - decoder internalJSONDecoder - decodeFunc DecodeFunc - ) - - if options.Optimize { - dec := gojson.NewDecoder(r) - - if outPtr != nil { - // If a custom type does not implement the unnmarshal json with context interface - // that is REQUIRED by the gojson, then fallback to the normal gojson decode without context support, - // so we protect compatibility against existing objects. - if _, supportsContext := outPtr.(unmarshalerContext); supportsContext { - decodeFunc = dec.DecodeContext - } else { - decodeFunc = wrapDecodeFunc(dec.Decode) - } - } else { - decodeFunc = dec.DecodeContext - } - - decoder = dec - } else { - dec := json.NewDecoder(r) - decodeFunc = wrapDecodeFunc(dec.Decode) - decoder = dec + if err := decoder.Decode(&outPtr); err != nil { + return err } - if options.DisallowUnknownFields { - decoder.DisallowUnknownFields() - } - - return decoder, decodeFunc + return ctx.app.Validate(outPtr) } // ReadJSON reads JSON from request's body and binds it to a value of any json-valid type. // // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error { - var options JSONReader - options.Optimize = ctx.shouldOptimize() - - if len(opts) > 0 { - options = opts[0] - } - - if ctx.IsRecordingBody() { - body, restoreBody, err := GetBody(ctx.request, true) - if err != nil { - return err - } - restoreBody() - - err = options.unmarshal(ctx.request.Context(), body, outPtr) - if err != nil { - return err - } - } else { - _, decodeFunc := options.getDecoder(ctx.request.Body, outPtr) - err := decodeFunc(ctx.request.Context(), outPtr) - if err != nil { - return err - } - } - - return ctx.app.Validate(outPtr) - - /* - b, err := ctx.GetBody() - if err != nil { - return err - } - - if options.Optimize { - return gojson.UnmarshalContext(ctx.request.Context(), b, outPtr) - } else { - return json.Unmarshal(b, outPtr) - } - */ + return ReadJSON(ctx, outPtr, opts...) } // ReadJSONStream is an alternative of ReadJSON which can reduce the memory load @@ -2543,21 +2447,16 @@ func (ctx *Context) ReadJSON(outPtr interface{}, opts ...JSONReader) error { // // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json-stream/main.go func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSONReader) error { - var options JSONReader - if len(opts) > 0 { - options = opts[0] - } + decoder := json.NewDecoder(ctx.request.Body) - decoder, decodeFunc := options.getDecoder(ctx.request.Body, nil) - - if options.ArrayStream { + if len(opts) > 0 && opts[0].ArrayStream { _, err := decoder.Token() // read open bracket. if err != nil { return err } for decoder.More() { // hile the array contains values. - if err = onDecode(decodeFunc); err != nil { + if err = onDecode(decoder.Decode); err != nil { return err } } @@ -2568,7 +2467,7 @@ func (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSON // while the array contains values for decoder.More() { - if err := onDecode(decodeFunc); err != nil { + if err := onDecode(decoder.Decode); err != nil { return err } } @@ -2788,7 +2687,7 @@ func (ctx *Context) ReadMultipartRelated() (MultipartRelated, error) { } defer part.Close() - b, err := ioutil.ReadAll(part) + b, err := io.ReadAll(part) if err != nil { return MultipartRelated{}, fmt.Errorf("multipart related: next part: read: %w", err) } @@ -3149,13 +3048,14 @@ func (ctx *Context) SetLastModified(modtime time.Time) { // that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`. // Usage: // ok, err := context.CheckIfModifiedSince(modTime) -// if err != nil { -// if errors.Is(err, context.ErrPreconditionFailed) { -// [handle missing client conditions,such as not valid request method...] -// }else { -// [the error is probably a time parse error...] -// } -// } +// +// if err != nil { +// if errors.Is(err, context.ErrPreconditionFailed) { +// [handle missing client conditions,such as not valid request method...] +// }else { +// [the error is probably a time parse error...] +// } +// } var ErrPreconditionFailed = errors.New("precondition failed") // CheckIfModifiedSince checks if the response is modified since the "modtime". @@ -3232,9 +3132,9 @@ func (ctx *Context) WriteWithExpiration(body []byte, modtime time.Time) (int, er // // This function may be used in the following cases: // -// * if response body is too big (more than iris.LimitRequestBodySize(if set)). -// * if response body is streamed from slow external sources. -// * if response body must be streamed to the client in chunks. +// - if response body is too big (more than iris.LimitRequestBodySize(if set)). +// - if response body is streamed from slow external sources. +// - if response body must be streamed to the client in chunks. // (aka `http server push`). func (ctx *Context) StreamWriter(writer func(w io.Writer) error) error { cancelCtx := ctx.Request().Context() @@ -3287,10 +3187,12 @@ func (ctx *Context) ClientSupportsEncoding(encodings ...string) bool { // will change the response writer to a compress writer instead. // All future write and rich write methods will respect this option. // Usage: -// app.Use(func(ctx iris.Context){ -// err := ctx.CompressWriter(true) -// ctx.Next() -// }) +// +// app.Use(func(ctx iris.Context){ +// err := ctx.CompressWriter(true) +// ctx.Next() +// }) +// // The recommendation is to compress data as much as possible and therefore to use this field, // but some types of resources, such as jpeg images, are already compressed. // Sometimes, using additional compression doesn't reduce payload size and @@ -3350,15 +3252,18 @@ func (ctx *Context) CompressWriter(enable bool) error { // All future calls of `ctx.GetBody/ReadXXX/UnmarshalBody` methods will respect this option. // // Usage: -// app.Use(func(ctx iris.Context){ -// err := ctx.CompressReader(true) -// ctx.Next() -// }) +// +// app.Use(func(ctx iris.Context){ +// err := ctx.CompressReader(true) +// ctx.Next() +// }) +// // More: -// if cr, ok := ctx.Request().Body.(*CompressReader); ok { -// cr.Src // the original request body -// cr.Encoding // the compression algorithm. -// } +// +// if cr, ok := ctx.Request().Body.(*CompressReader); ok { +// cr.Src // the original request body +// cr.Encoding // the compression algorithm. +// } // // It returns `ErrRequestNotCompressed` if client's request data are not compressed // (or empty) @@ -3592,15 +3497,16 @@ func (ctx *Context) fireFallbackViewOnce(err ErrViewNotExist) error { // is responsible to handle the error or render a different view. // // Usage: -// FallbackView(iris.FallbackView("fallback.html")) -// FallbackView(iris.FallbackViewLayout("layouts/fallback.html")) -// OR -// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error { -// err.Name is the previous template name. -// err.IsLayout reports whether the failure came from the layout template. -// err.Data is the template data provided to the previous View call. -// [...custom logic e.g. ctx.View("fallback", err.Data)] -// }) +// +// FallbackView(iris.FallbackView("fallback.html")) +// FallbackView(iris.FallbackViewLayout("layouts/fallback.html")) +// OR +// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error { +// err.Name is the previous template name. +// err.IsLayout reports whether the failure came from the layout template. +// err.Data is the template data provided to the previous View call. +// [...custom logic e.g. ctx.View("fallback", err.Data)] +// }) func (ctx *Context) FallbackView(providers ...FallbackViewProvider) { key := ctx.app.ConfigurationReadOnly().GetFallbackViewContextKey() if key == "" { @@ -3749,8 +3655,6 @@ type ProtoMarshalOptions = protojson.MarshalOptions // JSON contains the options for the JSON (Context's) Renderer. type JSON struct { - // http-specific - StreamingJSON bool `yaml:"StreamingJSON"` // content-specific UnescapeHTML bool `yaml:"UnescapeHTML"` Indent string `yaml:"Indent"` @@ -3772,8 +3676,7 @@ var DefaultJSONOptions = JSON{} // IsDefault reports whether this JSON options structure holds the default values. func (j *JSON) IsDefault() bool { - return j.StreamingJSON == DefaultJSONOptions.StreamingJSON && - j.UnescapeHTML == DefaultJSONOptions.UnescapeHTML && + return j.UnescapeHTML == DefaultJSONOptions.UnescapeHTML && j.Indent == DefaultJSONOptions.Indent && j.Prefix == DefaultJSONOptions.Prefix && j.ASCII == DefaultJSONOptions.ASCII && @@ -3858,28 +3761,24 @@ func (ctx *Context) handleSpecialJSONResponseValue(v interface{}, options *JSON) } // WriteJSON marshals the given interface object and writes the JSON response to the 'writer'. -func WriteJSON(ctx stdContext.Context, writer io.Writer, v interface{}, options *JSON, shouldOptimize bool) (int, error) { - if options.StreamingJSON { - var err error - if shouldOptimize { - // jsoniterConfig := jsoniter.Config{ - // EscapeHTML: !options.UnescapeHTML, - // IndentionStep: 4, - // }.Froze() - // enc := jsoniterConfig.NewEncoder(ctx.writer) - // err = enc.Encode(v) - enc := gojson.NewEncoder(writer) - enc.SetEscapeHTML(!options.UnescapeHTML) - enc.SetIndent(options.Prefix, options.Indent) - err = enc.EncodeContext(ctx, v) - } else { - enc := json.NewEncoder(writer) - enc.SetEscapeHTML(!options.UnescapeHTML) - enc.SetIndent(options.Prefix, options.Indent) - err = enc.Encode(v) - } +var WriteJSON = func(ctx *Context, v interface{}, options *JSON) error { + if !options.Secure && !options.ASCII && options.Prefix == "" { + // jsoniterConfig := jsoniter.Config{ + // EscapeHTML: !options.UnescapeHTML, + // IndentionStep: 4, + // }.Froze() + // enc := jsoniterConfig.NewEncoder(ctx.writer) + // err = enc.Encode(v) + // + // enc := gojson.NewEncoder(ctx.writer) + // enc.SetEscapeHTML(!options.UnescapeHTML) + // enc.SetIndent(options.Prefix, options.Indent) + // err = enc.EncodeContext(ctx, v) + enc := json.NewEncoder(ctx.writer) + enc.SetEscapeHTML(!options.UnescapeHTML) + enc.SetIndent(options.Prefix, options.Indent) - return 0, err + return enc.Encode(v) } var ( @@ -3887,35 +3786,15 @@ func WriteJSON(ctx stdContext.Context, writer io.Writer, v interface{}, options err error ) - // Let's keep it as it is. - // if !shouldOptimize && options.Indent == "" { - // options.Indent = " " - // } - if indent := options.Indent; indent != "" { - if shouldOptimize { - // result,err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent - result, err = gojson.MarshalIndent(v, "", indent) - } else { - result, err = json.MarshalIndent(v, "", indent) - } - + result, err = json.MarshalIndent(v, "", indent) result = append(result, newLineB...) } else { - if shouldOptimize { - // result, err = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal - if ctx != nil { - result, err = gojson.MarshalContext(ctx, v) - } else { - result, err = gojson.Marshal(v) - } - } else { - result, err = json.Marshal(v) - } + result, err = json.Marshal(v) } if err != nil { - return 0, err + return err } prependSecure := false @@ -3960,7 +3839,8 @@ func WriteJSON(ctx stdContext.Context, writer io.Writer, v interface{}, options result = append(stringToBytes(prefix), result...) } - return writer.Write(result) + _, err = ctx.Write(result) + return err } // See https://golang.org/src/strings/builder.go#L45 @@ -4007,25 +3887,27 @@ func (ctx *Context) handleContextError(err error) { } // JSON marshals the given "v" value to JSON and writes the response to the client. -// Look the Configuration.EnableProtoJSON/EnableEasyJSON and EnableOptimizations too. +// Look the Configuration.EnableProtoJSON and EnableEasyJSON too. // // It reports any JSON parser or write errors back to the caller. // Look the Application.SetContextErrorHandler to override the // default status code 500 with a custom error response. // -// It can, optionally, accept the JSON structure which may hold customizations over the -// final JSON response but keep in mind that the caller should NOT modify that JSON options -// value in another goroutine while JSON method is still running. -func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) { +// Customize the behavior of every `Context.JSON“ can be achieved +// by modifying the package-level `WriteJSON` function on program initilization. +func (ctx *Context) JSON(v interface{}, opts ...JSON) (err error) { var options *JSON if len(opts) > 0 { options = &opts[0] + } else { + // If no options are given safely read the already-initialized value. + options = &DefaultJSONOptions } - if n, err = ctx.writeJSON(v, options); err != nil { + if err = ctx.writeJSON(v, options); err != nil { // if no options are given or OmitErrorHandler is true // then do call the error handler (which may lead to a cycle). - if options == nil || !options.OmitErrorHandler { + if !options.OmitErrorHandler { ctx.handleContextError(err) } } @@ -4033,76 +3915,38 @@ func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) { return } -func (ctx *Context) writeJSON(v interface{}, options *JSON) (int, error) { +func (ctx *Context) writeJSON(v interface{}, options *JSON) error { ctx.ContentType(ContentJSONHeaderValue) // After content type given and before everything else, try handle proto or easyjson, no matter the performance mode. - if handled, n, err := ctx.handleSpecialJSONResponseValue(v, options); handled { - return n, err + if handled, _, err := ctx.handleSpecialJSONResponseValue(v, options); handled { + return err } - shouldOptimize := ctx.shouldOptimize() - if options == nil { - if shouldOptimize { - // If no options given and optimizations are enabled. - // write using the fast json marshaler with the http request context as soon as possible. - result, err := gojson.MarshalContext(ctx.request.Context(), v) - if err != nil { - return 0, err - } - - return ctx.Write(result) - } - - // Else if no options given neither optimizations are enabled, then safely read the already-initialized object. - options = &DefaultJSONOptions - } - - return WriteJSON(ctx, ctx.writer, v, options, shouldOptimize) + return WriteJSON(ctx, v, options) } var finishCallbackB = []byte(");") -// WriteJSONP marshals the given interface object and writes the JSON response to the writer. -func WriteJSONP(writer io.Writer, v interface{}, options JSONP, optimize bool) (int, error) { +// WriteJSONP marshals the given interface object and writes the JSONP response to the writer. +var WriteJSONP = func(ctx *Context, v interface{}, options *JSONP) (err error) { if callback := options.Callback; callback != "" { - n, err := writer.Write(stringToBytes(callback + "(")) + _, err = ctx.Write(stringToBytes(callback + "(")) if err != nil { - return n, err + return err } - defer writer.Write(finishCallbackB) + defer func() { + if err == nil { + ctx.Write(finishCallbackB) + } + }() } - if !optimize && options.Indent == "" { - options.Indent = " " - } - - if indent := options.Indent; indent != "" { - marshalIndent := json.MarshalIndent - if optimize { - // marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent - marshalIndent = gojson.MarshalIndent - } - - result, err := marshalIndent(v, "", indent) - if err != nil { - return 0, err - } - result = append(result, newLineB...) - return writer.Write(result) - } - - marshal := json.Marshal - if optimize { - // marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal - marshal = gojson.Marshal - } - - result, err := marshal(v) - if err != nil { - return 0, err - } - return writer.Write(result) + err = WriteJSON(ctx, v, &JSON{ + Indent: options.Indent, + OmitErrorHandler: options.OmitErrorHandler, + }) + return err } // DefaultJSONPOptions is the optional settings that are being used @@ -4114,14 +3958,16 @@ var DefaultJSONPOptions = JSONP{} // It reports any JSON parser or write errors back to the caller. // Look the Application.SetContextErrorHandler to override the // default status code 500 with a custom error response. -func (ctx *Context) JSONP(v interface{}, opts ...JSONP) (n int, err error) { - options := DefaultJSONPOptions +func (ctx *Context) JSONP(v interface{}, opts ...JSONP) (err error) { + var options *JSONP if len(opts) > 0 { - options = opts[0] + options = &opts[0] + } else { + options = &DefaultJSONPOptions } ctx.ContentType(ContentJavascriptHeaderValue) - if n, err = WriteJSONP(ctx.writer, v, options, ctx.shouldOptimize()); err != nil { + if err = WriteJSONP(ctx, v, options); err != nil { if !options.OmitErrorHandler { ctx.handleContextError(err) } @@ -4174,32 +4020,21 @@ func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { } // WriteXML marshals the given interface object and writes the XML response to the writer. -func WriteXML(writer io.Writer, v interface{}, options XML, optimize bool) (int, error) { +var WriteXML = func(ctx *Context, v interface{}, options *XML) error { if prefix := options.Prefix; prefix != "" { - n, err := writer.Write(stringToBytes(prefix)) + _, err := ctx.Write(stringToBytes(prefix)) if err != nil { - return n, err + return err } } - if !optimize && options.Indent == "" { - options.Indent = " " // Two spaces for XML is the default indentation when not optimized. + encoder := xml.NewEncoder(ctx.writer) + encoder.Indent("", options.Indent) + if err := encoder.Encode(v); err != nil { + return err } - if indent := options.Indent; indent != "" { - result, err := xml.MarshalIndent(v, "", indent) - if err != nil { - return 0, err - } - result = append(result, newLineB...) - return writer.Write(result) - } - - result, err := xml.Marshal(v) - if err != nil { - return 0, err - } - return writer.Write(result) + return encoder.Flush() } // DefaultXMLOptions is the optional settings that are being used @@ -4212,14 +4047,16 @@ var DefaultXMLOptions = XML{} // It reports any XML parser or write errors back to the caller. // Look the Application.SetContextErrorHandler to override the // default status code 500 with a custom error response. -func (ctx *Context) XML(v interface{}, opts ...XML) (n int, err error) { - options := DefaultXMLOptions +func (ctx *Context) XML(v interface{}, opts ...XML) (err error) { + var options *XML if len(opts) > 0 { - options = opts[0] + options = &opts[0] + } else { + options = &DefaultXMLOptions } ctx.ContentType(ContentXMLHeaderValue) - if n, err = WriteXML(ctx.writer, v, options, ctx.shouldOptimize()); err != nil { + if err = WriteXML(ctx, v, options); err != nil { if !options.OmitErrorHandler { ctx.handleContextError(err) } @@ -4239,7 +4076,7 @@ func (ctx *Context) XML(v interface{}, opts ...XML) (n int, err error) { // send a response of content type "application/problem+xml" instead. // // Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers -func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) (int, error) { +func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) error { options := DefaultProblemOptions if len(opts) > 0 { options = opts[0] @@ -4276,13 +4113,14 @@ func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) (int, error) } // WriteMarkdown parses the markdown to html and writes these contents to the writer. -func WriteMarkdown(writer io.Writer, markdownB []byte, options Markdown) (n int, err error) { +var WriteMarkdown = func(ctx *Context, markdownB []byte, options *Markdown) error { buf := blackfriday.Run(markdownB) if options.Sanitize { buf = bluemonday.UGCPolicy().SanitizeBytes(buf) } - return writer.Write(buf) + _, err := ctx.Write(buf) + return err } // DefaultMarkdownOptions is the optional settings that are being used @@ -4294,14 +4132,16 @@ var DefaultMarkdownOptions = Markdown{} // It reports any Markdown parser or write errors back to the caller. // Look the Application.SetContextErrorHandler to override the // default status code 500 with a custom error response. -func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (n int, err error) { - options := DefaultMarkdownOptions +func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (err error) { + var options *Markdown if len(opts) > 0 { - options = opts[0] + options = &opts[0] + } else { + options = &DefaultMarkdownOptions } ctx.ContentType(ContentHTMLHeaderValue) - if n, err = WriteMarkdown(ctx.writer, markdownB, options); err != nil { + if err = WriteMarkdown(ctx, markdownB, options); err != nil { if !options.OmitErrorHandler { ctx.handleContextError(err) } @@ -4310,31 +4150,46 @@ func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (n int, err err return } +// WriteYAML sends YAML response to the client. +var WriteYAML = func(ctx *Context, v interface{}, indentSpace int) error { + encoder := yaml.NewEncoder(ctx.writer) + encoder.SetIndent(indentSpace) + + if err := encoder.Encode(v); err != nil { + return err + } + + return encoder.Close() +} + // YAML marshals the given "v" value using the yaml marshaler and writes the result to the client. // // It reports any YAML parser or write errors back to the caller. // Look the Application.SetContextErrorHandler to override the // default status code 500 with a custom error response. -func (ctx *Context) YAML(v interface{}) (int, error) { - out, err := yaml.Marshal(v) - if err != nil { - ctx.handleContextError(err) - return 0, err - } - +func (ctx *Context) YAML(v interface{}) error { ctx.ContentType(ContentYAMLHeaderValue) - n, err := ctx.Write(out) + + err := WriteYAML(ctx, v, 0) if err != nil { ctx.handleContextError(err) + return err } - return n, err + return nil } // TextYAML calls the Context.YAML method but with the text/yaml content type instead. -func (ctx *Context) TextYAML(v interface{}) (int, error) { - ctx.contentTypeOnce(ContentYAMLTextHeaderValue, "") - return ctx.YAML(v) +func (ctx *Context) TextYAML(v interface{}) error { + ctx.ContentType(ContentYAMLTextHeaderValue) + + err := WriteYAML(ctx, v, 4) + if err != nil { + ctx.handleContextError(err) + return err + } + + return nil } // Protobuf marshals the given "v" value of proto Message and writes its result to the client. @@ -4583,19 +4438,54 @@ func (ctx *Context) Negotiate(v interface{}) (int, error) { case ContentTextHeaderValue, ContentHTMLHeaderValue: return ctx.WriteString(v.(string)) case ContentMarkdownHeaderValue: - return ctx.Markdown(v.([]byte)) + err := ctx.Markdown(v.([]byte)) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentJSONHeaderValue: - return ctx.JSON(v) + err := ctx.JSON(v) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue: - return ctx.Problem(v) + err := ctx.Problem(v) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentJavascriptHeaderValue: - return ctx.JSONP(v) + err := ctx.JSONP(v) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue: - return ctx.XML(v) + err := ctx.XML(v) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentYAMLHeaderValue: - return ctx.YAML(v) + err := ctx.YAML(v) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentYAMLTextHeaderValue: - return ctx.TextYAML(v) + err := ctx.TextYAML(v) + if err != nil { + return 0, err + } + + return ctx.writer.Written(), nil case ContentProtobufHeaderValue: msg, ok := v.(proto.Message) if !ok { @@ -5438,15 +5328,15 @@ const cookieOptionsContextKey = "iris.cookie.options" // cookies sent or received from the next Handler in the chain. // // Available builtin Cookie options are: -// * CookieAllowReclaim -// * CookieAllowSubdomains -// * CookieSecure -// * CookieHTTPOnly -// * CookieSameSite -// * CookiePath -// * CookieCleanPath -// * CookieExpires -// * CookieEncoding +// - CookieAllowReclaim +// - CookieAllowSubdomains +// - CookieSecure +// - CookieHTTPOnly +// - CookieSameSite +// - CookiePath +// - CookieCleanPath +// - CookieExpires +// - CookieEncoding // // Example at: https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie func (ctx *Context) AddCookieOptions(options ...CookieOption) { @@ -5554,8 +5444,9 @@ var SetCookieKVExpiration = 8760 * time.Hour // (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible) // ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath("")) // More: -// iris.CookieExpires(time.Duration) -// iris.CookieHTTPOnly(false) +// +// iris.CookieExpires(time.Duration) +// iris.CookieHTTPOnly(false) // // Examples: https://github.com/kataras/iris/tree/master/_examples/cookies/basic func (ctx *Context) SetCookieKV(name, value string, options ...CookieOption) { @@ -6102,11 +5993,11 @@ const userContextKey = "iris.user" // next handlers in the chain. // // The "i" input argument can be: -// - A value which completes the User interface -// - A map[string]interface{}. -// - A value which does not complete the whole User interface -// - A value which does not complete the User interface at all -// (only its `User().GetRaw` method is available). +// - A value which completes the User interface +// - A map[string]interface{}. +// - A value which does not complete the whole User interface +// - A value which does not complete the User interface at all +// (only its `User().GetRaw` method is available). // // Look the `User` method to retrieve it. func (ctx *Context) SetUser(i interface{}) error { @@ -6174,21 +6065,21 @@ func (ctx *Context) Deadline() (deadline time.Time, ok bool) { // // Done is provided for use in select statements: // -// // Stream generates values with DoSomething and sends them to out -// // until DoSomething returns an error or ctx.Done is closed. -// func Stream(ctx context.Context, out chan<- Value) error { -// for { -// v, err := DoSomething(ctx) -// if err != nil { -// return err -// } -// select { -// case <-ctx.Done(): -// return ctx.Err() -// case out <- v: -// } -// } -// } +// // Stream generates values with DoSomething and sends them to out +// // until DoSomething returns an error or ctx.Done is closed. +// func Stream(ctx context.Context, out chan<- Value) error { +// for { +// v, err := DoSomething(ctx) +// if err != nil { +// return err +// } +// select { +// case <-ctx.Done(): +// return ctx.Err() +// case out <- v: +// } +// } +// } // // See https://blog.golang.org/pipelines for more examples of how to use // a Done channel for cancellation. diff --git a/context/context_user.go b/context/context_user.go index d76ba6d4..3b138c05 100644 --- a/context/context_user.go +++ b/context/context_user.go @@ -166,10 +166,12 @@ func (u *SimpleUser) GetField(key string) (interface{}, error) { // UserMap can be used to convert a common map[string]interface{} to a User. // Usage: -// user := map[string]interface{}{ -// "username": "kataras", -// "age" : 27, -// } +// +// user := map[string]interface{}{ +// "username": "kataras", +// "age" : 27, +// } +// // ctx.SetUser(user) // OR // user := UserStruct{....} diff --git a/context/handler.go b/context/handler.go index 13f94128..60e03155 100644 --- a/context/handler.go +++ b/context/handler.go @@ -275,7 +275,6 @@ type Filter func(*Context) bool // Handlers here should act like middleware, they should contain `ctx.Next` to proceed // to the next handler of the chain. Those "handlers" are registered to the per-request context. // -// // It checks the "filter" and if passed then // it, correctly, executes the "handlers". // diff --git a/context/request_params.go b/context/request_params.go index b45dbe70..ea9b6c0b 100644 --- a/context/request_params.go +++ b/context/request_params.go @@ -131,11 +131,12 @@ func (r *RequestParams) GetIntUnslashed(key string) (int, bool) { // Key is the specific type, which should be unique. // The value is a function which accepts the parameter index // and it should return the value as the parameter type evaluator expects it. -// i.e [reflect.TypeOf("string")] = func(paramIndex int) interface{} { -// return func(ctx *Context) { -// return ctx.Params().GetEntryAt(paramIndex).ValueRaw.() -// } -// } +// +// i.e [reflect.TypeOf("string")] = func(paramIndex int) interface{} { +// return func(ctx *Context) { +// return ctx.Params().GetEntryAt(paramIndex).ValueRaw.() +// } +// } // // Read https://github.com/kataras/iris/tree/master/_examples/routing/macros for more details. // Checks for total available request parameters length @@ -261,7 +262,9 @@ var ParamResolvers = map[reflect.Type]func(paramIndex int) interface{}{ // and the parameter's index based on the registered path. // Usage: nameResolver := ParamResolverByKindAndKey(reflect.TypeOf(""), 0) // Inside a Handler: nameResolver.Call(ctx)[0] -// it will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros). +// +// it will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros). +// // It is only useful for dynamic binding of the parameter, it is used on "hero" package and it should be modified // only when Macros are modified in such way that the default selections for the available go std types are not enough. // diff --git a/context/response_recorder.go b/context/response_recorder.go index ab19c7aa..db3241ee 100644 --- a/context/response_recorder.go +++ b/context/response_recorder.go @@ -4,7 +4,7 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/textproto" "strconv" @@ -32,7 +32,7 @@ func releaseResponseRecorder(w *ResponseRecorder) { // A ResponseRecorder is used mostly for testing // in order to record and modify, if necessary, the body and status code and headers. // -// See `context.Recorder`` method too. +// See `context.Recorder“ method too. type ResponseRecorder struct { ResponseWriter @@ -366,7 +366,7 @@ func (w *ResponseRecorder) Result() *http.Response { // a modified copy of net/h } res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode)) if w.chunks != nil { - res.Body = ioutil.NopCloser(bytes.NewReader(w.chunks)) + res.Body = io.NopCloser(bytes.NewReader(w.chunks)) } else { res.Body = http.NoBody } diff --git a/context/status.go b/context/status.go index 59bd8ebe..af194872 100644 --- a/context/status.go +++ b/context/status.go @@ -111,7 +111,6 @@ func StatusText(code int) string { // Read more at `iris/Configuration#DisableAutoFireStatusCode` and // `iris/core/router/Party#OnAnyErrorCode` for relative information. // -// // Modify this variable when your Iris server or/and client // not follows the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html var StatusCodeNotSuccessful = func(statusCode int) bool { return statusCode >= 400 } diff --git a/core/handlerconv/from_std.go b/core/handlerconv/from_std.go index 404657b2..3bb8627f 100644 --- a/core/handlerconv/from_std.go +++ b/core/handlerconv/from_std.go @@ -10,9 +10,10 @@ import ( // FromStd converts native http.Handler & http.HandlerFunc to context.Handler. // // Supported form types: -// .FromStd(h http.Handler) -// .FromStd(func(w http.ResponseWriter, r *http.Request)) -// .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) +// +// .FromStd(h http.Handler) +// .FromStd(func(w http.ResponseWriter, r *http.Request)) +// .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) func FromStd(handler interface{}) context.Handler { switch h := handler.(type) { case context.Handler: diff --git a/core/memstore/memstore.go b/core/memstore/memstore.go index 1da39ca3..95322ece 100644 --- a/core/memstore/memstore.go +++ b/core/memstore/memstore.go @@ -730,7 +730,6 @@ func (e Entry) Value() interface{} { // Save same as `Set` // However, if "immutable" is true then saves it as immutable (same as `SetImmutable`). // -// // Returns the entry and true if it was just inserted, meaning that // it will return the entry and a false boolean if the entry exists and it has been updated. func (r *Store) Save(key string, value interface{}, immutable bool) (Entry, bool) { diff --git a/core/router/api_builder.go b/core/router/api_builder.go index f0a14874..c1f51240 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -377,8 +377,9 @@ func (api *APIBuilder) RegisterDependency(dependencies ...interface{}) { // (see ConfigureContainer().RegisterDependency) // or leave the framework to parse the request and fill the values accordingly. // The output of the "handlersFn" can be any output result: -// custom structs , string, []byte, int, error, -// a combination of the above, hero.Result(hero.View | hero.Response) and more. +// +// custom structs , string, []byte, int, error, +// a combination of the above, hero.Result(hero.View | hero.Response) and more. // // If more than one handler function is registered // then the execution happens without the nessecity of the `Context.Next` method, @@ -390,22 +391,22 @@ func (api *APIBuilder) RegisterDependency(dependencies ...interface{}) { // The client's request body and server's response body Go types. // Could be any data structure. // -// type ( -// request struct { -// Firstname string `json:"firstname"` -// Lastname string `json:"lastname"` -// } +// type ( +// request struct { +// Firstname string `json:"firstname"` +// Lastname string `json:"lastname"` +// } // -// response struct { -// ID uint64 `json:"id"` -// Message string `json:"message"` -// } -// ) +// response struct { +// ID uint64 `json:"id"` +// Message string `json:"message"` +// } +// ) // // Register the route hander. // -// HTTP VERB ROUTE PATH ROUTE HANDLER -// app.HandleFunc("PUT", "/users/{id:uint64}", updateUser) +// HTTP VERB ROUTE PATH ROUTE HANDLER +// app.HandleFunc("PUT", "/users/{id:uint64}", updateUser) // // Code the route handler function. // Path parameters and request body are binded @@ -413,26 +414,26 @@ func (api *APIBuilder) RegisterDependency(dependencies ...interface{}) { // The "id" uint64 binds to "{id:uint64}" route path parameter and // the "input" binds to client request data such as JSON. // -// func updateUser(id uint64, input request) response { -// // [custom logic...] +// func updateUser(id uint64, input request) response { +// // [custom logic...] // -// return response{ -// ID:id, -// Message: "User updated successfully", -// } -// } +// return response{ +// ID:id, +// Message: "User updated successfully", +// } +// } // // Simulate a client request which sends data // to the server and prints out the response. // -// curl --request PUT -d '{"firstname":"John","lastname":"Doe"}' \ -// -H "Content-Type: application/json" \ -// http://localhost:8080/users/42 +// curl --request PUT -d '{"firstname":"John","lastname":"Doe"}' \ +// -H "Content-Type: application/json" \ +// http://localhost:8080/users/42 // -// { -// "id": 42, -// "message": "User updated successfully" -// } +// { +// "id": 42, +// "message": "User updated successfully" +// } // // See the `ConfigureContainer` for more features regrading // the dependency injection, mvc and function handlers. @@ -474,11 +475,12 @@ func (api *APIBuilder) AllowMethods(methods ...string) Party { // For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what // even if no `ctx.Next()` is called in the previous handlers, including the begin(`Use`), // the main(`Handle`) and the done(`Done`) handlers themselves, then: -// Party#SetExecutionRules(iris.ExecutionRules { -// Begin: iris.ExecutionOptions{Force: true}, -// Main: iris.ExecutionOptions{Force: true}, -// Done: iris.ExecutionOptions{Force: true}, -// }) +// +// Party#SetExecutionRules(iris.ExecutionRules { +// Begin: iris.ExecutionOptions{Force: true}, +// Main: iris.ExecutionOptions{Force: true}, +// Done: iris.ExecutionOptions{Force: true}, +// }) // // Note that if : true then the only remained way to "break" the handler chain is by `ctx.StopExecution()` now that `ctx.Next()` does not matter. // @@ -567,11 +569,14 @@ func (api *APIBuilder) handle(errorCode int, method string, relativePath string, // otherwise use `Party` which can handle many paths with different handlers and middlewares. // // Usage: -// app.HandleMany("GET", "/user /user/{id:uint64} /user/me", genericUserHandler) +// +// app.HandleMany("GET", "/user /user/{id:uint64} /user/me", genericUserHandler) +// // At the other side, with `Handle` we've had to write: -// app.Handle("GET", "/user", userHandler) -// app.Handle("GET", "/user/{id:uint64}", userByIDHandler) -// app.Handle("GET", "/user/me", userMeHandler) +// +// app.Handle("GET", "/user", userHandler) +// app.Handle("GET", "/user/{id:uint64}", userByIDHandler) +// app.Handle("GET", "/user/me", userMeHandler) // // app.HandleMany("GET POST", "/path", handler) func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti string, handlers ...context.Handler) (routes []*Route) { @@ -606,7 +611,7 @@ func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti stri // // Alternatively, to get just the handler for that look the FileServer function instead. // -// api.HandleDir("/static", iris.Dir("./assets"), iris.DirOptions{IndexName: "/index.html", Compress: true}) +// api.HandleDir("/static", iris.Dir("./assets"), iris.DirOptions{IndexName: "/index.html", Compress: true}) // // Returns all the registered routes, including GET index and path patterm and HEAD. // @@ -904,12 +909,13 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P // Note: `iris#Party` and `core/router#Party` describes the exactly same interface. // // Usage: -// app.PartyFunc("/users", func(u iris.Party){ -// u.Use(authMiddleware, logMiddleware) -// u.Get("/", getAllUsers) -// u.Post("/", createOrUpdateUser) -// u.Delete("/", deleteUser) -// }) +// +// app.PartyFunc("/users", func(u iris.Party){ +// u.Use(authMiddleware, logMiddleware) +// u.Get("/", getAllUsers) +// u.Post("/", createOrUpdateUser) +// u.Delete("/", deleteUser) +// }) // // Look `Party` for more. func (api *APIBuilder) PartyFunc(relativePath string, partyBuilderFunc func(p Party)) Party { @@ -949,16 +955,21 @@ type ( // Useful when the api's dependencies amount are too much to pass on a function. // // Usage: -// app.PartyConfigure("/users", &api.UsersAPI{UserRepository: ..., ...}) +// +// app.PartyConfigure("/users", &api.UsersAPI{UserRepository: ..., ...}) +// // Where UsersAPI looks like: -// type UsersAPI struct { [...] } -// func(api *UsersAPI) Configure(router iris.Party) { -// router.Get("/{id:uuid}", api.getUser) -// [...] -// } +// +// type UsersAPI struct { [...] } +// func(api *UsersAPI) Configure(router iris.Party) { +// router.Get("/{id:uuid}", api.getUser) +// [...] +// } +// // Usage with (static) dependencies: -// app.RegisterDependency(userRepo, ...) -// app.PartyConfigure("/users", new(api.UsersAPI)) +// +// app.RegisterDependency(userRepo, ...) +// app.PartyConfigure("/users", new(api.UsersAPI)) func (api *APIBuilder) PartyConfigure(relativePath string, partyReg ...PartyConfigurator) Party { var child Party @@ -1626,15 +1637,16 @@ func (api *APIBuilder) RegisterView(viewEngine context.ViewEngine) { // FallbackView registers one or more fallback views for a template or a template layout. // Usage: -// FallbackView(iris.FallbackView("fallback.html")) -// FallbackView(iris.FallbackViewLayout("layouts/fallback.html")) -// OR -// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error { -// err.Name is the previous template name. -// err.IsLayout reports whether the failure came from the layout template. -// err.Data is the template data provided to the previous View call. -// [...custom logic e.g. ctx.View("fallback", err.Data)] -// }) +// +// FallbackView(iris.FallbackView("fallback.html")) +// FallbackView(iris.FallbackViewLayout("layouts/fallback.html")) +// OR +// FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error { +// err.Name is the previous template name. +// err.IsLayout reports whether the failure came from the layout template. +// err.Data is the template data provided to the previous View call. +// [...custom logic e.g. ctx.View("fallback", err.Data)] +// }) func (api *APIBuilder) FallbackView(provider context.FallbackViewProvider) { handler := func(ctx *context.Context) { ctx.FallbackView(provider) @@ -1653,9 +1665,10 @@ func (api *APIBuilder) FallbackView(provider context.FallbackViewProvider) { // app := iris.New() // app.RegisterView(iris.$VIEW_ENGINE("./views", ".$extension")) // my := app.Party("/my").Layout("layouts/mylayout.html") -// my.Get("/", func(ctx iris.Context) { -// ctx.View("page1.html") -// }) +// +// my.Get("/", func(ctx iris.Context) { +// ctx.View("page1.html") +// }) // // Examples: https://github.com/kataras/iris/tree/master/_examples/view func (api *APIBuilder) Layout(tmplLayoutFile string) Party { diff --git a/core/router/api_builder_benchmark_test.go b/core/router/api_builder_benchmark_test.go index b88b587d..d9b8015a 100644 --- a/core/router/api_builder_benchmark_test.go +++ b/core/router/api_builder_benchmark_test.go @@ -12,7 +12,6 @@ import ( "github.com/kataras/golog" ) -// // randStringBytesMaskImprSrc helps us to generate random paths for the test, // the below piece of code is external, as an answer to a stackoverflow question. // diff --git a/core/router/fs.go b/core/router/fs.go index d6315cbb..b83539d7 100644 --- a/core/router/fs.go +++ b/core/router/fs.go @@ -7,7 +7,6 @@ import ( "html" "html/template" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -1041,7 +1040,7 @@ func cacheFiles(ctx stdContext.Context, fs http.FileSystem, names []string, comp fi := newFileInfo(path.Base(name), inf.Mode(), inf.ModTime()) - contents, err := ioutil.ReadAll(f) + contents, err := io.ReadAll(f) f.Close() if err != nil { return err diff --git a/core/router/handler_execution_rules.go b/core/router/handler_execution_rules.go index fd94feb3..6c77af4f 100644 --- a/core/router/handler_execution_rules.go +++ b/core/router/handler_execution_rules.go @@ -6,9 +6,10 @@ import ( // ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves. // Usage: -// Party#SetExecutionRules(ExecutionRules { -// Done: ExecutionOptions{Force: true}, -// }) +// +// Party#SetExecutionRules(ExecutionRules { +// Done: ExecutionOptions{Force: true}, +// }) // // See `Party#SetExecutionRules` for more. type ExecutionRules struct { diff --git a/core/router/mime.go b/core/router/mime.go index 05e6db4c..b4c21bcd 100644 --- a/core/router/mime.go +++ b/core/router/mime.go @@ -560,9 +560,9 @@ func init() { // system's mime.types file(s) if available under one or more of these // names: // -// /etc/mime.types -// /etc/apache2/mime.types -// /etc/apache/mime.types +// /etc/mime.types +// /etc/apache2/mime.types +// /etc/apache/mime.types // // On Windows, MIME types are extracted from the registry. // diff --git a/core/router/route.go b/core/router/route.go index f35e2a98..e55a4419 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -491,8 +491,9 @@ func (r *Route) GetTitle() string { // Should be called after `Build` state. // // It prints the @method: @path (@description) (@route_rel_location) -// * @handler_name (@handler_rel_location) -// * @second_handler ... +// - @handler_name (@handler_rel_location) +// - @second_handler ... +// // If route and handler line:number locations are equal then the second is ignored. func (r *Route) Trace(w io.Writer, stoppedIndex int) { title := r.GetTitle() diff --git a/doc.go b/doc.go index 7f97f681..2e430d42 100644 --- a/doc.go +++ b/doc.go @@ -34,34 +34,33 @@ Easy to learn for new gophers and advanced features for experienced, it goes as Source code and other details for the project are available at GitHub: - https://github.com/kataras/iris + https://github.com/kataras/iris -Current Version +# Current Version -12.2.0-beta3 +12.2.0-beta4 -Installation +# Installation The only requirement is the Go Programming Language, at least version 1.18. - $ go get github.com/kataras/iris/v12@master + $ go get github.com/kataras/iris/v12@master Wiki: - https://github.com/kataras/iris/wiki + https://github.com/kataras/iris/wiki Examples: - https://github.com/kataras/iris/tree/master/_examples + https://github.com/kataras/iris/tree/master/_examples Middleware: - https://github.com/kataras/iris/tree/master/middleware - https://github.com/iris-contrib/middleware + https://github.com/kataras/iris/tree/master/middleware + https://github.com/iris-contrib/middleware Home Page: - https://iris-go.com - + https://iris-go.com */ package iris diff --git a/go.mod b/go.mod index 389e38a7..3951d662 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/fatih/structs v1.1.0 github.com/flosch/pongo2/v4 v4.0.2 github.com/go-redis/redis/v8 v8.11.5 - github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5 github.com/golang/snappy v0.0.4 github.com/google/uuid v1.3.0 github.com/gorilla/securecookie v1.1.1 diff --git a/go.sum b/go.sum index 895f1595..848f3f50 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,6 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= -github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5 h1:aeyOtISssR4sP36FAC9LV96PQqxzcbhz54EWv9U+ZGc= -github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= diff --git a/hero/container.go b/hero/container.go index ee3e392c..437366a1 100644 --- a/hero/container.go +++ b/hero/container.go @@ -326,23 +326,28 @@ func Handler(fn interface{}) context.Handler { // It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application, // as middleware or as simple route handler or subdomain's handler. // -// func(...) iris.Handler +// func(...) iris.Handler +// // - if are all static dependencies then // there is no reflection involved at serve-time. // -// func(pathParameter string, ...) +// func(pathParameter string, ...) +// // - one or more path parameters (e.g. :uid, :string, :int, :path, :uint64) // are automatically binded to the first input Go standard types (string, int, uint64 and e.t.c.) // -// func() error +// func() error +// // - if a function returns an error then this error's text is sent to the client automatically. // -// func() +// func() +// // - The result of the function is a dependency too. // If is a request-scope dependency (dynamic) then // this function will be called at every request. // -// func() +// func() +// // - If is static dependency (e.g. a database or a service) then its result // can be used as a static dependency to the next dependencies or to the controller/function itself. func (c *Container) Handler(fn interface{}) context.Handler { diff --git a/hero/func_result.go b/hero/func_result.go index ce925773..949d140a 100644 --- a/hero/func_result.go +++ b/hero/func_result.go @@ -27,11 +27,9 @@ func defaultResultHandler(ctx *context.Context, v interface{}) error { switch context.TrimHeaderValue(ctx.GetContentType()) { case context.ContentXMLHeaderValue, context.ContentXMLUnreadableHeaderValue: - _, err := ctx.XML(v) - return err + return ctx.XML(v) case context.ContentYAMLHeaderValue: - _, err := ctx.YAML(v) - return err + return ctx.YAML(v) case context.ContentProtobufHeaderValue: msg, ok := v.(proto.Message) if !ok { @@ -45,8 +43,7 @@ func defaultResultHandler(ctx *context.Context, v interface{}) error { return err default: // otherwise default to JSON. - _, err := ctx.JSON(v) - return err + return ctx.JSON(v) } } diff --git a/hero/func_result_test.go b/hero/func_result_test.go index dcb76dc9..09c7592c 100644 --- a/hero/func_result_test.go +++ b/hero/func_result_test.go @@ -92,7 +92,7 @@ func (e err) Dispatch(ctx iris.Context) { // write the status code based on the err's StatusCode. ctx.StatusCode(e.Status) // send to the client the whole object as json - _, _ = ctx.JSON(e) + _ = ctx.JSON(e) } func GetCustomErrorAsDispatcher() err { diff --git a/httptest/httptest.go b/httptest/httptest.go index f193e85e..3ff40ca4 100644 --- a/httptest/httptest.go +++ b/httptest/httptest.go @@ -98,9 +98,12 @@ func DefaultConfiguration() *Configuration { // New Prepares and returns a new test framework based on the "app". // Usage: -// httptest.New(t, app) +// +// httptest.New(t, app) +// // With options: -// httptest.New(t, app, httptest.URL(...), httptest.Debug(true), httptest.LogLevel("debug"), httptest.Strict(true)) +// +// httptest.New(t, app, httptest.URL(...), httptest.Debug(true), httptest.LogLevel("debug"), httptest.Strict(true)) // // Example at: https://github.com/kataras/iris/tree/master/_examples/testing/httptest. func New(t *testing.T, app *iris.Application, setters ...OptionSetter) *httpexpect.Expect { diff --git a/i18n/internal/locale.go b/i18n/internal/locale.go index 4d45ceeb..4ce5e567 100644 --- a/i18n/internal/locale.go +++ b/i18n/internal/locale.go @@ -152,7 +152,7 @@ func (loc *Locale) Tag() *language.Tag { } // Language should return the exact languagecode of this `Locale` -//that the user provided on `New` function. +// that the user provided on `New` function. // // Same as `Tag().String()` but it's static. func (loc *Locale) Language() string { diff --git a/i18n/loader.go b/i18n/loader.go index 662e2179..511b57db 100644 --- a/i18n/loader.go +++ b/i18n/loader.go @@ -3,7 +3,7 @@ package i18n import ( "encoding/json" "fmt" - "io/ioutil" + "os" "path/filepath" "strings" @@ -33,7 +33,7 @@ func Glob(globPattern string, options LoaderConfig) Loader { panic(err) } - return load(assetNames, ioutil.ReadFile, options) + return load(assetNames, os.ReadFile, options) } // Assets accepts a function that returns a list of filenames (physical or virtual), diff --git a/iris.go b/iris.go index 977fe02a..90245dfe 100644 --- a/iris.go +++ b/iris.go @@ -299,6 +299,7 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly { // Adding one or more outputs : app.Logger().AddOutput(io.Writer...) // // Adding custom levels requires import of the `github.com/kataras/golog` package: +// // First we create our level to a golog.Level // in order to be used in the Log functions. // var SuccessLevel golog.Level = 6 @@ -309,6 +310,7 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly { // // ColorfulText (Green Color[SUCC]) // ColorfulText: "\x1b[32m[SUCC]\x1b[0m", // } +// // Usage: // app.Logger().SetLevel("success") // app.Logger().Logf(SuccessLevel, "a custom leveled log message") @@ -437,12 +439,12 @@ func (app *Application) GetContextPool() *context.Pool { // // ExampleCode: // -// type contextErrorHandler struct{} -// func (e *contextErrorHandler) HandleContextError(ctx iris.Context, err error) { -// errors.InvalidArgument.Err(ctx, err) -// } -// ... -// app.SetContextErrorHandler(new(contextErrorHandler)) +// type contextErrorHandler struct{} +// func (e *contextErrorHandler) HandleContextError(ctx iris.Context, err error) { +// errors.InvalidArgument.Err(ctx, err) +// } +// ... +// app.SetContextErrorHandler(new(contextErrorHandler)) func (app *Application) SetContextErrorHandler(errHandler context.ErrorHandler) *Application { app.contextErrorHandler = errHandler return app @@ -653,9 +655,9 @@ func (app *Application) Shutdown(ctx stdContext.Context) error { // // import "github.com/kataras/iris/v12/core/errgroup" // -// errgroup.Walk(app.Build(), func(typ interface{}, err error) { -// app.Logger().Errorf("%s: %s", typ, err) -// }) +// errgroup.Walk(app.Build(), func(typ interface{}, err error) { +// app.Logger().Errorf("%s: %s", typ, err) +// }) func (app *Application) Build() error { if app.builded { return nil diff --git a/iris_guide.go b/iris_guide.go index cccce2df..d6d43d8c 100644 --- a/iris_guide.go +++ b/iris_guide.go @@ -237,7 +237,7 @@ type ( Step3 interface { // Health enables the /health route. // If "env" and "developer" are given, these fields will be populated to the client - // through headers and environemnt on health route. + // through headers and environment on health route. Health(b bool, env, developer string) Step4 } diff --git a/macro/handler/handler.go b/macro/handler/handler.go index 1c5fe144..948a8c0b 100644 --- a/macro/handler/handler.go +++ b/macro/handler/handler.go @@ -18,7 +18,9 @@ import ( // Note that the builtin macros return error too, but they're handled // by the `else` literal (error code). To change this behavior // and send a custom error response you have to register it: -// app.Macros().Get("uuid").HandleError(func(ctx iris.Context, paramIndex int, err error)). +// +// app.Macros().Get("uuid").HandleError(func(ctx iris.Context, paramIndex int, err error)). +// // You can also set custom macros by `app.Macros().Register`. // // See macro.HandleError to set it. diff --git a/middleware/accesslog/accesslog_test.go b/middleware/accesslog/accesslog_test.go index 315285d2..87601315 100644 --- a/middleware/accesslog/accesslog_test.go +++ b/middleware/accesslog/accesslog_test.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net/http" "net/http/httptest" "strings" @@ -324,7 +323,7 @@ func BenchmarkAccessLogAfterPrint(b *testing.B) { } func benchmarkAccessLogAfter(b *testing.B, withLogStruct, async bool) { - ac := New(ioutil.Discard) + ac := New(io.Discard) ac.Clock = TClock(time.Time{}) ac.BytesReceived = false ac.BytesReceivedBody = false diff --git a/middleware/accesslog/log.go b/middleware/accesslog/log.go index 517df92c..ca88aa9f 100644 --- a/middleware/accesslog/log.go +++ b/middleware/accesslog/log.go @@ -11,6 +11,7 @@ import ( ) // Log represents the log data specifically for the accesslog middleware. +// //easyjson:json type Log struct { // The AccessLog instance this Log was created of. diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index 97fbb85e..83b783ba 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -52,8 +52,9 @@ type ErrorHandler func(ctx *context.Context, err error) // The only required value is the Allow field. // // Usage: -// opts := Options { ... } -// auth := New(opts) +// +// opts := Options { ... } +// auth := New(opts) type Options struct { // Realm directive, read http://tools.ietf.org/html/rfc2617#section-1.2 for details. // E.g. "Authorization Required". @@ -171,17 +172,18 @@ type BasicAuth struct { // The result should be used to wrap an existing handler or the HTTP application's root router. // // Example Code: -// opts := basicauth.Options{ -// Realm: basicauth.DefaultRealm, -// ErrorHandler: basicauth.DefaultErrorHandler, -// MaxAge: 2 * time.Hour, -// GC: basicauth.GC{ -// Every: 3 * time.Hour, -// }, -// Allow: basicauth.AllowUsers(users), -// } -// auth := basicauth.New(opts) -// app.Use(auth) +// +// opts := basicauth.Options{ +// Realm: basicauth.DefaultRealm, +// ErrorHandler: basicauth.DefaultErrorHandler, +// MaxAge: 2 * time.Hour, +// GC: basicauth.GC{ +// Every: 3 * time.Hour, +// }, +// Allow: basicauth.AllowUsers(users), +// } +// auth := basicauth.New(opts) +// app.Use(auth) // // Access the user in the route handler with: ctx.User().GetRaw().(*myCustomType). // @@ -238,16 +240,18 @@ func New(opts Options) context.Handler { // are required as they are compared against the user input // when access to protected resource is requested. // A user list can defined with one of the following values: -// map[string]string form of: {username:password, ...} -// map[string]interface{} form of: {"username": {"password": "...", "other_field": ...}, ...} -// []T which T completes the User interface, where T is a struct value -// []T which T contains at least Username and Password fields. +// +// map[string]string form of: {username:password, ...} +// map[string]interface{} form of: {"username": {"password": "...", "other_field": ...}, ...} +// []T which T completes the User interface, where T is a struct value +// []T which T contains at least Username and Password fields. // // Usage: -// auth := Default(map[string]string{ -// "admin": "admin", -// "john": "p@ss", -// }) +// +// auth := Default(map[string]string{ +// "admin": "admin", +// "john": "p@ss", +// }) func Default(users interface{}, userOpts ...UserAuthOption) context.Handler { opts := Options{ Realm: DefaultRealm, @@ -260,7 +264,8 @@ func Default(users interface{}, userOpts ...UserAuthOption) context.Handler { // a filename to load the users from. // // Usage: -// auth := Load("users.yml") +// +// auth := Load("users.yml") func Load(jsonOrYamlFilename string, userOpts ...UserAuthOption) context.Handler { opts := Options{ Realm: DefaultRealm, diff --git a/middleware/basicauth/user.go b/middleware/basicauth/user.go index 4f66c1dc..06140e2e 100644 --- a/middleware/basicauth/user.go +++ b/middleware/basicauth/user.go @@ -3,7 +3,7 @@ package basicauth import ( "encoding/json" "fmt" - "io/ioutil" + "os" "reflect" "strings" @@ -16,8 +16,8 @@ import ( // ReadFile can be used to customize the way the // AllowUsersFile function is loading the filename from. // Example of usage: embedded users.yml file. -// Defaults to the `ioutil.ReadFile` which reads the file from the physical disk. -var ReadFile = ioutil.ReadFile +// Defaults to the `os.ReadFile` which reads the file from the physical disk. +var ReadFile = os.ReadFile // User is a partial part of the iris.User interface. // It's used to declare a static slice of registered User for authentication. @@ -48,10 +48,11 @@ type UserAuthOption func(*UserAuthOptions) // See https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf. // // Usage: -// Default(..., BCRYPT) OR -// Load(..., BCRYPT) OR -// Options.Allow = AllowUsers(..., BCRYPT) OR -// OPtions.Allow = AllowUsersFile(..., BCRYPT) +// +// Default(..., BCRYPT) OR +// Load(..., BCRYPT) OR +// Options.Allow = AllowUsers(..., BCRYPT) OR +// OPtions.Allow = AllowUsersFile(..., BCRYPT) func BCRYPT(opts *UserAuthOptions) { opts.ComparePassword = func(stored, userPassword string) bool { err := bcrypt.CompareHashAndPassword([]byte(stored), []byte(userPassword)) @@ -75,10 +76,11 @@ func toUserAuthOptions(opts []UserAuthOption) (options UserAuthOptions) { // AllowUsers is an AuthFunc which authenticates user input based on a (static) user list. // The "users" input parameter can be one of the following forms: -// map[string]string e.g. {username: password, username: password...}. -// []map[string]interface{} e.g. []{"username": "...", "password": "...", "other_field": ...}, ...}. -// []T which T completes the User interface. -// []T which T contains at least Username and Password fields. +// +// map[string]string e.g. {username: password, username: password...}. +// []map[string]interface{} e.g. []{"username": "...", "password": "...", "other_field": ...}, ...}. +// []T which T completes the User interface. +// []T which T contains at least Username and Password fields. // // Usage: // New(Options{Allow: AllowUsers(..., [BCRYPT])}) @@ -155,15 +157,17 @@ func userMap(usernamePassword map[string]string, opts ...UserAuthOption) AuthFun // loaded from a file on initialization. // // Example Code: -// New(Options{Allow: AllowUsersFile("users.yml", BCRYPT)}) +// +// New(Options{Allow: AllowUsersFile("users.yml", BCRYPT)}) +// // The users.yml file looks like the following: -// - username: kataras -// password: kataras_pass -// age: 27 -// role: admin -// - username: makis -// password: makis_password -// ... +// - username: kataras +// password: kataras_pass +// age: 27 +// role: admin +// - username: makis +// password: makis_password +// ... func AllowUsersFile(jsonOrYamlFilename string, opts ...UserAuthOption) AuthFunc { var ( usernamePassword map[string]string diff --git a/middleware/basicauth/user_test.go b/middleware/basicauth/user_test.go index 3ce80a12..b80919bc 100644 --- a/middleware/basicauth/user_test.go +++ b/middleware/basicauth/user_test.go @@ -2,7 +2,6 @@ package basicauth import ( "errors" - "io/ioutil" "os" "reflect" "testing" @@ -164,7 +163,7 @@ func TestAllowUsers(t *testing.T) { // Test YAML user loading with b-encrypted passwords. func TestAllowUsersFile(t *testing.T) { - f, err := ioutil.TempFile("", "*users.yml") + f, err := os.CreateTemp("", "*users.yml") if err != nil { t.Fatal(err) } diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 3acb1a82..e6b31ca5 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -85,17 +85,18 @@ type ( // please refer to: https://github.com/iris-contrib/middleware repository instead. // // Example Code: -// import "github.com/kataras/iris/v12/middleware/cors" -// import "github.com/kataras/iris/v12/x/errors" // -// app.UseRouter(cors.New(). -// HandleErrorFunc(func(ctx iris.Context, err error) { -// errors.FailedPrecondition.Err(ctx, err) -// }). -// ExtractOriginFunc(cors.StrictOriginExtractor). -// ReferrerPolicy(cors.NoReferrerWhenDowngrade). -// AllowOrigin("domain1.com,domain2.com,domain3.com"). -// Handler()) +// import "github.com/kataras/iris/v12/middleware/cors" +// import "github.com/kataras/iris/v12/x/errors" +// +// app.UseRouter(cors.New(). +// HandleErrorFunc(func(ctx iris.Context, err error) { +// errors.FailedPrecondition.Err(ctx, err) +// }). +// ExtractOriginFunc(cors.StrictOriginExtractor). +// ReferrerPolicy(cors.NoReferrerWhenDowngrade). +// AllowOrigin("domain1.com,domain2.com,domain3.com"). +// Handler()) func New() *CORS { return &CORS{ extractOriginFunc: DefaultOriginExtractor, diff --git a/middleware/grpc/grpc.go b/middleware/grpc/grpc.go index b073fdf1..8da66ac0 100644 --- a/middleware/grpc/grpc.go +++ b/middleware/grpc/grpc.go @@ -13,11 +13,12 @@ import ( // The Iris server SHOULD run under HTTP/2 and clients too. // // Usage: -// import grpcWrapper "github.com/kataras/iris/v12/middleware/grpc" -// [...] -// app := iris.New() -// grpcServer := grpc.NewServer() -// app.WrapRouter(grpcWrapper.New(grpcServer)) +// +// import grpcWrapper "github.com/kataras/iris/v12/middleware/grpc" +// [...] +// app := iris.New() +// grpcServer := grpc.NewServer() +// app.WrapRouter(grpcWrapper.New(grpcServer)) func New(grpcServer http.Handler) router.WrapperFunc { return func(w http.ResponseWriter, r *http.Request, mux http.HandlerFunc) { if r.ProtoMajor == 2 && strings.HasPrefix(r.Header.Get("Content-Type"), "application/grpc") { diff --git a/middleware/hcaptcha/hcaptcha.go b/middleware/hcaptcha/hcaptcha.go index e6cb41a3..54294df6 100644 --- a/middleware/hcaptcha/hcaptcha.go +++ b/middleware/hcaptcha/hcaptcha.go @@ -3,7 +3,7 @@ package hcaptcha import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "net/url" @@ -70,7 +70,9 @@ func New(secret string, options ...Option) context.Handler { // Handler is the HTTP route middleware featured hcaptcha validation. // It calls the `SiteVerify` method and fires the "next" when user completed the hcaptcha successfully, -// otherwise it calls the Client's `FailureHandler`. +// +// otherwise it calls the Client's `FailureHandler`. +// // The hcaptcha's `Response` (which contains any `ErrorCodes`) // is saved on the Request's Context (see `GetResponseFromContext`). func (c *Client) Handler(ctx *context.Context) { @@ -113,7 +115,7 @@ func SiteVerify(ctx *context.Context, secret string) (response Response) { return } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { response.ErrorCodes = append(response.ErrorCodes, err.Error()) diff --git a/middleware/jwt/blocklist/redis/blocklist.go b/middleware/jwt/blocklist/redis/blocklist.go index 74b63cd8..af389373 100644 --- a/middleware/jwt/blocklist/redis/blocklist.go +++ b/middleware/jwt/blocklist/redis/blocklist.go @@ -55,14 +55,14 @@ var _ jwt.Blocklist = (*Blocklist)(nil) // // Usage: // -// blocklist := NewBlocklist() -// blocklist.ClientOptions.Addr = ... -// err := blocklist.Connect() +// blocklist := NewBlocklist() +// blocklist.ClientOptions.Addr = ... +// err := blocklist.Connect() // // And register it: // -// verifier := jwt.NewVerifier(...) -// verifier.Blocklist = blocklist +// verifier := jwt.NewVerifier(...) +// verifier.Blocklist = blocklist func NewBlocklist() *Blocklist { return &Blocklist{ GetKey: defaultGetKey, diff --git a/middleware/jwt/signer.go b/middleware/jwt/signer.go index 583f8c00..3eca028a 100644 --- a/middleware/jwt/signer.go +++ b/middleware/jwt/signer.go @@ -31,8 +31,8 @@ type Signer struct { // // Usage: // -// signer := NewSigner(HS256, secret, 15*time.Minute) -// token, err := signer.Sign(userClaims{Username: "kataras"}) +// signer := NewSigner(HS256, secret, 15*time.Minute) +// token, err := signer.Sign(userClaims{Username: "kataras"}) func NewSigner(signatureAlg Alg, signatureKey interface{}, maxAge time.Duration) *Signer { if signatureAlg == HS256 { // A tiny helper if the end-developer uses string instead of []byte for hmac keys. diff --git a/middleware/jwt/verifier.go b/middleware/jwt/verifier.go index b5fd75c0..dacfcba5 100644 --- a/middleware/jwt/verifier.go +++ b/middleware/jwt/verifier.go @@ -61,24 +61,31 @@ type Verifier struct { // // Usage: // -// verifier := NewVerifier(HS256, secret) -// OR -// verifier := NewVerifier(HS256, secret, Expected{Issuer: "my-app"}) +// verifier := NewVerifier(HS256, secret) // -// claimsGetter := func() interface{} { return new(userClaims) } -// middleware := verifier.Verify(claimsGetter) // OR -// middleware := verifier.Verify(claimsGetter, Expected{Issuer: "my-app"}) +// +// verifier := NewVerifier(HS256, secret, Expected{Issuer: "my-app"}) +// +// claimsGetter := func() interface{} { return new(userClaims) } +// middleware := verifier.Verify(claimsGetter) +// +// OR +// +// middleware := verifier.Verify(claimsGetter, Expected{Issuer: "my-app"}) // // Register the middleware, e.g. -// app.Use(middleware) +// +// app.Use(middleware) // // Get the claims: -// claims := jwt.Get(ctx).(*userClaims) -// username := claims.Username +// +// claims := jwt.Get(ctx).(*userClaims) +// username := claims.Username // // Get the context user: -// username, err := ctx.User().GetUsername() +// +// username, err := ctx.User().GetUsername() func NewVerifier(signatureAlg Alg, signatureKey interface{}, validators ...TokenValidator) *Verifier { if signatureAlg == HS256 { // A tiny helper if the end-developer uses string instead of []byte for hmac keys. diff --git a/middleware/methodoverride/methodoverride.go b/middleware/methodoverride/methodoverride.go index 032b6833..68563f17 100644 --- a/middleware/methodoverride/methodoverride.go +++ b/middleware/methodoverride/methodoverride.go @@ -166,11 +166,12 @@ func Query(paramName string) Option { // to determinate the method to override with. // // Use cases: -// 1. When need to check only for headers and ignore other fields: -// New(Only(Headers("X-Custom-Header"))) // -// 2. When need to check only for (first) form field and (second) custom getter: -// New(Only(FormField("fieldName"), Getter(...))) +// 1. When need to check only for headers and ignore other fields: +// New(Only(Headers("X-Custom-Header"))) +// +// 2. When need to check only for (first) form field and (second) custom getter: +// New(Only(FormField("fieldName"), Getter(...))) func Only(o ...Option) Option { return func(opts *options) { opts.getters = opts.getters[0:0] @@ -185,7 +186,6 @@ func Only(o ...Option) Option { // that do not support certain HTTP operations such as DELETE or PUT for security reasons. // This wrapper will accept a method, based on criteria, to override the POST method with. // -// // Read more at: // https://github.com/kataras/iris/issues/1325 func New(opt ...Option) router.WrapperFunc { diff --git a/middleware/modrevision/modrevision.go b/middleware/modrevision/modrevision.go index 198339c8..fc3d4655 100644 --- a/middleware/modrevision/modrevision.go +++ b/middleware/modrevision/modrevision.go @@ -34,12 +34,13 @@ type Options struct { // for security reasons. // // Example Code: -// app.Get("/health", modrevision.New(modrevision.Options{ -// ServerName: "Iris Server", -// Env: "development", -// Developer: "kataras", -// TimeLocation: time.FixedZone("Greece/Athens", 10800), -// })) +// +// app.Get("/health", modrevision.New(modrevision.Options{ +// ServerName: "Iris Server", +// Env: "development", +// Developer: "kataras", +// TimeLocation: time.FixedZone("Greece/Athens", 7200), +// })) func New(opts Options) context.Handler { buildTime, buildRevision := context.BuildTime, context.BuildRevision if opts.UnixTime { diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index ec9b83ee..385c069e 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -41,7 +41,8 @@ var profileDescriptions = map[string]string{ // New returns a new pprof (profile, cmdline, symbol, goroutine, heap, threadcreate, debug/block) Middleware. // Note: Route MUST have the last named parameter wildcard named '{action:path}'. // Example: -// app.HandleMany("GET", "/debug/pprof /debug/pprof/{action:path}", pprof.New()) +// +// app.HandleMany("GET", "/debug/pprof /debug/pprof/{action:path}", pprof.New()) func New() context.Handler { return func(ctx *context.Context) { if action := ctx.Params().Get("action"); action != "" { diff --git a/middleware/recaptcha/recaptcha.go b/middleware/recaptcha/recaptcha.go index 291e0995..def402a5 100644 --- a/middleware/recaptcha/recaptcha.go +++ b/middleware/recaptcha/recaptcha.go @@ -3,7 +3,7 @@ package recaptcha import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/url" "time" @@ -50,7 +50,9 @@ func New(secret string) context.Handler { } // SiteVerify accepts context and the secret key(https://www.google.com/recaptcha) -// and returns the google's recaptcha response, if `response.Success` is true +// +// and returns the google's recaptcha response, if `response.Success` is true +// // then validation passed. // // Use `New` for middleware use instead. @@ -74,7 +76,7 @@ func SiteVerify(ctx *context.Context, secret string) (response Response) { return } - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) r.Body.Close() if err != nil { response.ErrorCodes = append(response.ErrorCodes, err.Error()) @@ -113,22 +115,24 @@ var recaptchaForm = `
// Example Code: // // Method: "POST" | Path: "/contact" -// func postContact(ctx *context.Context) { -// // [...] -// response := recaptcha.SiteVerify(ctx, recaptchaSecret) // -// if response.Success { -// // [your action here, i.e sendEmail(...)] -// } +// func postContact(ctx *context.Context) { +// // [...] +// response := recaptcha.SiteVerify(ctx, recaptchaSecret) // -// ctx.JSON(response) -// } +// if response.Success { +// // [your action here, i.e sendEmail(...)] +// } +// +// ctx.JSON(response) +// } // // Method: "GET" | Path: "/contact" -// func getContact(ctx *context.Context) { -// // render the recaptcha form -// ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, "/contact")) -// } +// +// func getContact(ctx *context.Context) { +// // render the recaptcha form +// ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, "/contact")) +// } func GetFormHTML(dataSiteKey string, postActionRelativePath string) string { return fmt.Sprintf(recaptchaForm, postActionRelativePath, dataSiteKey) } diff --git a/mvc/controller.go b/mvc/controller.go index cedbfca0..30d3d284 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -384,9 +384,11 @@ func (c *ControllerActivator) handleHTTPError(funcName string) *router.Route { // // Just like `Party#HandleMany`:, it returns the `[]*router.Routes`. // Usage: -// func (*Controller) BeforeActivation(b mvc.BeforeActivation) { -// b.HandleMany("GET", "/path /path1" /path2", "HandlePath") -// } +// +// func (*Controller) BeforeActivation(b mvc.BeforeActivation) { +// b.HandleMany("GET", "/path /path1" /path2", "HandlePath") +// } +// // HandleMany will override any routes of this "funcName". func (c *ControllerActivator) HandleMany(method, path, funcName string, middleware ...context.Handler) []*router.Route { return c.handleMany(method, path, funcName, true, middleware...) diff --git a/mvc/mvc.go b/mvc/mvc.go index 7b07731d..9fd1a981 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -86,7 +86,8 @@ func New(party router.Party) *Application { // this function simply calls the `New(party)` and its `.Configure(configurators...)`. // // A call of `mvc.New(app.Party("/path").Configure(buildMyMVC)` is equal to -// `mvc.Configure(app.Party("/path"), buildMyMVC)`. +// +// `mvc.Configure(app.Party("/path"), buildMyMVC)`. // // Read more at `New() Application` and `Application#Configure` methods. func Configure(party router.Party, configurators ...func(*Application)) *Application { diff --git a/mvc/versioning.go b/mvc/versioning.go index fcaf3078..e7297108 100644 --- a/mvc/versioning.go +++ b/mvc/versioning.go @@ -10,13 +10,13 @@ import ( // It requires a specific "version" constraint for a Controller, // e.g. ">1.0.0 <=2.0.0". // -// // Usage: -// m := mvc.New(dataRouter) -// m.Handle(new(v1Controller), mvc.Version("1.0.0"), mvc.Deprecated(mvc.DeprecationOptions{})) -// m.Handle(new(v2Controller), mvc.Version("2.3.0")) -// m.Handle(new(v3Controller), mvc.Version(">=3.0.0 <4.0.0")) -// m.Handle(new(noVersionController)) +// +// m := mvc.New(dataRouter) +// m.Handle(new(v1Controller), mvc.Version("1.0.0"), mvc.Deprecated(mvc.DeprecationOptions{})) +// m.Handle(new(v2Controller), mvc.Version("2.3.0")) +// m.Handle(new(v3Controller), mvc.Version(">=3.0.0 <4.0.0")) +// m.Handle(new(noVersionController)) // // See the `versioning` package's documentation for more information on // how the version is extracted from incoming requests. diff --git a/sessions/config.go b/sessions/config.go index a15fe6b8..fad2eeab 100644 --- a/sessions/config.go +++ b/sessions/config.go @@ -78,8 +78,13 @@ func (c Config) Validate() Config { } if c.SessionIDGenerator == nil { - c.SessionIDGenerator = func(*context.Context) string { - id, _ := uuid.NewRandom() + c.SessionIDGenerator = func(ctx *context.Context) string { + id, err := uuid.NewRandom() + if err != nil { + ctx.StopWithError(400, err) + return "" + } + return id.String() } } diff --git a/sessions/database.go b/sessions/database.go index ef6d054e..071d292b 100644 --- a/sessions/database.go +++ b/sessions/database.go @@ -94,24 +94,28 @@ func (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil } // immutable depends on the store, it may not implement it at all. func (s *mem) Set(sid string, key string, value interface{}, _ time.Duration, immutable bool) error { s.mu.RLock() - s.values[sid].Save(key, value, immutable) + store, ok := s.values[sid] s.mu.RUnlock() + if ok { + store.Save(key, value, immutable) + } return nil } func (s *mem) Get(sid string, key string) interface{} { s.mu.RLock() - v := s.values[sid].Get(key) + store, ok := s.values[sid] s.mu.RUnlock() + if ok { + return store.Get(key) + } - return v + return nil } func (s *mem) Decode(sid string, key string, outPtr interface{}) error { - s.mu.RLock() - v := s.values[sid].Get(key) - s.mu.RUnlock() + v := s.Get(sid, key) if v != nil { reflect.ValueOf(outPtr).Set(reflect.ValueOf(v)) } @@ -119,29 +123,45 @@ func (s *mem) Decode(sid string, key string, outPtr interface{}) error { } func (s *mem) Visit(sid string, cb func(key string, value interface{})) error { - s.values[sid].Visit(cb) + s.mu.RLock() + store, ok := s.values[sid] + s.mu.RUnlock() + if ok { + store.Visit(cb) + } + return nil } func (s *mem) Len(sid string) int { s.mu.RLock() - n := s.values[sid].Len() + store, ok := s.values[sid] s.mu.RUnlock() + if ok { + return store.Len() + } - return n + return 0 } func (s *mem) Delete(sid string, key string) (deleted bool) { s.mu.RLock() - deleted = s.values[sid].Remove(key) + store, ok := s.values[sid] s.mu.RUnlock() + if ok { + deleted = store.Remove(key) + } + return } func (s *mem) Clear(sid string) error { - s.mu.Lock() - s.values[sid].Reset() - s.mu.Unlock() + s.mu.RLock() + store, ok := s.values[sid] + s.mu.RUnlock() + if ok { + store.Reset() + } return nil } @@ -150,7 +170,6 @@ func (s *mem) Release(sid string) error { s.mu.Lock() delete(s.values, sid) s.mu.Unlock() - return nil } diff --git a/sessions/provider.go b/sessions/provider.go index 54493ad0..04005354 100644 --- a/sessions/provider.go +++ b/sessions/provider.go @@ -50,7 +50,6 @@ func (p *provider) newSession(man *Sessions, sid string, expires time.Duration) sid: sid, Man: man, provider: p, - flashes: make(map[string]*flashMessage), } onExpire := func() { @@ -99,9 +98,10 @@ func (p *provider) EndRequest(ctx *context.Context, session *Session) { // ErrNotFound may be returned from `UpdateExpiration` of a non-existing or // invalid session entry from memory storage or databases. // Usage: -// if err != nil && err.Is(err, sessions.ErrNotFound) { -// [handle error...] -// } +// +// if err != nil && err.Is(err, sessions.ErrNotFound) { +// [handle error...] +// } var ErrNotFound = errors.New("session not found") // UpdateExpiration resets the expiration of a session. diff --git a/sessions/session.go b/sessions/session.go index deb76122..068b8653 100644 --- a/sessions/session.go +++ b/sessions/session.go @@ -76,6 +76,7 @@ func (s *Session) runFlashGC() { delete(s.flashes, key) } } + s.mu.Unlock() } @@ -558,6 +559,10 @@ func (s *Session) SetImmutable(key string, value interface{}) { // If you want to define more than one flash messages, you will have to use different keys. func (s *Session) SetFlash(key string, value interface{}) { s.mu.Lock() + if s.flashes == nil { + s.flashes = make(map[string]*flashMessage) + } + s.flashes[key] = &flashMessage{value: value} s.mu.Unlock() } diff --git a/sessions/sessions_test.go b/sessions/sessions_test.go index f9c4b2cf..414c8f11 100644 --- a/sessions/sessions_test.go +++ b/sessions/sessions_test.go @@ -35,7 +35,7 @@ func testSessions(t *testing.T, app *iris.Application) { s := sessions.Get(ctx) sessValues := s.GetAll() - _, err := ctx.JSON(sessValues) + err := ctx.JSON(sessValues) if err != nil { t.Fatal(err) } @@ -141,8 +141,7 @@ func TestFlashMessages(t *testing.T) { } writeValues := func(ctx *context.Context, values map[string]interface{}) error { - _, err := ctx.JSON(values) - return err + return ctx.JSON(values) } app.Post("/set", func(ctx *context.Context) { diff --git a/versioning/group.go b/versioning/group.go index 10390133..93959d37 100644 --- a/versioning/group.go +++ b/versioning/group.go @@ -30,25 +30,31 @@ type Group struct { // any changes to its parent won't affect this one (e.g. register global middlewares afterwards). // // A version is extracted through the versioning.GetVersion function: -// Accept-Version: 1.0.0 -// Accept: application/json; version=1.0.0 -// You can customize it by setting a version based on the request context: -// api.Use(func(ctx *context.Context) { -// if version := ctx.URLParam("version"); version != "" { -// SetVersion(ctx, version) -// } // -// ctx.Next() -// }) +// Accept-Version: 1.0.0 +// Accept: application/json; version=1.0.0 +// +// You can customize it by setting a version based on the request context: +// +// api.Use(func(ctx *context.Context) { +// if version := ctx.URLParam("version"); version != "" { +// SetVersion(ctx, version) +// } +// +// ctx.Next() +// }) +// // OR: -// api.Use(versioning.FromQuery("version", "")) +// +// api.Use(versioning.FromQuery("version", "")) // // Examples at: _examples/routing/versioning // Usage: -// app := iris.New() -// api := app.Party("/api") -// v1 := versioning.NewGroup(api, ">=1.0.0 <2.0.0") -// v1.Get/Post/Put/Delete... +// +// app := iris.New() +// api := app.Party("/api") +// v1 := versioning.NewGroup(api, ">=1.0.0 <2.0.0") +// v1.Get/Post/Put/Delete... // // Valid ranges are: // - "<1.0.0" @@ -61,8 +67,10 @@ type Group struct { // A Range can consist of multiple ranges separated by space: // Ranges can be linked by logical AND: // - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" +// // but not "1.0.0" or "2.0.0" // - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 +// // except 2.0.3-beta.2 // // Ranges can also be linked by logical OR: @@ -72,7 +80,8 @@ type Group struct { // // Ranges can be combined by both AND and OR // -// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, +// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, +// // but not `4.2.1`, `2.1.1` func NewGroup(r API, version string) *Group { version = strings.ReplaceAll(version, ",", " ") diff --git a/versioning/version.go b/versioning/version.go index cf2adc87..9edf1811 100644 --- a/versioning/version.go +++ b/versioning/version.go @@ -187,11 +187,13 @@ func GetVersion(ctx *context.Context) string { // It can be used inside a middleware. // Example of how you can change the default behavior to extract a requested version (which is by headers) // from a "version" url parameter instead: -// func(ctx iris.Context) { // &version=1 -// version := ctx.URLParamDefault("version", "1.0.0") -// versioning.SetVersion(ctx, version) -// ctx.Next() -// } +// +// func(ctx iris.Context) { // &version=1 +// version := ctx.URLParamDefault("version", "1.0.0") +// versioning.SetVersion(ctx, version) +// ctx.Next() +// } +// // See `GetVersion` too. func SetVersion(ctx *context.Context, constraint string) { ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetVersionContextKey(), constraint) @@ -205,23 +207,24 @@ type AliasMap = map[string]string // for the children Parties(routers). It's respected by versioning Groups. // // Example Code: -// app := iris.New() // -// api := app.Party("/api") -// api.Use(Aliases(map[string]string{ -// versioning.Empty: "1.0.0", // when no version was provided by the client. -// "beta": "4.0.0", -// "stage": "5.0.0-alpha" -// })) +// app := iris.New() // -// v1 := NewGroup(api, ">=1.0.0 < 2.0.0") -// v1.Get/Post... +// api := app.Party("/api") +// api.Use(Aliases(map[string]string{ +// versioning.Empty: "1.0.0", // when no version was provided by the client. +// "beta": "4.0.0", +// "stage": "5.0.0-alpha" +// })) // -// v4 := NewGroup(api, ">=4.0.0 < 5.0.0") -// v4.Get/Post... +// v1 := NewGroup(api, ">=1.0.0 < 2.0.0") +// v1.Get/Post... // -// stage := NewGroup(api, "5.0.0-alpha") -// stage.Get/Post... +// v4 := NewGroup(api, ">=4.0.0 < 5.0.0") +// v4.Get/Post... +// +// stage := NewGroup(api, "5.0.0-alpha") +// stage.Get/Post... func Aliases(aliases AliasMap) context.Handler { cp := make(AliasMap, len(aliases)) // copy the map here so we are safe of later modifications by end-dev. for k, v := range aliases { diff --git a/view/django.go b/view/django.go index f0ba57b9..3e1205d5 100644 --- a/view/django.go +++ b/view/django.go @@ -50,7 +50,8 @@ type ( // through a Context or within filter functions. // // Example: -// AsValue("my string") +// +// AsValue("my string") // // Shortcut for `pongo2.AsValue`. var AsValue = pongo2.AsValue diff --git a/view/fs.go b/view/fs.go index b10d4a79..8e54fa5a 100644 --- a/view/fs.go +++ b/view/fs.go @@ -2,7 +2,7 @@ package view import ( "fmt" - "io/ioutil" + "io" "net/http" "path" "path/filepath" @@ -80,7 +80,7 @@ func asset(fs http.FileSystem, name string) ([]byte, error) { return nil, err } - contents, err := ioutil.ReadAll(f) + contents, err := io.ReadAll(f) f.Close() return contents, err } diff --git a/view/html.go b/view/html.go index a08a6094..6e875eb1 100644 --- a/view/html.go +++ b/view/html.go @@ -140,6 +140,7 @@ func (s *HTMLEngine) Reload(developmentMode bool) *HTMLEngine { // // missingkey: Control the behavior during execution if a map is // indexed with a key that is not present in the map. +// // "missingkey=default" or "missingkey=invalid" // The default behavior: Do nothing and continue execution. // If printed, the result of the index operation is the string @@ -148,7 +149,6 @@ func (s *HTMLEngine) Reload(developmentMode bool) *HTMLEngine { // The operation returns the zero value for the map type's element. // "missingkey=error" // Execution stops immediately with an error. -// func (s *HTMLEngine) Option(opt ...string) *HTMLEngine { s.rmu.Lock() s.options = append(s.options, opt...) @@ -172,7 +172,8 @@ func (s *HTMLEngine) Delims(left, right string) *HTMLEngine { // for the template file with its extension. // // Example: HTML("./templates", ".html").Layout("layouts/mainLayout.html") -// // mainLayout.html is inside: "./templates/layouts/". +// +// // mainLayout.html is inside: "./templates/layouts/". // // Note: Layout can be changed for a specific call // action with the option: "layout" on the iris' context.Render function. diff --git a/x/client/client.go b/x/client/client.go index c77fd044..59b2d529 100644 --- a/x/client/client.go +++ b/x/client/client.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net/http" "net/url" @@ -281,7 +280,7 @@ func (c *Client) Do(ctx context.Context, method, urlpath string, payload interfa // DrainResponseBody drains response body and close it, allowing the transport to reuse TCP connections. // It's automatically called on Client.ReadXXX methods on the end. func (c *Client) DrainResponseBody(resp *http.Response) { - _, _ = io.Copy(ioutil.Discard, resp.Body) + _, _ = io.Copy(io.Discard, resp.Body) resp.Body.Close() } @@ -398,7 +397,7 @@ func (c *Client) ReadJSON(ctx context.Context, dest interface{}, method, urlpath } // DBUG - // b, _ := ioutil.ReadAll(resp.Body) + // b, _ := io.ReadAll(resp.Body) // println(string(b)) // return json.Unmarshal(b, &dest) @@ -418,7 +417,7 @@ func (c *Client) ReadPlain(ctx context.Context, dest interface{}, method, urlpat return ExtractError(resp) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return err } @@ -495,7 +494,7 @@ func BindResponse(resp *http.Response, dest interface{}) (err error) { case contentTypeJSON: // the most common scenario on successful responses. return json.NewDecoder(resp.Body).Decode(&dest) case contentTypePlainText: - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) if err != nil { return err } diff --git a/x/client/error.go b/x/client/error.go index 4dfd03b1..06381264 100644 --- a/x/client/error.go +++ b/x/client/error.go @@ -2,7 +2,7 @@ package client import ( "encoding/json" - "io/ioutil" + "io" "net/http" "strings" ) @@ -39,7 +39,7 @@ func (e APIError) Error() string { // ExtractError returns the response wrapped inside an APIError. func ExtractError(resp *http.Response) APIError { - body, _ := ioutil.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) return APIError{ Response: resp, diff --git a/x/client/handler_transport.go b/x/client/handler_transport.go index f9470d05..7294aeff 100644 --- a/x/client/handler_transport.go +++ b/x/client/handler_transport.go @@ -3,7 +3,7 @@ package client import ( "bytes" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" ) @@ -27,7 +27,7 @@ func (t *handlerTransport) RoundTrip(req *http.Request) (*http.Response, error) reqCopy.TransferEncoding = []string{"chunked"} } } else { - reqCopy.Body = ioutil.NopCloser(bytes.NewReader(nil)) + reqCopy.Body = io.NopCloser(bytes.NewReader(nil)) } if reqCopy.RequestURI == "" { @@ -50,7 +50,7 @@ func (t *handlerTransport) RoundTrip(req *http.Request) (*http.Response, error) } if recorder.Body != nil { - resp.Body = ioutil.NopCloser(recorder.Body) + resp.Body = io.NopCloser(recorder.Body) } return &resp, nil diff --git a/x/client/option.go b/x/client/option.go index b8c771b6..3875e7d0 100644 --- a/x/client/option.go +++ b/x/client/option.go @@ -67,25 +67,27 @@ func RateLimit(requestsPerSecond int) Option { // and right after a response from the server is received. // // Example Output for request: -// [DBUG] 2022/03/01 21:54 Iris HTTP Client: POST / HTTP/1.1 -// Host: 127.0.0.1:50948 -// User-Agent: Go-http-client/1.1 -// Content-Length: 22 -// Accept: application/json -// Content-Type: application/json -// Accept-Encoding: gzip // -// {"firstname":"Makis"} +// [DBUG] 2022/03/01 21:54 Iris HTTP Client: POST / HTTP/1.1 +// Host: 127.0.0.1:50948 +// User-Agent: Go-http-client/1.1 +// Content-Length: 22 +// Accept: application/json +// Content-Type: application/json +// Accept-Encoding: gzip +// +// {"firstname":"Makis"} // // Example Output for response: -// [DBUG] 2022/03/01 21:54 Iris HTTP Client: HTTP/1.1 200 OK -// Content-Length: 27 -// Content-Type: application/json; charset=utf-8 -// Date: Tue, 01 Mar 2022 19:54:03 GMT // -// { -// "firstname": "Makis" -// } +// [DBUG] 2022/03/01 21:54 Iris HTTP Client: HTTP/1.1 200 OK +// Content-Length: 27 +// Content-Type: application/json; charset=utf-8 +// Date: Tue, 01 Mar 2022 19:54:03 GMT +// +// { +// "firstname": "Makis" +// } func Debug(c *Client) { handler := &debugRequestHandler{ logger: golog.Child("Iris HTTP Client: ").SetLevel("debug"), diff --git a/x/errors/errors.go b/x/errors/errors.go index 2ef2438a..bf6dfe78 100644 --- a/x/errors/errors.go +++ b/x/errors/errors.go @@ -56,11 +56,12 @@ var errorCodeMap = make(map[ErrorCodeName]ErrorCode) // See "RegisterErrorCode" and "RegisterErrorCodeMap" for alternatives. // // Example: -// var ( -// NotFound = errors.E("NOT_FOUND", http.StatusNotFound) -// ) -// ... -// NotFound.Details(ctx, "resource not found", "user with id: %q was not found", userID) +// +// var ( +// NotFound = errors.E("NOT_FOUND", http.StatusNotFound) +// ) +// ... +// NotFound.Details(ctx, "resource not found", "user with id: %q was not found", userID) // // This method MUST be called on initialization, before HTTP server starts as // the internal map is not protected by mutex. @@ -238,7 +239,8 @@ var ( // Error represents the JSON form of "http wire errors". // // Examples can be found at: -// https://github.com/kataras/iris/tree/master/_examples/routing/http-wire-errors. +// +// https://github.com/kataras/iris/tree/master/_examples/routing/http-wire-errors. type Error struct { ErrorCode ErrorCode `json:"http_error_code" yaml:"HTTPErrorCode"` Message string `json:"message,omitempty" yaml:"Message"` diff --git a/x/errors/validation_error.go b/x/errors/validation_error.go index e475a77b..6c199546 100644 --- a/x/errors/validation_error.go +++ b/x/errors/validation_error.go @@ -13,7 +13,8 @@ import ( // A validation error(s) can be given by ErrorCodeName's Validation or Err methods. // // Example can be found at: -// https://github.com/kataras/iris/tree/master/_examples/routing/http-wire-errors/custom-validation-errors +// +// https://github.com/kataras/iris/tree/master/_examples/routing/http-wire-errors/custom-validation-errors type ValidationError interface { error diff --git a/x/pagination/pagination.go b/x/pagination/pagination.go index 9981616a..aa13792b 100644 --- a/x/pagination/pagination.go +++ b/x/pagination/pagination.go @@ -114,43 +114,43 @@ type List[T any] struct { // // Example Code: // -// import "github.com/kataras/iris/v12/x/pagination" -// ...more code +// import "github.com/kataras/iris/v12/x/pagination" +// ...more code // -// type User struct { -// Firstname string `json:"firstname"` -// Lastname string `json:"lastname"` -// } +// type User struct { +// Firstname string `json:"firstname"` +// Lastname string `json:"lastname"` +// } // -// type ExtraUser struct { -// User -// ExtraData string -// } +// type ExtraUser struct { +// User +// ExtraData string +// } // -// func main() { -// users := []User{ -// {"Gerasimos", "Maropoulos"}, -// {"Efi", "Kwfidou"}, -// } +// func main() { +// users := []User{ +// {"Gerasimos", "Maropoulos"}, +// {"Efi", "Kwfidou"}, +// } // -// t := pagination.NewList(users, 100, nil, pagination.ListOptions{ -// Page: 1, -// Size: 50, -// }) +// t := pagination.NewList(users, 100, nil, pagination.ListOptions{ +// Page: 1, +// Size: 50, +// }) // -// // Optionally, transform a T list of objects to a V list of objects. -// v, err := pagination.TransformList(t, func(u User) (ExtraUser, error) { -// return ExtraUser{ -// User: u, -// ExtraData: "test extra data", -// }, nil -// }) -// if err != nil { panic(err) } +// // Optionally, transform a T list of objects to a V list of objects. +// v, err := pagination.TransformList(t, func(u User) (ExtraUser, error) { +// return ExtraUser{ +// User: u, +// ExtraData: "test extra data", +// }, nil +// }) +// if err != nil { panic(err) } // -// paginationJSON, err := json.MarshalIndent(v, "", " ") -// if err!=nil { panic(err) } -// fmt.Println(paginationJSON) -// } +// paginationJSON, err := json.MarshalIndent(v, "", " ") +// if err!=nil { panic(err) } +// fmt.Println(paginationJSON) +// } func NewList[T any](items []T, totalCount int64, filter any, opts ListOptions) *List[T] { pageSize := opts.GetLimit() @@ -199,13 +199,13 @@ func NewList[T any](items []T, totalCount int64, filter any, opts ListOptions) * // // Example Code: // -// listOfUsers := pagination.NewList(...) -// newListOfExtraUsers, err := pagination.TransformList(listOfUsers, func(u User) (ExtraUser, error) { -// return ExtraUser{ -// User: u, -// ExtraData: "test extra data", -// }, nil -// }) +// listOfUsers := pagination.NewList(...) +// newListOfExtraUsers, err := pagination.TransformList(listOfUsers, func(u User) (ExtraUser, error) { +// return ExtraUser{ +// User: u, +// ExtraData: "test extra data", +// }, nil +// }) func TransformList[T any, V any](list *List[T], transform func(T) (V, error)) (*List[V], error) { if list == nil { return &List[V]{