Skip to content

Commit

Permalink
Fixed router routing
Browse files Browse the repository at this point in the history
  • Loading branch information
themisir committed Aug 7, 2021
1 parent f9a6d30 commit 07bd227
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 76 deletions.
12 changes: 5 additions & 7 deletions app.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package gosnail
package main

import (
"net/http"

"github.com/themisir/gosnail/core"
)

type Handler interface {
Expand All @@ -25,13 +23,13 @@ func (a *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Method: r.Method,
QueryParams: &query,
body: r.Body,
headers: core.NewHeadersFromValues(r.Header),
headers: NewHeadersFromValues(r.Header),
req: r,
}
res := &Response{
StatusCode: http.StatusOK,
w: w,
statusCode: http.StatusOK,
headers: core.NewHeaders(),
headers: NewHeaders(),
headersSent: false,
}
ctx := &Context{
Expand All @@ -41,7 +39,7 @@ func (a *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

a.handler.Next(ctx, func() {
ctx.Response().SetStatusCode(404)
ctx.Response().StatusCode = http.StatusNotFound
ctx.Response().End()
})
}
Expand Down
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gosnail
package main

type Context struct {
req *Request
Expand Down
3 changes: 0 additions & 3 deletions example/go.mod

This file was deleted.

18 changes: 0 additions & 18 deletions example/main.go

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/themisir/gosnail
module gosnail

go 1.16
2 changes: 1 addition & 1 deletion core/headers.go → headers.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package core
package main

type Headers struct {
values map[string]string
Expand Down
62 changes: 62 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"fmt"
)

func main() {
router := NewRouter()
app := NewApplication(router)

router.Use(Any, func(ctx *Context, next func()) {
fmt.Printf("---> %s %s\n", ctx.Request().Method, ctx.Request().URL)
next()
fmt.Printf("<--- %d %s\n", ctx.Response().StatusCode, ctx.Request().URL)
})

router.Get("/", func(ctx *Context, next func()) {
fmt.Fprintf(ctx.Response(), "I got you!")
})

router2 := NewRouter()

router2.Get("/auth/login", func(ctx *Context, next func()) {
ctx.Response().Headers().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(ctx.Response(), `<form method="post" action="/auth/login?username=bob&password=pas$">
<button type="submit">click me</button>
</form>`)
})

router2.Post("/auth/login", func(ctx *Context, next func()) {
fmt.Fprintf(
ctx.Response(),
"Username: %s, Password: %s",
ctx.Request().Query("username"),
ctx.Request().Query("password"),
)
})

router.Use("/auth", router2.Next)

router3 := NewRouter()

router3.Use(Any, func(ctx *Context, next func()) {
if ctx.Request().Query("auth") != "1" {
ctx.Response().StatusCode = 401
ctx.Response().End()
return
}

next()
})

router3.Get("/token", func(ctx *Context, next func()) {
fmt.Fprintf(ctx.Response(), "Token: ce62ff2ec91f4ce2935c8b3c36a42722")
})

router.Use(Any, router3.Next)

if err := app.Listen(":8080"); err != nil {
panic(err)
}
}
8 changes: 3 additions & 5 deletions request.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package gosnail
package main

import (
"io"
"net/http"
"net/url"

"github.com/themisir/gosnail/core"
)

type Request struct {
Method string
URL *url.URL
QueryParams *url.Values
body io.ReadCloser
headers *core.Headers
headers *Headers
req *http.Request
}

func (r *Request) Headers() *core.Headers {
func (r *Request) Headers() *Headers {
return r.headers
}

Expand Down
31 changes: 20 additions & 11 deletions response.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
package gosnail
package main

import (
"errors"
"net/http"

"github.com/themisir/gosnail/core"
)

type Response struct {
StatusCode int
w http.ResponseWriter
statusCode int
headers *core.Headers
headers *Headers
headersSent bool
}

func (r *Response) Headers() *core.Headers {
func (r *Response) Headers() *Headers {
return r.headers
}

func (r *Response) StatusCode() int {
return r.statusCode
}
func (r *Response) SendHeaders() error {
if r.headersSent {
return errors.New("headers already sent")
}

for k, v := range r.headers.values {
r.w.Header().Add(k, v)
}

r.w.WriteHeader(r.StatusCode)
r.headersSent = true

func (r *Response) SetStatusCode(code int) {
r.statusCode = code
return nil
}

func (r *Response) Write(data []byte) (int, error) {
if !r.headersSent {
r.SendHeaders()
}
return r.w.Write(data)
}

Expand Down
87 changes: 58 additions & 29 deletions router.go
Original file line number Diff line number Diff line change
@@ -1,73 +1,102 @@
package gosnail
package main

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

type RouterHandler = func(*Context, func())

const Any = ""

type RouterEntry struct {
method string
path string
handlers []RouterHandler
method string
path string
pathRange bool
handlers []RouterHandler
next *RouterEntry
}

type Router struct {
entries []*RouterEntry
head *RouterEntry
foot *RouterEntry
}

func NewRouter() *Router {
return &Router{
entries: []*RouterEntry{},
}
return &Router{}
}

func (r *Router) Next(ctx *Context, next func()) {
for _, entry := range r.entries {
for entry := r.head; entry != nil; entry = entry.next {

if len(entry.handlers) == 0 {
continue
}

if ctx.req.Method != entry.method {
if entry.method != "" && entry.method != ctx.req.Method {
continue
}

if ctx.req.URL.Path != entry.path {
continue
}
if entry.path != "" && entry.path != ctx.req.URL.Path {
if entry.pathRange {
match := entry.path

if !strings.HasSuffix(match, "/") {
match = match + "/"
}

if len(entry.handlers) == 1 {
entry.handlers[0](ctx, next)
return
if !strings.HasPrefix(ctx.req.URL.Path, match) {
continue
}
} else {
continue
}
}

var nextFunc func()
i := -1
doContinue := false

nextFunc = func() {
i++
if i < len(entry.handlers) {
entry.handlers[i](ctx, nextFunc)
} else {
next()
for _, handler := range entry.handlers {
doContinue = false
handler(ctx, func() {
doContinue = true
})

if !doContinue {
break
}
}

nextFunc()
return
if !doContinue {
break
}
}

next()
}

func (r *Router) Handle(method string, path string, handlers ...RouterHandler) {
if len(handlers) > 0 {
r.entries = append(r.entries, &RouterEntry{
entry := &RouterEntry{
path: path,
method: method,
handlers: handlers,
})
}

if r.foot == nil {
r.head = entry
r.foot = entry
} else {
r.foot.next = entry
r.foot = entry
}
}
}

func (r *Router) Use(path string, handlers ...RouterHandler) {
r.Handle("", path, handlers...)
r.foot.pathRange = true
}

func (r *Router) Get(path string, handlers ...RouterHandler) {
r.Handle(http.MethodGet, path, handlers...)
}
Expand Down

0 comments on commit 07bd227

Please sign in to comment.