Skip to content

Commit

Permalink
Go 1.9 support
Browse files Browse the repository at this point in the history
  • Loading branch information
guregu committed Aug 25, 2017
1 parent 30dd520 commit fa67a6f
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 165 deletions.
24 changes: 1 addition & 23 deletions handler_17.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build go1.7
// +build go1.7,!go1.9

package kami

Expand All @@ -10,33 +10,11 @@ import (
netcontext "golang.org/x/net/context"
)

// HandlerType is the type of Handlers and types that kami internally converts to
// ContextHandler. In order to provide an expressive API, this type is an alias for
// interface{} that is named for the purposes of documentation, however only the
// following concrete types are accepted:
// - types that implement http.Handler
// - types that implement ContextHandler
// - func(http.ResponseWriter, *http.Request)
// - func(context.Context, http.ResponseWriter, *http.Request)
type HandlerType interface{}

// ContextHandler is like http.Handler but supports context.
type ContextHandler interface {
ServeHTTPContext(context.Context, http.ResponseWriter, *http.Request)
}

// OldContextHandler is like ContextHandler but uses the old x/net/context.
type OldContextHandler interface {
ServeHTTPContext(netcontext.Context, http.ResponseWriter, *http.Request)
}

// HandlerFunc is like http.HandlerFunc with context.
type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request)

func (h HandlerFunc) ServeHTTPContext(ctx context.Context, w http.ResponseWriter, r *http.Request) {
h(ctx, w, r)
}

// wrap tries to turn a HandlerType into a ContextHandler
func wrap(h HandlerType) ContextHandler {
switch x := h.(type) {
Expand Down
28 changes: 28 additions & 0 deletions handler_19.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// +build go1.9

package kami

import (
"context"
"fmt"
"net/http"
)

// wrap tries to turn a HandlerType into a ContextHandler
func wrap(h HandlerType) ContextHandler {
switch x := h.(type) {
case ContextHandler:
return x
case func(context.Context, http.ResponseWriter, *http.Request):
return HandlerFunc(x)
case http.Handler:
return HandlerFunc(func(_ context.Context, w http.ResponseWriter, r *http.Request) {
x.ServeHTTP(w, r)
})
case func(http.ResponseWriter, *http.Request):
return HandlerFunc(func(_ context.Context, w http.ResponseWriter, r *http.Request) {
x(w, r)
})
}
panic(fmt.Errorf("unsupported HandlerType: %T", h))
}
30 changes: 30 additions & 0 deletions handler_new.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// +build go1.7

package kami

import (
"context"
"net/http"
)

// HandlerType is the type of Handlers and types that kami internally converts to
// ContextHandler. In order to provide an expressive API, this type is an alias for
// interface{} that is named for the purposes of documentation, however only the
// following concrete types are accepted:
// - types that implement http.Handler
// - types that implement ContextHandler
// - func(http.ResponseWriter, *http.Request)
// - func(context.Context, http.ResponseWriter, *http.Request)
type HandlerType interface{}

// ContextHandler is like http.Handler but supports context.
type ContextHandler interface {
ServeHTTPContext(context.Context, http.ResponseWriter, *http.Request)
}

// HandlerFunc is like http.HandlerFunc with context.
type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request)

func (h HandlerFunc) ServeHTTPContext(ctx context.Context, w http.ResponseWriter, r *http.Request) {
h(ctx, w, r)
}
143 changes: 1 addition & 142 deletions middleware_17.go
Original file line number Diff line number Diff line change
@@ -1,146 +1,16 @@
// +build go1.7
// +build go1.7,!go1.9

package kami

import (
"context"
"fmt"
"net/http"
"unicode/utf8"

"github.com/zenazn/goji/web/mutil"
netcontext "golang.org/x/net/context"
)

// Middleware is a function that takes the current request context and returns a new request context.
// You can use middleware to build your context before your handler handles a request.
// As a special case, middleware that returns nil will halt middleware and handler execution (LogHandler will still run).
type Middleware func(context.Context, http.ResponseWriter, *http.Request) context.Context

// MiddlewareType represents types that kami can convert to Middleware.
// kami will try its best to convert standard, non-context middleware.
// See the Use function for important information about how kami middleware is run.
// The following concrete types are accepted:
// - Middleware
// - func(context.Context, http.ResponseWriter, *http.Request) context.Context
// - func(http.ResponseWriter, *http.Request) context.Context
// - func(http.Handler) http.Handler [* see Use docs]
// - func(http.ContextHandler) http.ContextHandler [* see Use docs]
// - http.Handler [read only]
// - func(http.ResponseWriter, *http.Request) [read only]
// The old x/net/context is also supported.
type MiddlewareType interface{}

// Afterware is a function that will run after middleware and the request.
// Afterware takes the request context and returns a new context, but unlike middleware,
// returning nil won't halt execution of other afterware.
type Afterware func(context.Context, mutil.WriterProxy, *http.Request) context.Context

// Afterware represents types that kami can convert to Afterware.
// The following concrete types are accepted:
// - Afterware
// - func(context.Context, mutil.WriterProxy, *http.Request) context.Context
// - func(context.Context, http.ResponseWriter, *http.Request) context.Context
// - func(context.Context, *http.Request) context.Context
// - func(context.Context) context.Context
// - Middleware types
// The old x/net/context is also supported.
type AfterwareType interface{}

// run runs the middleware chain for a particular request.
// run returns false if it should stop early.
func (m *wares) run(ctx context.Context, w http.ResponseWriter, r *http.Request) (*http.Request, context.Context, bool) {
if m.middleware != nil {
// hierarchical middleware
for i, c := range r.URL.Path {
if c == '/' || i == len(r.URL.Path)-1 {
mws, ok := m.middleware[r.URL.Path[:i+1]]
if !ok {
continue
}
for _, mw := range mws {
// return nil context to stop
result := mw(ctx, w, r)
if result == nil {
return r, ctx, false
}
if result != ctx {
r = r.WithContext(result)
}
ctx = result
}
}
}
}

if m.wildcards != nil {
// wildcard middleware
if wild, params := m.wildcards.Get(r.URL.Path); wild != nil {
if mws, ok := wild.(*[]Middleware); ok {
ctx = mergeParams(ctx, params)
r = r.WithContext(ctx)
for _, mw := range *mws {
result := mw(ctx, w, r)
if result == nil {
return r, ctx, false
}
if result != ctx {
r = r.WithContext(result)
}
ctx = result
}
}
}
}

return r, ctx, true
}

// after runs the afterware chain for a particular request.
// after can't stop early
func (m *wares) after(ctx context.Context, w mutil.WriterProxy, r *http.Request) (*http.Request, context.Context) {
if m.afterWildcards != nil {
// wildcard afterware
if wild, params := m.afterWildcards.Get(r.URL.Path); wild != nil {
if aws, ok := wild.(*[]Afterware); ok {
ctx = mergeParams(ctx, params)
r = r.WithContext(ctx)
for _, aw := range *aws {
result := aw(ctx, w, r)
if result != nil {
if result != ctx {
r = r.WithContext(result)
}
ctx = result
}
}
}
}
}

if m.afterware != nil {
// hierarchical afterware, like middleware in reverse
path := r.URL.Path
for len(path) > 0 {
chr, size := utf8.DecodeLastRuneInString(path)
if chr == '/' || len(path) == len(r.URL.Path) {
for _, aw := range m.afterware[path] {
result := aw(ctx, w, r)
if result != nil {
if result != ctx {
r = r.WithContext(result)
}
ctx = result
}
}
}
path = path[:len(path)-size]
}
}

return r, ctx
}

// convert turns standard http middleware into kami Middleware if needed.
func convert(mw MiddlewareType) Middleware {
switch x := mw.(type) {
Expand Down Expand Up @@ -263,17 +133,6 @@ func convertAW(aw AfterwareType) Afterware {
panic(fmt.Errorf("unsupported AfterwareType: %T", aw))
}

// dummyHandler is used to keep track of whether the next middleware was called or not.
type dummyHandler bool

func (dh *dummyHandler) ServeHTTP(http.ResponseWriter, *http.Request) {
*dh = true
}

func (dh *dummyHandler) ServeHTTPContext(_ context.Context, _ http.ResponseWriter, _ *http.Request) {
*dh = true
}

// oldDummyHandler is dummyHandler compatible with the old context type.
type oldDummyHandler bool

Expand Down
104 changes: 104 additions & 0 deletions middleware_19.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// +build go1.9

package kami

import (
"context"
"fmt"
"net/http"

"github.com/zenazn/goji/web/mutil"
)

// convert turns standard http middleware into kami Middleware if needed.
func convert(mw MiddlewareType) Middleware {
switch x := mw.(type) {
case Middleware:
return x
case func(context.Context, http.ResponseWriter, *http.Request) context.Context:
return Middleware(x)
case func(ContextHandler) ContextHandler:
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
var dh dummyHandler
x(&dh).ServeHTTPContext(ctx, w, r)
if !dh {
return nil
}
return ctx
}
case func(http.Handler) http.Handler:
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
var dh dummyHandler
x(&dh).ServeHTTP(w, r)
if !dh {
return nil
}
return ctx
}
case http.Handler:
return Middleware(func(_ context.Context, w http.ResponseWriter, r *http.Request) context.Context {
x.ServeHTTP(w, r)
return r.Context()
})
case func(w http.ResponseWriter, r *http.Request):
return Middleware(func(_ context.Context, w http.ResponseWriter, r *http.Request) context.Context {
x(w, r)
return r.Context()
})
case func(w http.ResponseWriter, r *http.Request) context.Context:
return Middleware(func(_ context.Context, w http.ResponseWriter, r *http.Request) context.Context {
return x(w, r)
})
}
panic(fmt.Errorf("unsupported MiddlewareType: %T", mw))
}

// convertAW
func convertAW(aw AfterwareType) Afterware {
switch x := aw.(type) {
case Afterware:
return x
case func(context.Context, mutil.WriterProxy, *http.Request) context.Context:
return Afterware(x)
case func(context.Context, *http.Request) context.Context:
return func(ctx context.Context, _ mutil.WriterProxy, r *http.Request) context.Context {
return x(ctx, r)
}
case func(context.Context) context.Context:
return func(ctx context.Context, _ mutil.WriterProxy, _ *http.Request) context.Context {
return x(ctx)
}
case Middleware:
return func(ctx context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
return x(ctx, w, r)
}
case func(context.Context, http.ResponseWriter, *http.Request) context.Context:
return func(ctx context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
return x(ctx, w, r)
}
case func(w http.ResponseWriter, r *http.Request) context.Context:
return Afterware(func(_ context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
return x(w, r)
})
case func(w mutil.WriterProxy, r *http.Request) context.Context:
return Afterware(func(_ context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
return x(w, r)
})
case http.Handler:
return Afterware(func(_ context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
x.ServeHTTP(w, r)
return r.Context()
})
case func(w http.ResponseWriter, r *http.Request):
return Afterware(func(_ context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
x(w, r)
return r.Context()
})
case func(w mutil.WriterProxy, r *http.Request):
return Afterware(func(_ context.Context, w mutil.WriterProxy, r *http.Request) context.Context {
x(w, r)
return r.Context()
})
}
panic(fmt.Errorf("unsupported AfterwareType: %T", aw))
}
Loading

0 comments on commit fa67a6f

Please sign in to comment.