Skip to content

Commit

Permalink
Add urlManage freture
Browse files Browse the repository at this point in the history
  • Loading branch information
leetaifook committed Dec 24, 2012
1 parent d500f16 commit 2059518
Show file tree
Hide file tree
Showing 2 changed files with 284 additions and 15 deletions.
225 changes: 225 additions & 0 deletions urlmanage/urlmanage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package urlmanage

import (
"golanger.com/framework/log"
"net/http"
"regexp"
"strconv"
"strings"
"sync"
)

/*
NC - Nocase:URL地址匹配对大小写敏感
S - Skip:忽略之后的规则
R - Redirect:发出一个HTTP重定向
N - Next:再次从第一个规则开始处理,但是使用当前重写后的URL地址
L - Last:停止处理接下来的规则
QSA - Qsappend:在新的URL地址后附加查询字符串部分,而不是替代
*/
type flag struct {
name string
param string
}

type rule struct {
regexp *regexp.Regexp
replace string
flags []flag
}

type UrlManage struct {
manage bool
rules []rule
mutex sync.RWMutex
}

func New() *UrlManage {
return &UrlManage{}
}

func (u *UrlManage) Manage() bool {
return u.manage
}

func (u *UrlManage) Start() {
u.mutex.Lock()
u.manage = true
u.mutex.Unlock()
}

func (u *UrlManage) Stop() {
u.mutex.Lock()
u.manage = false
u.mutex.Unlock()
}

func (u *UrlManage) addRule(expr, replace string, flags ...flag) {
if expr == "" || replace == "" {
log.Warn("UrlManage.addUrl: expr and reolace is empty")
return
}

r, err := regexp.Compile(expr)
if err != nil {
log.Warn("UrlManage.addUrl: regexp compile failed - ", err)
return
}

rl := rule{
regexp: r,
replace: replace,
flags: flags,
}

u.mutex.Lock()
u.rules = append(u.rules, rl)
u.mutex.Unlock()
}

func (u *UrlManage) doRule(rw http.ResponseWriter, req *http.Request) string {
in := req.URL.Path
out := in
lrs := len(u.rules)
RESTART:
for i := 0; i < lrs; i++ {
ur := u.rules[i]
if !ur.regexp.MatchString(in) {
continue
}

var skip bool
var restart bool
var last bool
var redirect bool
var redirectCode int
var queryStringAppend bool

if len(ur.flags) > 0 {
for _, f := range ur.flags {
switch f.name {
case "R":
redirect = true
redirectCode, _ = strconv.Atoi(f.param)
if redirectCode == 0 {
redirectCode = http.StatusFound
}
case "S":
skip = true
skipNum, _ := strconv.Atoi(f.param)
//循环后会自动加1,所以这里减1
skipNum = skipNum - 1
if skipNum > 0 {
i = i + skipNum
}
case "N":
restart = true
case "L":
last = true
case "QSA":
queryStringAppend = true
}
}
}

out = ur.regexp.ReplaceAllString(in, ur.replace)

if queryStringAppend {
if strings.Contains(out, "?") {
out += "&"
} else {
out += "?"
}

out += req.URL.RawQuery
}

if redirect {
http.Redirect(rw, req, out, redirectCode)
return `redirect`
}

if skip {
continue
}

if restart {
in = out
goto RESTART
}

if last {
break
}
}

return out
}

func (u *UrlManage) clearRule() {
u.mutex.Lock()
defer u.mutex.Unlock()
u.rules = make([]rule, 0)
}

func (u *UrlManage) loadRule(rules string) {
for _, r := range strings.Split(rules, "\n") {
u.AddRule(r)
}
}

func (u *UrlManage) parse(r string) (string, string, []flag) {
var expr, replace string
var flags []flag
r = regexp.MustCompile(`[[:blank:]]+`).ReplaceAllString(r, "`")
rs := strings.Split(r, "`")
lrs := len(rs)
if lrs >= 2 {
expr = rs[0]
replace = rs[1]
if lrs >= 3 {
flagss := strings.Split(strings.Trim(rs[2], `[]`), ",")
for _, f := range flagss {
if f == "NC" {
expr = `(?i)` + expr
continue
}

var fl flag
fs := strings.Split(f, "=")
fl = flag{
name: fs[0],
}
if len(fs) > 1 {
fl.param = fs[1]
}

flags = append(flags, fl)
}
}
}

return expr, replace, flags
}

func (u *UrlManage) AddRule(r string) {
expr, replace, flags := u.parse(r)
u.addRule(expr, replace, flags...)
}

func (u *UrlManage) ReWrite(rw http.ResponseWriter, req *http.Request) string {
out := req.URL.String()
if u.manage {
out = u.doRule(rw, req)
}

return out
}

func (u *UrlManage) LoadRule(rules string, reload bool) {
if reload {
u.clearRule()
}

u.loadRule(strings.TrimSpace(rules))
}
74 changes: 59 additions & 15 deletions web/config.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package web

import (
"bytes"
"encoding/json"
"golanger.com/framework/log"
"fmt"
"io/ioutil"
"os"
"regexp"
)

var (
regexpNote *regexp.Regexp = regexp.MustCompile(`#.*`)
regexpSpace *regexp.Regexp = regexp.MustCompile(`[\n\v\f\r]*`)
)

type Config struct {
Log bool `json:"Log"`
SupportLog bool `json:"SupportLog"`
LogWriteTo string `json:"LogWriteTo"`
LogLevel string `json:"LogLevel"`
SupportTemplate bool `json:"SupportTemplate"`
SupportSession bool `json:"SupportSession"`
SupportCookieSession bool `json:"SupportCookieSession"`
SupportI18n bool `json:"SupportI18n"`
SupportStatic bool `json:"SupportStatic"`
SupportUrlManage bool `json:"SupportUrlManage"`
SessionType string `json:"SessionType"`
RootStaticFiles string `json:"RootStaticFiles"`
DefaultLanguage string `json:"DefaultLanguage"`
Expand Down Expand Up @@ -45,8 +52,9 @@ type Config struct {
SiteRoot string `json:"SiteRoot"`
Environment map[string]string `json:"Environment"`
Database map[string]string `json:"Database"`
UrlManageRule []string `json:"UrlManageRule"`
M map[string]interface{} `json:"Custom"`
configPath string
configDir string
configLastModTime int64
}

Expand Down Expand Up @@ -75,25 +83,61 @@ func NewConfig() Config {
SiteRoot: "/",
Environment: map[string]string{},
Database: map[string]string{},
UrlManageRule: []string{},
}
}

func (c *Config) format(configPath string) []byte {
data, err := ioutil.ReadFile(configPath)
if err != nil {
log.Fatal("<Config.format> ", err)
fmt.Println("<Config.format> ")
panic(err)
}

return regexp.MustCompile(`#.*\n`).ReplaceAll(data, []byte("\n"))
return regexpSpace.ReplaceAll(regexpNote.ReplaceAll(data, []byte(``)), []byte(``))
}

func (c *Config) Load(configPath string) {
data := c.format(configPath)
func (c *Config) readDir(configDir string) []byte {
fis, err := ioutil.ReadDir(configDir)
if err != nil {
fmt.Println("<Config.readDir> ")
panic(err)
}

lfis := len(fis)
chContent := make(chan []byte, lfis)

for _, fi := range fis {
go func(chContent chan []byte, configPath string) {
chContent <- c.format(configPath)
}(chContent, configDir+"/"+fi.Name())
}

contentBuf := bytes.NewBufferString(`{`)
for i := 1; i <= lfis; i++ {
content := <-chContent
if len(content) == 0 {
continue
}

contentBuf.Write(content)
if i < lfis {
contentBuf.WriteString(",")
}
}

contentBuf.WriteString(`}`)

return contentBuf.Bytes()
}

func (c *Config) Load(configDir string) {
data := c.readDir(configDir)
err := json.Unmarshal(data, c)
if err != nil {
log.Debug("<Config.Load> jsonData:", string(data))
log.Fatal("<Config.Load> ", err)
fmt.Println("<Config.Load> jsonData:", string(data))
fmt.Println("<Config.Load> ")
panic(err)
}

c.UploadDirectory = c.AssetsDirectory + c.StaticDirectory + c.UploadDirectory
Expand All @@ -102,20 +146,20 @@ func (c *Config) Load(configPath string) {
c.StaticJsDirectory = c.AssetsDirectory + c.StaticDirectory + c.ThemeDirectory + c.StaticJsDirectory
c.StaticImgDirectory = c.AssetsDirectory + c.StaticDirectory + c.ThemeDirectory + c.StaticImgDirectory

c.configPath = configPath
dataFi, _ := os.Stat(configPath)
c.configDir = configDir
dataFi, _ := os.Stat(configDir)
c.configLastModTime = dataFi.ModTime().Unix()
}

func (c *Config) Reload() bool {
var b bool
configPath := c.configPath
dataFi, _ := os.Stat(configPath)
configDir := c.configDir
dataFi, _ := os.Stat(configDir)
if dataFi.ModTime().Unix() > c.configLastModTime {
data := c.format(configPath)
data := c.readDir(configDir)
*c = NewConfig()
json.Unmarshal(data, c)
c.configPath = configPath
c.configDir = configDir
c.configLastModTime = dataFi.ModTime().Unix()
c.UploadDirectory = c.AssetsDirectory + c.StaticDirectory + c.UploadDirectory
c.ThemeDirectory = c.ThemeDirectory + c.Theme + "/"
Expand Down

0 comments on commit 2059518

Please sign in to comment.