forked from cadey/xesite
move vendor to repo root
This commit is contained in:
parent
ee89843f5b
commit
6e292d4d1d
|
@ -1,59 +0,0 @@
|
||||||
package asarfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mime"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"layeh.com/asar"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ASARfs struct {
|
|
||||||
fin *os.File
|
|
||||||
ar *asar.Entry
|
|
||||||
notFound http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ASARfs) Close() error {
|
|
||||||
return a.fin.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ASARfs) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.RequestURI == "/" {
|
|
||||||
r.RequestURI = "/index.html"
|
|
||||||
}
|
|
||||||
|
|
||||||
f := a.ar.Find(strings.Split(r.RequestURI, "/")[1:]...)
|
|
||||||
if f == nil {
|
|
||||||
a.notFound.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ext := filepath.Ext(f.Name)
|
|
||||||
mimeType := mime.TypeByExtension(ext)
|
|
||||||
|
|
||||||
w.Header().Add("Content-Type", mimeType)
|
|
||||||
f.WriteTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(archivePath string, notFound http.Handler) (*ASARfs, error) {
|
|
||||||
fin, err := os.Open(archivePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
root, err := asar.Decode(fin)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := &ASARfs{
|
|
||||||
fin: fin,
|
|
||||||
ar: root,
|
|
||||||
notFound: notFound,
|
|
||||||
}
|
|
||||||
|
|
||||||
return a, nil
|
|
||||||
}
|
|
|
@ -2,3 +2,6 @@
|
||||||
a5b47d31c556af34a302ce5d659e6fea44d90de0 gopkg.in/yaml.v2
|
a5b47d31c556af34a302ce5d659e6fea44d90de0 gopkg.in/yaml.v2
|
||||||
b68094ba95c055dfda888baa8947dfe44c20b1ac github.com/Xe/asarfs
|
b68094ba95c055dfda888baa8947dfe44c20b1ac github.com/Xe/asarfs
|
||||||
5e4d0891fe789f2da0c2d5afada3b6a1ede6d64c layeh.com/asar
|
5e4d0891fe789f2da0c2d5afada3b6a1ede6d64c layeh.com/asar
|
||||||
|
33a50704c528b4b00db129f75c693facf7f3838b (dirty) github.com/Xe/asarfs
|
||||||
|
5e4d0891fe789f2da0c2d5afada3b6a1ede6d64c layeh.com/asar
|
||||||
|
3f7ce7b928e14ff890b067e5bbbc80af73690a9c github.com/urfave/negroni
|
|
@ -0,0 +1,117 @@
|
||||||
|
package asarfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"layeh.com/asar"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ASARfs serves the contents of an asar archive as an HTTP handler.
|
||||||
|
type ASARfs struct {
|
||||||
|
fin *os.File
|
||||||
|
ar *asar.Entry
|
||||||
|
notFound http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the underlying file used for the asar archive.
|
||||||
|
func (a *ASARfs) Close() error {
|
||||||
|
return a.fin.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open satisfies the http.FileSystem interface for ASARfs.
|
||||||
|
func (a *ASARfs) Open(name string) (http.File, error) {
|
||||||
|
if name == "/" {
|
||||||
|
name = "/index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
e := a.ar.Find(strings.Split(name, "/")[1:]...)
|
||||||
|
if e == nil {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &file{
|
||||||
|
Entry: e,
|
||||||
|
r: e.Open(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP satisfies the http.Handler interface for ASARfs.
|
||||||
|
func (a *ASARfs) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.RequestURI == "/" {
|
||||||
|
r.RequestURI = "/index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
f := a.ar.Find(strings.Split(r.RequestURI, "/")[1:]...)
|
||||||
|
if f == nil {
|
||||||
|
a.notFound.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := filepath.Ext(f.Name)
|
||||||
|
mimeType := mime.TypeByExtension(ext)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", mimeType)
|
||||||
|
f.WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new ASARfs pointer based on the filepath to the archive and
|
||||||
|
// a HTTP handler to hit when a file is not found.
|
||||||
|
func New(archivePath string, notFound http.Handler) (*ASARfs, error) {
|
||||||
|
fin, err := os.Open(archivePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := asar.Decode(fin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &ASARfs{
|
||||||
|
fin: fin,
|
||||||
|
ar: root,
|
||||||
|
notFound: notFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// file is an internal shim that mimics http.File for an asar entry.
|
||||||
|
type file struct {
|
||||||
|
*asar.Entry
|
||||||
|
r io.ReadSeeker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) Close() error {
|
||||||
|
f.r = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) Read(buf []byte) (n int, err error) {
|
||||||
|
return f.r.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return f.r.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) Readdir(count int) ([]os.FileInfo, error) {
|
||||||
|
result := []os.FileInfo{}
|
||||||
|
|
||||||
|
for _, e := range f.Entry.Children {
|
||||||
|
result = append(result, e.FileInfo())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) Stat() (os.FileInfo, error) {
|
||||||
|
return f.Entry.FileInfo(), nil
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package asarfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkHTTPFileSystem(b *testing.B) {
|
||||||
|
fs := http.FileServer(http.Dir("."))
|
||||||
|
|
||||||
|
l, s, err := setupHandler(fs)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
url := fmt.Sprintf("http://%s", l.Addr())
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
testHandler(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkASARfs(b *testing.B) {
|
||||||
|
fs, err := New("./static.asar", http.HandlerFunc(do404))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, s, err := setupHandler(fs)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
url := fmt.Sprintf("http://%s", l.Addr())
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
testHandler(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPreloadedASARfs(b *testing.B) {
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
testHandler(asarfsurl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkASARfsHTTPFilesystem(b *testing.B) {
|
||||||
|
fs, err := New("./static.asar", http.HandlerFunc(do404))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, s, err := setupHandler(http.FileServer(fs))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
url := fmt.Sprintf("http://%s", l.Addr())
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
testHandler(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPreloadedASARfsHTTPFilesystem(b *testing.B) {
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
testHandler(asarfshttpfsurl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func do404(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Error(w, "Not found", http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupHandler(h http.Handler) (net.Listener, *http.Server, error) {
|
||||||
|
l, err := net.Listen("tcp", ":0")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
s := &http.Server{
|
||||||
|
Handler: h,
|
||||||
|
}
|
||||||
|
go s.ListenAndServe()
|
||||||
|
|
||||||
|
return l, s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandler(u string) error {
|
||||||
|
num := rand.Intn(9)
|
||||||
|
num++
|
||||||
|
sub := rand.Intn(99)
|
||||||
|
|
||||||
|
fname := fmt.Sprintf("/static/%d/%d%d.json", num, num, sub)
|
||||||
|
|
||||||
|
resp, err := http.Get(u + fname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(ioutil.Discard, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
asarfsurl string
|
||||||
|
asarfshttpfsurl string
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
go func() {
|
||||||
|
fs, err := New("./static.asar", http.HandlerFunc(do404))
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
l, _, err := setupHandler(fs)
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
asarfsurl = fmt.Sprintf("http://%s", l.Addr().String())
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
fs, err := New("./static.asar", http.HandlerFunc(do404))
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
l, _, err := setupHandler(http.FileServer(fs))
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
asarfshttpfsurl = fmt.Sprintf("http://%s", l.Addr().String())
|
||||||
|
}()
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers.
|
||||||
|
//
|
||||||
|
// If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit.
|
||||||
|
//
|
||||||
|
// For a full guide visit http://github.com/urfave/negroni
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/urfave/negroni"
|
||||||
|
// "net/http"
|
||||||
|
// "fmt"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// mux := http.NewServeMux()
|
||||||
|
// mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
// fmt.Fprintf(w, "Welcome to the home page!")
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// n := negroni.Classic()
|
||||||
|
// n.UseHandler(mux)
|
||||||
|
// n.Run(":3000")
|
||||||
|
// }
|
||||||
|
package negroni
|
|
@ -0,0 +1,35 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ALogger interface
|
||||||
|
type ALogger interface {
|
||||||
|
Println(v ...interface{})
|
||||||
|
Printf(format string, v ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger is a middleware handler that logs the request as it goes in and the response as it goes out.
|
||||||
|
type Logger struct {
|
||||||
|
// ALogger implements just enough log.Logger interface to be compatible with other implementations
|
||||||
|
ALogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogger returns a new Logger instance
|
||||||
|
func NewLogger() *Logger {
|
||||||
|
return &Logger{log.New(os.Stdout, "[negroni] ", 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
start := time.Now()
|
||||||
|
l.Printf("Started %s %s", r.Method, r.URL.Path)
|
||||||
|
|
||||||
|
next(rw, r)
|
||||||
|
|
||||||
|
res := rw.(ResponseWriter)
|
||||||
|
l.Printf("Completed %v %s in %v", res.Status(), http.StatusText(res.Status()), time.Since(start))
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler handler is an interface that objects can implement to be registered to serve as middleware
|
||||||
|
// in the Negroni middleware stack.
|
||||||
|
// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
|
||||||
|
// passed in.
|
||||||
|
//
|
||||||
|
// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
|
||||||
|
type Handler interface {
|
||||||
|
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
|
||||||
|
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
|
||||||
|
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||||
|
|
||||||
|
func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
h(rw, r, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
type middleware struct {
|
||||||
|
handler Handler
|
||||||
|
next *middleware
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
|
||||||
|
// middleware. The next http.HandlerFunc is automatically called after the Handler
|
||||||
|
// is executed.
|
||||||
|
func Wrap(handler http.Handler) Handler {
|
||||||
|
return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
handler.ServeHTTP(rw, r)
|
||||||
|
next(rw, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
|
||||||
|
// Negroni middleware is evaluated in the order that they are added to the stack using
|
||||||
|
// the Use and UseHandler methods.
|
||||||
|
type Negroni struct {
|
||||||
|
middleware middleware
|
||||||
|
handlers []Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Negroni instance with no middleware preconfigured.
|
||||||
|
func New(handlers ...Handler) *Negroni {
|
||||||
|
return &Negroni{
|
||||||
|
handlers: handlers,
|
||||||
|
middleware: build(handlers),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Classic returns a new Negroni instance with the default middleware already
|
||||||
|
// in the stack.
|
||||||
|
//
|
||||||
|
// Recovery - Panic Recovery Middleware
|
||||||
|
// Logger - Request/Response Logging
|
||||||
|
// Static - Static File Serving
|
||||||
|
func Classic() *Negroni {
|
||||||
|
return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
n.middleware.ServeHTTP(NewResponseWriter(rw), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
|
||||||
|
func (n *Negroni) Use(handler Handler) {
|
||||||
|
if handler == nil {
|
||||||
|
panic("handler cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
n.handlers = append(n.handlers, handler)
|
||||||
|
n.middleware = build(n.handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseFunc adds a Negroni-style handler function onto the middleware stack.
|
||||||
|
func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
|
||||||
|
n.Use(HandlerFunc(handlerFunc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
|
||||||
|
func (n *Negroni) UseHandler(handler http.Handler) {
|
||||||
|
n.Use(Wrap(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseHandler adds a http.HandlerFunc-style handler function onto the middleware stack.
|
||||||
|
func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
|
||||||
|
n.UseHandler(http.HandlerFunc(handlerFunc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run is a convenience function that runs the negroni stack as an HTTP
|
||||||
|
// server. The addr string takes the same format as http.ListenAndServe.
|
||||||
|
func (n *Negroni) Run(addr string) {
|
||||||
|
l := log.New(os.Stdout, "[negroni] ", 0)
|
||||||
|
l.Printf("listening on %s", addr)
|
||||||
|
l.Fatal(http.ListenAndServe(addr, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a list of all the handlers in the current Negroni middleware chain.
|
||||||
|
func (n *Negroni) Handlers() []Handler {
|
||||||
|
return n.handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
func build(handlers []Handler) middleware {
|
||||||
|
var next middleware
|
||||||
|
|
||||||
|
if len(handlers) == 0 {
|
||||||
|
return voidMiddleware()
|
||||||
|
} else if len(handlers) > 1 {
|
||||||
|
next = build(handlers[1:])
|
||||||
|
} else {
|
||||||
|
next = voidMiddleware()
|
||||||
|
}
|
||||||
|
|
||||||
|
return middleware{handlers[0], &next}
|
||||||
|
}
|
||||||
|
|
||||||
|
func voidMiddleware() middleware {
|
||||||
|
return middleware{
|
||||||
|
HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
|
||||||
|
&middleware{},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
|
||||||
|
type Recovery struct {
|
||||||
|
Logger ALogger
|
||||||
|
PrintStack bool
|
||||||
|
ErrorHandlerFunc func(interface{})
|
||||||
|
StackAll bool
|
||||||
|
StackSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecovery returns a new instance of Recovery
|
||||||
|
func NewRecovery() *Recovery {
|
||||||
|
return &Recovery{
|
||||||
|
Logger: log.New(os.Stdout, "[negroni] ", 0),
|
||||||
|
PrintStack: true,
|
||||||
|
StackAll: false,
|
||||||
|
StackSize: 1024 * 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
if rw.Header().Get("Content-Type") == "" {
|
||||||
|
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|
||||||
|
stack := make([]byte, rec.StackSize)
|
||||||
|
stack = stack[:runtime.Stack(stack, rec.StackAll)]
|
||||||
|
|
||||||
|
f := "PANIC: %s\n%s"
|
||||||
|
rec.Logger.Printf(f, err, stack)
|
||||||
|
|
||||||
|
if rec.PrintStack {
|
||||||
|
fmt.Fprintf(rw, f, err, stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec.ErrorHandlerFunc != nil {
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
|
||||||
|
rec.Logger.Printf("%s\n", debug.Stack())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
rec.ErrorHandlerFunc(err)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
next(rw, r)
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about
|
||||||
|
// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter
|
||||||
|
// if the functionality calls for it.
|
||||||
|
type ResponseWriter interface {
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
// Status returns the status code of the response or 200 if the response has
|
||||||
|
// not been written (as this is the default response code in net/http)
|
||||||
|
Status() int
|
||||||
|
// Written returns whether or not the ResponseWriter has been written.
|
||||||
|
Written() bool
|
||||||
|
// Size returns the size of the response body.
|
||||||
|
Size() int
|
||||||
|
// Before allows for a function to be called before the ResponseWriter has been written to. This is
|
||||||
|
// useful for setting headers or any other operations that must happen before a response has been written.
|
||||||
|
Before(func(ResponseWriter))
|
||||||
|
}
|
||||||
|
|
||||||
|
type beforeFunc func(ResponseWriter)
|
||||||
|
|
||||||
|
// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter
|
||||||
|
func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
|
||||||
|
return &responseWriter{
|
||||||
|
ResponseWriter: rw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
status int
|
||||||
|
size int
|
||||||
|
beforeFuncs []beforeFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) WriteHeader(s int) {
|
||||||
|
rw.status = s
|
||||||
|
rw.callBefore()
|
||||||
|
rw.ResponseWriter.WriteHeader(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Write(b []byte) (int, error) {
|
||||||
|
if !rw.Written() {
|
||||||
|
// The status will be StatusOK if WriteHeader has not been called yet
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
size, err := rw.ResponseWriter.Write(b)
|
||||||
|
rw.size += size
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Status() int {
|
||||||
|
return rw.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Size() int {
|
||||||
|
return rw.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Written() bool {
|
||||||
|
return rw.status != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Before(before func(ResponseWriter)) {
|
||||||
|
rw.beforeFuncs = append(rw.beforeFuncs, before)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
hijacker, ok := rw.ResponseWriter.(http.Hijacker)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
|
||||||
|
}
|
||||||
|
return hijacker.Hijack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) CloseNotify() <-chan bool {
|
||||||
|
return rw.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) callBefore() {
|
||||||
|
for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
|
||||||
|
rw.beforeFuncs[i](rw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Flush() {
|
||||||
|
flusher, ok := rw.ResponseWriter.(http.Flusher)
|
||||||
|
if ok {
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Static is a middleware handler that serves static files in the given
|
||||||
|
// directory/filesystem. If the file does not exist on the filesystem, it
|
||||||
|
// passes along to the next middleware in the chain. If you desire "fileserver"
|
||||||
|
// type behavior where it returns a 404 for unfound files, you should consider
|
||||||
|
// using http.FileServer from the Go stdlib.
|
||||||
|
type Static struct {
|
||||||
|
// Dir is the directory to serve static files from
|
||||||
|
Dir http.FileSystem
|
||||||
|
// Prefix is the optional prefix used to serve the static directory content
|
||||||
|
Prefix string
|
||||||
|
// IndexFile defines which file to serve as index if it exists.
|
||||||
|
IndexFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatic returns a new instance of Static
|
||||||
|
func NewStatic(directory http.FileSystem) *Static {
|
||||||
|
return &Static{
|
||||||
|
Dir: directory,
|
||||||
|
Prefix: "",
|
||||||
|
IndexFile: "index.html",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
if r.Method != "GET" && r.Method != "HEAD" {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := r.URL.Path
|
||||||
|
// if we have a prefix, filter requests by stripping the prefix
|
||||||
|
if s.Prefix != "" {
|
||||||
|
if !strings.HasPrefix(file, s.Prefix) {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file = file[len(s.Prefix):]
|
||||||
|
if file != "" && file[0] != '/' {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f, err := s.Dir.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
// discard the error?
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to serve index file
|
||||||
|
if fi.IsDir() {
|
||||||
|
// redirect if missing trailing slash
|
||||||
|
if !strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file = path.Join(file, s.IndexFile)
|
||||||
|
f, err = s.Dir.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err = f.Stat()
|
||||||
|
if err != nil || fi.IsDir() {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeContent(rw, r, file, fi.ModTime(), f)
|
||||||
|
}
|
Loading…
Reference in New Issue