Skip to content

Commit

Permalink
rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
AmyangXYZ committed Jan 27, 2018
1 parent e8d616c commit b53b66e
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 126 deletions.
50 changes: 50 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -1 +1,51 @@
package sweetygo

import (
"net/http"
"net/url"
)

// Context provide a HTTP context for SweetyGo.
type Context struct {
sg *SweetyGo
Req *http.Request
Resp *responseWriter
handlers []HandlerFunc
}

// NewContext .
func NewContext(s *SweetyGo) *Context {
c := &Context{}
c.sg = s
c.handlers = make([]HandlerFunc, len(s.middlewares)+1)
copy(c.handlers, s.middlewares)
return c
}

// Init the context gotten from sync pool.
func (c *Context) Init(w http.ResponseWriter, r *http.Request) {
c.Resp = &responseWriter{w, 0}
c.Req = r
c.handlers = c.handlers[:len(c.sg.middlewares)]
}

// Next execute next middleware or router.
func (c *Context) Next() {
n := len(c.handlers)
switch {
case n > 1:
c.handlers[0](c)
c.handlers = c.handlers[1:]
c.Next()
case n == 1:
c.handlers[0](c)
default:
return
}
}

// Params returns route params
func (c *Context) Params() url.Values {
c.Req.ParseForm()
return c.Req.Form
}
31 changes: 9 additions & 22 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,28 @@ package main

import (
"fmt"
"net/http"

"github.com/AmyangXYZ/sweetygo"
)

func main() {
app := sweetygo.New(root)
app := sweetygo.New()

app.USE(sweetygo.Logger())
app.GET("/static/*files", staticServer)
// app.USE(sweetygo.Logger())
// app.GET("/static/*files", staticServer)
app.GET("/", home)
app.POST("/api", home)
app.GET("/usr/:user/:sex/:age", hello)

app.RunServer(":8080")
}

func root(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
http.NotFound(w, r)
func home(c *Context) {
c.Resp.WriteHeader(200)
fmt.Fprintf(c.Resp, "Welcome \n")
}

func home(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
w.WriteHeader(200)
fmt.Fprintf(w, "Welcome \n")
}

func staticServer(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
staticHandle := http.StripPrefix("/static/",
http.FileServer(http.Dir("./static")))
staticHandle.ServeHTTP(w, r)
}

func hello(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
r.ParseForm()
params := r.Form
w.WriteHeader(200)
fmt.Fprintf(w, "Hello %s\n", params["user"][0])
func hello(c *Context) {
c.Resp.WriteHeader(200)
fmt.Fprintf(c.Resp, "Hello %s\n", c.Params["user"][0])
}
16 changes: 6 additions & 10 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@ package sweetygo

import (
"log"
"net/http"
"os"
"strings"
"time"
)

// Logger is a built-in middleware
func Logger() HandlerFunc {
func Logger(c *Context) {
logger := log.New(os.Stdout, "*SweetyGo*", 0)
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
start := time.Now()
rw := &responseWriteReader{w, 0}
next(rw, r)
end := time.Since(start)
logger.Printf(" -- %s - [%v] \"%s %s\" %d - %v",
strings.Split(r.RemoteAddr, ":")[0], start.Format("2006-01-02 15:04:05"), r.Method, r.URL.Path, rw.status, end)
}
start := time.Now()
c.Next()
logger.Printf(" -- %s - [%v] \"%s %s\" %d - %v",
strings.Split(c.Req.RemoteAddr, ":")[0], start.Format("2006-01-02 15:04:05"),
c.Req.Method, c.Req.URL.Path, c.Resp.status, time.Since(start))
}
8 changes: 4 additions & 4 deletions responsewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import (
"net/http"
)

// responseWriteReader implement ResponseWriter
type responseWriteReader struct {
// responseWriter implement ResponseWriter
type responseWriter struct {
http.ResponseWriter
status int
}

// WriteHeader sends an HTTP response header with status code,
// and stores the code
func (w *responseWriteReader) WriteHeader(code int) {
func (w *responseWriter) WriteHeader(code int) {
w.status = code
w.ResponseWriter.WriteHeader(code)
}

// Hijack implements the http.Hijacker interface to allow an HTTP handler to
// take over the connection
func (w *responseWriteReader) Hijack() (net.Conn, *bufio.ReadWriter, error) {
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack()
}
106 changes: 28 additions & 78 deletions router.go
Original file line number Diff line number Diff line change
@@ -1,119 +1,69 @@
package sweetygo

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

// Router is based on Radix Tree
// rootHandler is for runMiddleware
type Router struct {
tree *Trie
rootHandler HandlerFunc
middlewares []HandlerFunc
// NotFoundHandler .
func NotFoundHandler(c *Context) {
http.NotFound(c.Resp, c.Req)
}

// New Router
func New(root HandlerFunc) *Router {
tree := Trie{
component: "/",
methods: make(map[string]HandlerFunc),
}
return &Router{tree: &tree,
middlewares: make([]HandlerFunc, 0),
rootHandler: root}
}

// USE middlewares for router
func (r *Router) USE(middleware ...HandlerFunc) {
r.middlewares = append(r.middlewares, middleware...)
}

func runMiddleware(w http.ResponseWriter, req *http.Request, middleware Middleware) {
middleware.ServeHTTP(w, req)
}

// build a middleware list
func mwareList(middleware []HandlerFunc, handler HandlerFunc) Middleware {
var next Middleware

if len(middleware) == 0 {
return finalHandler(handler)
} else if len(middleware) > 1 {
next = mwareList(middleware[1:], handler)
} else {
next = finalHandler(handler)
}

return Middleware{middleware[0], &next}
}
func (s *SweetyGo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := s.pool.Get().(*Context)
c.Init(w, r)

func finalHandler(handler HandlerFunc) Middleware {
return Middleware{
HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
handler(w, r, next)
}),
&Middleware{}}
}

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req.ParseForm()
params := req.Form

node := r.tree.Search(strings.Split(req.URL.Path, "/")[1:], params)
if node != nil && node.methods[req.Method] != nil {
runMiddleware(w, req, mwareList(r.middlewares, node.methods[req.Method]))
node := s.tree.Search(strings.Split(r.URL.Path, "/")[1:], c.Params())
if node != nil && node.methods[r.Method] != nil {
c.handlers = append(c.handlers, node.methods[r.Method])
} else {
runMiddleware(w, req, mwareList(r.middlewares, r.rootHandler))
c.handlers = append(c.handlers, s.notFoundHandler)
}
}

// RunServer at the given addr
func (r *Router) RunServer(addr string) {
fmt.Printf("*SweetyGo* -- Listen on %s\n", addr)
http.ListenAndServe(addr, r)
c.Next()
s.pool.Put(c)
}

// Handle register custom METHOD request HandlerFunc
func (r *Router) Handle(method, path string, handler HandlerFunc) {
func (s *SweetyGo) Handle(method, path string, handler HandlerFunc) {
if len(path) < 1 || path[0] != '/' {
panic("Path should be like '/sweety/go'")
}
r.tree.Insert(method, path, handler)
s.tree.Insert(method, path, handler)
}

// GET register GET request handler
func (r *Router) GET(path string, handler HandlerFunc) {
r.Handle("GET", path, handler)
func (s *SweetyGo) GET(path string, handler HandlerFunc) {
s.Handle("GET", path, handler)
}

// HEAD register HEAD request handler
func (r *Router) HEAD(path string, handler HandlerFunc) {
r.Handle("HEAD", path, handler)
func (s *SweetyGo) HEAD(path string, handler HandlerFunc) {
s.Handle("HEAD", path, handler)
}

// OPTIONS register OPTIONS request handler
func (r *Router) OPTIONS(path string, handler HandlerFunc) {
r.Handle("OPTIONS", path, handler)
func (s *SweetyGo) OPTIONS(path string, handler HandlerFunc) {
s.Handle("OPTIONS", path, handler)
}

// POST register POST request handler
func (r *Router) POST(path string, handler HandlerFunc) {
r.Handle("POST", path, handler)
func (s *SweetyGo) POST(path string, handler HandlerFunc) {
s.Handle("POST", path, handler)
}

// PUT register PUT request handler
func (r *Router) PUT(path string, handler HandlerFunc) {
r.Handle("PUT", path, handler)
func (s *SweetyGo) PUT(path string, handler HandlerFunc) {
s.Handle("PUT", path, handler)
}

// PATCH register PATCH request HandlerFunc
func (r *Router) PATCH(path string, handler HandlerFunc) {
r.Handle("PATCH", path, handler)
func (s *SweetyGo) PATCH(path string, handler HandlerFunc) {
s.Handle("PATCH", path, handler)
}

// DELETE register DELETE request handler
func (r *Router) DELETE(path string, handler HandlerFunc) {
r.Handle("DELETE", path, handler)
func (s *SweetyGo) DELETE(path string, handler HandlerFunc) {
s.Handle("DELETE", path, handler)
}
52 changes: 40 additions & 12 deletions sweety.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
package sweetygo

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

type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
// HandlerFunc context handler func
type HandlerFunc func(*Context)

// Middleware handler
type Middleware interface{}

type HandlerFunc func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
// SweetyGo is Suuuuuuuuper Sweetie!
type SweetyGo struct {
tree *Trie
pool sync.Pool
notFoundHandler HandlerFunc
middlewares []HandlerFunc
}

type Middleware struct {
handler Handler
next *Middleware
// New SweetyGo App
func New() *SweetyGo {
tree := &Trie{
component: "/",
methods: make(map[string]HandlerFunc),
}
s := &SweetyGo{tree: tree,
notFoundHandler: NotFoundHandler,
middlewares: make([]HandlerFunc, 0),
}
s.pool = sync.Pool{
New: func() interface{} {
return NewContext(s)
},
}
return s
}

func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
h(w, r, next)
// USE middlewares for SweetyGo
func (s *SweetyGo) USE(middleware ...HandlerFunc) {
s.middlewares = append(s.middlewares, middleware...)
}

func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.handler.ServeHTTP(w, r, m.next.ServeHTTP)
// RunServer at the given addr
func (s *SweetyGo) RunServer(addr string) {
fmt.Printf("*SweetyGo* -- Listen on %s\n", addr)
http.ListenAndServe(addr, s)
}

0 comments on commit b53b66e

Please sign in to comment.