diff --git a/HISTORY.md b/HISTORY.md
index 0034b187..bc93b411 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -431,18 +431,18 @@ New Package-level Variables:
- `iris.DirListRich` to override the default look and feel if the `DirOptions.ShowList` was set to true, can be passed to `DirOptions.DirList` field.
- `iris.DirListRichOptions` to pass on `iris.DirListRich` method.
-- `iris.ErrGzipNotSupported` to export the `context.ErrGzipNotSupported` when trying to write gzip but client does not support.
-- `iris.GzipReader` middleware to decode gzip requests on next read actions.
+- `iris.Compress` and `iris.CompressReader` middleware to compress responses and decode compressed request data respectfully.
- `iris.B, KB, MB, GB, TB, PB, EB` for byte units.
- `TLSNoRedirect` to disable automatic "http://" to "https://" redirections (see below)
- `CookieAllowReclaim`, `CookieAllowSubdomains`, `CookieSameSite`, `CookieSecure` and `CookieEncoding` to bring previously sessions-only features to all cookies in the request.
New Context Methods:
+- `Context.Compress(bool) error` and `Context.CompressReader(bool) error`
- `Context.Clone() Context` returns a copy of the Context.
- `Context.IsCanceled() bool` reports whether the request has been canceled by the client.
- `Context.IsSSL() bool` reports whether the request is under HTTPS SSL (New `Configuration.SSLProxyHeaders` and `HostProxyHeaders` fields too).
-- `Context.GzipReader(enable bool)` method and `iris.GzipReader` middleware to enable future request read body calls to decompress data using gzip, [example](_examples/request-body/read-gzip).
+- `Context.CompressReader(enable bool)` method and `iris.CompressReader` middleware to enable future request read body calls to decompress data, [example](_examples/compression/main.go).
- `Context.RegisterDependency(v interface{})` and `Context.UnregisterDependency(typ reflect.Type)` to register/remove struct dependencies on serve-time through a middleware.
- `Context.SetID(id interface{})` and `Context.GetID() interface{}` added to register a custom unique indetifier to the Context, if necessary.
- `Context.GetDomain() string` returns the domain.
@@ -469,16 +469,22 @@ New Context Methods:
Breaking Changes:
+- `ctx.Gzip(boolean)` replaced with `ctx.Compress(boolean) error`.
+- `ctx.GzipReader(boolean) error` replaced with `ctx.CompressReader(boolean) error`.
+- `iris.Gzip` replaced with `iris.Compress` (middleware).
+- `iris.GzipReader` replaced with `iris.CompressReader` (middleware).
+- `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding("gzip", "br" ...) bool`.
+- `ctx.GzipResponseWriter()` is **removed**.
- `Party.HandleDir` now returns a list of `[]*Route` (GET and HEAD) instead of GET only.
- `Context.OnClose` and `Context.OnCloseConnection` now both accept an `iris.Handler` instead of a simple `func()` as their callback.
- `Context.StreamWriter(writer func(w io.Writer) bool)` changed to `StreamWriter(writer func(w io.Writer) error) error` and it's now the `Context.Request().Context().Done()` channel that is used to receive any close connection/manual cancel signals, instead of the deprecated `ResponseWriter().CloseNotify()` one. Same for the `Context.OnClose` and `Context.OnCloseConnection` methods.
-- Fixed handler's error response not be respected when response recorder or gzip writer was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder or gzip writer).
+- Fixed handler's error response not be respected when response recorder was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder).
- `Context.String()` (rarely used by end-developers) it does not return a unique string anymore, to achieve the old representation you must call the new `Context.SetID` method first.
- `iris.CookieEncode` and `CookieDecode` are replaced with the `iris.CookieEncoding`.
- `sessions#Config.Encode` and `Decode` are removed in favor of (the existing) `Encoding` field.
- `versioning.GetVersion` now returns an empty string if version wasn't found.
- Change the MIME type of `Javascript .js` and `JSONP` as the HTML specification now recommends to `"text/javascript"` instead of the obselete `"application/javascript"`. This change was pushed to the `Go` language itself as well. See .
-- Remove the last input argument of `enableGzipCompression` in `Context.ServeContent`, `ServeFile` methods. This was deprecated a few versions ago. A middleware (`app.Use(iris.Gzip)`) or a prior call to `Context.Gzip(true)` will enable gzip compression. Also these two methods and `Context.SendFile` one now support `Content-Range` and `Accept-Ranges` correctly out of the box (`net/http` had a bug, which is now fixed).
+- Remove the last input argument of `enableGzipCompression` in `Context.ServeContent`, `ServeFile` methods. This was deprecated a few versions ago. A middleware (`app.Use(iris.Compress)`) or a prior call to `Context.Compress(true)` will enable compression. Also these two methods and `Context.SendFile` one now support `Content-Range` and `Accept-Ranges` correctly out of the box (`net/http` had a bug, which is now fixed).
- `Context.ServeContent` no longer returns an error, see `ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` new methods too.
- `route.Trace() string` changed to `route.Trace(w io.Writer)`, to achieve the same result just pass a `bytes.Buffer`
- `var mvc.AutoBinding` removed as the default behavior now resolves such dependencies automatically (see [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)).
@@ -486,6 +492,8 @@ Breaking Changes:
- `mvc#BeforeActivation.Dependencies().Add` should be replaced with `mvc#BeforeActivation.Dependencies().Register` instead
- **REMOVE** the `kataras/iris/v12/typescript` package in favor of the new [iris-cli](https://github.com/kataras/iris-cli). Also, the alm typescript online editor was removed as it is deprecated by its author, please consider using the [designtsx](https://designtsx.com/) instead.
+There is a breaking change on the type alias of `iris.Context` which now points to the `*context.Context` instead of the `context.Context` interface. The **interface has been removed** and the ability to **override** the Context **is not** available any more. When we added the ability from end-developers to override the Context years ago, we have never imagine that we will ever had such a featured Context with more than 4000 lines of code. As of Iris 2020, it is difficult and un-productive from an end-developer to override the Iris Context, and as far as we know, nobody uses this feature anymore because of that exact reason. Beside the overriding feature support end, if you still use the `context.Context` instead of `iris.Context`, it's the time to do it: please find-and-replace to `iris.Context` as wikis, book and all examples shows for the past 3 years. For the 99.9% of the users there is no a single breaking change, you already using `iris.Context` so you are in the "safe zone".
+
# Su, 16 February 2020 | v12.1.8
New Features:
diff --git a/NOTICE b/NOTICE
index f664b4ed..753c1e64 100644
--- a/NOTICE
+++ b/NOTICE
@@ -22,7 +22,10 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911
a16f63faca
bluemonday 0a75d7616912ab9 https://github.com/microcosm-cc/
beb9cc6f7283ec1 bluemonday
- 917c61b135
+ 917c61b135
+ brotli c3da72aa01ed78f https://github.com/andybalholm/brotli
+ 164593b9624fd91
+ d25082d2d2
closestmatch 1fbe626be92eb4c https://github.com/schollz/closestmatch
347d182cae9f8f0
0a046bf2f4
@@ -97,4 +100,4 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911
f5c1929ae8
uuid cb32006e483f2a2 https://github.com/google/uuid
3230e24209cf185
- c65b477dbf
+ c65b477dbf
diff --git a/_examples/README.md b/_examples/README.md
index 8831c8db..2459edd3 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -54,9 +54,6 @@
* [Reverse Routing](routing/reverse/main.go)
* [Router Wrapper](routing/custom-wrapper/main.go)
* [Custom Router](routing/custom-router/main.go)
- * Custom Context
- * [Method Overriding](routing/custom-context/method-overriding/main.go)
- * [New Implementation](routing/custom-context/new-implementation/main.go)
* Subdomains
* [Single](routing/subdomains/single/main.go)
* [Multi](routing/subdomains/multi/main.go)
@@ -129,12 +126,10 @@
* [Bind Custom per type](request-body/read-custom-per-type/main.go)
* [Bind Custom via Unmarshaler](request-body/read-custom-via-unmarshaler/main.go)
* [Bind Many times](request-body/read-many/main.go)
- * [Read/Bind Gzip compressed data](request-body/read-gzip/main.go)
* Response Writer
* [Content Negotiation](response-writer/content-negotiation)
* [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and Binary](response-writer/write-rest/main.go)
* [Protocol Buffers](response-writer/protobuf/main.go)
- * [Write Gzip](response-writer/write-gzip/main.go)
* [HTTP/2 Server Push](response-writer/http2push/main.go)
* [Stream Writer](response-writer/stream-writer/main.go)
* [Transactions](response-writer/transactions/main.go)
@@ -143,6 +138,10 @@
* Cache
* [Simple](response-writer/cache/simple/main.go)
* [Client-Side (304)](response-writer/cache/client-side/main.go)
+* Compression
+ * [Server-Side](compression/main.go)
+ * [Client-Side](compression/client/main.go)
+ * [Client-Side (using Iris)](compress/client-using-iris/main.go)
* Localization and Internationalization
* [i18n](i18n/main.go)
* Authentication, Authorization & Bot Detection
diff --git a/_examples/compression/client-using-iris/main.go b/_examples/compression/client-using-iris/main.go
new file mode 100644
index 00000000..59775aab
--- /dev/null
+++ b/_examples/compression/client-using-iris/main.go
@@ -0,0 +1,112 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/kataras/iris/v12/context"
+)
+
+const baseURL = "http://localhost:8080"
+
+// Available options:
+// - "gzip",
+// - "deflate",
+// - "br" (for brotli),
+// - "snappy" and
+// - "s2"
+const encoding = context.BROTLI
+
+var client = http.DefaultClient
+
+func main() {
+ fmt.Printf("Running client example on: %s\n", baseURL)
+
+ getExample()
+ postExample()
+}
+
+func getExample() {
+ endpoint := baseURL + "/"
+ req, err := http.NewRequest(http.MethodGet, endpoint, nil)
+ if err != nil {
+ panic(err)
+ }
+ // Required to receive server's compressed data.
+ req.Header.Set("Accept-Encoding", encoding)
+
+ resp, err := client.Do(req)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ // decompress server's compressed reply.
+ cr, err := context.NewCompressReader(resp.Body, encoding)
+ if err != nil {
+ panic(err)
+ }
+ defer cr.Close()
+
+ body, err := ioutil.ReadAll(cr)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("Received from server: %s", string(body))
+}
+
+type payload struct {
+ Username string `json:"username"`
+}
+
+func postExample() {
+ buf := new(bytes.Buffer)
+
+ // Compress client's data.
+ cw, err := context.NewCompressWriter(buf, encoding, -1)
+ if err != nil {
+ panic(err)
+ }
+
+ json.NewEncoder(cw).Encode(payload{Username: "Edward"})
+
+ // `Close` or `Flush` required before `NewRequest` call.
+ cw.Close()
+
+ endpoint := baseURL + "/"
+
+ req, err := http.NewRequest(http.MethodPost, endpoint, buf)
+ if err != nil {
+ panic(err)
+ }
+ req.Header.Set("Content-Type", "application/json")
+
+ // Required to send gzip compressed data to the server.
+ req.Header.Set("Content-Encoding", encoding)
+ // Required to receive server's compressed data.
+ req.Header.Set("Accept-Encoding", encoding)
+
+ resp, err := client.Do(req)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ // Decompress server's compressed reply.
+ cr, err := context.NewCompressReader(resp.Body, encoding)
+ if err != nil {
+ panic(err)
+ }
+ defer cr.Close() // Closes the request body too.
+
+ body, err := ioutil.ReadAll(cr)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("Server replied with: %s", string(body))
+}
diff --git a/_examples/compression/client/main.go b/_examples/compression/client/main.go
new file mode 100644
index 00000000..6c39294a
--- /dev/null
+++ b/_examples/compression/client/main.go
@@ -0,0 +1,102 @@
+package main
+
+import (
+ "bytes"
+ "compress/gzip"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+)
+
+var client = http.DefaultClient
+
+const baseURL = "http://localhost:8080"
+
+func main() {
+ fmt.Printf("Running client example on: %s\n", baseURL)
+
+ getExample()
+ postExample()
+}
+
+func getExample() {
+ endpoint := baseURL + "/"
+ req, err := http.NewRequest(http.MethodGet, endpoint, nil)
+ if err != nil {
+ panic(err)
+ }
+ // Required to receive server's compressed data.
+ req.Header.Set("Accept-Encoding", "gzip")
+
+ resp, err := client.Do(req)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ // decompress server's compressed reply.
+ r, err := gzip.NewReader(resp.Body)
+ if err != nil {
+ panic(err)
+ }
+ defer r.Close()
+
+ body, err := ioutil.ReadAll(r)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("Received from server: %s", string(body))
+}
+
+type payload struct {
+ Username string `json:"username"`
+}
+
+func postExample() {
+ buf := new(bytes.Buffer)
+
+ // Compress client's data.
+ w := gzip.NewWriter(buf)
+
+ b, err := json.Marshal(payload{Username: "Edward"})
+ if err != nil {
+ panic(err)
+ }
+ w.Write(b)
+ w.Close()
+
+ endpoint := baseURL + "/"
+
+ req, err := http.NewRequest(http.MethodPost, endpoint, buf)
+ if err != nil {
+ panic(err)
+ }
+ req.Header.Set("Content-Type", "application/json")
+
+ // Required to send gzip compressed data to the server.
+ req.Header.Set("Content-Encoding", "gzip")
+ // Required to receive server's compressed data.
+ req.Header.Set("Accept-Encoding", "gzip")
+
+ resp, err := client.Do(req)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ // Decompress server's compressed reply.
+ r, err := gzip.NewReader(resp.Body)
+ if err != nil {
+ panic(err)
+ }
+ defer r.Close()
+
+ body, err := ioutil.ReadAll(r)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("Server replied with: %s", string(body))
+}
diff --git a/_examples/compression/main.go b/_examples/compression/main.go
new file mode 100644
index 00000000..6f73a7e0
--- /dev/null
+++ b/_examples/compression/main.go
@@ -0,0 +1,64 @@
+package main
+
+import "github.com/kataras/iris/v12"
+
+func main() {
+ app := newApp()
+ app.Logger().SetLevel("debug")
+ app.Listen(":8080")
+}
+
+func newApp() *iris.Application {
+ app := iris.New()
+ // HERE and you are ready to GO:
+ app.Use(iris.Compress, iris.CompressReader)
+
+ app.Get("/", send)
+ app.Post("/", receive)
+
+ return app
+}
+
+type payload struct {
+ Username string `json:"username"`
+}
+
+func send(ctx iris.Context) {
+ ctx.JSON(payload{
+ Username: "Makis",
+ })
+}
+
+func receive(ctx iris.Context) {
+ var p payload
+ if err := ctx.ReadJSON(&p); err != nil {
+ ctx.Application().Logger().Debugf("ReadJSON: %v", err)
+ }
+
+ ctx.WriteString(p.Username)
+}
+
+/* Manually:
+func enableCompression(ctx iris.Context) {
+ // Enable writing using compression (deflate, gzip, brotli, snappy, s2):
+ err := ctx.Compress(true)
+ if err != nil {
+ ctx.Application().Logger().Debugf("writer: %v", err)
+ // if you REQUIRE server to SEND compressed data then `return` here.
+ // return
+ }
+
+ // Enable reading and binding request's compressed data:
+ err = ctx.CompressReader(true)
+ if err != nil &&
+ // on GET we don't expect writing with gzip from client
+ ctx.Method() != iris.MethodGet {
+ ctx.Application().Logger().Debugf("reader: %v", err)
+ // if you REQUIRE server to RECEIVE only
+ // compressed data then `return` here.
+ // return
+ }
+
+ ctx.Next()
+}
+*/
diff --git a/_examples/compression/main_test.go b/_examples/compression/main_test.go
new file mode 100644
index 00000000..e63f0134
--- /dev/null
+++ b/_examples/compression/main_test.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "encoding/json"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/kataras/iris/v12/context"
+ "github.com/kataras/iris/v12/httptest"
+)
+
+func TestCompression(t *testing.T) {
+ app := newApp()
+ e := httptest.New(t, app)
+
+ var expectedReply = payload{Username: "Makis"}
+ body := e.GET("/").WithHeader(context.AcceptEncodingHeaderKey, context.GZIP).Expect().
+ Status(httptest.StatusOK).
+ ContentEncoding(context.GZIP).
+ ContentType(context.ContentJSONHeaderValue).Body().Raw()
+
+ // Note that .Expect() consumes the response body
+ // and stores it to unexported "contents" field
+ // therefore, we retrieve it as string and put it to a new buffer.
+ r := strings.NewReader(body)
+ cr, err := context.NewCompressReader(r, context.GZIP)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cr.Close()
+
+ var got payload
+ if err = json.NewDecoder(cr).Decode(&got); err != nil {
+ t.Fatal(err)
+ }
+
+ if !reflect.DeepEqual(expectedReply, got) {
+ t.Fatalf("expected %#+v but got %#+v", expectedReply, got)
+ }
+}
diff --git a/_examples/convert-handlers/negroni-like/main.go b/_examples/convert-handlers/negroni-like/main.go
index ff67100b..be09c9e3 100644
--- a/_examples/convert-handlers/negroni-like/main.go
+++ b/_examples/convert-handlers/negroni-like/main.go
@@ -39,6 +39,3 @@ func negronilikeTestMiddleware(w http.ResponseWriter, r *http.Request, next http
w.WriteHeader(iris.StatusBadRequest)
w.Write([]byte("Bad request"))
}
-
-// Look "routing/custom-context" if you want to convert a custom handler with a custom Context
-// to a context.Handler.
diff --git a/_examples/convert-handlers/nethttp/main.go b/_examples/convert-handlers/nethttp/main.go
index d56ccd8f..d78c14ee 100644
--- a/_examples/convert-handlers/nethttp/main.go
+++ b/_examples/convert-handlers/nethttp/main.go
@@ -29,6 +29,3 @@ func main() {
func nativeTestMiddleware(w http.ResponseWriter, r *http.Request) {
println("Request path: " + r.URL.Path)
}
-
-// Look "routing/custom-context" if you want to convert a custom handler with a custom Context
-// to a context.Handler.
diff --git a/_examples/file-server/basic/main.go b/_examples/file-server/basic/main.go
index 001e89b2..0060e7dd 100644
--- a/_examples/file-server/basic/main.go
+++ b/_examples/file-server/basic/main.go
@@ -23,7 +23,7 @@ func newApp() *iris.Application {
// if end developer does not managed to handle it by hand.
IndexName: "/index.html",
// When files should served under compression.
- Gzip: false,
+ Compress: false,
// List the files inside the current requested directory if `IndexName` not found.
ShowList: false,
// If `ShowList` is true then this function will be used instead of the default one to show the list of files of a current requested directory(dir).
diff --git a/_examples/file-server/file-server/main.go b/_examples/file-server/file-server/main.go
index 993cd7a5..76610ba2 100644
--- a/_examples/file-server/file-server/main.go
+++ b/_examples/file-server/file-server/main.go
@@ -54,7 +54,7 @@ func main() {
filesRouter := app.Party("/files")
{
filesRouter.HandleDir("/", uploadDir, iris.DirOptions{
- Gzip: false,
+ Compress: true,
ShowList: true,
// Optionally, force-send files to the client inside of showing to the browser.
diff --git a/_examples/file-server/file-server/views/dirlist.html b/_examples/file-server/file-server/views/dirlist.html
index 2fecf72a..2b676f0c 100644
--- a/_examples/file-server/file-server/views/dirlist.html
+++ b/_examples/file-server/file-server/views/dirlist.html
@@ -83,7 +83,11 @@
{{ range $idx, $file := .Files }}
{{ $idx }} |
+ {{ if $file.Download }}
+ {{ $file.Name }} |
+ {{ else }}
{{ $file.Name }} |
+ {{ end }}
{{ if $file.Info.IsDir }}
Dir |
{{ else }}
diff --git a/_examples/request-body/read-gzip/main.go b/_examples/request-body/read-gzip/main.go
deleted file mode 100644
index def777b8..00000000
--- a/_examples/request-body/read-gzip/main.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package main
-
-import (
- "github.com/kataras/iris/v12"
-)
-
-func main() {
- app := newApp()
- app.Logger().SetLevel("debug")
-
- app.Listen(":8080")
-}
-
-type payload struct {
- Message string `json:"message"`
-}
-
-func newApp() *iris.Application {
- app := iris.New()
-
- // GzipReader is a middleware which enables gzip decompression,
- // when client sends gzip compressed data.
- //
- // A shortcut of:
- // func(ctx iris.Context) {
- // ctx.GzipReader(true)
- // ctx.Next()
- // }
- app.Use(iris.GzipReader)
-
- app.Post("/", func(ctx iris.Context) {
- // Bind incoming gzip compressed JSON to "p".
- var p payload
- if err := ctx.ReadJSON(&p); err != nil {
- ctx.StopWithError(iris.StatusBadRequest, err)
- return
- }
-
- // Send back the message as plain text.
- ctx.WriteString(p.Message)
- })
-
- return app
-}
diff --git a/_examples/request-body/read-gzip/main_test.go b/_examples/request-body/read-gzip/main_test.go
deleted file mode 100644
index f75206ea..00000000
--- a/_examples/request-body/read-gzip/main_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package main
-
-import (
- "bytes"
- "compress/gzip"
- "encoding/json"
- "testing"
-
- "github.com/kataras/iris/v12/httptest"
-)
-
-func TestGzipReader(t *testing.T) {
- app := newApp()
-
- expected := payload{Message: "test"}
- b, err := json.Marshal(expected)
- if err != nil {
- t.Fatal(err)
- }
-
- buf := new(bytes.Buffer)
- w := gzip.NewWriter(buf)
- _, err = w.Write(b)
- if err != nil {
- t.Fatal(err)
- }
- err = w.Close()
- if err != nil {
- t.Fatal(err)
- }
-
- e := httptest.New(t, app)
- // send gzip compressed.
- e.POST("/").WithHeader("Content-Encoding", "gzip").WithHeader("Content-Type", "application/json").
- WithBytes(buf.Bytes()).Expect().Status(httptest.StatusOK).Body().Equal(expected.Message)
- // raw.
- e.POST("/").WithJSON(expected).Expect().Status(httptest.StatusOK).Body().Equal(expected.Message)
-}
diff --git a/_examples/response-writer/write-gzip/main.go b/_examples/response-writer/write-gzip/main.go
deleted file mode 100644
index 080e4164..00000000
--- a/_examples/response-writer/write-gzip/main.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package main
-
-import "github.com/kataras/iris/v12"
-
-func main() {
- app := iris.New()
- // app.Use(iris.Gzip)
- // func(ctx iris.Context) { ctx.Gzip(true/false)}
- // OR:
- app.Get("/", func(ctx iris.Context) {
- ctx.WriteGzip([]byte("Hello World!"))
- ctx.Header("X-Custom",
- "Headers can be set here after WriteGzip as well, because the data are kept before sent to the client when using the context's GzipResponseWriter and ResponseRecorder.")
- })
-
- app.Get("/2", func(ctx iris.Context) {
- // same as the `WriteGzip`.
- // However GzipResponseWriter gives you more options, like
- // reset data, disable and more, look its methods.
- ctx.GzipResponseWriter().WriteString("Hello World!")
- })
-
- app.Listen(":8080")
-}
diff --git a/_examples/routing/custom-context/method-overriding/main.go b/_examples/routing/custom-context/method-overriding/main.go
deleted file mode 100644
index edde8988..00000000
--- a/_examples/routing/custom-context/method-overriding/main.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package main
-
-// In this package I'll show you how to override the existing Context's functions and methods.
-// You can easly navigate to the custom-context example to see how you can add new functions
-// to your own context (need a custom handler).
-//
-// This way is far easier to understand and it's faster when you want to override existing methods:
-import (
- "reflect"
-
- "github.com/kataras/iris/v12"
- "github.com/kataras/iris/v12/context"
-)
-
-// Create your own custom Context, put any fields you wanna need.
-type MyContext struct {
- // Optional Part 1: embed (optional but required if you don't want to override all context's methods)
- iris.Context
-}
-
-var _ iris.Context = &MyContext{} // optionally: validate on compile-time if MyContext implements context.Context.
-
-// The only one important if you will override the Context
-// with an embedded context.Context inside it.
-// Required in order to run the handlers via this "*MyContext".
-func (ctx *MyContext) Do(handlers context.Handlers) {
- context.Do(ctx, handlers)
-}
-
-// The second one important if you will override the Context
-// with an embedded context.Context inside it.
-// Required in order to run the chain of handlers via this "*MyContext".
-func (ctx *MyContext) Next() {
- context.Next(ctx)
-}
-
-// Override any context's method you want...
-// [...]
-
-func (ctx *MyContext) HTML(format string, args ...interface{}) (int, error) {
- ctx.Application().Logger().Infof("Executing .HTML function from MyContext")
-
- ctx.ContentType("text/html")
- return ctx.Writef(format, args...)
-}
-
-func main() {
- app := iris.New()
- // app.Logger().SetLevel("debug")
-
- // The only one Required:
- // here is how you define how your own context will
- // be created and acquired from the iris' generic context pool.
- app.ContextPool.Attach(func() iris.Context {
- return &MyContext{
- // Optional Part 3:
- Context: context.NewContext(app),
- }
- })
-
- // Register a view engine on .html files inside the ./view/** directory.
- app.RegisterView(iris.HTML("./view", ".html"))
-
- // register your route, as you normally do
- app.Handle("GET", "/", recordWhichContextJustForProofOfConcept, func(ctx iris.Context) {
- // use the context's overridden HTML method.
- ctx.HTML(" Hello from my custom context's HTML!
")
- })
-
- // this will be executed by the MyContext.Context
- // if MyContext is not directly define the View function by itself.
- app.Handle("GET", "/hi/{firstname:alphabetical}", recordWhichContextJustForProofOfConcept, func(ctx iris.Context) {
- firstname := ctx.Params().Get("firstname")
-
- ctx.ViewData("firstname", firstname)
- ctx.Gzip(true)
-
- ctx.View("hi.html")
- })
-
- app.Listen(":8080")
-}
-
-// should always print "($PATH) Handler is executing from 'MyContext'"
-func recordWhichContextJustForProofOfConcept(ctx iris.Context) {
- ctx.Application().Logger().Infof("(%s) Handler is executing from: '%s'", ctx.Path(), reflect.TypeOf(ctx).Elem().Name())
- ctx.Next()
-}
-
-// Look "new-implementation" to see how you can create an entirely new Context with new functions.
diff --git a/_examples/routing/custom-context/method-overriding/view/hi.html b/_examples/routing/custom-context/method-overriding/view/hi.html
deleted file mode 100644
index 7999c305..00000000
--- a/_examples/routing/custom-context/method-overriding/view/hi.html
+++ /dev/null
@@ -1 +0,0 @@
- Hi {{.firstname}}
\ No newline at end of file
diff --git a/_examples/routing/custom-context/new-implementation/main.go b/_examples/routing/custom-context/new-implementation/main.go
deleted file mode 100644
index c518cc61..00000000
--- a/_examples/routing/custom-context/new-implementation/main.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package main
-
-import (
- "sync"
-
- "github.com/kataras/iris/v12"
- "github.com/kataras/iris/v12/sessions"
-)
-
-// Owner is our application structure, it contains the methods or fields we need,
-// think it as the owner of our *Context.
-type Owner struct {
- // define here the fields that are global
- // and shared to all clients.
- sessionsManager *sessions.Sessions
-}
-
-// this package-level variable "application" will be used inside context to communicate with our global Application.
-var owner = &Owner{
- sessionsManager: sessions.New(sessions.Config{Cookie: "mysessioncookie"}),
-}
-
-// Context is our custom context.
-// Let's implement a context which will give us access
-// to the client's Session with a trivial `ctx.Session()` call.
-type Context struct {
- iris.Context
- session *sessions.Session
-}
-
-// Session returns the current client's session.
-func (ctx *Context) Session() *sessions.Session {
- // this help us if we call `Session()` multiple times in the same handler
- if ctx.session == nil {
- // start a new session if not created before.
- ctx.session = owner.sessionsManager.Start(ctx.Context)
- }
-
- return ctx.session
-}
-
-// Bold will send a bold text to the client.
-func (ctx *Context) Bold(text string) {
- ctx.HTML("" + text + "")
-}
-
-var contextPool = sync.Pool{New: func() interface{} {
- return &Context{}
-}}
-
-func acquire(original iris.Context) *Context {
- ctx := contextPool.Get().(*Context)
- ctx.Context = original // set the context to the original one in order to have access to iris's implementation.
- ctx.session = nil // reset the session
- return ctx
-}
-
-func release(ctx *Context) {
- contextPool.Put(ctx)
-}
-
-// Handler will convert our handler of func(*Context) to an iris Handler,
-// in order to be compatible with the HTTP API.
-func Handler(h func(*Context)) iris.Handler {
- return func(original iris.Context) {
- ctx := acquire(original)
- h(ctx)
- release(ctx)
- }
-}
-
-func newApp() *iris.Application {
- app := iris.New()
-
- // Work as you did before, the only difference
- // is that the original context.Handler should be wrapped with our custom
- // `Handler` function.
- app.Get("/", Handler(func(ctx *Context) {
- ctx.Bold("Hello from our *Context")
- }))
-
- app.Post("/set", Handler(func(ctx *Context) {
- nameFieldValue := ctx.FormValue("name")
- ctx.Session().Set("name", nameFieldValue)
- ctx.Writef("set session = " + nameFieldValue)
- }))
-
- app.Get("/get", Handler(func(ctx *Context) {
- name := ctx.Session().GetString("name")
- ctx.Writef(name)
- }))
-
- return app
-}
-
-func main() {
- app := newApp()
-
- // GET: http://localhost:8080
- // POST: http://localhost:8080/set
- // GET: http://localhost:8080/get
- app.Listen(":8080")
-}
diff --git a/_examples/routing/custom-context/new-implementation/main_test.go b/_examples/routing/custom-context/new-implementation/main_test.go
deleted file mode 100644
index 49c782d5..00000000
--- a/_examples/routing/custom-context/new-implementation/main_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package main
-
-import (
- "testing"
-
- "github.com/kataras/iris/v12/httptest"
-)
-
-func TestCustomContextNewImpl(t *testing.T) {
- app := newApp()
- e := httptest.New(t, app, httptest.URL("http://localhost:8080"))
-
- e.GET("/").Expect().
- Status(httptest.StatusOK).
- ContentType("text/html").
- Body().Equal("Hello from our *Context")
-
- expectedName := "iris"
- e.POST("/set").WithFormField("name", expectedName).Expect().
- Status(httptest.StatusOK).
- Body().Equal("set session = " + expectedName)
-
- e.GET("/get").Expect().
- Status(httptest.StatusOK).
- Body().Equal(expectedName)
-}
diff --git a/_examples/routing/custom-router/main.go b/_examples/routing/custom-router/main.go
index 5d9e8e4b..3ec24fe2 100644
--- a/_examples/routing/custom-router/main.go
+++ b/_examples/routing/custom-router/main.go
@@ -14,7 +14,7 @@ import (
Build(provider router.RoutesProvider) error
- RouteExists reports whether a particular route exists.
RouteExists(ctx iris.Context, method, path string) bool
- - FireErrorCode(ctx context.Context) should handle the given ctx.GetStatusCode().
+ - 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/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go
index f33ece35..8cc0ce73 100644
--- a/_examples/routing/dynamic-path/main.go
+++ b/_examples/routing/dynamic-path/main.go
@@ -179,7 +179,7 @@ func main() {
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
id := ctx.Params().GetUint64Default("id", 0)
friendid := ctx.Params().GetUint64Default("friendid", 0)
- ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
+ ctx.Writef("Hello id: %d looking for friend id: %d", id, friendid)
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
// :uint8 0 to 255.
diff --git a/_examples/view/herotemplate/app.go b/_examples/view/herotemplate/app.go
index 3765f65e..76f57812 100644
--- a/_examples/view/herotemplate/app.go
+++ b/_examples/view/herotemplate/app.go
@@ -17,7 +17,7 @@ func main() {
app := iris.New()
app.Get("/users", func(ctx iris.Context) {
- ctx.Gzip(true)
+ ctx.Compress(true)
ctx.ContentType("text/html")
userList := []string{
@@ -35,11 +35,9 @@ func main() {
// template.UserList(userList, buffer)
// ctx.Write(buffer.Bytes())
- // using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool),
- // iris context implements the io.Writer by its ResponseWriter
- // which is an enhanced version of the standard http.ResponseWriter
- // but still 100% compatible, GzipResponseWriter too:
- // _, err := template.UserListToWriter(userList, ctx.GzipResponseWriter())
+ // iris context implements the io.Writer:
+ // _, err := template.UserListToWriter(userList, ctx)
+ // OR:
buffer := new(bytes.Buffer)
template.UserList(userList, buffer)
diff --git a/_examples/view/overview/main.go b/_examples/view/overview/main.go
index bfe47c00..8222175e 100644
--- a/_examples/view/overview/main.go
+++ b/_examples/view/overview/main.go
@@ -14,9 +14,9 @@ func main() {
// - {{ current }}
app.RegisterView(iris.HTML("./templates", ".html"))
app.Get("/", func(ctx iris.Context) {
- ctx.ViewData("Name", "iris") // the .Name inside the ./templates/hi.html
- ctx.Gzip(true) // enable gzip for big files
- ctx.View("hi.html") // render the template with the file name relative to the './templates'
+ ctx.Compress(true) // enable compression based on Accept-Encoding (e.g. "gzip").
+ ctx.ViewData("Name", "iris") // the .Name inside the ./templates/hi.html.
+ ctx.View("hi.html") // render the template with the file name relative to the './templates'.
})
// http://localhost:8080/
diff --git a/_examples/view/quicktemplate/controllers/execute_template.go b/_examples/view/quicktemplate/controllers/execute_template.go
index 02eb9005..153f6136 100644
--- a/_examples/view/quicktemplate/controllers/execute_template.go
+++ b/_examples/view/quicktemplate/controllers/execute_template.go
@@ -6,9 +6,9 @@ import (
"github.com/kataras/iris/v12"
)
-// ExecuteTemplate renders a "tmpl" partial template to the `context#ResponseWriter`.
+// ExecuteTemplate renders a "tmpl" partial template to the `Context.ResponseWriter`.
func ExecuteTemplate(ctx iris.Context, tmpl templates.Partial) {
- ctx.Gzip(true)
+ ctx.Compress(true)
ctx.ContentType("text/html")
- templates.WriteTemplate(ctx.ResponseWriter(), tmpl)
+ templates.WriteTemplate(ctx, tmpl)
}
diff --git a/_examples/view/template_html_1/main.go b/_examples/view/template_html_1/main.go
index 95ffc6fa..9e8508ee 100644
--- a/_examples/view/template_html_1/main.go
+++ b/_examples/view/template_html_1/main.go
@@ -16,7 +16,7 @@ func main() {
// TIP: append .Reload(true) to reload the templates on each request.
app.Get("/", func(ctx iris.Context) {
- ctx.Gzip(true)
+ ctx.Compress(true)
ctx.ViewData("", mypage{"My Page title", "Hello world!"})
ctx.View("mypage.html")
// Note that: you can pass "layout" : "otherLayout.html" to bypass the config's Layout property
diff --git a/_examples/view/template_jet_1_embedded/bindata.go b/_examples/view/template_jet_1_embedded/bindata.go
index d76ad2ba..11989e7b 100644
--- a/_examples/view/template_jet_1_embedded/bindata.go
+++ b/_examples/view/template_jet_1_embedded/bindata.go
@@ -201,9 +201,9 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
- "views/includes/_partial.jet": viewsIncludes_partialJet,
- "views/includes/blocks.jet": viewsIncludesBlocksJet,
- "views/index.jet": viewsIndexJet,
+ "views/includes/_partial.jet": viewsIncludes_partialJet,
+ "views/includes/blocks.jet": viewsIncludesBlocksJet,
+ "views/index.jet": viewsIndexJet,
"views/layouts/application.jet": viewsLayoutsApplicationJet,
}
@@ -246,15 +246,16 @@ type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
+
var _bintree = &bintree{nil, map[string]*bintree{
- "views": &bintree{nil, map[string]*bintree{
- "includes": &bintree{nil, map[string]*bintree{
- "_partial.jet": &bintree{viewsIncludes_partialJet, map[string]*bintree{}},
- "blocks.jet": &bintree{viewsIncludesBlocksJet, map[string]*bintree{}},
+ "views": {nil, map[string]*bintree{
+ "includes": {nil, map[string]*bintree{
+ "_partial.jet": {viewsIncludes_partialJet, map[string]*bintree{}},
+ "blocks.jet": {viewsIncludesBlocksJet, map[string]*bintree{}},
}},
- "index.jet": &bintree{viewsIndexJet, map[string]*bintree{}},
- "layouts": &bintree{nil, map[string]*bintree{
- "application.jet": &bintree{viewsLayoutsApplicationJet, map[string]*bintree{}},
+ "index.jet": {viewsIndexJet, map[string]*bintree{}},
+ "layouts": {nil, map[string]*bintree{
+ "application.jet": {viewsLayoutsApplicationJet, map[string]*bintree{}},
}},
}},
}}
@@ -305,4 +306,3 @@ func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}
-
diff --git a/_examples/webassembly/main.go b/_examples/webassembly/main.go
index 81468afd..37803c84 100644
--- a/_examples/webassembly/main.go
+++ b/_examples/webassembly/main.go
@@ -17,7 +17,7 @@ func main() {
app.HandleDir("/", "./client")
app.Get("/", func(ctx iris.Context) {
- // ctx.Gzip(true)
+ // ctx.Compress(true)
ctx.ServeFile("./client/hello.html")
})
diff --git a/aliases.go b/aliases.go
index a1c6bc13..1813a426 100644
--- a/aliases.go
+++ b/aliases.go
@@ -1,10 +1,15 @@
package iris
import (
+ "net/http"
+
+ "github.com/kataras/iris/v12/cache"
"github.com/kataras/iris/v12/context"
+ "github.com/kataras/iris/v12/core/handlerconv"
"github.com/kataras/iris/v12/core/host"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/hero"
+ "github.com/kataras/iris/v12/view"
)
type (
@@ -15,7 +20,7 @@ type (
//
// Developers send responses to the client's request through a Context.
// Developers get request information from the client's request by a Context.
- Context = context.Context
+ Context = *context.Context
// UnmarshalerFunc a shortcut, an alias for the `context#UnmarshalerFunc` type
// which implements the `context#Unmarshaler` interface for reading request's body
// via custom decoders, most of them already implement the `context#UnmarshalerFunc`
@@ -126,7 +131,7 @@ type (
// Any custom or builtin `CookieOption` is valid,
// see `CookiePath`, `CookieCleanPath`, `CookieExpires` and `CookieHTTPOnly` for more.
//
- // An alias for the `context/Context#CookieOption`.
+ // An alias for the `context.CookieOption`.
CookieOption = context.CookieOption
// N is a struct which can be passed on the `Context.Negotiate` method.
// It contains fields which should be filled based on the `Context.Negotiation()`
@@ -134,6 +139,395 @@ type (
// which should be a string or []byte.
// It completes the `context/context.ContentSelector` interface.
//
- // An alias for the `context/Context#N`.
+ // An alias for the `context.N`.
N = context.N
)
+
+// Constants for input argument at `router.RouteRegisterRule`.
+// See `Party#SetRegisterRule`.
+const (
+ // RouteOverride replaces an existing route with the new one, the default rule.
+ RouteOverride = router.RouteOverride
+ // RouteSkip keeps the original route and skips the new one.
+ RouteSkip = router.RouteSkip
+ // RouteError log when a route already exists, shown after the `Build` state,
+ // server never starts.
+ RouteError = router.RouteError
+ // RouteOverlap will overlap the new route to the previous one.
+ // If the route stopped and its response can be reset then the new route will be execute.
+ RouteOverlap = router.RouteOverlap
+)
+
+// Contains the enum values of the `Context.GetReferrer()` method,
+// shortcuts of the context subpackage.
+const (
+ ReferrerInvalid = context.ReferrerInvalid
+ ReferrerIndirect = context.ReferrerIndirect
+ ReferrerDirect = context.ReferrerDirect
+ ReferrerEmail = context.ReferrerEmail
+ ReferrerSearch = context.ReferrerSearch
+ ReferrerSocial = context.ReferrerSocial
+
+ ReferrerNotGoogleSearch = context.ReferrerNotGoogleSearch
+ ReferrerGoogleOrganicSearch = context.ReferrerGoogleOrganicSearch
+ ReferrerGoogleAdwords = context.ReferrerGoogleAdwords
+)
+
+// NoLayout to disable layout for a particular template file
+// A shortcut for the `view#NoLayout`.
+const NoLayout = view.NoLayout
+
+var (
+ // HTML view engine.
+ // Shortcut of the kataras/iris/view.HTML.
+ HTML = view.HTML
+ // Django view engine.
+ // Shortcut of the kataras/iris/view.Django.
+ Django = view.Django
+ // Handlebars view engine.
+ // Shortcut of the kataras/iris/view.Handlebars.
+ Handlebars = view.Handlebars
+ // Pug view engine.
+ // Shortcut of the kataras/iris/view.Pug.
+ Pug = view.Pug
+ // Amber view engine.
+ // Shortcut of the kataras/iris/view.Amber.
+ Amber = view.Amber
+ // Jet view engine.
+ // Shortcut of the kataras/iris/view.Jet.
+ Jet = view.Jet
+)
+
+var (
+ // Compress is a middleware which enables writing
+ // using compression, if client supports.
+ Compress = func(ctx Context) {
+ ctx.Compress(true)
+ ctx.Next()
+ }
+ // CompressReader is a middleware which enables decompression,
+ // when client sends compressed data.
+ //
+ // Similar to: func(ctx iris.Context) {
+ // ctx.CompressReader(true)
+ // ctx.Next()
+ // }
+ CompressReader = func(ctx Context) {
+ ctx.CompressReader(true)
+ ctx.Next()
+ }
+)
+
+var (
+ // RegisterOnInterrupt registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.
+ //
+ // A shortcut for the `host#RegisterOnInterrupt`.
+ RegisterOnInterrupt = host.RegisterOnInterrupt
+
+ // LimitRequestBodySize is a middleware which sets a request body size limit
+ // for all next handlers in the chain.
+ //
+ // A shortcut for the `context#LimitRequestBodySize`.
+ LimitRequestBodySize = context.LimitRequestBodySize
+ // NewConditionalHandler returns a single Handler which can be registered
+ // as a middleware.
+ // Filter is just a type of Handler which returns a boolean.
+ // 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".
+ //
+ // If passed, this function makes sure that the Context's information
+ // about its per-request handler chain based on the new "handlers" is always updated.
+ //
+ // If not passed, then simply the Next handler(if any) is executed and "handlers" are ignored.
+ // Example can be found at: _examples/routing/conditional-chain.
+ //
+ // A shortcut for the `context#NewConditionalHandler`.
+ NewConditionalHandler = context.NewConditionalHandler
+ // FileServer returns a Handler which serves files from a specific system, phyisical, directory
+ // or an embedded one.
+ // The first parameter is the directory, relative to the executable program.
+ // The second optional parameter is any optional settings that the caller can use.
+ //
+ // See `Party#HandleDir` too.
+ // Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
+ // A shortcut for the `router.FileServer`.
+ FileServer = router.FileServer
+ // DirListRich can be passed to `DirOptions.DirList` field
+ // to override the default file listing appearance.
+ // Read more at: `core/router.DirListRich`.
+ DirListRich = router.DirListRich
+ // StripPrefix returns a handler that serves HTTP requests
+ // by removing the given prefix from the request URL's Path
+ // and invoking the handler h. StripPrefix handles a
+ // request for a path that doesn't begin with prefix by
+ // replying with an HTTP 404 not found error.
+ //
+ // Usage:
+ // fileserver := iris.FileServer("./static_files", DirOptions {...})
+ // h := iris.StripPrefix("/static", fileserver)
+ // app.Get("/static/{file:path}", h)
+ // app.Head("/static/{file:path}", h)
+ StripPrefix = router.StripPrefix
+ // FromStd converts native http.Handler, http.HandlerFunc & func(w, r, next) 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))
+ //
+ // A shortcut for the `handlerconv#FromStd`.
+ FromStd = handlerconv.FromStd
+ // Cache is a middleware providing server-side cache functionalities
+ // to the next handlers, can be used as: `app.Get("/", iris.Cache, aboutHandler)`.
+ // It should be used after Static methods.
+ // See `iris#Cache304` for an alternative, faster way.
+ //
+ // Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/#caching
+ Cache = cache.Handler
+ // NoCache is a middleware which overrides the Cache-Control, Pragma and Expires headers
+ // in order to disable the cache during the browser's back and forward feature.
+ //
+ // A good use of this middleware is on HTML routes; to refresh the page even on "back" and "forward" browser's arrow buttons.
+ //
+ // See `iris#StaticCache` for the opposite behavior.
+ //
+ // A shortcut of the `cache#NoCache`
+ NoCache = cache.NoCache
+ // StaticCache middleware for caching static files by sending the "Cache-Control" and "Expires" headers to the client.
+ // It accepts a single input parameter, the "cacheDur", a time.Duration that it's used to calculate the expiration.
+ //
+ // If "cacheDur" <=0 then it returns the `NoCache` middleware instaed to disable the caching between browser's "back" and "forward" actions.
+ //
+ // Usage: `app.Use(iris.StaticCache(24 * time.Hour))` or `app.Use(iris.StaticCache(-1))`.
+ // A middleware, which is a simple Handler can be called inside another handler as well, example:
+ // cacheMiddleware := iris.StaticCache(...)
+ // func(ctx iris.Context){
+ // cacheMiddleware(ctx)
+ // [...]
+ // }
+ //
+ // A shortcut of the `cache#StaticCache`
+ StaticCache = cache.StaticCache
+ // Cache304 sends a `StatusNotModified` (304) whenever
+ // the "If-Modified-Since" request header (time) is before the
+ // time.Now() + expiresEvery (always compared to their UTC values).
+ // Use this, which is a shortcut of the, `chache#Cache304` instead of the "github.com/kataras/iris/v12/cache" or iris.Cache
+ // for better performance.
+ // Clients that are compatible with the http RCF (all browsers are and tools like postman)
+ // will handle the caching.
+ // The only disadvantage of using that instead of server-side caching
+ // is that this method will send a 304 status code instead of 200,
+ // So, if you use it side by side with other micro services
+ // you have to check for that status code as well for a valid response.
+ //
+ // Developers are free to extend this method's behavior
+ // by watching system directories changes manually and use of the `ctx.WriteWithExpiration`
+ // with a "modtime" based on the file modified date,
+ // similar to the `HandleDir`(which sends status OK(200) and browser disk caching instead of 304).
+ //
+ // A shortcut of the `cache#Cache304`.
+ Cache304 = cache.Cache304
+
+ // CookieAllowReclaim accepts the Context itself.
+ // If set it will add the cookie to (on `CookieSet`, `CookieSetKV`, `CookieUpsert`)
+ // or remove the cookie from (on `CookieRemove`) the Request object too.
+ //
+ // A shortcut for the `context#CookieAllowReclaim`.
+ CookieAllowReclaim = context.CookieAllowReclaim
+ // CookieAllowSubdomains set to the Cookie Options
+ // in order to allow subdomains to have access to the cookies.
+ // It sets the cookie's Domain field (if was empty) and
+ // it also sets the cookie's SameSite to lax mode too.
+ //
+ // A shortcut for the `context#CookieAllowSubdomains`.
+ CookieAllowSubdomains = context.CookieAllowSubdomains
+ // CookieSameSite sets a same-site rule for cookies to set.
+ // SameSite allows a server to define a cookie attribute making it impossible for
+ // the browser to send this cookie along with cross-site requests. The main
+ // goal is to mitigate the risk of cross-origin information leakage, and provide
+ // some protection against cross-site request forgery attacks.
+ //
+ // See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
+ //
+ // A shortcut for the `context#CookieSameSite`.
+ CookieSameSite = context.CookieHTTPOnly
+ // CookieSecure sets the cookie's Secure option if the current request's
+ // connection is using TLS. See `CookieHTTPOnly` too.
+ //
+ // A shortcut for the `context#CookieSecure`.
+ CookieSecure = context.CookieSecure
+ // CookieHTTPOnly is a `CookieOption`.
+ // Use it to set the cookie's HttpOnly field to false or true.
+ // HttpOnly field defaults to true for `RemoveCookie` and `SetCookieKV`.
+ //
+ // A shortcut for the `context#CookieHTTPOnly`.
+ CookieHTTPOnly = context.CookieHTTPOnly
+ // CookiePath is a `CookieOption`.
+ // Use it to change the cookie's Path field.
+ //
+ // A shortcut for the `context#CookiePath`.
+ CookiePath = context.CookiePath
+ // CookieCleanPath is a `CookieOption`.
+ // Use it to clear the cookie's Path field, exactly the same as `CookiePath("")`.
+ //
+ // A shortcut for the `context#CookieCleanPath`.
+ CookieCleanPath = context.CookieCleanPath
+ // CookieExpires is a `CookieOption`.
+ // Use it to change the cookie's Expires and MaxAge fields by passing the lifetime of the cookie.
+ //
+ // A shortcut for the `context#CookieExpires`.
+ CookieExpires = context.CookieExpires
+ // CookieEncoding accepts a value which implements `Encode` and `Decode` methods.
+ // It calls its `Encode` on `Context.SetCookie, UpsertCookie, and SetCookieKV` methods.
+ // And on `Context.GetCookie` method it calls its `Decode`.
+ //
+ // A shortcut for the `context#CookieEncoding`.
+ CookieEncoding = context.CookieEncoding
+
+ // IsErrPath can be used at `context#ReadForm` and `context#ReadQuery`.
+ // It reports whether the incoming error is type of `formbinder.ErrPath`,
+ // which can be ignored when server allows unknown post values to be sent by the client.
+ //
+ // A shortcut for the `context#IsErrPath`.
+ IsErrPath = context.IsErrPath
+ // ErrEmptyForm is the type error which API users can make use of
+ // to check if a form was empty on `Context.ReadForm`.
+ //
+ // A shortcut for the `context#ErrEmptyForm`.
+ ErrEmptyForm = context.ErrEmptyForm
+ // NewProblem returns a new Problem.
+ // Head over to the `Problem` type godoc for more.
+ //
+ // A shortcut for the `context#NewProblem`.
+ NewProblem = context.NewProblem
+ // XMLMap wraps a map[string]interface{} to compatible xml marshaler,
+ // in order to be able to render maps as XML on the `Context.XML` method.
+ //
+ // Example: `Context.XML(XMLMap("Root", map[string]interface{}{...})`.
+ //
+ // A shortcut for the `context#XMLMap`.
+ XMLMap = context.XMLMap
+ // ErrStopExecution if returned from a hero middleware or a request-scope dependency
+ // stops the handler's execution, see _examples/dependency-injection/basic/middleware.
+ ErrStopExecution = hero.ErrStopExecution
+ // ErrHijackNotSupported is returned by the Hijack method to
+ // indicate that Hijack feature is not available.
+ //
+ // A shortcut for the `context#ErrHijackNotSupported`.
+ ErrHijackNotSupported = context.ErrHijackNotSupported
+ // ErrPushNotSupported is returned by the Push method to
+ // indicate that HTTP/2 Push support is not available.
+ //
+ // A shortcut for the `context#ErrPushNotSupported`.
+ ErrPushNotSupported = context.ErrPushNotSupported
+)
+
+// HTTP Methods copied from `net/http`.
+const (
+ MethodGet = http.MethodGet
+ MethodPost = http.MethodPost
+ MethodPut = http.MethodPut
+ MethodDelete = http.MethodDelete
+ MethodConnect = http.MethodConnect
+ MethodHead = http.MethodHead
+ MethodPatch = http.MethodPatch
+ MethodOptions = http.MethodOptions
+ MethodTrace = http.MethodTrace
+ // MethodNone is an iris-specific "virtual" method
+ // to store the "offline" routes.
+ MethodNone = router.MethodNone
+)
+
+// HTTP status codes as registered with IANA.
+// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.
+// Raw Copy from the future(tip) net/http std package in order to recude the import path of "net/http" for the users.
+const (
+ StatusContinue = http.StatusContinue
+ StatusSwitchingProtocols = http.StatusSwitchingProtocols
+ StatusProcessing = http.StatusProcessing
+ StatusEarlyHints = http.StatusEarlyHints
+ StatusOK = http.StatusOK
+ StatusCreated = http.StatusCreated
+ StatusAccepted = http.StatusAccepted
+ StatusNonAuthoritativeInfo = http.StatusNonAuthoritativeInfo
+ StatusNoContent = http.StatusNoContent
+ StatusResetContent = http.StatusResetContent
+ StatusPartialContent = http.StatusPartialContent
+ StatusMultiStatus = http.StatusMultiStatus
+ StatusAlreadyReported = http.StatusAlreadyReported
+ StatusIMUsed = http.StatusIMUsed
+
+ StatusMultipleChoices = http.StatusMultipleChoices
+ StatusMovedPermanently = http.StatusMovedPermanently
+ StatusFound = http.StatusFound
+ StatusSeeOther = http.StatusSeeOther
+ StatusNotModified = http.StatusNotModified
+ StatusUseProxy = http.StatusUseProxy
+
+ StatusTemporaryRedirect = http.StatusTemporaryRedirect
+ StatusPermanentRedirect = http.StatusPermanentRedirect
+
+ StatusBadRequest = http.StatusBadRequest
+ StatusUnauthorized = http.StatusUnauthorized
+ StatusPaymentRequired = http.StatusPaymentRequired
+ StatusForbidden = http.StatusForbidden
+ StatusNotFound = http.StatusNotFound
+ StatusMethodNotAllowed = http.StatusMethodNotAllowed
+ StatusNotAcceptable = http.StatusNotAcceptable
+ StatusProxyAuthRequired = http.StatusProxyAuthRequired
+ StatusRequestTimeout = http.StatusRequestTimeout
+ StatusConflict = http.StatusConflict
+ StatusGone = http.StatusGone
+ StatusLengthRequired = http.StatusLengthRequired
+ StatusPreconditionFailed = http.StatusPreconditionFailed
+ StatusRequestEntityTooLarge = http.StatusRequestEntityTooLarge
+ StatusPayloadTooRage = StatusRequestEntityTooLarge
+ StatusRequestURITooLong = http.StatusRequestURITooLong
+ StatusUnsupportedMediaType = http.StatusUnsupportedMediaType
+ StatusRequestedRangeNotSatisfiable = http.StatusRequestedRangeNotSatisfiable
+ StatusExpectationFailed = http.StatusExpectationFailed
+ StatusTeapot = http.StatusTeapot
+ StatusMisdirectedRequest = http.StatusMisdirectedRequest
+ StatusUnprocessableEntity = http.StatusUnprocessableEntity
+ StatusLocked = http.StatusLocked
+ StatusFailedDependency = http.StatusFailedDependency
+ StatusTooEarly = http.StatusTooEarly
+ StatusUpgradeRequired = http.StatusUpgradeRequired
+ StatusPreconditionRequired = http.StatusPreconditionRequired
+ StatusTooManyRequests = http.StatusTooManyRequests
+ StatusRequestHeaderFieldsTooLarge = http.StatusRequestHeaderFieldsTooLarge
+ StatusUnavailableForLegalReasons = http.StatusUnavailableForLegalReasons
+ // Unofficial Client Errors.
+ StatusPageExpired = context.StatusPageExpired
+ StatusBlockedByWindowsParentalControls = context.StatusBlockedByWindowsParentalControls
+ StatusInvalidToken = context.StatusInvalidToken
+ StatusTokenRequired = context.StatusTokenRequired
+ //
+ StatusInternalServerError = http.StatusInternalServerError
+ StatusNotImplemented = http.StatusNotImplemented
+ StatusBadGateway = http.StatusBadGateway
+ StatusServiceUnavailable = http.StatusServiceUnavailable
+ StatusGatewayTimeout = http.StatusGatewayTimeout
+ StatusHTTPVersionNotSupported = http.StatusHTTPVersionNotSupported
+ StatusVariantAlsoNegotiates = http.StatusVariantAlsoNegotiates
+ StatusInsufficientStorage = http.StatusInsufficientStorage
+ StatusLoopDetected = http.StatusLoopDetected
+ StatusNotExtended = http.StatusNotExtended
+ StatusNetworkAuthenticationRequired = http.StatusNetworkAuthenticationRequired
+ // Unofficial Server Errors.
+ StatusBandwidthLimitExceeded = context.StatusBandwidthLimitExceeded
+ StatusInvalidSSLCertificate = context.StatusInvalidSSLCertificate
+ StatusSiteOverloaded = context.StatusSiteOverloaded
+ StatusSiteFrozen = context.StatusSiteFrozen
+ StatusNetworkReadTimeout = context.StatusNetworkReadTimeout
+)
+
+// StatusText returns a text for the HTTP status code. It returns the empty
+// string if the code is unknown.
+//
+// Shortcut for core/router#StatusText.
+var StatusText = context.StatusText
diff --git a/cache/browser.go b/cache/browser.go
index 5ed1230f..e456b91e 100644
--- a/cache/browser.go
+++ b/cache/browser.go
@@ -31,7 +31,7 @@ const (
// A good use of this middleware is on HTML routes; to refresh the page even on "back" and "forward" browser's arrow buttons.
//
// See `cache#StaticCache` for the opposite behavior.
-var NoCache = func(ctx context.Context) {
+var NoCache = func(ctx *context.Context) {
ctx.Header(context.CacheControlHeaderKey, CacheControlHeaderValue)
ctx.Header(PragmaHeaderKey, PragmaNoCacheHeaderValue)
ctx.Header(ExpiresHeaderKey, ExpiresNeverHeaderValue)
@@ -59,7 +59,7 @@ var StaticCache = func(cacheDur time.Duration) context.Handler {
}
cacheControlHeaderValue := "public, max-age=" + strconv.Itoa(int(cacheDur.Seconds()))
- return func(ctx context.Context) {
+ return func(ctx *context.Context) {
cacheUntil := time.Now().Add(cacheDur).Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())
ctx.Header(ExpiresHeaderKey, cacheUntil)
ctx.Header(context.CacheControlHeaderKey, cacheControlHeaderValue)
@@ -98,7 +98,7 @@ const ifNoneMatchHeaderKey = "If-None-Match"
//
// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching and
// https://en.wikipedia.org/wiki/HTTP_ETag
-var ETag = func(ctx context.Context) {
+var ETag = func(ctx *context.Context) {
key := ctx.Request().URL.Path
ctx.Header(context.ETagHeaderKey, key)
if match := ctx.GetHeader(ifNoneMatchHeaderKey); match == key {
@@ -126,7 +126,7 @@ var ETag = func(ctx context.Context) {
// can be used on Party's that contains a static handler,
// i.e `HandleDir`.
var Cache304 = func(expiresEvery time.Duration) context.Handler {
- return func(ctx context.Context) {
+ return func(ctx *context.Context) {
now := time.Now()
if modified, err := ctx.CheckIfModifiedSince(now.Add(-expiresEvery)); !modified && err == nil {
ctx.WriteNotModified()
diff --git a/cache/cache_test.go b/cache/cache_test.go
index 94703428..f9a75bd1 100644
--- a/cache/cache_test.go
+++ b/cache/cache_test.go
@@ -97,12 +97,12 @@ func TestClientNoCache(t *testing.T) {
app := iris.New()
var n uint32
- app.Get("/", cache.Handler(cacheDuration), func(ctx context.Context) {
+ app.Get("/", cache.Handler(cacheDuration), func(ctx *context.Context) {
atomic.AddUint32(&n, 1)
ctx.Write([]byte(expectedBodyStr))
})
- app.Get("/nocache", cache.Handler(cacheDuration), func(ctx context.Context) {
+ app.Get("/nocache", cache.Handler(cacheDuration), func(ctx *context.Context) {
client.NoCache(ctx) // <----
atomic.AddUint32(&n, 1)
ctx.Write([]byte(expectedBodyStr))
@@ -120,7 +120,7 @@ func TestCache(t *testing.T) {
app.Use(cache.Handler(cacheDuration))
- app.Get("/", func(ctx context.Context) {
+ app.Get("/", func(ctx *context.Context) {
atomic.AddUint32(&n, 1)
ctx.Write([]byte(expectedBodyStr))
})
@@ -130,7 +130,7 @@ func TestCache(t *testing.T) {
expectedBodyStr2 = "This is the other"
)
- app.Get("/other", func(ctx context.Context) {
+ app.Get("/other", func(ctx *context.Context) {
atomic.AddUint32(&n2, 1)
ctx.Write([]byte(expectedBodyStr2))
})
@@ -154,7 +154,7 @@ func TestCacheValidator(t *testing.T) {
app := iris.New()
var n uint32
- h := func(ctx context.Context) {
+ h := func(ctx *context.Context) {
atomic.AddUint32(&n, 1)
ctx.Write([]byte(expectedBodyStr))
}
@@ -164,7 +164,7 @@ func TestCacheValidator(t *testing.T) {
managedCache := cache.Cache(cacheDuration)
managedCache.AddRule(rule.Validator([]rule.PreValidator{
- func(ctx context.Context) bool {
+ func(ctx *context.Context) bool {
// should always invalid for cache, don't bother to go to try to get or set cache
return ctx.Request().URL.Path != "/invalid"
},
@@ -173,7 +173,7 @@ func TestCacheValidator(t *testing.T) {
managedCache2 := cache.Cache(cacheDuration)
managedCache2.AddRule(rule.Validator(nil,
[]rule.PostValidator{
- func(ctx context.Context) bool {
+ func(ctx *context.Context) bool {
// it's passed the Claim and now Valid checks if the response contains a header of "DONT"
return ctx.ResponseWriter().Header().Get("DONT") == ""
},
@@ -183,7 +183,7 @@ func TestCacheValidator(t *testing.T) {
app.Get("/valid", validCache.ServeHTTP, h)
app.Get("/invalid", managedCache.ServeHTTP, h)
- app.Get("/invalid2", managedCache2.ServeHTTP, func(ctx context.Context) {
+ app.Get("/invalid2", managedCache2.ServeHTTP, func(ctx *context.Context) {
atomic.AddUint32(&n, 1)
ctx.Header("DONT", "DO not cache that response even if it was claimed")
ctx.Write([]byte(expectedBodyStr))
diff --git a/cache/client/client.go b/cache/client/client.go
index cb572cfd..5d89c429 100644
--- a/cache/client/client.go
+++ b/cache/client/client.go
@@ -101,7 +101,7 @@ const (
// if <=minimumAllowedCacheDuration then the server will try to parse from "cache-control" header
//
// client-side function
-func (h *ClientHandler) ServeHTTP(ctx context.Context) {
+func (h *ClientHandler) ServeHTTP(ctx *context.Context) {
// check for deniers, if at least one of them return true
// for this specific request, then skip the whole cache
if !h.rule.Claim(ctx) {
diff --git a/cache/client/handler.go b/cache/client/handler.go
index 0e10b014..ffeae084 100644
--- a/cache/client/handler.go
+++ b/cache/client/handler.go
@@ -63,17 +63,17 @@ func (h *Handler) AddRule(r rule.Rule) *Handler {
return h
}
-var emptyHandler = func(ctx context.Context) {
+var emptyHandler = func(ctx *context.Context) {
ctx.StopWithText(500, "cache: empty body handler")
}
-func parseLifeChanger(ctx context.Context) entry.LifeChanger {
+func parseLifeChanger(ctx *context.Context) entry.LifeChanger {
return func() time.Duration {
return time.Duration(ctx.MaxAge()) * time.Second
}
}
-func (h *Handler) ServeHTTP(ctx context.Context) {
+func (h *Handler) ServeHTTP(ctx *context.Context) {
// check for pre-cache validators, if at least one of them return false
// for this specific request, then skip the whole cache
bodyHandler := ctx.NextHandler()
diff --git a/cache/client/rule/chained.go b/cache/client/rule/chained.go
index a2765eaf..20a29960 100644
--- a/cache/client/rule/chained.go
+++ b/cache/client/rule/chained.go
@@ -39,7 +39,7 @@ func Chained(rule Rule, next ...Rule) Rule {
}
// Claim validator
-func (c *chainedRule) Claim(ctx context.Context) bool {
+func (c *chainedRule) Claim(ctx *context.Context) bool {
if !c.Rule.Claim(ctx) {
return false
}
@@ -47,7 +47,7 @@ func (c *chainedRule) Claim(ctx context.Context) bool {
}
// Valid validator
-func (c *chainedRule) Valid(ctx context.Context) bool {
+func (c *chainedRule) Valid(ctx *context.Context) bool {
if !c.Rule.Valid(ctx) {
return false
}
diff --git a/cache/client/rule/conditional.go b/cache/client/rule/conditional.go
index 6e50adb8..a07f0cd3 100644
--- a/cache/client/rule/conditional.go
+++ b/cache/client/rule/conditional.go
@@ -33,11 +33,11 @@ func Conditional(claimPredicate func() bool, validPredicate func() bool) Rule {
}
// Claim validator
-func (c *conditionalRule) Claim(ctx context.Context) bool {
+func (c *conditionalRule) Claim(ctx *context.Context) bool {
return c.claimPredicate()
}
// Valid validator
-func (c *conditionalRule) Valid(ctx context.Context) bool {
+func (c *conditionalRule) Valid(ctx *context.Context) bool {
return c.validPredicate()
}
diff --git a/cache/client/rule/header.go b/cache/client/rule/header.go
index ab77dc0e..b118fec4 100644
--- a/cache/client/rule/header.go
+++ b/cache/client/rule/header.go
@@ -45,11 +45,11 @@ func HeaderValid(valid ruleset.HeaderPredicate) Rule {
}
// Claim validator
-func (h *headerRule) Claim(ctx context.Context) bool {
+func (h *headerRule) Claim(ctx *context.Context) bool {
return h.claim(ctx.Request().Header.Get)
}
// Valid validator
-func (h *headerRule) Valid(ctx context.Context) bool {
+func (h *headerRule) Valid(ctx *context.Context) bool {
return h.valid(ctx.ResponseWriter().Header().Get)
}
diff --git a/cache/client/rule/not_satisfied.go b/cache/client/rule/not_satisfied.go
index 3727ab6c..cfd85f9a 100644
--- a/cache/client/rule/not_satisfied.go
+++ b/cache/client/rule/not_satisfied.go
@@ -13,10 +13,10 @@ func NotSatisfied() Rule {
return ¬SatisfiedRule{}
}
-func (n *notSatisfiedRule) Claim(context.Context) bool {
+func (n *notSatisfiedRule) Claim(*context.Context) bool {
return false
}
-func (n *notSatisfiedRule) Valid(context.Context) bool {
+func (n *notSatisfiedRule) Valid(*context.Context) bool {
return false
}
diff --git a/cache/client/rule/rule.go b/cache/client/rule/rule.go
index 3758c4c4..92fe252f 100644
--- a/cache/client/rule/rule.go
+++ b/cache/client/rule/rule.go
@@ -1,11 +1,9 @@
package rule
-import (
- "github.com/kataras/iris/v12/context"
-)
+import "github.com/kataras/iris/v12/context"
// Rule a superset of validators
type Rule interface {
- Claim(ctx context.Context) bool
- Valid(ctx context.Context) bool
+ Claim(ctx *context.Context) bool
+ Valid(ctx *context.Context) bool
}
diff --git a/cache/client/rule/satisfied.go b/cache/client/rule/satisfied.go
index 8e34eec6..9fd40875 100644
--- a/cache/client/rule/satisfied.go
+++ b/cache/client/rule/satisfied.go
@@ -15,10 +15,10 @@ func Satisfied() Rule {
return &satisfiedRule{}
}
-func (n *satisfiedRule) Claim(context.Context) bool {
+func (n *satisfiedRule) Claim(*context.Context) bool {
return true
}
-func (n *satisfiedRule) Valid(context.Context) bool {
+func (n *satisfiedRule) Valid(*context.Context) bool {
return true
}
diff --git a/cache/client/rule/validator.go b/cache/client/rule/validator.go
index a4e84baf..1f8ca36c 100644
--- a/cache/client/rule/validator.go
+++ b/cache/client/rule/validator.go
@@ -1,8 +1,6 @@
package rule
-import (
- "github.com/kataras/iris/v12/context"
-)
+import "github.com/kataras/iris/v12/context"
// Validators are introduced to implement the RFC about cache (https://tools.ietf.org/html/rfc7234#section-1.1).
@@ -18,7 +16,7 @@ import (
// One function, accepts the request and returns false if should be denied/ignore, otherwise true.
// if at least one return false then the original handler will execute as it's
// and the whole cache action(set & get) should be ignored, it will be never go to the step of post-cache validations.
-type PreValidator func(context.Context) bool
+type PreValidator func(*context.Context) bool
// PostValidator type is is introduced to implement the second part of the RFC about cache.
//
@@ -32,7 +30,7 @@ type PreValidator func(context.Context) bool
// 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
+type PostValidator func(*context.Context) bool
// validatorRule is a rule witch receives PreValidators and PostValidators
// it's a 'complete set of rules', you can call it as a Responsible Validator,
@@ -68,7 +66,7 @@ func Validator(preValidators []PreValidator, postValidators []PostValidator) Rul
// Claim returns true if incoming request can claim for a cached handler
// the original handler should run as it is and exit
-func (v *validatorRule) Claim(ctx context.Context) bool {
+func (v *validatorRule) Claim(ctx *context.Context) bool {
// check for pre-cache validators, if at least one of them return false
// for this specific request, then skip the whole cache
for _, shouldProcess := range v.preValidators {
@@ -82,7 +80,7 @@ func (v *validatorRule) Claim(ctx context.Context) bool {
// Valid returns true if incoming request and post-response from the original handler
// is valid to be store to the cache, if not(false) then the consumer should just exit
// otherwise(true) the consumer should store the cached response
-func (v *validatorRule) Valid(ctx context.Context) bool {
+func (v *validatorRule) Valid(ctx *context.Context) bool {
// check if it's a valid response, if it's not then just return.
for _, valid := range v.postValidators {
if !valid(ctx) {
diff --git a/cache/client/ruleset.go b/cache/client/ruleset.go
index d9d8fccc..52ec35a1 100644
--- a/cache/client/ruleset.go
+++ b/cache/client/ruleset.go
@@ -28,6 +28,6 @@ var DefaultRuleSet = rule.Chained(
// NoCache disables the cache for a particular request,
// can be used as a middleware or called manually from the handler.
-func NoCache(ctx context.Context) {
+func NoCache(ctx *context.Context) {
ctx.Header(cfg.NoCacheHeader, "true")
}
diff --git a/cli.go b/cli.go
new file mode 100644
index 00000000..c9eef3fa
--- /dev/null
+++ b/cli.go
@@ -0,0 +1,120 @@
+package iris
+
+// +------------------------------------------------------------+
+// | Bridge code between iris-cli and iris web application |
+// | https://github.com/kataras/iris-cli |
+// +------------------------------------------------------------+
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/kataras/iris/v12/context"
+ "github.com/kataras/iris/v12/core/router"
+ "gopkg.in/yaml.v3"
+)
+
+// injectLiveReload tries to check if this application
+// runs under https://github.com/kataras/iris-cli and if so
+// then it checks if the livereload is enabled and then injects
+// the watch listener (js script) on every HTML response.
+// It has a slight performance cost but
+// this (iris-cli with watch and livereload enabled)
+// is meant to be used only in development mode.
+// It does a full reload at the moment and if the port changed
+// at runtime it will fire 404 instead of redirecting to the correct port (that's a TODO).
+//
+// tryInjectLiveReload runs right before Build -> BuildRouter.
+func injectLiveReload(contextPool *context.Pool, router *router.Router) (bool, error) {
+ conf := struct {
+ Running bool `yaml:"Running,omitempty"`
+ LiveReload struct {
+ Disable bool `yaml:"Disable"`
+ Port int `yaml:"Port"`
+ } `yaml:"LiveReload"`
+ }{}
+ // defaults to disabled here.
+ conf.LiveReload.Disable = true
+
+ wd, err := os.Getwd()
+ if err != nil {
+ return false, err
+ }
+
+ for _, path := range []string{".iris.yml" /*, "../.iris.yml", "../../.iris.yml" */} {
+ path = filepath.Join(wd, path)
+
+ if _, err := os.Stat(path); err == nil {
+ inFile, err := os.OpenFile(path, os.O_RDONLY, 0644)
+ if err != nil {
+ return false, err
+ }
+
+ dec := yaml.NewDecoder(inFile)
+ err = dec.Decode(&conf)
+ inFile.Close()
+ if err != nil {
+ return false, err
+ }
+
+ break
+ }
+ }
+
+ if !conf.Running || conf.LiveReload.Disable {
+ return false, nil
+ }
+
+ scriptReloadJS := []byte(fmt.Sprintf(``, conf.LiveReload.Port))
+
+ bodyCloseTag := []byte("