| package router |
| |
| import ( |
| "net/http" |
| |
| "github.com/docker/docker/api/server/httputils" |
| "golang.org/x/net/context" |
| ) |
| |
| // RouteWrapper wraps a route with extra functionality. |
| // It is passed in when creating a new route. |
| type RouteWrapper func(r Route) Route |
| |
| // localRoute defines an individual API route to connect |
| // with the docker daemon. It implements Route. |
| type localRoute struct { |
| method string |
| path string |
| handler httputils.APIFunc |
| } |
| |
| // Handler returns the APIFunc to let the server wrap it in middlewares. |
| func (l localRoute) Handler() httputils.APIFunc { |
| return l.handler |
| } |
| |
| // Method returns the http method that the route responds to. |
| func (l localRoute) Method() string { |
| return l.method |
| } |
| |
| // Path returns the subpath where the route responds to. |
| func (l localRoute) Path() string { |
| return l.path |
| } |
| |
| // NewRoute initializes a new local route for the router. |
| func NewRoute(method, path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| var r Route = localRoute{method, path, handler} |
| for _, o := range opts { |
| r = o(r) |
| } |
| return r |
| } |
| |
| // NewGetRoute initializes a new route with the http method GET. |
| func NewGetRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| return NewRoute("GET", path, handler, opts...) |
| } |
| |
| // NewPostRoute initializes a new route with the http method POST. |
| func NewPostRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| return NewRoute("POST", path, handler, opts...) |
| } |
| |
| // NewPutRoute initializes a new route with the http method PUT. |
| func NewPutRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| return NewRoute("PUT", path, handler, opts...) |
| } |
| |
| // NewDeleteRoute initializes a new route with the http method DELETE. |
| func NewDeleteRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| return NewRoute("DELETE", path, handler, opts...) |
| } |
| |
| // NewOptionsRoute initializes a new route with the http method OPTIONS. |
| func NewOptionsRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| return NewRoute("OPTIONS", path, handler, opts...) |
| } |
| |
| // NewHeadRoute initializes a new route with the http method HEAD. |
| func NewHeadRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { |
| return NewRoute("HEAD", path, handler, opts...) |
| } |
| |
| func cancellableHandler(h httputils.APIFunc) httputils.APIFunc { |
| return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
| if notifier, ok := w.(http.CloseNotifier); ok { |
| notify := notifier.CloseNotify() |
| notifyCtx, cancel := context.WithCancel(ctx) |
| finished := make(chan struct{}) |
| defer close(finished) |
| ctx = notifyCtx |
| go func() { |
| select { |
| case <-notify: |
| cancel() |
| case <-finished: |
| } |
| }() |
| } |
| return h(ctx, w, r, vars) |
| } |
| } |
| |
| // WithCancel makes new route which embeds http.CloseNotifier feature to |
| // context.Context of handler. |
| func WithCancel(r Route) Route { |
| return localRoute{ |
| method: r.Method(), |
| path: r.Path(), |
| handler: cancellableHandler(r.Handler()), |
| } |
| } |