Commit fe0ade8e by Bogdan Ungureanu

Adding pat library

parent 44519877
...@@ -4,8 +4,9 @@ GO libraries ...@@ -4,8 +4,9 @@ GO libraries
Libraries Libraries
--------- ---------
fs/inotify - inotify library fs/inotify - inotify library
net/cluster - simple network cluster net/cluster - simple network cluster
os/daemon - daemonize library net/http/pat - http pat router library
util/ini - ini file library os/daemon - daemonize library
util/ini - ini file library
// Package pat implements a simple URL pattern muxer
package pat
import (
"net/http"
"net/url"
"strings"
)
type PatternServeMux struct {
handlers map[string][]*patHandler
}
// New returns a new PatternServeMux.
func New() *PatternServeMux {
return &PatternServeMux{make(map[string][]*patHandler)}
}
// ServeHTTP matches r.URL.Path against its routing table using the rules
// described above.
func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, ph := range p.handlers[r.Method] {
if params, ok := ph.try(r.URL.Path); ok {
if len(params) > 0 {
r.URL.RawQuery = url.Values(params).Encode() + "&" + r.URL.RawQuery
}
ph.ServeHTTP(w, r)
return
}
}
allowed := make([]string, 0, len(p.handlers))
for meth, handlers := range p.handlers {
if meth == r.Method {
continue
}
for _, ph := range handlers {
if _, ok := ph.try(r.URL.Path); ok {
allowed = append(allowed, meth)
}
}
}
if len(allowed) == 0 {
http.NotFound(w, r)
return
}
w.Header().Add("Allow", strings.Join(allowed, ", "))
http.Error(w, "Method Not Allowed", 405)
}
// Head will register a pattern with a handler for HEAD requests.
func (p *PatternServeMux) Head(pat string, h http.Handler) {
p.Add("HEAD", pat, h)
}
// Get will register a pattern with a handler for GET requests.
// It also registers pat for HEAD requests. If this needs to be overridden, use
// Head before Get with pat.
func (p *PatternServeMux) Get(pat string, h http.Handler) {
p.Add("HEAD", pat, h)
p.Add("GET", pat, h)
}
// Post will register a pattern with a handler for POST requests.
func (p *PatternServeMux) Post(pat string, h http.Handler) {
p.Add("POST", pat, h)
}
// Put will register a pattern with a handler for PUT requests.
func (p *PatternServeMux) Put(pat string, h http.Handler) {
p.Add("PUT", pat, h)
}
// Del will register a pattern with a handler for DELETE requests.
func (p *PatternServeMux) Del(pat string, h http.Handler) {
p.Add("DELETE", pat, h)
}
// Options will register a pattern with a handler for OPTIONS requests.
func (p *PatternServeMux) Options(pat string, h http.Handler) {
p.Add("OPTIONS", pat, h)
}
// Add will register a pattern with a handler for meth requests.
func (p *PatternServeMux) Add(meth, pat string, h http.Handler) {
p.handlers[meth] = append(p.handlers[meth], &patHandler{pat, h})
n := len(pat)
if n > 0 && pat[n-1] == '/' {
p.Add(meth, pat[:n-1], http.RedirectHandler(pat, http.StatusMovedPermanently))
}
}
// Tail returns the trailing string in path after the final slash for a pat ending with a slash.
//
// Examples:
//
// Tail("/hello/:title/", "/hello/mr/mizerany") == "mizerany"
// Tail("/:a/", "/x/y/z") == "y/z"
//
func Tail(pat, path string) string {
var i, j int
for i < len(path) {
switch {
case j >= len(pat):
if pat[len(pat)-1] == '/' {
return path[j-1:]
}
return ""
case pat[j] == ':':
var nextc byte
_, nextc, j = match(pat, isAlnum, j+1)
_, _, i = match(path, matchPart(nextc), i)
case path[i] == pat[j]:
i++
j++
default:
return ""
}
}
return ""
}
type patHandler struct {
pat string
http.Handler
}
func (ph *patHandler) try(path string) (url.Values, bool) {
p := make(url.Values)
var i, j int
for i < len(path) {
switch {
case j >= len(ph.pat):
if ph.pat != "/" && len(ph.pat) > 0 && ph.pat[len(ph.pat)-1] == '/' {
return p, true
}
return nil, false
case ph.pat[j] == ':':
var name, val string
var nextc byte
name, nextc, j = match(ph.pat, isAlnum, j+1)
val, _, i = match(path, matchPart(nextc), i)
p.Add(":"+name, val)
case path[i] == ph.pat[j]:
i++
j++
default:
return nil, false
}
}
if j != len(ph.pat) {
return nil, false
}
return p, true
}
func matchPart(b byte) func(byte) bool {
return func(c byte) bool {
return c != b && c != '/'
}
}
func match(s string, f func(byte) bool, i int) (matched string, next byte, j int) {
j = i
for j < len(s) && f(s[j]) {
j++
}
if j < len(s) {
next = s[j]
}
return s[i:j], next, j
}
func isAlpha(ch byte) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
}
func isDigit(ch byte) bool {
return '0' <= ch && ch <= '9'
}
func isAlnum(ch byte) bool {
return isAlpha(ch) || isDigit(ch)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment