Skip to content

Commit

Permalink
README changed(not finished)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedia committed Apr 25, 2023
1 parent a6e6421 commit 7244fb7
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 77 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Copyright (c) 2013 Julien Schmidt
Copyright (c) 2015-2016, 招牌疯子
Copyright (c) 2018-present Sergio Andres Virviescas Santana, fasthttp
Copyright (c) 2023-present pedia

All rights reserved.

Expand Down
33 changes: 14 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# Router

[![Test status](https://github.com/pedia/router/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/pedia/router/actions?workflow=test)
[![Coverage Status](https://coveralls.io/repos/fasthttp/router/badge.svg?branch=master&service=github)](https://coveralls.io/github/fasthttp/router?branch=master)
[![Coverage Status](https://coveralls.io/repos/pedia/router/badge.svg?branch=master&service=github)](https://coveralls.io/github/pedia/router?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/pedia/router)](https://goreportcard.com/report/github.com/pedia/router)
[![GoDev](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/pedia/router)
[![GitHub release](https://img.shields.io/github/release/fasthttp/router.svg)](https://github.com/pedia/router/releases)
[![GitHub release](https://img.shields.io/github/release/pedia/router.svg)](https://github.com/pedia/router/releases)

Router is a lightweight high performance HTTP request router (also called _multiplexer_ or just _mux_ for short) for [fasthttp](https://github.com/valyala/fasthttp).
Router is a lightweight high performance HTTP request router (also called _multiplexer_ or just _mux_ for short) for [go](https://pkg.go.dev/net/http).

This router is optimized for high performance and a small memory footprint. It scales well even with very long paths and a large number of routes. A compressing dynamic trie (radix tree) structure is used for efficient matching.

Based on [fasthttp/router](https://github.com/fasthttp/router).
Based on [julienschmidt/httprouter](https://github.com/julienschmidt/httprouter).

## Features
Expand Down Expand Up @@ -78,31 +79,33 @@ package main
import (
"fmt"
"log"
"net/http"

"github.com/pedia/router"
"github.com/valyala/fasthttp"
)

func Index(ctx *fasthttp.RequestCtx) {
ctx.WriteString("Welcome!")
// Index is the index handler
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome!\n")
}

func Hello(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "Hello, %s!\n", ctx.UserValue("name"))
// Hello is the Hello handler
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, %s!\n", router.UserValue(r, "name"))
}

func main() {
r := router.New()
r.GET("/", Index)
r.GET("/hello/{name}", Hello)

log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
log.Fatal(http.ListenAndServe(":8080", r))
}
```

### Named parameters

As you can see, `{name}` is a _named parameter_. The values are accessible via `RequestCtx.UserValues`. You can get the value of a parameter by using the `ctx.UserValue("name")`.
As you can see, `{name}` is a _named parameter_. The values are accessible via `router.UserValues`. You can get the value of a parameter by using the `router.UserValue("name")`.

Named parameters only match a single path segment:

Expand Down Expand Up @@ -186,14 +189,6 @@ For even better scalability, the child nodes on each tree level are ordered by p
└-
```

## Why doesn't this work with `http.Handler`?

Because fasthttp doesn't provide http.Handler. See this [description](https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp).

Fasthttp works with [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) functions instead of objects implementing Handler interface. So a Router provides a [Handler](https://pkg.go.dev/github.com/pedia/router#Router.Handler) interface to implement the fasthttp.ListenAndServe interface.

Just try it out for yourself, the usage of Router is very straightforward. The package is compact and minimalistic, but also probably one of the easiest routers to set up.

## Where can I find Middleware _X_?

This package just provides a very efficient request router with a few extra features. The router is just a [`http.Handler`](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler), you can chain any `http.Handler` compatible middleware before the router. Or you could [just write your own](https://justinas.org/writing-http-middleware-in-go/), it's very easy!
Expand All @@ -209,7 +204,7 @@ Have a look at these middleware examples:

You can use another [http.Handler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler), for example another router, to handle requests which could not be matched by this router by using the [Router.NotFound](https://pkg.go.dev/github.com/pedia/router#Router.NotFound) handler. This allows chaining.

### Static files
### Static files(Not Ready)

The `NotFound` handler can for example be used to serve static files from the root path `/` (like an index.html file along with other assets):

Expand Down
71 changes: 37 additions & 34 deletions _examples/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ import (
"encoding/base64"
"fmt"
"log"
"net/http"
"strings"

scrypt "github.com/elithrar/simple-scrypt"
"github.com/pedia/router"
"github.com/elithrar/simple-scrypt"
"github.com/valyala/fasthttp"
)

// basicAuth returns the username and password provided in the request's
// Authorization header, if the request uses HTTP Basic Authentication.
// See RFC 2617, Section 2.
func basicAuth(ctx *fasthttp.RequestCtx) (username, password string, ok bool) {
auth := ctx.Request.Header.Peek("Authorization")
if auth == nil {
func basicAuth(r *http.Request) (username, password string, ok bool) {
auth := r.Header.Get("Authorization")
if auth == "" {
return
}
return parseBasicAuth(string(auth))
Expand All @@ -61,68 +61,71 @@ func parseBasicAuth(auth string) (username, password string, ok bool) {
}

// BasicAuth is the basic auth handler
func BasicAuth(h http.Handler, requiredUser string, requiredPasswordHash []byte) http.Handler {
return http.Handler(func(ctx *fasthttp.RequestCtx) {
func BasicAuth(h http.HandlerFunc, requiredUser string, requiredPasswordHash []byte) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get the Basic Authentication credentials
user, password, hasAuth := basicAuth(ctx)
// WARNING:
user, password, hasAuth := basicAuth(r)

// WARNING:
// DO NOT use plain-text passwords for real apps.
// A simple string comparison using == is vulnerable to a timing attack.
// Instead, use the hash comparison function found in your hash library.
// This example uses scrypt, which is a solid choice for secure hashing:
// go get -u github.com/elithrar/simple-scrypt

if hasAuth && user == requiredUser {

// Uses the parameters from the existing derived key. Return an error if they don't match.
err := scrypt.CompareHashAndPassword(requiredPasswordHash, []byte(password))

if err != nil {
if err != nil {

// log error and request Basic Authentication again below.
log.Fatal(err)
} else {

} else {

// Delegate request to the given handle
h(ctx)
h(w, r)
return
}

}

}

// Request Basic Authentication otherwise
ctx.Error(http.StatusMessage(http.StatusUnauthorized), http.StatusUnauthorized)
ctx.Response.Header.Set("WWW-Authenticate", "Basic realm=Restricted")
})
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
}
}

// Index is the index handler
func Index(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Not protected!\n")
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Not protected!\n")
}

// Protected is the Protected handler
func Protected(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Protected!\n")
func Protected(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Protected!\n")
}

func main() {
user := "gordon"
pass := "secret!"

// generate a hashed password from the password above:
hashedPassword, err := scrypt.GenerateFromPassword([]byte(pass), scrypt.DefaultParams)
if err != nil {
log.Fatal(err)
}
if err != nil {
log.Fatal(err)
}

r := router.New()
r.GET("/", Index)
r.GET("/protected/", BasicAuth(Protected, user, hashedPassword))

log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
s := http.Server{Addr: ":8080", Handler: r}
log.Fatal(http.ListenAndServe(":8080", r))

// curl -vs --user gordon:secret! http://127.0.0.1:8080/protected/
}
```
29 changes: 18 additions & 11 deletions _examples/basic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,48 @@ package main
import (
"fmt"
"log"
"net/http"

"github.com/pedia/router"
"github.com/valyala/fasthttp"
)

// Index is the index handler
func Index(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Welcome!\n")
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome!\n")
}

// Hello is the Hello handler
func Hello(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "hello, %s!\n", ctx.UserValue("name"))
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, %s!\n", router.UserValue(r, "name"))
}

// MultiParams is the multi params handler
func MultiParams(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "hi, %s, %s!\n", ctx.UserValue("name"), ctx.UserValue("word"))
func MultiParams(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hi, %s, %s!\n", router.UserValue(r, "name"), router.UserValue(r, "word"))
}

// RegexParams is the params handler with regex validation
func RegexParams(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hi, %s\n", router.UserValue(r, "name"))
}

// QueryArgs is used for uri query args test #11:
// if the req uri is /ping?name=foo, output: Pong! foo
// if the req uri is /piNg?name=foo, redirect to /ping, output: Pong!
func QueryArgs(ctx *fasthttp.RequestCtx) {
name := ctx.QueryArgs().Peek("name")
fmt.Fprintf(ctx, "Pong! %s\n", string(name))
func QueryArgs(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Fprintf(w, "Pong! %s\n", name)
}

func main() {
r := router.New()
r.GET("/", Index)
r.GET("/hello/{name}", Hello)
r.GET("/multi/{name}/{word}", MultiParams)
r.GET("/regex/{name:[a-zA-Z]+}/test", RegexParams)
r.GET("/optional/{name?:[a-zA-Z]+}/{word?}", MultiParams)
r.GET("/ping", QueryArgs)

log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
log.Fatal(http.ListenAndServe(":8080", r))
}
```
29 changes: 16 additions & 13 deletions _examples/hosts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,35 @@ package main
import (
"fmt"
"log"
"net/http"

"github.com/pedia/router"
"github.com/valyala/fasthttp"
)

// Index is the index handler
func Index(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Welcome!\n")
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome!\n")
}

// Hello is the Hello handler
func Hello(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "hello, %s!\n", ctx.UserValue("name"))
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, %s!\n", router.UserValue(r, "name"))
}

// HostSwitch is the host-handler map
// We need an object that implements the http.Handler interface.
// We just use a map here, in which we map host names (with port) to http.Handlers
type HostSwitch map[string]http.Handler
type HostSwitch map[string]http.HandlerFunc

// CheckHost Implement a CheckHost method on our new type
func (hs HostSwitch) CheckHost(ctx *fasthttp.RequestCtx) {
func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Check if a http.Handler is registered for the given host.
// If yes, use it to handle the request.
if handler := hs[string(ctx.Host())]; handler != nil {
handler(ctx)
if handler := hs[string(r.Host)]; handler != nil {
handler(w, r)
} else {
// Handle host names for wich no handler is registered
ctx.Error("Forbidden", 403) // Or Redirect?
// Handle host names for which no handler is registered
w.WriteHeader(403) // Or Redirect?
}
}

Expand All @@ -55,9 +55,12 @@ func main() {
// Make a new HostSwitch and insert the router (our http handler)
// for example.com and port 12345
hs := make(HostSwitch)
hs["example.com:12345"] = r.Handler
hs["example.com:12345"] = r.ServeHTTP

// Use the HostSwitch to listen and serve on port 12345
log.Fatal(fasthttp.ListenAndServe(":12345", hs.CheckHost))
log.Fatal(http.ListenAndServe(":12345", hs))

// curl -vs -H "Host: example.com:12345" http://127.0.0.1:12345/
// curl -vs http://127.0.0.1:12345/
}
```

0 comments on commit 7244fb7

Please sign in to comment.