Example #1
0
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
func New(log *log.Logger, h http.Handler) http.Handler {
	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		c := karambie.Context(res)

		defer func() {
			if err := recover(); err != nil {

				if c.Status() != 0 {
					// something has been written as response
					// unrecoverable, re-panic
					panic(err)
				} else {
					stack := stack(3)
					c.Set(errInstance, err)
					c.Set(stackInstance, stack)

					logPrintf(log, "PANIC: %s\n%s", err, stack)

					if h == nil {
						res.Header().Set("Content-Type", "text/html")
						res.WriteHeader(http.StatusInternalServerError)
						res.Write([]byte(fmt.Sprintf(
							panicHtml, err,
							template.HTMLEscapeString(string(stack)))))
					} else {
						h.ServeHTTP(res, req)
					}
				}
			}
		}()

		c.Next()
	})
}
Example #2
0
func main() {
	list := karambie.List()
	route := mux.NewRouter()       // use gorilla as router
	martini := martinihelper.New() // and also martini as middleware, see below

	currentDir, _ := os.Getwd()

	log := log.New(os.Stdout, "[Karambie] ", 0)

	logger := logger.New(log, false)                               // log every request
	recovery := recovery.New(log, nil)                             // recover if panic
	notfoundhandler := notfoundhandler.New(true, nil)              // show 404, add trailing slash to url if necessary
	static := static.New(filepath.Join(currentDir, "public"), log) // serve static file in folder "public"

	// register logger service for martini
	martini.Map(log)

	// the list is immutable, every calling to Add or AddFunc will create new list
	list = list.Add(logger, recovery)
	list = list.Add(karambie.Later(notfoundhandler))
	list = list.Add(karambie.Later(static))
	// or you can use karambie/middleware.Common() to build those list

	// list is [logger, recovery, notfoundhandler, static]
	// but the order of execution is [logger, recovery, static, notfoundhandler]
	// karambie.Later will create new handler that will be executed after succeeding handler
	// list processing will stop if one of them respond the request (http response status != 0)

	secureList := list.Add(martini.Conv(auth.Basic("user", "pass"))) // execution of secureList is [logger, recovery, auth, static, notfoundhandler]
	list = list.Add(martini.Conv(render.Renderer()))                 // execution of list is       [logger, recovery, render, static, notfoundhandler]
	// list != secureList, because it is immutable, every calling to Add or AddFunc will create new list

	// using http.HandlerFunc style
	route.Handle("/helloworld", list.AddFunc(hello)) // [logger, recovery, render, hello]
	// 'static' and 'notfoundhandler' will be ignored because 'hello' response the request

	// we can user list as NotFoundHandler (handle static file, and show error 404 if necessary)
	route.NotFoundHandler = list // [logger, recovery, static, notfoundhandler]
	// 'notfoundhandler' will be ignored if 'static' response the request

	// using martini.Handler style and gorilla routing
	route.Handle("/sayhi/{name}", list.Add(martini.Conv(sayhi))) // [logger, recovery, render, sayhi]

	// use secureList for sensitive resource
	route.Handle("/secret", secureList.AddFunc(secret)) // [logger, recovery, auth, secret]

	// add filterheader to list
	apiList := list.AddFunc(filterheader) // execution of apiList is [logger, recovery, filterheader, static, notfoundhandler]
	route.Handle("/api", apiList.AddFunc(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		// this handler will not be called if 'filterheader' is not passed

		// get apikey
		ctx := karambie.Context(rw)
		key := ctx.Get("apikey").(string)

		fmt.Fprintln(rw, "Your api key : "+key)
	})))

	http.ListenAndServe(":3000", route)
}
Example #3
0
// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out.
// return http.Handler
func New(log *log.Logger, excludeHttpOk bool) http.Handler {
	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		c := karambie.Context(res)

		start := time.Now()

		addr := req.Header.Get("X-Real-IP")
		if addr == "" {
			addr = req.Header.Get("X-Forwarded-For")
			if addr == "" {
				addr = req.RemoteAddr
			}
		}

		defer func() {
			if err := recover(); err != nil {
				logPrintf(log, "UNHANDLED PANIC")
				panic(err)
			}
		}()

		c.Next()

		if excludeHttpOk && c.Status() == http.StatusOK {
			return
		}

		logPrintf(log,
			"%s %s for %s -> %v %s (written %d bytes) in %v\n",
			req.Method, req.URL.Path, addr,
			c.Status(), http.StatusText(c.Status()), c.Written(), time.Since(start),
		)
	})
}
Example #4
0
func New() (http.Handler, *sync.WaitGroup) {
	var waiter sync.WaitGroup

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		waiter.Add(1)
		defer waiter.Done()
		karambie.Context(rw).Next()
	}), &waiter
}
Example #5
0
func filterheader(rw http.ResponseWriter, r *http.Request) {
	if key := r.Header.Get("X-Api-Key"); len(key) == 0 {
		rw.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(rw, "X-Api-Key header not defined")
	} else {
		ctx := karambie.Context(rw)
		ctx.Set("apikey", key)
		// dont write anything, if you do that, the chain list will be stop
	}
}
Example #6
0
// convert martini middleware into http.Handler
func (this *MartiniHelper) Conv(h martini.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rwc := karambie.Context(rw)
		c := this.context(rwc, r)

		vals, err := c.Invoke(h)
		if err != nil {
			panic(err)
		}

		if rwc.Status() == 0 {
			// if the handler returned something, write it to the http response
			if len(vals) > 0 {
				ev := c.Get(reflect.TypeOf(martini.ReturnHandler(nil)))
				handleReturn := ev.Interface().(martini.ReturnHandler)
				handleReturn(c, vals)
			}
		}
	})
}
Example #7
0
// if redirect is true, it will try to add trailing slash to the url.
// if h is nil, default handler will be use
func New(redirect bool, h http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		c := karambie.Context(rw)
		if c.Status() == 0 {
			if redirect && !strings.HasSuffix(r.URL.Path, "/") {
				u, _ := url.Parse(r.URL.String())
				u.Path += "/"
				http.Redirect(rw, r, u.String(), http.StatusTemporaryRedirect)
			} else {
				if h == nil {
					c.Header().Set("Content-Type", "text/html")
					c.WriteHeader(http.StatusNotFound)
					c.Write([]byte(fmt.Sprintf(template, r.URL)))
				} else {
					h.ServeHTTP(c, r)
				}
			}
		}
	})
}