From 929b00a24b0571415bb5c0f5a7fdc00868bf2ccf Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Fri, 17 Feb 2017 04:57:51 +0200 Subject: [PATCH] HTTP/2 Push https://github.com/kataras/iris/issues/565 Former-commit-id: 32af4dd8ef18a5fb2fa88aa8b87e71a594faa6f2 --- HISTORY.md | 4 ++- configuration_test.go | 6 ---- iris.go | 3 +- response_recorder.go | 63 ++++++++++++++++-------------------- response_writer.go | 74 ++++++++++++++++++------------------------- 5 files changed, 62 insertions(+), 88 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e03a34b8..dc9dc268 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,12 +10,14 @@ Users already notified for some breaking-changes, this section will help you to adapt the new changes to your application, it contains an overview of the new features too. +- Shutdown with `app.Shutdown(context.Context) error`, no need for any third-parties, with `EventPolicy.Interrupted` and Go's 1.8 Gracefully Shutdown feature you're ready to go! +- HTTP/2 Go 1.8 `context.Push(target string, opts *http.PushOptions) error` is supported - Router (two lines to add, new features) - Template engines (two lines to add, same features as before, except their easier configuration) - Basic middleware, that have been written by me, are transfared to the main repository[/middleware](https://github.com/kataras/iris/tree/master/middleware) with a lot of improvements to the `recover middleware` (see the next) - `func(http.ResponseWriter, r *http.Request, next http.HandlerFunc)` signature is fully compatible using `iris.ToHandler` helper wrapper func, without any need of custom boilerplate code. So all net/http middleware out there are supported, no need to re-invert the world here, search to the internet and you'll find a suitable to your case. - Developers can use a `yaml` files for the configuration using the `iris.YAML` function: `app := iris.New(iris.YAML("myconfiguration.yaml"))` -- Shutdown with `app.Shutdown(context.Context) error`, no need for any third-parties, with `EventPolicy.Interrupted` and Go's 1.8 Gracefully Shutdown feature you're ready to go! + Fixes: diff --git a/configuration_test.go b/configuration_test.go index 64bc925d..1c37a5d9 100644 --- a/configuration_test.go +++ b/configuration_test.go @@ -113,7 +113,6 @@ func TestConfigurationYAML(t *testing.T) { EnablePathEscape: false FireMethodNotAllowed: true DisableBodyConsumptionOnUnmarshal: true - DisableBodyConsumptionOnUnmarshal: true TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT Charset: UTF-8 Gzip: true @@ -165,11 +164,6 @@ func TestConfigurationYAML(t *testing.T) { t.Fatalf("error on TestConfigurationYAML: Expected DisableBodyConsumptionOnUnmarshal %v but got %v", expected, c.DisableBodyConsumptionOnUnmarshal) } - if expected := true; c.DisableBodyConsumptionOnUnmarshal != expected { - t.Fatalf("error on TestConfigurationYAML: Expected DisableBodyConsumptionOnUnmarshal %v but got %v", - expected, c.DisableBodyConsumptionOnUnmarshal) - } - if expected := "Mon, 01 Jan 2006 15:04:05 GMT"; c.TimeFormat != expected { t.Fatalf("error on TestConfigurationYAML: Expected TimeFormat %s but got %s", expected, c.TimeFormat) } diff --git a/iris.go b/iris.go index 660cbe66..7f35e202 100644 --- a/iris.go +++ b/iris.go @@ -443,8 +443,9 @@ func (s *Framework) Serve(ln net.Listener) error { if s.ln != nil { return errors.New("server is already started and listening") } - // maybe a 'race' here but user should not call .Serve more than one time especially in more than one go routines... + s.ln = ln + s.closedManually = false s.Boot() // post any panics to the user defined logger. diff --git a/response_recorder.go b/response_recorder.go index c8b3fb2e..956ba066 100644 --- a/response_recorder.go +++ b/response_recorder.go @@ -159,44 +159,35 @@ func (w *ResponseRecorder) Flush() { w.ResetBody() } -// NOTE: Users: Uncomment the below code if you are already using Go from master branch. -// HTTP/2 Go 1.8 Push feature, -// as described in the source code(master): -// https://github.com/golang/go/blob/master/src/net/http/http.go#L119 . -// I have already tested the feature on my machine, but -// in order to avoid breaking the users' workspace: -// Uncomment these when 1.8 released (I guess in the middle of February) -// TODO: +// 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. // -// // 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 ErrNotSupported 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 { -// w.flushResponse() -// err := w.responseWriter.Push(target, opts) -// // NOTE: we have to reset them even if the push failed. -// w.ResetBody() -// w.ResetHeaders() +// 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. // -// return err -// } +// 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 *ResponseRecorder) Push(target string, opts *http.PushOptions) error { + w.flushResponse() + err := w.responseWriter.Push(target, opts) + // NOTE: we have to reset them even if the push failed. + w.ResetBody() + w.ResetHeaders() + + return err +} // clone returns a clone of this response writer // it copies the header, status code, headers and the beforeFlush finally returns a new ResponseRecorder diff --git a/response_writer.go b/response_writer.go index f19fade8..23db9e79 100644 --- a/response_writer.go +++ b/response_writer.go @@ -61,16 +61,7 @@ type ResponseWriter interface { http.Flusher http.Hijacker http.CloseNotifier - // NOTE: Users: Uncomment the below code if you are already using Go from master branch. - // HTTP/2 Go 1.8 Push feature, - // as described in the source code(master): - // https://github.com/golang/go/blob/master/src/net/http/http.go#L119 . - // I have already tested the feature on my machine, but - // in order to avoid breaking the users' workspace: - // Uncomment these when 1.8 released (I guess in the middle of February) - // TODO: - // - // http.Pusher + http.Pusher Writef(format string, a ...interface{}) (n int, err error) WriteString(s string) (n int, err error) @@ -223,44 +214,39 @@ func (w *responseWriter) Flush() { } } -// NOTE: Users: Uncomment the below code if you are already using Go from master branch. -// HTTP/2 Go 1.8 Push feature, -// as described in the source code(master): -// https://github.com/golang/go/blob/master/src/net/http/http.go#L119 . -// I have already tested the feature on my machine, but -// in order to avoid breaking the users' workspace: -// Uncomment these when 1.8 released (I guess in the middle of February) -// TODO: +// 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. // -// // 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 ErrNotSupported 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 { +// 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. // -// if pusher, isPusher := w.ResponseWriter.(http.Pusher); isPusher { -// return pusher.Push(target, opts) -// } +// 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. // -// return errors.New("HTTP/2 Push feature is not supported, yet.") -// } +// 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 ErrPushNotSupported +} // CloseNotify returns a channel that receives at most a // single value (true) when the client connection has gone