diff --git a/.travis.yml b/.travis.yml
index 35410e2..1db3926 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,33 +1,17 @@
 language: go
 
-matrix:
-  include:
-    - go: 1.2
-      install:
-      - go get golang.org/x/tools/cmd/cover
-      - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
-      - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-    - go: 1.3
-      install:
-      - go get golang.org/x/tools/cmd/cover
-      - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
-      - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-    - go: 1.4
-      install:
-      - go get golang.org/x/tools/cmd/cover
-      - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
-      - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-    - go: 1.5
-      install:
-      - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
-      - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-    - go: 1.6
-      install:
-      - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
-      - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-    - go: tip
-      install:
-      - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
-      - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
-script:
-  - go test -cover ./...
+go:
+    - "1.5.x"
+    - "1.6.x"
+    - "1.7.x"
+    - "1.8.x"
+    - "1.9.x"
+    - "1.10.x"
+    - "1.11.x"
+
+install:
+    - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
+    - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
+
+script: go test -cover ./...
+
diff --git a/debian/changelog b/debian/changelog
index d5ec320..97dc895 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-zenazn-goji (1.0+git20190601.a16712d-1) UNRELEASED; urgency=medium
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 23 Jun 2019 05:55:00 +0000
+
 golang-github-zenazn-goji (1.0+git20170812.c05078c-4) unstable; urgency=medium
 
   * Team upload.
diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000..33a9488
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1 @@
+example
diff --git a/graceful/graceful.go b/graceful/graceful.go
index ff9b186..19490e0 100644
--- a/graceful/graceful.go
+++ b/graceful/graceful.go
@@ -9,7 +9,6 @@ package graceful
 
 import (
 	"net"
-	"runtime"
 	"sync/atomic"
 
 	"github.com/zenazn/goji/graceful/listener"
@@ -50,12 +49,16 @@ func peacefulError(err error) error {
 	// Unfortunately Go doesn't really give us a better way to select errors
 	// than this, so *shrug*.
 	if oe, ok := err.(*net.OpError); ok {
-		errOp := "accept"
-		if runtime.GOOS == "windows" {
-			errOp = "AcceptEx"
-		}
-		if oe.Op == errOp && oe.Err.Error() == errClosing {
-			return nil
+		switch oe.Op {
+		// backward compatibility: older golang returns AcceptEx on Windows.
+		// Current golang returns "accept" consistently. It's platform independent.
+		// See https://github.com/golang/go/commit/b0f4ee533a875c258ac1030ee382f0ffe2de304b
+		case "AcceptEx":
+			fallthrough
+		case "accept":
+			if oe.Err.Error() == errClosing {
+				return nil
+			}
 		}
 	}
 	return err
diff --git a/graceful/signal.go b/graceful/signal.go
index 60612b8..55061a5 100644
--- a/graceful/signal.go
+++ b/graceful/signal.go
@@ -12,8 +12,8 @@ import (
 
 var mu sync.Mutex // protects everything that follows
 var listeners = make([]*listener.T, 0)
-var prehooks = make([]func(), 0)
-var posthooks = make([]func(), 0)
+var prehooks = make([]func(os.Signal), 0)
+var posthooks = make([]func(os.Signal), 0)
 var closing int32
 var doubleKick, timeout time.Duration
 
@@ -40,14 +40,41 @@ func ResetSignals() {
 	signal.Stop(sigchan)
 }
 
+// PreHookWithSignal registers a function to be called before any of this
+// package's normal shutdown actions, which recieves the signal that caused the
+// shutdown (or nil for manual shutdowns). All listeners will be called in the
+// order they were added, from a single goroutine.
+func PreHookWithSignal(f func(os.Signal)) {
+	mu.Lock()
+	defer mu.Unlock()
+
+	prehooks = append(prehooks, f)
+}
+
 // PreHook registers a function to be called before any of this package's normal
 // shutdown actions. All listeners will be called in the order they were added,
 // from a single goroutine.
 func PreHook(f func()) {
+	PreHookWithSignal(func(_ os.Signal) {
+		f()
+	})
+}
+
+// PostHookWithSignal registers a function to be called after all of this
+// package's normal shutdown actions, which receives the signal that caused the
+// shutdown (or nil for manual shutdowns). All listeners will be called in the
+// order they were added, from a single goroutine, and are guaranteed to be
+// called after all listening connections have been closed, but before Wait()
+// returns.
+//
+// If you've Hijacked any connections that must be gracefully shut down in some
+// other way (since this library disowns all hijacked connections), it's
+// reasonable to use a PostHook to signal and wait for them.
+func PostHookWithSignal(f func(os.Signal)) {
 	mu.Lock()
 	defer mu.Unlock()
 
-	prehooks = append(prehooks, f)
+	posthooks = append(posthooks, f)
 }
 
 // PostHook registers a function to be called after all of this package's normal
@@ -59,23 +86,22 @@ func PreHook(f func()) {
 // other way (since this library disowns all hijacked connections), it's
 // reasonable to use a PostHook to signal and wait for them.
 func PostHook(f func()) {
-	mu.Lock()
-	defer mu.Unlock()
-
-	posthooks = append(posthooks, f)
+	PostHookWithSignal(func(_ os.Signal) {
+		f()
+	})
 }
 
 // Shutdown manually triggers a shutdown from your application. Like Wait,
 // blocks until all connections have gracefully shut down.
 func Shutdown() {
-	shutdown(false)
+	shutdown(false, nil)
 }
 
 // ShutdownNow triggers an immediate shutdown from your application. All
 // connections (not just those that are idle) are immediately closed, even if
 // they are in the middle of serving a request.
 func ShutdownNow() {
-	shutdown(true)
+	shutdown(true, nil)
 }
 
 // DoubleKickWindow sets the length of the window during which two back-to-back
@@ -123,30 +149,30 @@ func init() {
 func sigLoop() {
 	var last time.Time
 	for {
-		<-sigchan
+		sig := <-sigchan
 		now := time.Now()
 		mu.Lock()
 		force := doubleKick != 0 && now.Sub(last) < doubleKick
 		if t := timeout; t != 0 && !force {
 			go func() {
 				time.Sleep(t)
-				shutdown(true)
+				shutdown(true, sig)
 			}()
 		}
 		mu.Unlock()
-		go shutdown(force)
+		go shutdown(force, sig)
 		last = now
 	}
 }
 
 var preOnce, closeOnce, forceOnce, postOnce, notifyOnce sync.Once
 
-func shutdown(force bool) {
+func shutdown(force bool, sig os.Signal) {
 	preOnce.Do(func() {
 		mu.Lock()
 		defer mu.Unlock()
 		for _, f := range prehooks {
-			f()
+			f(sig)
 		}
 	})
 
@@ -164,7 +190,7 @@ func shutdown(force bool) {
 		mu.Lock()
 		defer mu.Unlock()
 		for _, f := range posthooks {
-			f()
+			f(sig)
 		}
 	})
 
diff --git a/web/func_equal_test.go b/web/func_equal_test.go
index 9080a5a..690d506 100644
--- a/web/func_equal_test.go
+++ b/web/func_equal_test.go
@@ -64,7 +64,7 @@ func TestFuncEqual(t *testing.T) {
 	for _, test := range funcEqualTests {
 		r := funcEqual(test.a, test.b)
 		if r != test.result {
-			t.Errorf("funcEqual(%v, %v) should have been %v",
+			t.Errorf("funcEqual(%p, %p) should have been %v",
 				test.a, test.b, test.result)
 		}
 	}
diff --git a/web/middleware.go b/web/middleware.go
index 7a3476e..985a6c9 100644
--- a/web/middleware.go
+++ b/web/middleware.go
@@ -148,7 +148,7 @@ func (m *mStack) Abandon(middleware interface{}) error {
 	}
 
 	copy(m.stack[i:], m.stack[i+1:])
-	m.stack = m.stack[:len(m.stack)-1 : len(m.stack)]
+	m.stack = m.stack[: len(m.stack)-1 : len(m.stack)]
 
 	m.invalidate()
 	return nil
diff --git a/web/mutil/writer_proxy.go b/web/mutil/writer_proxy.go
index 9f6d776..fb4a08b 100644
--- a/web/mutil/writer_proxy.go
+++ b/web/mutil/writer_proxy.go
@@ -1,3 +1,5 @@
+// +build !go1.8
+
 package mutil
 
 import (
@@ -62,6 +64,7 @@ func (b *basicWriter) WriteHeader(code int) {
 		b.ResponseWriter.WriteHeader(code)
 	}
 }
+
 func (b *basicWriter) Write(buf []byte) (int, error) {
 	b.WriteHeader(http.StatusOK)
 	n, err := b.ResponseWriter.Write(buf)
@@ -75,20 +78,25 @@ func (b *basicWriter) Write(buf []byte) (int, error) {
 	b.bytes += n
 	return n, err
 }
+
 func (b *basicWriter) maybeWriteHeader() {
 	if !b.wroteHeader {
 		b.WriteHeader(http.StatusOK)
 	}
 }
+
 func (b *basicWriter) Status() int {
 	return b.code
 }
+
 func (b *basicWriter) BytesWritten() int {
 	return b.bytes
 }
+
 func (b *basicWriter) Tee(w io.Writer) {
 	b.tee = w
 }
+
 func (b *basicWriter) Unwrap() http.ResponseWriter {
 	return b.ResponseWriter
 }
@@ -105,14 +113,17 @@ func (f *fancyWriter) CloseNotify() <-chan bool {
 	cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
 	return cn.CloseNotify()
 }
+
 func (f *fancyWriter) Flush() {
 	fl := f.basicWriter.ResponseWriter.(http.Flusher)
 	fl.Flush()
 }
+
 func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
 	hj := f.basicWriter.ResponseWriter.(http.Hijacker)
 	return hj.Hijack()
 }
+
 func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
 	if f.basicWriter.tee != nil {
 		return io.Copy(&f.basicWriter, r)
@@ -122,11 +133,6 @@ func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
 	return rf.ReadFrom(r)
 }
 
-var _ http.CloseNotifier = &fancyWriter{}
-var _ http.Flusher = &fancyWriter{}
-var _ http.Hijacker = &fancyWriter{}
-var _ io.ReaderFrom = &fancyWriter{}
-
 type flushWriter struct {
 	basicWriter
 }
@@ -136,4 +142,10 @@ func (f *flushWriter) Flush() {
 	fl.Flush()
 }
 
-var _ http.Flusher = &flushWriter{}
+var (
+	_ http.CloseNotifier = &fancyWriter{}
+	_ http.Flusher       = &fancyWriter{}
+	_ http.Hijacker      = &fancyWriter{}
+	_ io.ReaderFrom      = &fancyWriter{}
+	_ http.Flusher       = &flushWriter{}
+)
diff --git a/web/mutil/writer_proxy_go1_8.go b/web/mutil/writer_proxy_go1_8.go
new file mode 100644
index 0000000..2019c98
--- /dev/null
+++ b/web/mutil/writer_proxy_go1_8.go
@@ -0,0 +1,136 @@
+// +build go1.8
+
+package mutil
+
+import (
+	"bufio"
+	"io"
+	"net"
+	"net/http"
+)
+
+// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook
+// into various parts of the response process.
+type WriterProxy interface {
+	http.ResponseWriter
+	// Status returns the HTTP status of the request, or 0 if one has not
+	// yet been sent.
+	Status() int
+	// BytesWritten returns the total number of bytes sent to the client.
+	BytesWritten() int
+	// Tee causes the response body to be written to the given io.Writer in
+	// addition to proxying the writes through. Only one io.Writer can be
+	// tee'd to at once: setting a second one will overwrite the first.
+	// Writes will be sent to the proxy before being written to this
+	// io.Writer. It is illegal for the tee'd writer to be modified
+	// concurrently with writes.
+	Tee(io.Writer)
+	// Unwrap returns the original proxied target.
+	Unwrap() http.ResponseWriter
+}
+
+// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to
+// hook into various parts of the response process.
+func WrapWriter(w http.ResponseWriter) WriterProxy {
+	_, fl := w.(http.Flusher)
+	_, hj := w.(http.Hijacker)
+	_, rf := w.(io.ReaderFrom)
+
+	bw := basicWriter{ResponseWriter: w}
+	if fl && hj && rf {
+		return &fancyWriter{bw}
+	}
+	return &bw
+}
+
+// basicWriter wraps a http.ResponseWriter that implements the minimal
+// http.ResponseWriter interface.
+type basicWriter struct {
+	http.ResponseWriter
+	wroteHeader bool
+	code        int
+	bytes       int
+	tee         io.Writer
+}
+
+func (b *basicWriter) WriteHeader(code int) {
+	if !b.wroteHeader {
+		b.code = code
+		b.wroteHeader = true
+		b.ResponseWriter.WriteHeader(code)
+	}
+}
+
+func (b *basicWriter) Write(buf []byte) (int, error) {
+	b.WriteHeader(http.StatusOK)
+	n, err := b.ResponseWriter.Write(buf)
+	if b.tee != nil {
+		_, err2 := b.tee.Write(buf[:n])
+		// Prefer errors generated by the proxied writer.
+		if err == nil {
+			err = err2
+		}
+	}
+	b.bytes += n
+	return n, err
+}
+
+func (b *basicWriter) maybeWriteHeader() {
+	if !b.wroteHeader {
+		b.WriteHeader(http.StatusOK)
+	}
+}
+
+func (b *basicWriter) Status() int {
+	return b.code
+}
+
+func (b *basicWriter) BytesWritten() int {
+	return b.bytes
+}
+
+func (b *basicWriter) Tee(w io.Writer) {
+	b.tee = w
+}
+
+func (b *basicWriter) Unwrap() http.ResponseWriter {
+	return b.ResponseWriter
+}
+
+// fancyWriter is a writer that additionally satisfies http.Pusher,
+// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
+// of wrapping the http.ResponseWriter that package http gives you, in order to
+// make the proxied object support the full method set of the proxied object.
+type fancyWriter struct {
+	basicWriter
+}
+
+func (f *fancyWriter) Push(target string, opts *http.PushOptions) error {
+	return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts)
+}
+
+func (f *fancyWriter) Flush() {
+	fl := f.basicWriter.ResponseWriter.(http.Flusher)
+	fl.Flush()
+}
+
+func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+	hj := f.basicWriter.ResponseWriter.(http.Hijacker)
+	return hj.Hijack()
+}
+
+func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
+	if f.basicWriter.tee != nil {
+		return io.Copy(&f.basicWriter, r)
+	}
+	rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
+	f.basicWriter.maybeWriteHeader()
+	return rf.ReadFrom(r)
+}
+
+var (
+	_ http.Pusher   = &fancyWriter{}
+	_ http.Flusher  = &fancyWriter{}
+	_ http.Hijacker = &fancyWriter{}
+	_ io.ReaderFrom = &fancyWriter{}
+)