| Index: server/router/router.go
|
| diff --git a/server/router/router.go b/server/router/router.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..554b94d6da08dbbb2a445abdb1a20760c371b6f9
|
| --- /dev/null
|
| +++ b/server/router/router.go
|
| @@ -0,0 +1,174 @@
|
| +// Copyright 2016 The LUCI Authors. All rights reserved.
|
| +// Use of this source code is governed under the Apache License, Version 2.0
|
| +// that can be found in the LICENSE file.
|
| +
|
| +// Package router provides an HTTP router with support for middleware and
|
| +// subrouters. It wraps around julienschmidt/httprouter.
|
| +package router
|
| +
|
| +import (
|
| + "net/http"
|
| + "strings"
|
| +
|
| + "github.com/julienschmidt/httprouter"
|
| + "golang.org/x/net/context"
|
| +)
|
| +
|
| +type (
|
| + // Router is the main type for the package. To create a Router, use New.
|
| + Router struct {
|
| + hrouter *httprouter.Router
|
| + middleware MiddlewareChain
|
| + BasePath string
|
| + }
|
| +
|
| + // Context contains the context, response writer, request, and params shared
|
| + // across Middleware and Handler functions.
|
| + Context struct {
|
| + context.Context
|
| + writer http.ResponseWriter
|
| + Request *http.Request
|
| + Params httprouter.Params
|
| + status int
|
| + }
|
| +)
|
| +
|
| +var (
|
| + _ http.Handler = (*Router)(nil)
|
| + _ http.ResponseWriter = (*Context)(nil)
|
| + _ context.Context = (*Context)(nil)
|
| +)
|
| +
|
| +// New creates a Router.
|
| +func New() *Router {
|
| + return &Router{
|
| + hrouter: httprouter.New(),
|
| + BasePath: "/",
|
| + }
|
| +}
|
| +
|
| +// Use adds middleware chains to the group. The added middleware applies to
|
| +// all handlers registered on the router and to all handlers registered on
|
| +// routers that may be derived from the router (using Subrouter).
|
| +func (r *Router) Use(mc MiddlewareChain) {
|
| + r.middleware = append(r.middleware, mc...)
|
| +}
|
| +
|
| +// Subrouter creates a new router with an updated base path.
|
| +// The new router copies middleware and configuration from the
|
| +// router it derives from.
|
| +func (r *Router) Subrouter(relativePath string) *Router {
|
| + newRouter := &Router{
|
| + hrouter: r.hrouter,
|
| + BasePath: makeBasePath(r.BasePath, relativePath),
|
| + }
|
| + if len(r.middleware) > 0 {
|
| + newRouter.middleware = make(MiddlewareChain, len(r.middleware))
|
| + copy(newRouter.middleware, r.middleware)
|
| + }
|
| + return newRouter
|
| +}
|
| +
|
| +// GET is a shortcut for router.Handle("GET", path, mc, h)
|
| +func (r *Router) GET(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("GET", path, mc, h)
|
| +}
|
| +
|
| +// HEAD is a shortcut for router.Handle("HEAD", path, mc, h)
|
| +func (r *Router) HEAD(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("HEAD", path, mc, h)
|
| +}
|
| +
|
| +// OPTIONS is a shortcut for router.Handle("OPTIONS", path, mc, h)
|
| +func (r *Router) OPTIONS(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("OPTIONS", path, mc, h)
|
| +}
|
| +
|
| +// POST is a shortcut for router.Handle("POST", path, mc, h)
|
| +func (r *Router) POST(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("POST", path, mc, h)
|
| +}
|
| +
|
| +// PUT is a shortcut for router.Handle("PUT", path, mc, h)
|
| +func (r *Router) PUT(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("PUT", path, mc, h)
|
| +}
|
| +
|
| +// PATCH is a shortcut for router.Handle("PATCH", path, mc, h)
|
| +func (r *Router) PATCH(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("PATCH", path, mc, h)
|
| +}
|
| +
|
| +// DELETE is a shortcut for router.Handle("DELETE", path, mc, h)
|
| +func (r *Router) DELETE(path string, mc MiddlewareChain, h Handler) {
|
| + r.Handle("DELETE", path, mc, h)
|
| +}
|
| +
|
| +// Handle registers a middleware chain and a handler for the given method and
|
| +// path. len(mc)==0 is allowed. See https://godoc.org/github.com/julienschmidt/httprouter
|
| +// for documentation on how the path may be formatted.
|
| +func (r *Router) Handle(method, path string, mc MiddlewareChain, h Handler) {
|
| + handle := r.adapt(mc, h)
|
| + r.hrouter.Handle(method, httprouter.CleanPath(r.BasePath+path), handle)
|
| +}
|
| +
|
| +// ServeHTTP makes Router implement the http.Handler interface.
|
| +func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
| + r.hrouter.ServeHTTP(rw, req)
|
| +}
|
| +
|
| +// adapt adapts given middleware chain and handler into a httprouter-style handle.
|
| +func (r *Router) adapt(mc MiddlewareChain, h Handler) httprouter.Handle {
|
| + return httprouter.Handle(func(rw http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
| + // TODO(maybe): Use a free list for making Contexts.
|
| + run(&Context{
|
| + Context: context.Background(),
|
| + writer: rw,
|
| + Request: req,
|
| + Params: p,
|
| + }, r.middleware, mc, h)
|
| + })
|
| +}
|
| +
|
| +func makeBasePath(base, relative string) string {
|
| + path := httprouter.CleanPath(base + relative)
|
| + if !strings.HasSuffix(path, "/") {
|
| + path += "/"
|
| + }
|
| + return path
|
| +}
|
| +
|
| +// Header is used to implement http.ResponseWriter interface.
|
| +func (c *Context) Header() http.Header {
|
| + return c.writer.Header()
|
| +}
|
| +
|
| +// Write is used to implement http.ResponseWriter interface.
|
| +// If called from a Middleware function, the rules mentioned in the
|
| +// documentation for Middleware must be followed.
|
| +func (c *Context) Write(p []byte) (int, error) {
|
| + if !c.Written() {
|
| + c.status = http.StatusOK
|
| + }
|
| + return c.writer.Write(p)
|
| +}
|
| +
|
| +// WriteHeader is used to implement http.ResponseWriter interface.
|
| +// If called from a Middleware function, the rules mentioned in the
|
| +// documentation for Middleware must be followed.
|
| +func (c *Context) WriteHeader(code int) {
|
| + if !c.Written() {
|
| + c.status = code
|
| + }
|
| + c.writer.WriteHeader(code)
|
| +}
|
| +
|
| +// StatusCode returns the written HTTP status code or 0 if it was not written.
|
| +func (c *Context) StatusCode() int {
|
| + return c.status
|
| +}
|
| +
|
| +// Written indicates whether a HTTP response has been written.
|
| +func (c *Context) Written() bool {
|
| + return c.status != 0
|
| +}
|
|
|