Skip to content

Instantly share code, notes, and snippets.

@ismiyati
Last active June 26, 2019 18:44
Show Gist options
  • Select an option

  • Save ismiyati/cdea6d446c7cffc7f6cf026321ddc853 to your computer and use it in GitHub Desktop.

Select an option

Save ismiyati/cdea6d446c7cffc7f6cf026321ddc853 to your computer and use it in GitHub Desktop.
latihan membuat router http server
package main
import (
"fmt"
"net/http"
"net/url"
)
type (
Params struct {
url *url.URL
keys []string
vals [32][2]int
}
Brnch struct {
hndlr func(http.ResponseWriter, *http.Request, Params)
prmsKey []string
nxt Route
}
Route map[string]Brnch
RouteCfg []struct {
path string
hndlr func(http.ResponseWriter, *http.Request, Params)
}
)
func (prms *Params) ByName(name string) (val string) {
url := prms.url
for i, key := range prms.keys {
if key == name {
bgnEnd := prms.vals[i]
val = url.Path[bgnEnd[0]:bgnEnd[1]]
}
}
return
}
func RouteTrie(routeCfg RouteCfg) (route Route) {
route = Route{}
for _, rCfg := range routeCfg {
r := route
path := rCfg.path
var prmsKey []string
bgn := 0
for end, chr := range path {
if chr == '/' {
sgmnt := path[bgn:end]
if sgmnt[0] == ':' {
prmsKey = append(prmsKey, sgmnt[1:])
sgmnt = ":"
}
_, ok := r[sgmnt]
if !ok {
r[sgmnt] = Brnch{nil, nil, Route{}}
}
bgn = end + 1
r = r[sgmnt].nxt
}
}
sgmnt := path[bgn:]
if sgmnt[0] == ':' {
prmsKey = append(prmsKey, sgmnt[1:])
sgmnt = ":"
}
_, ok := r[sgmnt]
if !ok {
r[sgmnt] = Brnch{rCfg.hndlr, prmsKey, nil}
}
}
return
}
func NewRoute(routeCfg RouteCfg, notFound, methodNotAllowed func(http.ResponseWriter, *http.Request)) (ServeHTTP http.HandlerFunc) {
route := RouteTrie(routeCfg)
ServeHTTP = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
brnch, ok := route[r.Method]
if !ok {
methodNotAllowed(w, r)
return
}
var brnch_ Brnch
prms := Params{}
url := r.URL
bgn, i := 1, 0
for end, chr := range url.Path {
if end != 0 && chr == '/' {
brnch_, ok = brnch.nxt[url.Path[bgn:end]]
if ok {
brnch = brnch_
} else {
brnch, ok = brnch.nxt[":"]
if ok {
prms.vals[i] = [2]int{bgn, end}
i++
} else {
notFound(w, r)
return
}
}
bgn = end + 1
}
}
prms.url = url
brnch_, ok = brnch.nxt[url.Path[bgn:]]
if ok && brnch_.hndlr != nil {
prms.keys = brnch_.prmsKey
brnch_.hndlr(w, r, prms)
} else {
brnch, ok = brnch.nxt[":"]
if ok && brnch.hndlr != nil {
prms.vals[i] = [2]int{bgn, len(url.Path)}
prms.keys = brnch.prmsKey
brnch.hndlr(w, r, prms)
} else {
notFound(w, r)
return
}
}
})
return
}
func main() {
newHndlr := func(path string) (hndlr func(http.ResponseWriter, *http.Request, Params)) {
hndlr = func(w http.ResponseWriter, r *http.Request, params Params) {
w.WriteHeader(http.StatusOK)
url := r.URL
fmt.Fprintf(w, "path %s\n", path)
for i, key := range params.keys {
bgnEnd := params.vals[i]
fmt.Fprintf(w, "param %d key: %s, val: %s, params.ByName(\"%s\") == \"%s\"\n", i, key, url.Path[bgnEnd[0]:bgnEnd[1]], key, params.ByName(key))
}
if len(params.keys) == 0 {
w.Write([]byte("no params"))
}
}
return
}
routeCfg := RouteCfg{
{"GET/def/ghi/jkl", newHndlr("/def/ghi/jkl")},
/*
url /def/ghi/jkl
no params
*/
{"GET/def/:ghi/jkl", newHndlr("/def/:ghi/jkl")},
/*
url /def/GHI/jkl
param 0 key: ghi, val: GHI
*/
{"GET/def/:ghi/mno/:pqr/stu", newHndlr("/def/:ghi/mno/:pqr/stu")},
/*
url /def/GHI/mno/PQR/stu
param 0 key: ghi, val: GHI
param 1 key: pqr, val: PQR
*/
{"GET/def/:ghi/mno/:pqr/:stu", newHndlr("/def/:ghi/mno/:pqr/:stu")},
/*
url /def/GHI/mno/PQR/STU
param 0 key: ghi, val: GHI
param 1 key: pqr, val: PQR
param 2 key: stu, val: STU
*/
{"GET/:def/:ghi/pqr", newHndlr("/:def/:ghi/pqr")},
/*
url /DEF/GHI/pqr
param 0 key: def, val: DEF
param 1 key: ghi, val: GHI
*/
}
ServeHTTP := NewRoute(
routeCfg,
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("not found"))
},
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("method not allowed"))
},
)
http.ListenAndServe(":8080", ServeHTTP)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment