diff --git a/HISTORY.md b/HISTORY.md
index cb4f984c..d3d04fc5 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -429,8 +429,9 @@ Other Improvements:
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.DirListRich` to override the default look and feel if the `DirOptions.ShowList` was set to true, can be passed to `DirOptions.DirList` field.
+- `DirOptions.PushTargets` for http/2 push on index [*](https://github.com/kataras/iris/tree/master/_examples/file-server/http2push/main.go).
- `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)
diff --git a/_examples/README.md b/_examples/README.md
index 2459edd3..54b6127f 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -78,6 +78,7 @@
* [Profiling](pprof/main.go)
* File Server
* [File Server](file-server/file-server/main.go)
+ * [HTTP/2 Push Targets](file-server/http2push/main.go)
* [Favicon](file-server/favicon/main.go)
* [Basic](file-server/basic/main.go)
* [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)
diff --git a/_examples/file-server/embedding-files-into-app/main.go b/_examples/file-server/embedding-files-into-app/main.go
index 1b239cf8..bb4be341 100644
--- a/_examples/file-server/embedding-files-into-app/main.go
+++ b/_examples/file-server/embedding-files-into-app/main.go
@@ -7,8 +7,7 @@ import (
// Follow these steps first:
// $ go get -u github.com/go-bindata/go-bindata/...
// $ go-bindata ./assets/...
-// $ go build
-// $ ./embedding-files-into-app
+// $ go run .
// "physical" files are not used, you can delete the "assets" folder and run the example.
//
// See `file-server/embedding-gziped-files-into-app` example as well.
diff --git a/_examples/file-server/http2push/assets/css/main.css b/_examples/file-server/http2push/assets/css/main.css
new file mode 100644
index 00000000..7db3df1d
--- /dev/null
+++ b/_examples/file-server/http2push/assets/css/main.css
@@ -0,0 +1,3 @@
+body {
+ background-color: black;
+}
diff --git a/_examples/file-server/http2push/assets/favicon.ico b/_examples/file-server/http2push/assets/favicon.ico
new file mode 100644
index 00000000..c370da51
Binary files /dev/null and b/_examples/file-server/http2push/assets/favicon.ico differ
diff --git a/_examples/file-server/http2push/assets/index.html b/_examples/file-server/http2push/assets/index.html
new file mode 100644
index 00000000..839b2a4f
--- /dev/null
+++ b/_examples/file-server/http2push/assets/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+ File Server
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_examples/file-server/http2push/assets/js/main.js b/_examples/file-server/http2push/assets/js/main.js
new file mode 100644
index 00000000..f97190ca
--- /dev/null
+++ b/_examples/file-server/http2push/assets/js/main.js
@@ -0,0 +1,5 @@
+console.log("example");
+
+function onClick() {
+ window.alert("button clicked");
+}
\ No newline at end of file
diff --git a/_examples/file-server/http2push/main.go b/_examples/file-server/http2push/main.go
new file mode 100644
index 00000000..a14d26ef
--- /dev/null
+++ b/_examples/file-server/http2push/main.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "github.com/kataras/iris/v12"
+)
+
+var opts = iris.DirOptions{
+ IndexName: "/index.html",
+ // Optionally register files (map's absolute values) to be served
+ // when a specific path (map's key WITHOUT prefix) is requested
+ // is fired before client asks (HTTP/2 Push).
+ // E.g. "/" (which serves the `IndexName` if not empty).
+ //
+ // Note: Requires running server under TLS,
+ // that's why we use ListenAndServeTLS below.
+ PushTargets: map[string][]string{
+ "/": {
+ "/public/favicon.ico",
+ "/public/js/main.js",
+ "/public/css/main.css",
+ },
+ },
+ Compress: true,
+ ShowList: true,
+}
+
+func main() {
+ app := iris.New()
+ app.HandleDir("/public", "./assets", opts)
+
+ // Open your browser's Network tools,
+ // navigate to https://127.0.0.1/public.
+ // you should see `Initiator` tab: "Push / public".
+ app.Run(iris.TLS(":443", "mycert.crt", "mykey.key"))
+}
diff --git a/_examples/file-server/http2push/mycert.crt b/_examples/file-server/http2push/mycert.crt
new file mode 100644
index 00000000..9db93b09
--- /dev/null
+++ b/_examples/file-server/http2push/mycert.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2
+MjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2
+OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV
+wKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu
+amn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/
+DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS
+QQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN
+rhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID
+gSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l
+Ch8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9
+v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/
+91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN
+GQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j
+BBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9
+xVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG
+SIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe
+HY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY
+rdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz
+Bl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ
++2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax
+9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe
+y/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA
+6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b
+YzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8=
+-----END CERTIFICATE-----
diff --git a/_examples/file-server/http2push/mykey.key b/_examples/file-server/http2push/mykey.key
new file mode 100644
index 00000000..39f7b3af
--- /dev/null
+++ b/_examples/file-server/http2push/mykey.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+W2TzuSMYNDu876twbTVQJED3mwOAQ3J7
+-----END PRIVATE KEY-----
diff --git a/_examples/response-writer/http2push/main.go b/_examples/response-writer/http2push/main.go
index d7fce749..23ea1d80 100644
--- a/_examples/response-writer/http2push/main.go
+++ b/_examples/response-writer/http2push/main.go
@@ -4,7 +4,11 @@
// to need for the page they’re requesting.
package main
-import "github.com/kataras/iris/v12"
+import (
+ "net/http"
+
+ "github.com/kataras/iris/v12"
+)
func main() {
app := iris.New()
@@ -22,14 +26,17 @@ func pushHandler(ctx iris.Context) {
// If the target is a path, it will inherit the scheme and host of the
// parent request.
target := "/main.js"
- err := ctx.ResponseWriter().Push(target, nil)
- if err != nil {
- if err == iris.ErrPushNotSupported {
- ctx.StopWithText(iris.StatusHTTPVersionNotSupported, "HTTP/2 push not supported.")
- } else {
- ctx.StopWithError(iris.StatusInternalServerError, err)
+
+ if pusher, ok := ctx.ResponseWriter().(http.Pusher); ok {
+ err := pusher.Push(target, nil)
+ if err != nil {
+ if err == iris.ErrPushNotSupported {
+ ctx.StopWithText(iris.StatusHTTPVersionNotSupported, "HTTP/2 push not supported.")
+ } else {
+ ctx.StopWithError(iris.StatusInternalServerError, err)
+ }
+ return
}
- return
}
ctx.HTML(``, target)
diff --git a/context/compress.go b/context/compress.go
index ea8f3241..a1d9b272 100644
--- a/context/compress.go
+++ b/context/compress.go
@@ -164,6 +164,10 @@ type CompressResponseWriter struct {
CompressWriter
ResponseWriter
+ http.Pusher
+ http.Hijacker
+ http.CloseNotifier
+
Disabled bool
Encoding string
Level int
@@ -195,12 +199,15 @@ func AcquireCompressResponseWriter(w ResponseWriter, r *http.Request, level int)
if level == -1 && encoding == BROTLI {
level = 6
}
- // Writer exists, encoding matching and it's valid because it has a non nil encWriter;
- // just reset to reduce allocations.
- if v.Encoding == encoding && v.Level == level && v.CompressWriter != nil {
- v.CompressWriter.Reset(w)
- return v, nil
- }
+
+ /*
+ // Writer exists, encoding matching and it's valid because it has a non nil encWriter;
+ // just reset to reduce allocations.
+ if v.Encoding == encoding && v.Level == level && v.CompressWriter != nil {
+ v.CompressWriter.Reset(w)
+ return v, nil
+ }
+ */
v.Encoding = encoding
@@ -213,6 +220,26 @@ func AcquireCompressResponseWriter(w ResponseWriter, r *http.Request, level int)
v.CompressWriter = encWriter
AddCompressHeaders(w.Header(), encoding)
+
+ pusher, ok := w.(http.Pusher)
+ if !ok {
+ pusher = nil // make sure interface value is nil.
+ }
+
+ hijacker, ok := w.(http.Hijacker)
+ if !ok {
+ hijacker = nil
+ }
+
+ closeNotifier, ok := w.(http.CloseNotifier)
+ if !ok {
+ closeNotifier = nil
+ }
+
+ v.Pusher = pusher
+ v.Hijacker = hijacker
+ v.CloseNotifier = closeNotifier
+
return v, nil
}
diff --git a/context/response_recorder.go b/context/response_recorder.go
index 0b406d91..caf511a3 100644
--- a/context/response_recorder.go
+++ b/context/response_recorder.go
@@ -1,6 +1,7 @@
package context
import (
+ "errors"
"net/http"
"sync"
)
@@ -31,6 +32,10 @@ func releaseResponseRecorder(w *ResponseRecorder) {
// rec := context.Recorder()
type ResponseRecorder struct {
ResponseWriter
+
+ http.Hijacker
+ http.CloseNotifier
+
// keep track of the body in order to be
// resetable and useful inside custom transactions
chunks []byte
@@ -50,6 +55,20 @@ func (w *ResponseRecorder) Naive() http.ResponseWriter {
// prepares itself, the response recorder, to record and send response to the client.
func (w *ResponseRecorder) BeginRecord(underline ResponseWriter) {
w.ResponseWriter = underline
+
+ hijacker, ok := underline.(http.Hijacker)
+ if !ok {
+ hijacker = nil
+ }
+
+ closeNotifier, ok := underline.(http.CloseNotifier)
+ if !ok {
+ closeNotifier = nil
+ }
+
+ w.Hijacker = hijacker
+ w.CloseNotifier = closeNotifier
+
w.headers = underline.Header()
w.ResetBody()
}
@@ -236,6 +255,10 @@ func (w *ResponseRecorder) Flush() {
w.ResetBody()
}
+// ErrPushNotSupported is returned by the Push method to
+// indicate that HTTP/2 Push support is not available.
+var ErrPushNotSupported = errors.New("push feature is not supported by this ResponseWriter")
+
// Push initiates an HTTP/2 server push. This constructs a synthetic
// request using the given target and options, serializes that request
// into a PUSH_PROMISE frame, then dispatches that request using the
@@ -256,12 +279,19 @@ func (w *ResponseRecorder) Flush() {
//
// Push returns ErrPushNotSupported if the client has disabled push or if push
// is not supported on the underlying connection.
-func (w *ResponseRecorder) Push(target string, opts *http.PushOptions) error {
+func (w *ResponseRecorder) Push(target string, opts *http.PushOptions) (err error) {
w.FlushResponse()
- err := w.ResponseWriter.Push(target, opts)
+
+ if pusher, ok := w.ResponseWriter.(http.Pusher); ok {
+ err = pusher.Push(target, opts)
+ if err != nil && err.Error() == http.ErrNotSupported.ErrorString {
+ return ErrPushNotSupported
+ }
+ }
+
// NOTE: we have to reset them even if the push failed.
w.ResetBody()
w.ResetHeaders()
- return err
+ return ErrPushNotSupported
}
diff --git a/context/response_writer.go b/context/response_writer.go
index 573ccd10..0816ce2c 100644
--- a/context/response_writer.go
+++ b/context/response_writer.go
@@ -20,12 +20,6 @@ import (
// has returned.
type ResponseWriter interface {
http.ResponseWriter
- http.Flusher
- http.Hijacker
- // Note:
- // The http.CloseNotifier interface is deprecated. New code should use Request.Context instead.
- http.CloseNotifier
- http.Pusher
// Naive returns the simple, underline and original http.ResponseWriter
// that backends this response writer.
@@ -87,10 +81,8 @@ type ResponseWriter interface {
// the buffered data may not reach the client until the response
// completes.
Flusher() (http.Flusher, bool)
-
- // CloseNotifier indicates if the protocol supports the underline connection closure notification.
- // Warning: The http.CloseNotifier interface is deprecated. New code should use Request.Context instead.
- CloseNotifier() (http.CloseNotifier, bool)
+ // Flush sends any buffered data to the client.
+ Flush() // required by compress writer.
}
// ResponseWriterBodyReseter can be implemented by
@@ -139,6 +131,11 @@ func releaseResponseWriter(w ResponseWriter) {
// it writes directly to the underline http.ResponseWriter
type responseWriter struct {
http.ResponseWriter
+ http.Pusher
+ http.Hijacker // Note:
+ // The http.CloseNotifier interface is deprecated. New code should use Request.Context instead.
+ http.CloseNotifier
+
statusCode int // the saved status code which will be used from the cache service
// statusCodeSent bool // reply header has been (logically) written | no needed any more as we have a variable to catch total len of written bytes
written int // the total size of bytes were written
@@ -172,6 +169,29 @@ func (w *responseWriter) BeginResponse(underline http.ResponseWriter) {
w.written = NoWritten
w.statusCode = defaultStatusCode
w.ResponseWriter = underline
+
+ pusher, ok := underline.(http.Pusher)
+ if !ok {
+ pusher = nil // make sure interface value is nil.
+ }
+
+ hijacker, ok := underline.(http.Hijacker)
+ if !ok {
+ hijacker = nil
+ }
+
+ // This interface is obselete by Go authors
+ // and we only capture it
+ // for compatible reasons. End-developers SHOULD replace
+ // the use of CloseNotifier with the: Request.Context().Done() channel.
+ closeNotifier, ok := underline.(http.CloseNotifier)
+ if !ok {
+ closeNotifier = nil
+ }
+
+ w.Pusher = pusher
+ w.Hijacker = hijacker
+ w.CloseNotifier = closeNotifier
}
// EndResponse is the last function which is called right before the server sent the final response.
@@ -365,71 +385,3 @@ func (w *responseWriter) Flush() {
flusher.Flush()
}
}
-
-// ErrPushNotSupported is returned by the Push method to
-// indicate that HTTP/2 Push support is not available.
-var ErrPushNotSupported = errors.New("push feature is not supported by this ResponseWriter")
-
-// Push initiates an HTTP/2 server push. This constructs a synthetic
-// request using the given target and options, serializes that request
-// into a PUSH_PROMISE frame, then dispatches that request using the
-// server's request handler. If opts is nil, default options are used.
-//
-// The target must either be an absolute path (like "/path") or an absolute
-// URL that contains a valid host and the same scheme as the parent request.
-// If the target is a path, it will inherit the scheme and host of the
-// parent request.
-//
-// The HTTP/2 spec disallows recursive pushes and cross-authority pushes.
-// Push may or may not detect these invalid pushes; however, invalid
-// pushes will be detected and canceled by conforming clients.
-//
-// Handlers that wish to push URL X should call Push before sending any
-// data that may trigger a request for URL X. This avoids a race where the
-// client issues requests for X before receiving the PUSH_PROMISE for X.
-//
-// Push returns ErrPushNotSupported if the client has disabled push or if push
-// is not supported on the underlying connection.
-func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
- if pusher, isPusher := w.ResponseWriter.(http.Pusher); isPusher {
- err := pusher.Push(target, opts)
- if err != nil && err.Error() == http.ErrNotSupported.ErrorString {
- return ErrPushNotSupported
- }
- return err
- }
- return ErrPushNotSupported
-}
-
-// CloseNotifier indicates if the protocol supports the underline connection closure notification.
-func (w *responseWriter) CloseNotifier() (http.CloseNotifier, bool) {
- notifier, supportsCloseNotify := w.ResponseWriter.(http.CloseNotifier)
- return notifier, supportsCloseNotify
-}
-
-// CloseNotify returns a channel that receives at most a
-// single value (true) when the client connection has gone
-// away.
-//
-// CloseNotify may wait to notify until Request.Body has been
-// fully read.
-//
-// After the Handler has returned, there is no guarantee
-// that the channel receives a value.
-//
-// If the protocol is HTTP/1.1 and CloseNotify is called while
-// processing an idempotent request (such a GET) while
-// HTTP/1.1 pipelining is in use, the arrival of a subsequent
-// pipelined request may cause a value to be sent on the
-// returned channel. In practice HTTP/1.1 pipelining is not
-// enabled in browsers and not seen often in the wild. If this
-// is a problem, use HTTP/2 or only use CloseNotify on methods
-// such as POST.
-func (w *responseWriter) CloseNotify() <-chan bool {
- if notifier, ok := w.CloseNotifier(); ok {
- return notifier.CloseNotify()
- }
-
- ch := make(chan bool, 1)
- return ch
-}
diff --git a/core/router/fs.go b/core/router/fs.go
index 2e12ac0e..fd108149 100644
--- a/core/router/fs.go
+++ b/core/router/fs.go
@@ -44,6 +44,11 @@ type DirOptions struct {
// that another handler, called index handler, is auto-registered by the framework
// if end developer does not managed to handle it by hand.
IndexName string
+ // PushTargets optionally absolute filenames (map's value) to be served without any
+ // additional client's requests (HTTP/2 Push)
+ // when a specific path (map's key) is requested and
+ // it's not a directory (it's an `IndexFile`).
+ PushTargets map[string][]string
// When files should served under compression.
Compress bool
@@ -472,6 +477,16 @@ func FileServer(directory string, opts ...DirOptions) context.Handler {
// // ctx.ResponseWriter().Header().Set(context.ContentEncodingHeaderKey, context.GzipHeaderValue)
// }
+ if indexFound && len(options.PushTargets) > 0 && !options.Attachments.Enable {
+ if indexAssets, ok := options.PushTargets[name]; ok {
+ if pusher, ok := ctx.ResponseWriter().(http.Pusher); ok {
+ for _, indexAsset := range indexAssets {
+ pusher.Push(indexAsset, nil)
+ }
+ }
+ }
+ }
+
// If limit is 0 then same as ServeContent.
ctx.ServeContentWithRate(f, info.Name(), info.ModTime(), options.Attachments.Limit, options.Attachments.Burst)
if serveCode := ctx.GetStatusCode(); context.StatusCodeNotSuccessful(serveCode) {
diff --git a/go.mod b/go.mod
index c3669d9c..bd9bb357 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.14
require (
github.com/BurntSushi/toml v0.3.1
- github.com/CloudyKit/jet/v4 v4.0.0
+ github.com/CloudyKit/jet/v4 v4.0.2
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398
github.com/andybalholm/brotli v1.0.1-0.20200619015827-c3da72aa01ed
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible