vi

package module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 19, 2023 License: MIT Imports: 16 Imported by: 0

README

Vi Router

pre-commit Go Reference ci workflow codecov

"This project draws inspiration from gorilla/mux, and I appreciate the ease of working with the library.However, its performance falls short of my expectations. After conducting research, my goal is to develop a high-performance HTTP router that not only outperforms but also retains the convenient API of mux, enhanced with additional support for regex. I welcome any feedback as this will be my first open source projects."

Installation

go get -u github.com/diontr00/vi

TestRegex

If you want to make sure whether the regex matcher work as you expected , you can use TestMatcher function in the init function

func init() {
    RegisterHelper("ip", `((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|
    [1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])`)
    RegisterHelper("phone", `([+]?[\s0-9]+)?(\d{3}|[(]?[0-9]+[)])?([-]?[\s]?[0-9])+`)

    // Expect match
    errs := TestMatcher(true, "/user/{id:[0-9]+}/:ip/:phone?", map[vi.TestUrl]vi.TestResult{
        "/user/101/192.168.0.1/999-9999999":
        { "id" : "101" , "ip": "192.168.0.1", "phone": "999-9999999"},
        "/user/102/192.168.0.2/+48(12)504-203-260":
        { "id" : "102" , "ip": "192.168.0.2", "phone": "+48(12)504-203-260"},
        "/user/103/192.168.0.3":
        { "id" : "103" , "ip": "192.168.0.3"},
        "/user/104/192.168.0.4/555-5555-555" :
        { "id" : "104" , "ip": "192.168.0.4" ,"phone": "555-5555-555"}
 })

    for i := range errs {
        fmt.Println(errs[i])
    }

    // Expect not match
    errs := TestMatcher(false , ....)
}

Simple Usage

package main

import (
    "github.com/diontr00/vi"
)

func main(){
    customNotFoundHandler  := func(w http.ResponseWriter , r *http.Request) {
   w.WriteHeader(http.StatusNotFound)
   w.Write([]byte(customNotFoundMsg))
    }

    mux := vi.New(&vi.Config{Banner : false ,  NotFoundHandler: customNotFoundHandler})
    vi.RegisterHelper("ip" , `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
    mux.Get("/location/:ip", func(w http.ResponseWriter , r *http.Request){
            ip := vi.GetParam("ip")
            msg := fmt.Sprintf("You have search ip addres %s \n", ip)
            w.Write([]byte(msg))
    })

    srv :=  &http.Server{
        Addre: ":8080" ,
        Handler: vi,
    }

    done := make(chan os.Signal, 1)
 signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

 go func() {
  if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
   log.Fatalf("listen: %s\n", err)
  }
 }()
 log.Print("Server Started")

 <-done
 log.Print("Server Stopped")

 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 defer func() {
  // extra handling here
  cancel()
 }()

 if err := srv.Shutdown(ctx); err != nil {
  log.Fatalf("Server Shutdown Failed:%+v", err)
 }
 log.Print("Server Exited Properly")
}

Serving Static Files

This receipt will serve any "userimage".png file under userfile static folder. Note that prefix is use to append to asset path, so userimage.png eventually become /static/userfile/userimage.png

Other properties:

  • Root: Can be either http.Dir or embed with go embed fs
  • MaxAge: Can be set in term of second to control the cache header
  • Next: Skip function , will skip when query param ignore is true in this scenario
  • NotFoundFile: if not found , it will be serve
func main() {
    vi :=  vi.New()
    vi.Static("/{userimage:[0-9a-zA-Z_.]+}.png" , &StaticConfig{
        Root : http.Dir("static"),
        Prefix: "userfile",
        Index : "index.html",
        MaxAge: 2,
        Next : func(w http.ResponseWriter , r *http.Request) bool {
            return r.URL.Query().Get("ignore") == "true"
        },
        NotFoundFile : "error/notfound.html"
    } )

    http.ListenAndServe(":8080", vi)
}


Matching Rule

  • Named parameter

    • Syntax: :name
    • Example: /student/:name
    • Explain: Match name as word
  • Name with regex pattern

    • Syntax: {name:regex-pattern}
    • Example: /student/{id:[0-9]+}
    • Explain: Match id as number
  • Helper pattern

    • :id : short for /student/{id:[0-9]+}
    • :name : short for /{name:[0-9a-zA-Z]+}

Benchmark

Run benchmark and test with ginkgo:

ginkgo

Documentation

Index

Constants

View Source
const (
	Version = "under development"
)

Variables

This section is empty.

Functions

func GetParam added in v0.4.0

func GetParam(r *http.Request, key string) (paramValue string)

Get the matched param that store inside request context

func New

func New(config *Config) *vi

Return new vi

func RegisterHelper added in v0.3.1

func RegisterHelper(pattern, regex string)

Use to register global helper pattern to be use in param matching example : ip ,`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}` then you can use something like v.Get("/location/:ip", ...)

func TestMatcher added in v0.3.2

func TestMatcher(expect bool, pattern string, tests ...map[TestUrl]ExpectMatch) []error

Can be use to validate whether the regex pattern match correctly

Types

type Config added in v0.3.0

type Config struct {
	// When set to false , this will turn off the banner
	Banner bool
	// Set the custom not found error handler , if not set the default not fault will be use
	NotFoundHandler http.HandlerFunc
}

Configuration for vi instance

type ExpectMatch added in v0.3.2

type ExpectMatch map[string]string

Expect test result , for each param expect value in TestMatcher

type StaticConfig added in v0.4.0

type StaticConfig struct {
	// Root is a filesystem  that provides access to a
	// collection of files and directories , use http.Dir("folder name") or embed FS  with http.FS()
	// Required
	Root http.FileSystem
	// Defines prefix that will be add to be when reading a file from FileSystem
	// Use only when using go embed FS for Root
	// Optional  default to ""
	Prefix string
	// Name of the index file for serving
	// Optional default to index.html
	Index string
	// The value for the cache-control HTTP-Header when response , its define in term of second , default value to 0
	// Optional default to 0
	MaxAge int
	//  Next defines a function  that allow to skip a scenario when it return true
	// Optional default to nil
	Next func(w http.ResponseWriter, r *http.Request) bool
	// File to return if path is not found. Useful for SPA's
	// Optional default to 404 not found
	NotFoundFile string
}

Static defines configuration options when defining static route

type TestUrl added in v0.3.2

type TestUrl string

Represent the url to be test again pattern in TestMatcher

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL