Răsfoiți Sursa

Code optimization

刘河 6 ani în urmă
părinte
comite
b189fb1b6e
100 a modificat fișierele cu 14854 adăugiri și 684 ștergeri
  1. 93 142
      bridge/bridge.go
  2. 6 6
      client/client.go
  3. 0 2
      client/control.go
  4. 4 13
      client/local.go
  5. 4 0
      cmd/nps/nps.go
  6. 4 2
      conf/clients.csv
  7. 3 3
      conf/hosts.csv
  8. 10 17
      conf/npc.conf
  9. 6 7
      conf/nps.conf
  10. 3 5
      conf/tasks.csv
  11. 11 0
      lib/common/util.go
  12. 6 1
      lib/config/config.go
  13. 2 4
      lib/conn/conn.go
  14. 50 0
      lib/conn/listener.go
  15. 8 3
      lib/crypt/tls.go
  16. 183 224
      lib/file/file.go
  17. 32 28
      lib/file/obj.go
  18. 9 5
      server/proxy/base.go
  19. 44 49
      server/proxy/http.go
  20. 4 17
      server/proxy/p2p.go
  21. 15 42
      server/proxy/socks5.go
  22. 11 33
      server/proxy/tcp.go
  23. 4 2
      server/proxy/udp.go
  24. 84 66
      server/server.go
  25. 18 12
      server/test/test.go
  26. 1 1
      server/tool/utils.go
  27. 6 0
      vender/github.com/astaxie/beego/.gitignore
  28. 4 0
      vender/github.com/astaxie/beego/.gosimpleignore
  29. 63 0
      vender/github.com/astaxie/beego/.travis.yml
  30. 52 0
      vender/github.com/astaxie/beego/CONTRIBUTING.md
  31. 13 0
      vender/github.com/astaxie/beego/LICENSE
  32. 63 0
      vender/github.com/astaxie/beego/README.md
  33. 416 0
      vender/github.com/astaxie/beego/admin.go
  34. 75 0
      vender/github.com/astaxie/beego/admin_test.go
  35. 286 0
      vender/github.com/astaxie/beego/adminui.go
  36. 497 0
      vender/github.com/astaxie/beego/app.go
  37. 123 0
      vender/github.com/astaxie/beego/beego.go
  38. 510 0
      vender/github.com/astaxie/beego/config.go
  39. 242 0
      vender/github.com/astaxie/beego/config/config.go
  40. 55 0
      vender/github.com/astaxie/beego/config/config_test.go
  41. 87 0
      vender/github.com/astaxie/beego/config/env/env.go
  42. 75 0
      vender/github.com/astaxie/beego/config/env/env_test.go
  43. 134 0
      vender/github.com/astaxie/beego/config/fake.go
  44. 504 0
      vender/github.com/astaxie/beego/config/ini.go
  45. 190 0
      vender/github.com/astaxie/beego/config/ini_test.go
  46. 266 0
      vender/github.com/astaxie/beego/config/json.go
  47. 222 0
      vender/github.com/astaxie/beego/config/json_test.go
  48. 228 0
      vender/github.com/astaxie/beego/config/xml/xml.go
  49. 125 0
      vender/github.com/astaxie/beego/config/xml/xml_test.go
  50. 316 0
      vender/github.com/astaxie/beego/config/yaml/yaml.go
  51. 115 0
      vender/github.com/astaxie/beego/config/yaml/yaml_test.go
  52. 138 0
      vender/github.com/astaxie/beego/config_test.go
  53. 232 0
      vender/github.com/astaxie/beego/context/acceptencoder.go
  54. 59 0
      vender/github.com/astaxie/beego/context/acceptencoder_test.go
  55. 262 0
      vender/github.com/astaxie/beego/context/context.go
  56. 47 0
      vender/github.com/astaxie/beego/context/context_test.go
  57. 668 0
      vender/github.com/astaxie/beego/context/input.go
  58. 207 0
      vender/github.com/astaxie/beego/context/input_test.go
  59. 395 0
      vender/github.com/astaxie/beego/context/output.go
  60. 78 0
      vender/github.com/astaxie/beego/context/param/conv.go
  61. 69 0
      vender/github.com/astaxie/beego/context/param/methodparams.go
  62. 37 0
      vender/github.com/astaxie/beego/context/param/options.go
  63. 149 0
      vender/github.com/astaxie/beego/context/param/parsers.go
  64. 84 0
      vender/github.com/astaxie/beego/context/param/parsers_test.go
  65. 12 0
      vender/github.com/astaxie/beego/context/renderer.go
  66. 27 0
      vender/github.com/astaxie/beego/context/response.go
  67. 683 0
      vender/github.com/astaxie/beego/controller.go
  68. 181 0
      vender/github.com/astaxie/beego/controller_test.go
  69. 17 0
      vender/github.com/astaxie/beego/doc.go
  70. 474 0
      vender/github.com/astaxie/beego/error.go
  71. 88 0
      vender/github.com/astaxie/beego/error_test.go
  72. 44 0
      vender/github.com/astaxie/beego/filter.go
  73. 68 0
      vender/github.com/astaxie/beego/filter_test.go
  74. 110 0
      vender/github.com/astaxie/beego/flash.go
  75. 54 0
      vender/github.com/astaxie/beego/flash_test.go
  76. 8 0
      vender/github.com/astaxie/beego/go.mod
  77. 7 0
      vender/github.com/astaxie/beego/go.sum
  78. 39 0
      vender/github.com/astaxie/beego/grace/conn.go
  79. 166 0
      vender/github.com/astaxie/beego/grace/grace.go
  80. 62 0
      vender/github.com/astaxie/beego/grace/listener.go
  81. 363 0
      vender/github.com/astaxie/beego/grace/server.go
  82. 103 0
      vender/github.com/astaxie/beego/hooks.go
  83. 97 0
      vender/github.com/astaxie/beego/httplib/README.md
  84. 624 0
      vender/github.com/astaxie/beego/httplib/httplib.go
  85. 226 0
      vender/github.com/astaxie/beego/httplib/httplib_test.go
  86. 111 0
      vender/github.com/astaxie/beego/log.go
  87. 83 0
      vender/github.com/astaxie/beego/logs/accesslog.go
  88. 186 0
      vender/github.com/astaxie/beego/logs/alils/alils.go
  89. 13 0
      vender/github.com/astaxie/beego/logs/alils/config.go
  90. 1038 0
      vender/github.com/astaxie/beego/logs/alils/log.pb.go
  91. 42 0
      vender/github.com/astaxie/beego/logs/alils/log_config.go
  92. 819 0
      vender/github.com/astaxie/beego/logs/alils/log_project.go
  93. 271 0
      vender/github.com/astaxie/beego/logs/alils/log_store.go
  94. 91 0
      vender/github.com/astaxie/beego/logs/alils/machine_group.go
  95. 62 0
      vender/github.com/astaxie/beego/logs/alils/request.go
  96. 111 0
      vender/github.com/astaxie/beego/logs/alils/signature.go
  97. 28 0
      vender/github.com/astaxie/beego/logs/color.go
  98. 428 0
      vender/github.com/astaxie/beego/logs/color_windows.go
  99. 294 0
      vender/github.com/astaxie/beego/logs/color_windows_test.go
  100. 117 0
      vender/github.com/astaxie/beego/logs/conn.go

+ 93 - 142
bridge/bridge.go

@@ -14,7 +14,6 @@ import (
 	"github.com/cnlh/nps/server/tool"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
-	"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
 	"net"
 	"os"
 	"strconv"
@@ -28,7 +27,6 @@ type Client struct {
 	signal    *conn.Conn
 	file      *mux.Mux
 	retryTime int // it will be add 1 when ping not ok until to 3 will close the client
-	sync.RWMutex
 }
 
 func NewClient(t, f *mux.Mux, s *conn.Conn) *Client {
@@ -40,56 +38,38 @@ func NewClient(t, f *mux.Mux, s *conn.Conn) *Client {
 }
 
 type Bridge struct {
-	TunnelPort   int //通信隧道端口
-	Client       map[int]*Client
-	tunnelType   string //bridge type kcp or tcp
-	OpenTask     chan *file.Tunnel
-	CloseTask    chan *file.Tunnel
-	CloseClient  chan int
-	SecretChan   chan *conn.Secret
-	clientLock   sync.RWMutex
-	Register     map[string]time.Time
-	registerLock sync.RWMutex
-	ipVerify     bool
-	runList      map[int]interface{}
+	TunnelPort  int //通信隧道端口
+	Client      sync.Map
+	Register    sync.Map
+	tunnelType  string //bridge type kcp or tcp
+	OpenTask    chan *file.Tunnel
+	CloseTask   chan *file.Tunnel
+	CloseClient chan int
+	SecretChan  chan *conn.Secret
+	ipVerify    bool
+	runList     map[int]interface{}
 }
 
 func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList map[int]interface{}) *Bridge {
-	t := new(Bridge)
-	t.TunnelPort = tunnelPort
-	t.Client = make(map[int]*Client)
-	t.tunnelType = tunnelType
-	t.OpenTask = make(chan *file.Tunnel)
-	t.CloseTask = make(chan *file.Tunnel)
-	t.CloseClient = make(chan int)
-	t.Register = make(map[string]time.Time)
-	t.ipVerify = ipVerify
-	t.runList = runList
-	t.SecretChan = make(chan *conn.Secret)
-	return t
+	return &Bridge{
+		TunnelPort:  tunnelPort,
+		tunnelType:  tunnelType,
+		OpenTask:    make(chan *file.Tunnel),
+		CloseTask:   make(chan *file.Tunnel),
+		CloseClient: make(chan int),
+		SecretChan:  make(chan *conn.Secret),
+		ipVerify:    ipVerify,
+		runList:     runList,
+	}
 }
 
 func (s *Bridge) StartTunnel() error {
 	go s.ping()
 	if s.tunnelType == "kcp" {
-		listener, err := kcp.ListenWithOptions(beego.AppConfig.String("bridge_ip")+":"+beego.AppConfig.String("bridge_port"), nil, 150, 3)
-		if err != nil {
-			logs.Error(err)
-			os.Exit(0)
-			return err
-		}
 		logs.Info("server start, the bridge type is %s, the bridge port is %d", s.tunnelType, s.TunnelPort)
-		go func() {
-			for {
-				c, err := listener.AcceptKCP()
-				conn.SetUdpSession(c)
-				if err != nil {
-					logs.Warn(err)
-					continue
-				}
-				go s.cliProcess(conn.NewConn(c))
-			}
-		}()
+		return conn.NewKcpListenerAndProcess(beego.AppConfig.String("bridge_ip")+":"+beego.AppConfig.String("bridge_port"), func(c net.Conn) {
+			s.cliProcess(conn.NewConn(c))
+		})
 	} else {
 		listener, err := connection.GetBridgeListener(s.tunnelType)
 		if err != nil {
@@ -97,16 +77,9 @@ func (s *Bridge) StartTunnel() error {
 			os.Exit(0)
 			return err
 		}
-		go func() {
-			for {
-				c, err := listener.Accept()
-				if err != nil {
-					logs.Warn(err)
-					continue
-				}
-				go s.cliProcess(conn.NewConn(c))
-			}
-		}()
+		conn.Accept(listener, func(c net.Conn) {
+			s.cliProcess(conn.NewConn(c))
+		})
 	}
 	return nil
 }
@@ -115,10 +88,10 @@ func (s *Bridge) StartTunnel() error {
 func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) {
 	for {
 		if info, status, err := c.GetHealthInfo(); err != nil {
-			logs.Error(err)
 			break
 		} else if !status { //the status is true , return target to the targetArr
-			for _, v := range file.GetCsvDb().Tasks {
+			file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+				v := value.(*file.Tunnel)
 				if v.Client.Id == id && v.Mode == "tcp" && strings.Contains(v.Target, info) {
 					v.Lock()
 					if v.TargetArr == nil || (len(v.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) {
@@ -131,8 +104,10 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) {
 					v.HealthRemoveArr = append(v.HealthRemoveArr, info)
 					v.Unlock()
 				}
-			}
-			for _, v := range file.GetCsvDb().Hosts {
+				return true
+			})
+			file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+				v := value.(*file.Host)
 				if v.Client.Id == id && strings.Contains(v.Target, info) {
 					v.Lock()
 					if v.TargetArr == nil || (len(v.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) {
@@ -145,26 +120,33 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) {
 					v.HealthRemoveArr = append(v.HealthRemoveArr, info)
 					v.Unlock()
 				}
-			}
+				return true
+			})
 		} else { //the status is false,remove target from the targetArr
-			for _, v := range file.GetCsvDb().Tasks {
+			file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+				v := value.(*file.Tunnel)
 				if v.Client.Id == id && v.Mode == "tcp" && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.TargetArr, info) {
 					v.Lock()
 					v.TargetArr = append(v.TargetArr, info)
 					v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info)
 					v.Unlock()
 				}
-			}
-			for _, v := range file.GetCsvDb().Hosts {
+				return true
+			})
+
+			file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+				v := value.(*file.Host)
 				if v.Client.Id == id && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.TargetArr, info) {
 					v.Lock()
 					v.TargetArr = append(v.TargetArr, info)
 					v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info)
 					v.Unlock()
 				}
-			}
+				return true
+			})
 		}
 	}
+	s.DelClient(id, )
 }
 
 //验证失败,返回错误验证flag,并且关闭连接
@@ -216,17 +198,15 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
 	return
 }
 
-func (s *Bridge) DelClient(id int, isOther bool) {
-	s.clientLock.Lock()
-	defer s.clientLock.Unlock()
-	if v, ok := s.Client[id]; ok {
+func (s *Bridge) DelClient(id int) {
+	if v, ok := s.Client.Load(id); ok {
 		if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore {
 			s.CloseClient <- c.Id
 		}
-		if v.signal != nil {
-			v.signal.Close()
+		if v.(*Client).signal != nil {
+			v.(*Client).signal.Close()
 		}
-		delete(s.Client, id)
+		s.Client.Delete(id)
 	}
 }
 
@@ -235,35 +215,22 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 	switch typeVal {
 	case common.WORK_MAIN:
 		//the vKey connect by another ,close the client of before
-		s.clientLock.Lock()
-		if v, ok := s.Client[id]; ok {
-			s.clientLock.Unlock()
-			if v.signal != nil {
-				v.signal.WriteClose()
+		if v, ok := s.Client.LoadOrStore(id, NewClient(nil, nil, c)); ok {
+			if v.(*Client).signal != nil {
+				v.(*Client).signal.WriteClose()
 			}
-			v.Lock()
-			v.signal = c
-			v.Unlock()
-		} else {
-			s.Client[id] = NewClient(nil, nil, c)
-			s.clientLock.Unlock()
+			v.(*Client).signal = c
 		}
 		go s.GetHealthFromClient(id, c)
 		logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
 	case common.WORK_CHAN:
-		s.clientLock.Lock()
-		if v, ok := s.Client[id]; ok {
-			s.clientLock.Unlock()
-			v.Lock()
-			v.tunnel = mux.NewMux(c.Conn, s.tunnelType)
-			v.Unlock()
-		} else {
-			s.Client[id] = NewClient(mux.NewMux(c.Conn, s.tunnelType), nil, nil)
-			s.clientLock.Unlock()
+		muxConn := mux.NewMux(c.Conn, s.tunnelType)
+		if v, ok := s.Client.LoadOrStore(id, NewClient(muxConn, nil, nil)); ok {
+			v.(*Client).tunnel = muxConn
 		}
 	case common.WORK_CONFIG:
 		var isPub bool
-		client, err := file.GetCsvDb().GetClient(id);
+		client, err := file.GetCsvDb().GetClient(id)
 		if err == nil {
 			if client.VerifyKey == beego.AppConfig.String("public_vkey") {
 				isPub = true
@@ -271,6 +238,10 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 				isPub = false
 			}
 		}
+		if !isPub && !client.ConfigConnAllow {
+			c.Close()
+			return
+		}
 		binary.Write(c, binary.LittleEndian, isPub)
 		go s.getConfig(c, isPub, client)
 	case common.WORK_REGISTER:
@@ -280,15 +251,9 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 			s.SecretChan <- conn.NewSecret(string(b), c)
 		}
 	case common.WORK_FILE:
-		s.clientLock.Lock()
-		if v, ok := s.Client[id]; ok {
-			s.clientLock.Unlock()
-			v.Lock()
-			v.file = mux.NewMux(c.Conn, s.tunnelType)
-			v.Unlock()
-		} else {
-			s.Client[id] = NewClient(nil, mux.NewMux(c.Conn, s.tunnelType), nil)
-			s.clientLock.Unlock()
+		muxConn := mux.NewMux(c.Conn, s.tunnelType)
+		if v, ok := s.Client.LoadOrStore(id, NewClient(nil, muxConn, nil)); ok {
+			v.(*Client).file = muxConn
 		}
 	case common.WORK_P2P:
 		//read md5 secret
@@ -297,21 +262,18 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 		} else if t := file.GetCsvDb().GetTaskByMd5Password(string(b)); t == nil {
 			return
 		} else {
-			s.clientLock.Lock()
-			if v, ok := s.Client[t.Client.Id]; !ok {
-				s.clientLock.Unlock()
+			if v, ok := s.Client.Load(t.Client.Id); !ok {
 				return
 			} else {
-				s.clientLock.Unlock()
 				//向密钥对应的客户端发送与服务端udp建立连接信息,地址,密钥
-				v.signal.Write([]byte(common.NEW_UDP_CONN))
+				v.(*Client).signal.Write([]byte(common.NEW_UDP_CONN))
 				svrAddr := beego.AppConfig.String("p2p_ip") + ":" + beego.AppConfig.String("p2p_port")
 				if err != nil {
 					logs.Warn("get local udp addr error")
 					return
 				}
-				v.signal.WriteLenContent([]byte(svrAddr))
-				v.signal.WriteLenContent(b)
+				v.(*Client).signal.WriteLenContent([]byte(svrAddr))
+				v.(*Client).signal.WriteLenContent(b)
 				//向该请求者发送建立连接请求,服务器地址
 				c.WriteLenContent([]byte(svrAddr))
 			}
@@ -325,46 +287,36 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 func (s *Bridge) register(c *conn.Conn) {
 	var hour int32
 	if err := binary.Read(c, binary.LittleEndian, &hour); err == nil {
-		s.registerLock.Lock()
-		s.Register[common.GetIpByAddr(c.Conn.RemoteAddr().String())] = time.Now().Add(time.Hour * time.Duration(hour))
-		s.registerLock.Unlock()
+		s.Register.Store(common.GetIpByAddr(c.Conn.RemoteAddr().String()), time.Now().Add(time.Hour*time.Duration(hour)))
 	}
 }
 
 func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string, t *file.Tunnel) (target net.Conn, err error) {
-	s.clientLock.Lock()
-	if v, ok := s.Client[clientId]; ok {
-		s.clientLock.Unlock()
-
+	if v, ok := s.Client.Load(clientId); ok {
 		//If ip is restricted to do ip verification
 		if s.ipVerify {
-			s.registerLock.Lock()
 			ip := common.GetIpByAddr(linkAddr)
-			if v, ok := s.Register[ip]; !ok {
-				s.registerLock.Unlock()
+			if v, ok := s.Register.Load(ip); !ok {
 				return nil, errors.New(fmt.Sprintf("The ip %s is not in the validation list", ip))
 			} else {
-				s.registerLock.Unlock()
-				if !v.After(time.Now()) {
+				if !v.(time.Time).After(time.Now()) {
 					return nil, errors.New(fmt.Sprintf("The validity of the ip %s has expired", ip))
 				}
 			}
 		}
 		var tunnel *mux.Mux
 		if t != nil && t.Mode == "file" {
-			tunnel = v.file
+			tunnel = v.(*Client).file
 		} else {
-			tunnel = v.tunnel
+			tunnel = v.(*Client).tunnel
 		}
 		if tunnel == nil {
 			err = errors.New("the client connect error")
 			return
 		}
-
 		if target, err = tunnel.NewConn(); err != nil {
 			return
 		}
-
 		if t != nil && t.Mode == "file" {
 			return
 		}
@@ -374,7 +326,6 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string, t
 			return
 		}
 	} else {
-		s.clientLock.Unlock()
 		err = errors.New(fmt.Sprintf("the client %d is not connect", clientId))
 	}
 	return
@@ -385,24 +336,24 @@ func (s *Bridge) ping() {
 	for {
 		select {
 		case <-ticker.C:
-			s.clientLock.Lock()
 			arr := make([]int, 0)
-			for k, v := range s.Client {
+			s.Client.Range(func(key, value interface{}) bool {
+				v := value.(*Client)
 				if v.tunnel == nil || v.signal == nil {
 					v.retryTime += 1
 					if v.retryTime >= 3 {
-						arr = append(arr, k)
+						arr = append(arr, key.(int))
 					}
-					continue
+					return true
 				}
 				if v.tunnel.IsClose {
-					arr = append(arr, k)
+					arr = append(arr, key.(int))
 				}
-			}
-			s.clientLock.Unlock()
+				return true
+			})
 			for _, v := range arr {
 				logs.Info("the client %d closed", v)
-				s.DelClient(v, false)
+				s.DelClient(v)
 			}
 		}
 	}
@@ -427,18 +378,20 @@ loop:
 				if err != nil {
 					break loop
 				}
-				file.GetCsvDb().Lock()
-				for _, v := range file.GetCsvDb().Hosts {
+				file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+					v := value.(*file.Host)
 					if v.Client.Id == id {
 						str += v.Remark + common.CONN_DATA_SEQ
 					}
-				}
-				for _, v := range file.GetCsvDb().Tasks {
+					return true
+				})
+				file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+					v := value.(*file.Tunnel)
 					if _, ok := s.runList[v.Id]; ok && v.Client.Id == id {
 						str += v.Remark + common.CONN_DATA_SEQ
 					}
-				}
-				file.GetCsvDb().Unlock()
+					return true
+				})
 				binary.Write(c, binary.LittleEndian, int32(len([]byte(str))))
 				binary.Write(c, binary.LittleEndian, []byte(str))
 			}
@@ -456,9 +409,7 @@ loop:
 				}
 				c.WriteAddOk()
 				c.Write([]byte(client.VerifyKey))
-				s.clientLock.Lock()
-				s.Client[client.Id] = NewClient(nil, nil, nil)
-				s.clientLock.Unlock()
+				s.Client.Store(client.Id, NewClient(nil, nil, nil))
 			}
 		case common.NEW_HOST:
 			h, err := c.GetHostInfo()
@@ -518,7 +469,7 @@ loop:
 							tl.Target = strconv.Itoa(targets[i])
 						}
 					}
-					tl.Id = file.GetCsvDb().GetTaskId()
+					tl.Id = int(file.GetCsvDb().GetTaskId())
 					tl.Status = true
 					tl.Flow = new(file.Flow)
 					tl.NoStore = true
@@ -547,7 +498,7 @@ loop:
 		}
 	}
 	if fail && client != nil {
-		s.DelClient(client.Id, false)
+		s.DelClient(client.Id)
 	}
 	c.Close()
 }

+ 6 - 6
client/client.go

@@ -49,12 +49,6 @@ retry:
 	s.processor(c)
 }
 
-func (s *TRPClient) Close() {
-	s.tunnel.Close()
-	s.signal.Close()
-	s.ticker.Stop()
-}
-
 //处理
 func (s *TRPClient) processor(c *conn.Conn) {
 	s.signal = c
@@ -232,3 +226,9 @@ loop:
 		}
 	}
 }
+
+func (s *TRPClient) Close() {
+	s.tunnel.Close()
+	s.signal.Close()
+	s.ticker.Stop()
+}

+ 0 - 2
client/control.go

@@ -31,11 +31,9 @@ func GetTaskStatus(path string) {
 	if err != nil {
 		log.Fatalln(err)
 	}
-
 	if _, err := c.Write([]byte(common.WORK_STATUS)); err != nil {
 		log.Fatalln(err)
 	}
-
 	//read now vKey and write to server
 	if f, err := common.ReadAllFromFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt")); err != nil {
 		log.Fatalln(err)

+ 4 - 13
client/local.go

@@ -11,7 +11,6 @@ import (
 	"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
 	"net"
 	"net/http"
-	"strings"
 )
 
 var LocalServer []*net.TCPListener
@@ -51,21 +50,13 @@ func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error
 	}
 	LocalServer = append(LocalServer, listener)
 	logs.Info("successful start-up of local monitoring, port", l.Port)
-	for {
-		c, err := listener.AcceptTCP()
-		if err != nil {
-			if strings.Contains(err.Error(), "use of closed network connection") {
-				break
-			}
-			logs.Info(err)
-			continue
-		}
+	conn.Accept(listener, func(c net.Conn) {
 		if l.Type == "secret" {
-			go processSecret(c, config, l)
+			processSecret(c, config, l)
 		} else {
-			go processP2P(c, config, l)
+			processP2P(c, config, l)
 		}
-	}
+	})
 	return nil
 }
 

+ 4 - 0
cmd/nps/nps.go

@@ -3,6 +3,7 @@ package main
 import (
 	"flag"
 	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/lib/crypt"
 	"github.com/cnlh/nps/lib/daemon"
 	"github.com/cnlh/nps/lib/file"
 	"github.com/cnlh/nps/lib/install"
@@ -10,6 +11,7 @@ import (
 	"github.com/cnlh/nps/server"
 	"github.com/cnlh/nps/server/connection"
 	"github.com/cnlh/nps/server/test"
+	"github.com/cnlh/nps/server/tool"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
 	_ "github.com/cnlh/nps/web/routers"
@@ -60,5 +62,7 @@ func main() {
 	}
 	logs.Info("the version of server is %s ,allow client version to be %s", version.VERSION, version.GetVersion())
 	connection.InitConnectionService()
+	crypt.InitTls(filepath.Join(beego.AppPath, "conf", "server.pem"), filepath.Join(beego.AppPath, "conf", "server.key"))
+	tool.InitAllowPort()
 	server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"))
 }

+ 4 - 2
conf/clients.csv

@@ -1,2 +1,4 @@
-2,corjmrbhr33otit1,,true,,,0,false,0,0,0
-5,2dyy78gj7b9zw09l,,true,,,1,false,0,0,0
+8,sl6ytyj7lv5224zr,,true,,,0,false,0,0,0,0
+2,sqk6hfw7qirdmuep,,true,,,1,false,0,0,0,0
+11,1hh13x99zrw90lg7,,true,,,0,false,0,0,0,1
+5,phmgbeyns2yvtl6m,,true,,,0,false,0,0,0,1

+ 3 - 3
conf/hosts.csv

@@ -1,3 +1,3 @@
-c.o.com,10.1.50.196:4000,5,,,,/,2,7543392,22379,all
-a.o.com,127.0.0.1:8080,2,,,,/,3,0,0,all
-b.o.com,127.0.0.1:8082,5,,,,/,4,0,0,all
+a.o.com,127.0.0.1:8080,2,,,,/,1,0,60307,all
+c.o.com,127.0.0.1:8082,2,,,,/,2,0,0,http
+c.o.com,127.0.0.1:8082,2,,,,/,3,0,0,https

+ 10 - 17
conf/npc.conf

@@ -1,11 +1,8 @@
 [common]
 server=127.0.0.1:8024
 tp=tcp
-vkey=2dyy78gj7b9zw09l
+vkey=123
 auto_reconnection=true
-[web]
-host=b.o.com
-target=10.1.50.203:80
 
 [health_check_test1]
 health_check_timeout=1
@@ -21,7 +18,9 @@ health_check_max_failed=3
 health_check_interval=1
 health_check_type=tcp
 health_check_target=127.0.0.1:8083,127.0.0.1:8082
-
+[web]
+host=b.o.com
+target=127.0.0.1:8080
 
 [tcp]
 mode=tcp
@@ -40,26 +39,20 @@ port=9004
 
 [file]
 mode=file
-port=9100
+port=9009
 local_path=./
 strip_pre=/web/
 
-[s_ssh]
-mode=secret
-password=1234
-target=123.206.77.88:22
-
 [udp]
 mode=udp
 port=53
 target=114.114.114.114:53
 
-[p2p_ssh]
+[secret_ssh]
 port=2001
-password=p2pssh
-target=123.206.77.88:22
-
+password=sec
 
-[secret_ssh]
+[p2p_ssh]
 port=2002
-password=1234
+password=c
+target=123.206.77.88:22

+ 6 - 7
conf/nps.conf

@@ -1,17 +1,15 @@
 appname = nps
 #Boot mode(dev|pro)
-runmode = dev
-
-
+runmode = pro
 
 #HTTP(S) proxy port, no startup if empty
 http_proxy_port=80
-#https_proxy_port=443
+https_proxy_port=443
 https_just_proxy=true
 #certFile absolute path
-pem_path=conf/server.pem
+#pem_path=conf/server.pem
 #KeyFile absolute path
-key_path=conf/server.key
+#key_path=conf/server.key
 
 ##bridge
 bridge_type=tcp
@@ -42,7 +40,8 @@ web_username=admin
 web_password=123
 web_port = 8080
 web_ip=0.0.0.0
-#Web API unauthenticated IP address
+
+#Web API unauthenticated IP address(the len of auth_crypt_key must be 16)
 auth_key=test
 auth_crypt_key =1234567812345678
 

+ 3 - 5
conf/tasks.csv

@@ -1,5 +1,3 @@
-8025,socks5,,1,1,2,,0,0,
-8026,httpProxy,,1,2,2,,0,0,
-9002,tcp,127.0.0.1:8082,1,3,2,,0,0,
-9003,socks5,,1,5,5,,0,0,
-9009,tcp,127.0.0.1:8082,1,21,5,,8244480,2382592,
+0,p2p,,1,25,5,,0,0,p2ptest
+0,p2p,,1,1,2,,0,0,c
+0,secret,123.206.77.88:22,1,11,2,sec,0,0,sec

+ 11 - 0
lib/common/util.go

@@ -13,8 +13,10 @@ import (
 	"net/http"
 	"os"
 	"regexp"
+	"sort"
 	"strconv"
 	"strings"
+	"sync"
 )
 
 //Get the corresponding IP address through domain name
@@ -344,3 +346,12 @@ func BytesToNum(b []byte) int {
 	x, _ := strconv.Atoi(str)
 	return int(x)
 }
+
+func GetMapKeys(m sync.Map) (keys []int) {
+	m.Range(func(key, value interface{}) bool {
+		keys = append(keys, key.(int))
+		return true
+	})
+	sort.Ints(keys)
+	return
+}

+ 6 - 1
lib/config/config.go

@@ -146,7 +146,12 @@ func dealCommon(s string) *CommonConfig {
 func dealHost(s string) *file.Host {
 	h := &file.Host{}
 	var headerChange string
-	for _, v := range strings.Split(s, "\n") {
+	var configDataArr []string
+	configDataArr = strings.Split(s, "\n")
+	if len(configDataArr) < 3 {
+		configDataArr = strings.Split(s, "\r\n")
+	}
+	for _, v := range configDataArr {
 		item := strings.Split(v, "=")
 		if len(item) == 0 {
 			continue

+ 2 - 4
lib/conn/conn.go

@@ -23,8 +23,6 @@ import (
 	"time"
 )
 
-const cryptKey = "1234567812345678"
-
 type Conn struct {
 	Conn net.Conn
 	sync.Mutex
@@ -246,7 +244,7 @@ func (s *Conn) GetHostInfo() (h *file.Host, err error) {
 	} else {
 		arr := strings.Split(string(buf[:l]), common.CONN_DATA_SEQ)
 		h = new(file.Host)
-		h.Id = file.GetCsvDb().GetHostId()
+		h.Id = int(file.GetCsvDb().GetHostId())
 		h.Host = arr[0]
 		h.Target = arr[1]
 		h.HeaderChange = arr[2]
@@ -339,7 +337,7 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
 		t.Mode = arr[0]
 		t.Ports = arr[1]
 		t.Target = arr[2]
-		t.Id = file.GetCsvDb().GetTaskId()
+		t.Id = int(file.GetCsvDb().GetTaskId())
 		t.Status = true
 		t.Flow = new(file.Flow)
 		t.Remark = arr[3]

+ 50 - 0
lib/conn/listener.go

@@ -0,0 +1,50 @@
+package conn
+
+import (
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+	"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
+	"net"
+	"strings"
+)
+
+func NewTcpListenerAndProcess(addr string, f func(c net.Conn), listener *net.Listener) error {
+	var err error
+	*listener, err = net.Listen("tcp", addr)
+	if err != nil {
+		return err
+	}
+	Accept(*listener, f)
+	return nil
+}
+
+func NewKcpListenerAndProcess(addr string, f func(c net.Conn)) error {
+	kcpListener, err := kcp.ListenWithOptions(addr, nil, 150, 3)
+	if err != nil {
+		logs.Error(err)
+		return err
+	}
+	for {
+		c, err := kcpListener.AcceptKCP()
+		SetUdpSession(c)
+		if err != nil {
+			logs.Warn(err)
+			continue
+		}
+		go f(c)
+	}
+	return nil
+}
+
+func Accept(l net.Listener, f func(c net.Conn)) {
+	for {
+		c, err := l.Accept()
+		if err != nil {
+			if strings.Contains(err.Error(), "use of closed network connection") {
+				break
+			}
+			logs.Warn(err)
+			continue
+		}
+		go f(c)
+	}
+}

+ 8 - 3
lib/crypt/tls.go

@@ -2,15 +2,20 @@ package crypt
 
 import (
 	"crypto/tls"
-	"github.com/cnlh/nps/vender/github.com/astaxie/beego"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
 	"net"
 	"os"
-	"path/filepath"
 )
 
+var pemPath, keyPath string
+
+func InitTls(pem, key string) {
+	pemPath = pem
+	keyPath = key
+}
+
 func NewTlsServerConn(conn net.Conn) net.Conn {
-	cert, err := tls.LoadX509KeyPair(filepath.Join(beego.AppPath, "conf", "server.pem"), filepath.Join(beego.AppPath, "conf", "server.key"))
+	cert, err := tls.LoadX509KeyPair(pemPath, keyPath)
 	if err != nil {
 		logs.Error(err)
 		os.Exit(0)

+ 183 - 224
lib/file/file.go

@@ -15,6 +15,7 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"sync/atomic"
 )
 
 func NewCsv(runPath string) *Csv {
@@ -24,15 +25,14 @@ func NewCsv(runPath string) *Csv {
 }
 
 type Csv struct {
-	Tasks            []*Tunnel
-	Path             string
-	Hosts            []*Host   //域名列表
-	Clients          []*Client //客户端
-	RunPath          string    //存储根目录
-	ClientIncreaseId int       //客户端id
-	TaskIncreaseId   int       //任务自增ID
-	HostIncreaseId   int
-	sync.RWMutex
+	Tasks            sync.Map
+	Hosts            sync.Map //域名列表
+	HostsTmp         sync.Map
+	Clients          sync.Map //客户端
+	RunPath          string   //存储根目录
+	ClientIncreaseId int32    //客户端id
+	TaskIncreaseId   int32    //任务自增ID
+	HostIncreaseId   int32    //host increased id
 }
 
 func (s *Csv) StoreTasksToCsv() {
@@ -43,10 +43,10 @@ func (s *Csv) StoreTasksToCsv() {
 	}
 	defer csvFile.Close()
 	writer := csv.NewWriter(csvFile)
-	s.Lock()
-	for _, task := range s.Tasks {
+	s.Tasks.Range(func(key, value interface{}) bool {
+		task := value.(*Tunnel)
 		if task.NoStore {
-			continue
+			return true
 		}
 		record := []string{
 			strconv.Itoa(task.Port),
@@ -64,8 +64,8 @@ func (s *Csv) StoreTasksToCsv() {
 		if err != nil {
 			logs.Error(err.Error())
 		}
-	}
-	s.Unlock()
+		return true
+	})
 	writer.Flush()
 }
 
@@ -94,7 +94,6 @@ func (s *Csv) LoadTaskFromCsv() {
 		logs.Error("Profile Opening Error:", path)
 		os.Exit(0)
 	}
-	var tasks []*Tunnel
 	// 将每一行数据保存到内存slice中
 	for _, item := range records {
 		post := &Tunnel{
@@ -112,104 +111,79 @@ func (s *Csv) LoadTaskFromCsv() {
 		if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[5])); err != nil {
 			continue
 		}
-		tasks = append(tasks, post)
-		if post.Id > s.TaskIncreaseId {
-			s.TaskIncreaseId = post.Id
+		s.Tasks.Store(post.Id, post)
+		if post.Id > int(s.TaskIncreaseId) {
+			s.TaskIncreaseId = int32(s.TaskIncreaseId)
 		}
 	}
-	s.Tasks = tasks
-}
-
-func (s *Csv) GetTaskId() int {
-	s.Lock()
-	defer s.Unlock()
-	s.TaskIncreaseId++
-	return s.TaskIncreaseId
-}
-
-func (s *Csv) GetHostId() int {
-	s.Lock()
-	defer s.Unlock()
-	s.HostIncreaseId++
-	return s.HostIncreaseId
 }
 
-func (s *Csv) GetIdByVerifyKey(vKey string, addr string) (int, error) {
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Clients {
+func (s *Csv) GetIdByVerifyKey(vKey string, addr string) (id int, err error) {
+	var exist bool
+	s.Clients.Range(func(key, value interface{}) bool {
+		v := value.(*Client)
 		if common.Getverifyval(v.VerifyKey) == vKey && v.Status {
-			if arr := strings.Split(addr, ":"); len(arr) > 0 {
-				v.Addr = arr[0]
-			}
-			return v.Id, nil
+			v.Addr = common.GetIpByAddr(addr)
+			id = v.Id
+			exist = true
+			return false
 		}
+		return true
+	})
+	if exist {
+		return
 	}
 	return 0, errors.New("not found")
 }
 
-func (s *Csv) NewTask(t *Tunnel) error {
-	s.Lock()
-	for _, v := range s.Tasks {
+func (s *Csv) NewTask(t *Tunnel) (err error) {
+	s.Tasks.Range(func(key, value interface{}) bool {
+		v := value.(*Tunnel)
 		if (v.Mode == "secret" || v.Mode == "p2p") && v.Password == t.Password {
-			return errors.New(fmt.Sprintf("Secret mode keys %s must be unique", t.Password))
+			err = errors.New(fmt.Sprintf("Secret mode keys %s must be unique", t.Password))
+			return false
 		}
+		return true
+	})
+	if err != nil {
+		return
 	}
 	t.Flow = new(Flow)
-	s.Tasks = append(s.Tasks, t)
-	s.Unlock()
+	s.Tasks.Store(t.Id, t)
 	s.StoreTasksToCsv()
-	return nil
+	return
 }
 
 func (s *Csv) UpdateTask(t *Tunnel) error {
-	s.Lock()
-	for _, v := range s.Tasks {
-		if v.Id == t.Id {
-			s.Unlock()
-			s.StoreTasksToCsv()
-			return nil
-		}
-	}
-	s.Unlock()
-	return errors.New("the task is not exist")
+	s.Tasks.Store(t.Id, t)
+	s.StoreTasksToCsv()
+	return nil
 }
 
 func (s *Csv) DelTask(id int) error {
-	s.Lock()
-	for k, v := range s.Tasks {
-		if v.Id == id {
-			s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
-			s.Unlock()
-			s.StoreTasksToCsv()
-			return nil
-		}
-	}
-	s.Unlock()
-	return errors.New("不存在")
+	s.Tasks.Delete(id)
+	s.StoreTasksToCsv()
+	return nil
 }
 
 //md5 password
-func (s *Csv) GetTaskByMd5Password(p string) *Tunnel {
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Tasks {
-		if crypt.Md5(v.Password) == p {
-			return v
+func (s *Csv) GetTaskByMd5Password(p string) (t *Tunnel) {
+	s.Tasks.Range(func(key, value interface{}) bool {
+		if crypt.Md5(value.(*Tunnel).Password) == p {
+			t = value.(*Tunnel)
+			return false
 		}
-	}
-	return nil
+		return true
+	})
+	return
 }
 
-func (s *Csv) GetTask(id int) (v *Tunnel, err error) {
-	s.Lock()
-	defer s.Unlock()
-	for _, v = range s.Tasks {
-		if v.Id == id {
-			return
-		}
+func (s *Csv) GetTask(id int) (t *Tunnel, err error) {
+	if v, ok := s.Tasks.Load(id); ok {
+		t = v.(*Tunnel)
+		return
 	}
-	err = errors.New("未找到")
+	err = errors.New("not found")
 	return
 }
 
@@ -224,11 +198,10 @@ func (s *Csv) StoreHostToCsv() {
 	writer := csv.NewWriter(csvFile)
 	// 将map中的Post转换成slice,因为csv的Write需要slice参数
 	// 并写入csv文件
-	s.Lock()
-	defer s.Unlock()
-	for _, host := range s.Hosts {
+	s.Hosts.Range(func(key, value interface{}) bool {
+		host := value.(*Host)
 		if host.NoStore {
-			continue
+			return true
 		}
 		record := []string{
 			host.Host,
@@ -247,7 +220,9 @@ func (s *Csv) StoreHostToCsv() {
 		if err1 != nil {
 			panic(err1)
 		}
-	}
+		return true
+	})
+
 	// 确保所有内存数据刷到csv文件
 	writer.Flush()
 }
@@ -259,7 +234,6 @@ func (s *Csv) LoadClientFromCsv() {
 		logs.Error("Profile Opening Error:", path)
 		os.Exit(0)
 	}
-	var clients []*Client
 	// 将每一行数据保存到内存slice中
 	for _, item := range records {
 		post := &Client{
@@ -276,8 +250,8 @@ func (s *Csv) LoadClientFromCsv() {
 			},
 			MaxConn: common.GetIntNoErrByStr(item[10]),
 		}
-		if post.Id > s.ClientIncreaseId {
-			s.ClientIncreaseId = post.Id
+		if post.Id > int(s.ClientIncreaseId) {
+			s.ClientIncreaseId = int32(post.Id)
 		}
 		if post.RateLimit > 0 {
 			post.Rate = rate.NewRate(int64(post.RateLimit * 1024))
@@ -285,9 +259,13 @@ func (s *Csv) LoadClientFromCsv() {
 		}
 		post.Flow = new(Flow)
 		post.Flow.FlowLimit = int64(common.GetIntNoErrByStr(item[9]))
-		clients = append(clients, post)
+		if len(item) >= 12 {
+			post.ConfigConnAllow = common.GetBoolByStr(item[11])
+		} else {
+			post.ConfigConnAllow = true
+		}
+		s.Clients.Store(post.Id, post)
 	}
-	s.Clients = clients
 }
 
 func (s *Csv) LoadHostFromCsv() {
@@ -297,7 +275,6 @@ func (s *Csv) LoadHostFromCsv() {
 		logs.Error("Profile Opening Error:", path)
 		os.Exit(0)
 	}
-	var hosts []*Host
 	// 将每一行数据保存到内存slice中
 	for _, item := range records {
 		post := &Host{
@@ -320,37 +297,40 @@ func (s *Csv) LoadHostFromCsv() {
 		} else {
 			post.Scheme = "all"
 		}
-		hosts = append(hosts, post)
-		if post.Id > s.HostIncreaseId {
-			s.HostIncreaseId = post.Id
+		s.Hosts.Store(post.Id, post)
+		if post.Id > int(s.HostIncreaseId) {
+			s.HostIncreaseId = int32(post.Id)
 		}
+		//store host to hostMap if the host url is none
 	}
-	s.Hosts = hosts
 }
 
 func (s *Csv) DelHost(id int) error {
-	s.Lock()
-	for k, v := range s.Hosts {
-		if v.Id == id {
-			s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...)
-			s.Unlock()
-			s.StoreHostToCsv()
-			return nil
-		}
-	}
-	s.Unlock()
-	return errors.New("不存在")
+	s.Hosts.Delete(id)
+	s.StoreHostToCsv()
+	return nil
+}
+
+func (s *Csv) GetMapLen(m sync.Map) int {
+	var c int
+	m.Range(func(key, value interface{}) bool {
+		c++
+		return true
+	})
+	return c
 }
 
 func (s *Csv) IsHostExist(h *Host) bool {
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Hosts {
+	var exist bool
+	s.Hosts.Range(func(key, value interface{}) bool {
+		v := value.(*Host)
 		if v.Host == h.Host && h.Location == v.Location && (v.Scheme == "all" || v.Scheme == h.Scheme) {
-			return true
+			exist = true
+			return false
 		}
-	}
-	return false
+		return true
+	})
+	return exist
 }
 
 func (s *Csv) NewHost(t *Host) error {
@@ -361,37 +341,27 @@ func (s *Csv) NewHost(t *Host) error {
 		t.Location = "/"
 	}
 	t.Flow = new(Flow)
-	s.Lock()
-	s.Hosts = append(s.Hosts, t)
-	s.Unlock()
+	s.Hosts.Store(t.Id, t)
 	s.StoreHostToCsv()
 	return nil
 }
 
-func (s *Csv) UpdateHost(t *Host) error {
-	s.Lock()
-	for _, v := range s.Hosts {
-		if v.Host == t.Host {
-			s.Unlock()
-			s.StoreHostToCsv()
-			return nil
-		}
-	}
-	s.Unlock()
-	return errors.New("不存在")
-}
-
-func (s *Csv) GetHost(start, length int, id int) ([]*Host, int) {
+func (s *Csv) GetHost(start, length int, id int, search string) ([]*Host, int) {
 	list := make([]*Host, 0)
 	var cnt int
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Hosts {
-		if id == 0 || v.Client.Id == id {
-			cnt++
-			if start--; start < 0 {
-				if length--; length > 0 {
-					list = append(list, v)
+	keys := common.GetMapKeys(s.Hosts)
+	for _, key := range keys {
+		if value, ok := s.Hosts.Load(key); ok {
+			v := value.(*Host)
+			if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.Host, search) || strings.Contains(v.Remark, search)) {
+				continue
+			}
+			if id == 0 || v.Client.Id == id {
+				cnt++
+				if start--; start < 0 {
+					if length--; length > 0 {
+						list = append(list, v)
+					}
 				}
 			}
 		}
@@ -400,17 +370,9 @@ func (s *Csv) GetHost(start, length int, id int) ([]*Host, int) {
 }
 
 func (s *Csv) DelClient(id int) error {
-	s.Lock()
-	for k, v := range s.Clients {
-		if v.Id == id {
-			s.Clients = append(s.Clients[:k], s.Clients[k+1:]...)
-			s.Unlock()
-			s.StoreClientsToCsv()
-			return nil
-		}
-	}
-	s.Unlock()
-	return errors.New("不存在")
+	s.Clients.Delete(id)
+	s.StoreClientsToCsv()
+	return nil
 }
 
 func (s *Csv) NewClient(c *Client) error {
@@ -427,106 +389,100 @@ reset:
 		return errors.New("Vkey duplicate, please reset")
 	}
 	if c.Id == 0 {
-		c.Id = s.GetClientId()
+		c.Id = int(s.GetClientId())
 	}
 	if c.Flow == nil {
 		c.Flow = new(Flow)
 	}
-	s.Lock()
-	s.Clients = append(s.Clients, c)
-	s.Unlock()
+	s.Clients.Store(c.Id, c)
 	s.StoreClientsToCsv()
 	return nil
 }
 
-func (s *Csv) VerifyVkey(vkey string, id int) bool {
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Clients {
+func (s *Csv) VerifyVkey(vkey string, id int) (res bool) {
+	res = true
+	s.Clients.Range(func(key, value interface{}) bool {
+		v := value.(*Client)
 		if v.VerifyKey == vkey && v.Id != id {
+			res = false
 			return false
 		}
-	}
-	return true
+		return true
+	})
+	return res
+}
+
+func (s *Csv) GetClientId() int32 {
+	return atomic.AddInt32(&s.ClientIncreaseId, 1)
+}
+
+func (s *Csv) GetTaskId() int32 {
+	return atomic.AddInt32(&s.TaskIncreaseId, 1)
 }
 
-func (s *Csv) GetClientId() int {
-	s.Lock()
-	defer s.Unlock()
-	s.ClientIncreaseId++
-	return s.ClientIncreaseId
+func (s *Csv) GetHostId() int32 {
+	return atomic.AddInt32(&s.HostIncreaseId, 1)
 }
 
 func (s *Csv) UpdateClient(t *Client) error {
-	s.Lock()
-	for _, v := range s.Clients {
-		if v.Id == t.Id {
-			v.Cnf = t.Cnf
-			v.VerifyKey = t.VerifyKey
-			v.Remark = t.Remark
-			v.RateLimit = t.RateLimit
-			v.Flow = t.Flow
-			v.Rate = t.Rate
-			s.Unlock()
-			s.StoreClientsToCsv()
-			return nil
-		}
-	}
-	s.Unlock()
-	return errors.New("该客户端不存在")
+	s.Clients.Store(t.Id, t)
+	return nil
 }
 
-func (s *Csv) GetClientList(start, length int) ([]*Client, int) {
+func (s *Csv) GetClientList(start, length int, search string) ([]*Client, int) {
 	list := make([]*Client, 0)
 	var cnt int
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Clients {
-		if v.NoDisplay {
-			continue
-		}
-		cnt++
-		if start--; start < 0 {
-			if length--; length > 0 {
-				list = append(list, v)
+	keys := common.GetMapKeys(s.Clients)
+	for _, key := range keys {
+		if value, ok := s.Clients.Load(key); ok {
+			v := value.(*Client)
+			if v.NoDisplay {
+				continue
+			}
+			if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.VerifyKey, search) || strings.Contains(v.Remark, search)) {
+				continue
+			}
+			cnt++
+			if start--; start < 0 {
+				if length--; length > 0 {
+					list = append(list, v)
+				}
 			}
 		}
 	}
 	return list, cnt
 }
 
-func (s *Csv) GetClient(id int) (v *Client, err error) {
-	s.Lock()
-	defer s.Unlock()
-	for _, v = range s.Clients {
-		if v.Id == id {
-			return
-		}
+func (s *Csv) GetClient(id int) (c *Client, err error) {
+	if v, ok := s.Clients.Load(id); ok {
+		c = v.(*Client)
+		return
 	}
 	err = errors.New("未找到客户端")
 	return
 }
 func (s *Csv) GetClientIdByVkey(vkey string) (id int, err error) {
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Clients {
+	var exist bool
+	s.Clients.Range(func(key, value interface{}) bool {
+		v := value.(*Client)
 		if crypt.Md5(v.VerifyKey) == vkey {
+			exist = true
 			id = v.Id
-			return
+			return false
 		}
+		return true
+	})
+	if exist {
+		return
 	}
 	err = errors.New("未找到客户端")
 	return
 }
 
 func (s *Csv) GetHostById(id int) (h *Host, err error) {
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Hosts {
-		if v.Id == id {
-			h = v
-			return
-		}
+	if v, ok := s.Hosts.Load(id); ok {
+		h = v.(*Host)
+		return
 	}
 	err = errors.New("The host could not be parsed")
 	return
@@ -537,24 +493,25 @@ func (s *Csv) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
 	var hosts []*Host
 	//Handling Ported Access
 	host = common.GetIpByAddr(host)
-	s.Lock()
-	defer s.Unlock()
-	for _, v := range s.Hosts {
+	s.Hosts.Range(func(key, value interface{}) bool {
+		v := value.(*Host)
 		if v.IsClose {
-			continue
+			return true
 		}
 		//Remove http(s) http(s)://a.proxy.com
 		//*.proxy.com *.a.proxy.com  Do some pan-parsing
 		tmp := strings.Replace(v.Host, "*", `\w+?`, -1)
 		var re *regexp.Regexp
 		if re, err = regexp.Compile(tmp); err != nil {
-			return
+			return true
 		}
 		if len(re.FindAllString(host, -1)) > 0 && (v.Scheme == "all" || v.Scheme == r.URL.Scheme) {
 			//URL routing
 			hosts = append(hosts, v)
 		}
-	}
+		return true
+	})
+
 	for _, v := range hosts {
 		//If not set, default matches all
 		if v.Location == "" {
@@ -572,6 +529,7 @@ func (s *Csv) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
 	err = errors.New("The host could not be parsed")
 	return
 }
+
 func (s *Csv) StoreClientsToCsv() {
 	// 创建文件
 	csvFile, err := os.Create(filepath.Join(s.RunPath, "conf", "clients.csv"))
@@ -580,11 +538,10 @@ func (s *Csv) StoreClientsToCsv() {
 	}
 	defer csvFile.Close()
 	writer := csv.NewWriter(csvFile)
-	s.Lock()
-	defer s.Unlock()
-	for _, client := range s.Clients {
+	s.Clients.Range(func(key, value interface{}) bool {
+		client := value.(*Client)
 		if client.NoStore {
-			continue
+			return true
 		}
 		record := []string{
 			strconv.Itoa(client.Id),
@@ -598,11 +555,13 @@ func (s *Csv) StoreClientsToCsv() {
 			strconv.Itoa(client.RateLimit),
 			strconv.Itoa(int(client.Flow.FlowLimit)),
 			strconv.Itoa(int(client.MaxConn)),
+			common.GetStrByBool(client.ConfigConnAllow),
 		}
 		err := writer.Write(record)
 		if err != nil {
 			logs.Error(err.Error())
 		}
-	}
+		return true
+	})
 	writer.Flush()
 }

+ 32 - 28
lib/file/obj.go

@@ -23,21 +23,22 @@ func (s *Flow) Add(in, out int64) {
 }
 
 type Client struct {
-	Cnf       *Config
-	Id        int        //id
-	VerifyKey string     //验证密钥
-	Addr      string     //客户端ip地址
-	Remark    string     //备注
-	Status    bool       //是否开启
-	IsConnect bool       //是否连接
-	RateLimit int        //速度限制 /kb
-	Flow      *Flow      //流量
-	Rate      *rate.Rate //速度控制
-	NoStore   bool
-	NoDisplay bool
-	MaxConn   int //客户端最大连接数
-	NowConn   int //当前连接数
-	id        int
+	Cnf             *Config
+	Id              int        //id
+	VerifyKey       string     //验证密钥
+	Addr            string     //客户端ip地址
+	Remark          string     //备注
+	Status          bool       //是否开启
+	IsConnect       bool       //是否连接
+	RateLimit       int        //速度限制 /kb
+	Flow            *Flow      //流量
+	Rate            *rate.Rate //速度控制
+	NoStore         bool
+	NoDisplay       bool
+	MaxConn         int //客户端最大连接数
+	NowConn         int //当前连接数
+	id              int
+	ConfigConnAllow bool
 	sync.RWMutex
 }
 
@@ -84,26 +85,29 @@ func (s *Client) ModifyTarget() {
 
 }
 
-func (s *Client) HasTunnel(t *Tunnel) bool {
-	GetCsvDb().Lock()
-	defer GetCsvDb().Unlock()
-	for _, v := range GetCsvDb().Tasks {
+func (s *Client) HasTunnel(t *Tunnel) (exist bool) {
+	GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+		v := value.(*Tunnel)
 		if v.Client.Id == s.Id && v.Port == t.Port {
-			return true
+			exist = true
+			return false
 		}
-	}
-	return false
+		return true
+	})
+	return
 }
 
 func (s *Client) HasHost(h *Host) bool {
-	GetCsvDb().Lock()
-	defer GetCsvDb().Unlock()
-	for _, v := range GetCsvDb().Hosts {
+	var has bool
+	GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+		v := value.(*Host)
 		if v.Client.Id == s.Id && v.Host == h.Host && h.Location == v.Location {
-			return true
+			has = true
+			return false
 		}
-	}
-	return false
+		return true
+	})
+	return has
 }
 
 type Tunnel struct {

+ 9 - 5
server/proxy/base.go

@@ -64,17 +64,19 @@ func (s *BaseServer) auth(r *http.Request, c *conn.Conn, u, p string) error {
 	return nil
 }
 
-func (s *BaseServer) checkFlow() error {
-	if s.task.Client.Flow.FlowLimit > 0 && (s.task.Client.Flow.FlowLimit<<20) < (s.task.Client.Flow.ExportFlow+s.task.Client.Flow.InletFlow) {
+func (s *BaseServer) CheckFlowAndConnNum(client *file.Client) error {
+	if client.Flow.FlowLimit > 0 && (client.Flow.FlowLimit<<20) < (client.Flow.ExportFlow+client.Flow.InletFlow) {
 		return errors.New("Traffic exceeded")
 	}
+	if !client.GetConn() {
+		return errors.New("Connections exceed the current client limit")
+	}
 	return nil
 }
 
 //与客户端建立通道
-func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string) error {
+func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string, f func()) error {
 	link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String())
-
 	if target, err := s.bridge.SendLinkInfo(client.Id, link, c.Conn.RemoteAddr().String(), s.task); err != nil {
 		logs.Warn("task id %d get connection from client id %d  error %s", s.task.Id, client.Id, err.Error())
 		c.Close()
@@ -84,9 +86,11 @@ func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string,
 			//HTTP proxy crypt or compress
 			conn.GetConn(target, link.Crypt, link.Compress, client.Rate, true).Write(rb)
 		}
+		if f != nil {
+			f()
+		}
 		conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, client.Rate, s.task.Flow, true)
 	}
-
 	client.AddConn()
 	return nil
 }

+ 44 - 49
server/proxy/http.go

@@ -28,8 +28,9 @@ type httpServer struct {
 	httpsPort     int //https监听端口
 	pemPath       string
 	keyPath       string
-	stop          chan bool
-	httpslistener net.Listener
+	httpServer    *http.Server
+	httpsServer   *http.Server
+	httpsListener net.Listener
 }
 
 func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer {
@@ -47,7 +48,6 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer {
 		httpsPort: httpsPort,
 		pemPath:   pemPath,
 		keyPath:   keyPath,
-		stop:      make(chan bool),
 	}
 }
 
@@ -58,13 +58,17 @@ func (s *httpServer) processHttps(c net.Conn) {
 		return
 	}
 	var host *file.Host
-	file.GetCsvDb().Lock()
-	for _, v := range file.GetCsvDb().Hosts {
+	file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+		v := value.(*file.Host)
+		if v.Scheme != "https" && v.Scheme != "all" {
+			return true
+		}
 		if bytes.Index(buf[:n], []byte(v.Host)) >= 0 && (host == nil || len(host.Host) < len(v.Host)) {
 			host = v
+			return false
 		}
-	}
-	file.GetCsvDb().Unlock()
+		return true
+	})
 	if host == nil {
 		logs.Error("new https connection can't be parsed!", c.RemoteAddr().String())
 		c.Close()
@@ -76,40 +80,37 @@ func (s *httpServer) processHttps(c net.Conn) {
 	r.URL = new(url.URL)
 	r.URL.Scheme = "https"
 	r.Host = host.Host
-	//read the host form connection
-	if !host.Client.GetConn() { //conn num limit
-		logs.Notice("connections exceed the current client %d limit %d ,now connection num %d", host.Client.Id, host.Client.MaxConn, host.Client.NowConn)
+	if err := s.CheckFlowAndConnNum(host.Client); err != nil {
+		logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error())
 		c.Close()
 		return
 	}
-	//流量限制
-	if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
-		logs.Warn("Traffic exceeded client id %s", host.Client.Id)
+	if err = s.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
+		logs.Warn("auth error", err, r.RemoteAddr)
 		return
 	}
 	if targetAddr, err = host.GetRandomTarget(); err != nil {
 		logs.Warn(err.Error())
 	}
 	logs.Trace("new https connection,clientId %d,host %s,remote address %s", host.Client.Id, r.Host, c.RemoteAddr().String())
-	s.DealClient(conn.NewConn(c), host.Client, targetAddr, buf[:n], common.CONN_TCP)
+	s.DealClient(conn.NewConn(c), host.Client, targetAddr, buf[:n], common.CONN_TCP, nil)
 }
 
 func (s *httpServer) Start() error {
 	var err error
-	var httpSrv, httpsSrv *http.Server
 	if s.errorContent, err = common.ReadAllFromFile(filepath.Join(common.GetRunPath(), "web", "static", "page", "error.html")); err != nil {
 		s.errorContent = []byte("easyProxy 404")
 	}
 
 	if s.httpPort > 0 {
-		httpSrv = s.NewServer(s.httpPort, "http")
+		s.httpServer = s.NewServer(s.httpPort, "http")
 		go func() {
 			l, err := connection.GetHttpListener()
 			if err != nil {
 				logs.Error(err)
 				os.Exit(0)
 			}
-			err = httpSrv.Serve(l)
+			err = s.httpServer.Serve(l)
 			if err != nil {
 				logs.Error(err)
 				os.Exit(0)
@@ -117,23 +118,16 @@ func (s *httpServer) Start() error {
 		}()
 	}
 	if s.httpsPort > 0 {
-		if !common.FileExists(s.pemPath) {
-			os.Exit(0)
-		}
-		if !common.FileExists(s.keyPath) {
-			logs.Error("ssl keyFile %s exist", s.keyPath)
-			os.Exit(0)
-		}
-		httpsSrv = s.NewServer(s.httpsPort, "https")
+		s.httpsServer = s.NewServer(s.httpsPort, "https")
 		go func() {
-			l, err := connection.GetHttpsListener()
+			s.httpsListener, err = connection.GetHttpsListener()
 			if err != nil {
 				logs.Error(err)
 				os.Exit(0)
 			}
 			if b, err := beego.AppConfig.Bool("https_just_proxy"); err == nil && b {
 				for {
-					c, err := l.Accept()
+					c, err := s.httpsListener.Accept()
 					if err != nil {
 						logs.Error(err)
 						break
@@ -141,7 +135,15 @@ func (s *httpServer) Start() error {
 					go s.processHttps(c)
 				}
 			} else {
-				err = httpsSrv.ServeTLS(l, s.pemPath, s.keyPath)
+				if !common.FileExists(s.pemPath) {
+					logs.Error("ssl certFile %s exist", s.keyPath)
+					os.Exit(0)
+				}
+				if !common.FileExists(s.keyPath) {
+					logs.Error("ssl keyFile %s exist", s.keyPath)
+					os.Exit(0)
+				}
+				err = s.httpsServer.ServeTLS(s.httpsListener, s.pemPath, s.keyPath)
 				if err != nil {
 					logs.Error(err)
 					os.Exit(0)
@@ -149,20 +151,19 @@ func (s *httpServer) Start() error {
 			}
 		}()
 	}
-	select {
-	case <-s.stop:
-		if httpSrv != nil {
-			httpsSrv.Close()
-		}
-		if httpsSrv != nil {
-			httpsSrv.Close()
-		}
-	}
 	return nil
 }
 
 func (s *httpServer) Close() error {
-	s.stop <- true
+	if s.httpsListener != nil {
+		s.httpsListener.Close()
+	}
+	if s.httpsServer != nil {
+		s.httpsServer.Close()
+	}
+	if s.httpServer != nil {
+		s.httpServer.Close()
+	}
 	return nil
 }
 
@@ -196,23 +197,17 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
 	if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil {
 		logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI)
 		goto end
-	} else if !host.Client.GetConn() { //conn num limit
-		logs.Notice("connections exceed the current client %d limit %d ,now connection num %d", host.Client.Id, host.Client.MaxConn, host.Client.NowConn)
+	}
+	if err := s.CheckFlowAndConnNum(host.Client); err != nil {
+		logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error())
 		c.Close()
 		return
-	} else {
-		logs.Trace("new %s connection,clientId %d,host %s,url %s,remote address %s", r.URL.Scheme, host.Client.Id, r.Host, r.URL, r.RemoteAddr)
-		lastHost = host
 	}
+	logs.Trace("new %s connection,clientId %d,host %s,url %s,remote address %s", r.URL.Scheme, host.Client.Id, r.Host, r.URL, r.RemoteAddr)
+	lastHost = host
 	for {
 	start:
 		if isConn {
-			//流量限制
-			if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
-				logs.Warn("Traffic exceeded client id %s", host.Client.Id)
-				break
-			}
-			//权限控制
 			if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
 				logs.Warn("auth error", err, r.RemoteAddr)
 				break

+ 4 - 17
server/proxy/p2p.go

@@ -3,8 +3,7 @@ package proxy
 import (
 	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/conn"
-	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
-	"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
+	"net"
 	"strconv"
 	"time"
 )
@@ -30,21 +29,9 @@ func NewP2PServer(p2pPort int) *P2PServer {
 }
 
 func (s *P2PServer) Start() error {
-	kcpListener, err := kcp.ListenWithOptions(":"+strconv.Itoa(s.p2pPort), nil, 150, 3)
-	if err != nil {
-		logs.Error(err)
-		return err
-	}
-	for {
-		c, err := kcpListener.AcceptKCP()
-		conn.SetUdpSession(c)
-		if err != nil {
-			logs.Warn(err)
-			continue
-		}
-		go s.p2pProcess(conn.NewConn(c))
-	}
-	return nil
+	return conn.NewKcpListenerAndProcess(":"+strconv.Itoa(s.p2pPort), func(c net.Conn) {
+		s.p2pProcess(conn.NewConn(c))
+	})
 }
 
 func (s *P2PServer) p2pProcess(c *conn.Conn) {

+ 15 - 42
server/proxy/socks5.go

@@ -11,7 +11,6 @@ import (
 	"io"
 	"net"
 	"strconv"
-	"strings"
 )
 
 const (
@@ -141,18 +140,9 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
 	} else {
 		ltype = common.CONN_TCP
 	}
-	//s.DealClient(conn.NewConn(c), addr, nil, ltype)
-	link := conn.NewLink(ltype, addr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.RemoteAddr().String())
-
-	if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.RemoteAddr().String(), s.task); err != nil {
-		c.Close()
-		return
-	} else {
+	s.DealClient(conn.NewConn(c), s.task.Client, addr, nil, ltype, func() {
 		s.sendReply(c, succeeded)
-		conn.CopyWaitGroup(target, c, link.Crypt, link.Compress, s.task.Client.Rate, s.task.Flow, true)
-	}
-
-	s.task.Client.AddConn()
+	})
 	return
 }
 
@@ -261,37 +251,15 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error {
 
 //start
 func (s *Sock5ModeServer) Start() error {
-	var err error
-	s.listener, err = net.Listen("tcp", ":"+strconv.Itoa(s.task.Port))
-	if err != nil {
-		return err
-	}
-	for {
-		conn, err := s.listener.Accept()
-		if err != nil {
-			if strings.Contains(err.Error(), "use of closed network connection") {
-				break
-			}
-			logs.Warn("accept error: ", err)
-		}
-		if err := s.checkFlow(); err != nil {
-			logs.Warn("client id %d  task id %d  error  %s", s.task.Client.Id, s.task.Id, err.Error())
-			conn.Close()
-		}
-		if s.task.Client.GetConn() {
-			logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, conn.RemoteAddr())
-			go s.handleConn(conn)
-		} else {
-			logs.Warn("Connections exceed the current client %d limit", s.task.Client.Id)
-			conn.Close()
+	return conn.NewTcpListenerAndProcess(":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
+		if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
+			logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error())
+			c.Close()
+			return
 		}
-	}
-	return nil
-}
-
-//close
-func (s *Sock5ModeServer) Close() error {
-	return s.listener.Close()
+		logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr())
+		s.handleConn(c)
+	}, &s.listener)
 }
 
 //new
@@ -301,3 +269,8 @@ func NewSock5ModeServer(bridge *bridge.Bridge, task *file.Tunnel) *Sock5ModeServ
 	s.task = task
 	return s
 }
+
+//close
+func (s *Sock5ModeServer) Close() error {
+	return s.listener.Close()
+}

+ 11 - 33
server/proxy/tcp.go

@@ -12,13 +12,13 @@ import (
 	"net"
 	"net/http"
 	"path/filepath"
-	"strings"
+	"strconv"
 )
 
 type TunnelModeServer struct {
 	BaseServer
 	process  process
-	listener *net.TCPListener
+	listener net.Listener
 }
 
 //tcp|http|host
@@ -32,33 +32,15 @@ func NewTunnelModeServer(process process, bridge *bridge.Bridge, task *file.Tunn
 
 //开始
 func (s *TunnelModeServer) Start() error {
-	var err error
-	s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.task.Port, ""})
-	if err != nil {
-		return err
-	}
-	for {
-		c, err := s.listener.AcceptTCP()
-		if err != nil {
-			if strings.Contains(err.Error(), "use of closed network connection") {
-				break
-			}
-			logs.Info(err)
-			continue
-		}
-		if err := s.checkFlow(); err != nil {
-			logs.Warn("client id %d  task id %d  error  %s", s.task.Client.Id, s.task.Id, err.Error())
+	return conn.NewTcpListenerAndProcess(":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
+		if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
+			logs.Warn("client id %d, task id %d,error %s, when tcp connection", s.task.Client.Id, s.task.Id, err.Error())
 			c.Close()
+			return
 		}
-		if s.task.Client.GetConn() {
-			logs.Trace("New tcp connection,local port %d,client %d,remote address %s", s.task.Port, s.task.Client.Id, c.RemoteAddr())
-			go s.process(conn.NewConn(c), s)
-		} else {
-			logs.Info("Connections exceed the current client %d limit", s.task.Client.Id)
-			c.Close()
-		}
-	}
-	return nil
+		logs.Trace("new tcp connection,local port %d,client %d,remote address %s", s.task.Port, s.task.Client.Id, c.RemoteAddr())
+		s.process(conn.NewConn(c), s)
+	}, &s.listener)
 }
 
 //close
@@ -78,10 +60,6 @@ func (s *WebServer) Start() error {
 		stop := make(chan struct{})
 		<-stop
 	}
-	//if !common.TestTcpPort(p) {
-	//	//	logs.Error("Web management port %d is occupied", p)
-	//	//	os.Exit(0)
-	//	//}
 	beego.BConfig.WebConfig.Session.SessionOn = true
 	beego.SetStaticPath("/static", filepath.Join(common.GetRunPath(), "web", "static"))
 	beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views"))
@@ -115,7 +93,7 @@ func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error {
 		logs.Warn("tcp port %d ,client id %d,task id %d connect error %s", s.task.Port, s.task.Client.Id, s.task.Id, err.Error())
 		return err
 	}
-	return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP)
+	return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP, nil)
 }
 
 //http代理模式
@@ -133,5 +111,5 @@ func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error {
 	if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil {
 		return err
 	}
-	return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP)
+	return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP, nil)
 }

+ 4 - 2
server/proxy/udp.go

@@ -46,10 +46,12 @@ func (s *UdpModeServer) Start() error {
 }
 
 func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
-	link := conn.NewLink(common.CONN_UDP, s.task.Target, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String())
-	if err := s.checkFlow(); err != nil {
+	if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
+		logs.Warn("client id %d, task id %d,error %s, when udp connection", s.task.Client.Id, s.task.Id, err.Error())
 		return
 	}
+	defer s.task.Client.AddConn()
+	link := conn.NewLink(common.CONN_UDP, s.task.Target, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String())
 	if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, addr.String(), s.task); err != nil {
 		return
 	} else {

+ 84 - 66
server/server.go

@@ -16,6 +16,7 @@ import (
 	"math"
 	"os"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -40,11 +41,12 @@ func InitFromCsv() {
 		RunList[c.Id] = nil
 	}
 	//Initialize services in server-side files
-	for _, v := range file.GetCsvDb().Tasks {
-		if v.Status {
-			AddTask(v)
+	file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+		if value.(*file.Tunnel).Status {
+			AddTask(value.(*file.Tunnel))
 		}
-	}
+		return true
+	})
 }
 
 func DealBridgeTask() {
@@ -66,7 +68,7 @@ func DealBridgeTask() {
 					logs.Info("Connections exceed the current client %d limit", t.Client.Id)
 					s.Conn.Close()
 				} else if t.Status {
-					go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target, nil, common.CONN_TCP)
+					go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target, nil, common.CONN_TCP, nil)
 				} else {
 					s.Conn.Close()
 					logs.Trace("This key %s cannot be processed,status is close", s.Password)
@@ -82,10 +84,12 @@ func DealBridgeTask() {
 //start a new server
 func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
 	Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ip_limit")), RunList)
-	if err := Bridge.StartTunnel(); err != nil {
-		logs.Error("start server bridge error", err)
-		os.Exit(0)
-	}
+	go func() {
+		if err := Bridge.StartTunnel(); err != nil {
+			logs.Error("start server bridge error", err)
+			os.Exit(0)
+		}
+	}()
 	if p, err := beego.AppConfig.Int("p2p_port"); err == nil {
 		logs.Info("start p2p server port", p)
 		go proxy.NewP2PServer(p).Start()
@@ -107,7 +111,7 @@ func dealClientFlow() {
 	for {
 		select {
 		case <-ticker.C:
-			dealClientData(file.GetCsvDb().Clients)
+			dealClientData()
 		}
 	}
 }
@@ -148,6 +152,8 @@ func StopServer(id int) error {
 				return err
 			}
 			logs.Info("stop server id %d", id)
+		} else {
+			logs.Warn("stop server id %d error", id)
 		}
 		if t, err := file.GetCsvDb().GetTask(id); err != nil {
 			return err
@@ -214,29 +220,34 @@ func DelTask(id int) error {
 }
 
 //get task list by page num
-func GetTunnel(start, length int, typeVal string, clientId int) ([]*file.Tunnel, int) {
+func GetTunnel(start, length int, typeVal string, clientId int, search string) ([]*file.Tunnel, int) {
 	list := make([]*file.Tunnel, 0)
 	var cnt int
-	file.GetCsvDb().Lock()
-	defer file.GetCsvDb().Unlock()
-	for _, v := range file.GetCsvDb().Tasks {
-		if (typeVal != "" && v.Mode != typeVal) || (typeVal == "" && clientId != v.Client.Id) {
-			continue
-		}
-		cnt++
-		if _, ok := Bridge.Client[v.Client.Id]; ok {
-			v.Client.IsConnect = true
-		} else {
-			v.Client.IsConnect = false
-		}
-		if start--; start < 0 {
-			if length--; length > 0 {
-				if _, ok := RunList[v.Id]; ok {
-					v.RunStatus = true
-				} else {
-					v.RunStatus = false
+	keys := common.GetMapKeys(file.GetCsvDb().Tasks)
+	for _, key := range keys {
+		if value, ok := file.GetCsvDb().Tasks.Load(key); ok {
+			v := value.(*file.Tunnel)
+			if (typeVal != "" && v.Mode != typeVal) || (typeVal == "" && clientId != v.Client.Id) {
+				continue
+			}
+			if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || v.Port == common.GetIntNoErrByStr(search) || strings.Contains(v.Password, search) || strings.Contains(v.Remark, search)) {
+				continue
+			}
+			cnt++
+			if _, ok := Bridge.Client.Load(v.Client.Id); ok {
+				v.Client.IsConnect = true
+			} else {
+				v.Client.IsConnect = false
+			}
+			if start--; start < 0 {
+				if length--; length > 0 {
+					if _, ok := RunList[v.Id]; ok {
+						v.RunStatus = true
+					} else {
+						v.RunStatus = false
+					}
+					list = append(list, v)
 				}
-				list = append(list, v)
 			}
 		}
 	}
@@ -244,60 +255,64 @@ func GetTunnel(start, length int, typeVal string, clientId int) ([]*file.Tunnel,
 }
 
 //获取客户端列表
-func GetClientList(start, length int) (list []*file.Client, cnt int) {
-	list, cnt = file.GetCsvDb().GetClientList(start, length)
-	dealClientData(list)
+func GetClientList(start, length int, search string) (list []*file.Client, cnt int) {
+	list, cnt = file.GetCsvDb().GetClientList(start, length, search)
+	dealClientData()
 	return
 }
 
-func dealClientData(list []*file.Client) {
-	file.GetCsvDb().Lock()
-	defer file.GetCsvDb().Unlock()
-	for _, v := range list {
-		if _, ok := Bridge.Client[v.Id]; ok {
+func dealClientData() {
+	file.GetCsvDb().Clients.Range(func(key, value interface{}) bool {
+		v := value.(*file.Client)
+		if _, ok := Bridge.Client.Load(v.Id); ok {
 			v.IsConnect = true
 		} else {
 			v.IsConnect = false
 		}
 		v.Flow.InletFlow = 0
 		v.Flow.ExportFlow = 0
-		for _, h := range file.GetCsvDb().Hosts {
+		file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+			h := value.(*file.Host)
 			if h.Client.Id == v.Id {
 				v.Flow.InletFlow += h.Flow.InletFlow
 				v.Flow.ExportFlow += h.Flow.ExportFlow
 			}
-		}
-		for _, t := range file.GetCsvDb().Tasks {
+			return true
+		})
+		file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+			t := value.(*file.Tunnel)
 			if t.Client.Id == v.Id {
 				v.Flow.InletFlow += t.Flow.InletFlow
 				v.Flow.ExportFlow += t.Flow.ExportFlow
 			}
-		}
-	}
+			return true
+		})
+		return true
+	})
 	return
 }
 
 //根据客户端id删除其所属的所有隧道和域名
 func DelTunnelAndHostByClientId(clientId int) {
 	var ids []int
-	file.GetCsvDb().Lock()
-	for _, v := range file.GetCsvDb().Tasks {
+	file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+		v := value.(*file.Tunnel)
 		if v.Client.Id == clientId {
 			ids = append(ids, v.Id)
 		}
-	}
-	file.GetCsvDb().Unlock()
+		return true
+	})
 	for _, id := range ids {
 		DelTask(id)
 	}
 	ids = ids[:0]
-	file.GetCsvDb().Lock()
-	for _, v := range file.GetCsvDb().Hosts {
+	file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool {
+		v := value.(*file.Host)
 		if v.Client.Id == clientId {
 			ids = append(ids, v.Id)
 		}
-	}
-	file.GetCsvDb().Unlock()
+		return true
+	})
 	for _, id := range ids {
 		file.GetCsvDb().DelHost(id)
 	}
@@ -305,32 +320,31 @@ func DelTunnelAndHostByClientId(clientId int) {
 
 //关闭客户端连接
 func DelClientConnect(clientId int) {
-	Bridge.DelClient(clientId, false)
+	Bridge.DelClient(clientId)
 }
 
 func GetDashboardData() map[string]interface{} {
 	data := make(map[string]interface{})
-	data["hostCount"] = len(file.GetCsvDb().Hosts)
-	data["clientCount"] = len(file.GetCsvDb().Clients) - 1 //Remove the public key client
-	list := file.GetCsvDb().Clients
-	dealClientData(list)
+	data["hostCount"] = file.GetCsvDb().GetMapLen(file.GetCsvDb().Hosts)
+	data["clientCount"] = file.GetCsvDb().GetMapLen(file.GetCsvDb().Clients) - 1 //Remove the public key client
+	dealClientData()
 	c := 0
 	var in, out int64
-	for _, v := range list {
+	file.GetCsvDb().Clients.Range(func(key, value interface{}) bool {
+		v := value.(*file.Client)
 		if v.IsConnect {
 			c += 1
 		}
 		in += v.Flow.InletFlow
 		out += v.Flow.ExportFlow
-	}
+		return true
+	})
 	data["clientOnlineCount"] = c
 	data["inletFlowCount"] = int(in)
 	data["exportFlowCount"] = int(out)
 	var tcp, udp, secret, socks5, p2p, http int
-	file.GetCsvDb().Lock()
-	defer file.GetCsvDb().Unlock()
-	for _, v := range file.GetCsvDb().Tasks {
-		switch v.Mode {
+	file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+		switch value.(*file.Tunnel).Mode {
 		case "tcp":
 			tcp += 1
 		case "socks5":
@@ -344,7 +358,9 @@ func GetDashboardData() map[string]interface{} {
 		case "secret":
 			secret += 1
 		}
-	}
+		return true
+	})
+
 	data["tcpC"] = tcp
 	data["udpCount"] = udp
 	data["socks5Count"] = socks5
@@ -360,9 +376,11 @@ func GetDashboardData() map[string]interface{} {
 	data["p2pPort"] = beego.AppConfig.String("p2p_port")
 	data["logLevel"] = beego.AppConfig.String("log_level")
 	tcpCount := 0
-	for _, v := range file.GetCsvDb().Clients {
-		tcpCount += v.NowConn
-	}
+
+	file.GetCsvDb().Clients.Range(func(key, value interface{}) bool {
+		tcpCount += value.(*file.Client).NowConn
+		return true
+	})
 	data["tcpCount"] = tcpCount
 	cpuPercet, _ := cpu.Percent(0, true)
 	var cpuAll float64

+ 18 - 12
server/test/test.go

@@ -5,19 +5,23 @@ import (
 	"github.com/cnlh/nps/lib/file"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego"
 	"log"
+	"path/filepath"
 	"strconv"
 )
 
 func TestServerConfig() {
 	var postTcpArr []int
 	var postUdpArr []int
-	for _, v := range file.GetCsvDb().Tasks {
+	file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool {
+		v := value.(*file.Tunnel)
 		if v.Mode == "udp" {
 			isInArr(&postUdpArr, v.Port, v.Remark, "udp")
-		} else {
+		} else if v.Port != 0 {
+
 			isInArr(&postTcpArr, v.Port, v.Remark, "tcp")
 		}
-	}
+		return true
+	})
 	p, err := beego.AppConfig.Int("web_port")
 	if err != nil {
 		log.Fatalln("Getting web management port error :", err)
@@ -43,16 +47,18 @@ func TestServerConfig() {
 		}
 	}
 	if p := beego.AppConfig.String("https_proxy_port"); p != "" {
-		if port, err := strconv.Atoi(p); err != nil {
-			log.Fatalln("get https port error", err)
-		} else {
-			if !common.FileExists(beego.AppConfig.String("pemPath")) {
-				log.Fatalf("ssl certFile %s is not exist", beego.AppConfig.String("pemPath"))
-			}
-			if !common.FileExists(beego.AppConfig.String("ketPath")) {
-				log.Fatalf("ssl keyFile %s is not exist", beego.AppConfig.String("pemPath"))
+		if b, err := beego.AppConfig.Bool("https_just_proxy"); !(err == nil && b) {
+			if port, err := strconv.Atoi(p); err != nil {
+				log.Fatalln("get https port error", err)
+			} else {
+				if !common.FileExists(filepath.Join(beego.AppPath, beego.AppConfig.String("pemPath"))) {
+					log.Fatalf("ssl certFile %s is not exist", beego.AppConfig.String("pemPath"))
+				}
+				if !common.FileExists(filepath.Join(beego.AppPath, beego.AppConfig.String("ketPath"))) {
+					log.Fatalf("ssl keyFile %s is not exist", beego.AppConfig.String("pemPath"))
+				}
+				isInArr(&postTcpArr, port, "http port", "tcp")
 			}
-			isInArr(&postTcpArr, port, "http port", "tcp")
 		}
 	}
 }

+ 1 - 1
server/tool/utils.go

@@ -7,7 +7,7 @@ import (
 
 var ports []int
 
-func init() {
+func InitAllowPort() {
 	p := beego.AppConfig.String("allow_ports")
 	ports = common.GetPorts(p)
 }

+ 6 - 0
vender/github.com/astaxie/beego/.gitignore

@@ -0,0 +1,6 @@
+.idea
+.vscode
+.DS_Store
+*.swp
+*.swo
+beego.iml

+ 4 - 0
vender/github.com/astaxie/beego/.gosimpleignore

@@ -0,0 +1,4 @@
+github.com/cnlh/nps/vender/github.com/astaxie/beego/*/*:S1012
+github.com/cnlh/nps/vender/github.com/astaxie/beego/*:S1012
+github.com/cnlh/nps/vender/github.com/astaxie/beego/*/*:S1007
+github.com/cnlh/nps/vender/github.com/astaxie/beego/*:S1007

+ 63 - 0
vender/github.com/astaxie/beego/.travis.yml

@@ -0,0 +1,63 @@
+language: go
+
+go:
+  - "1.9.7"
+  - "1.10.3"
+  - "1.11"
+services:
+  - redis-server
+  - mysql
+  - postgresql
+  - memcached
+env:
+  - ORM_DRIVER=sqlite3   ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db
+  - ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
+before_install:
+ - git clone git://github.com/ideawu/ssdb.git
+ - cd ssdb
+ - make
+ - cd ..
+install:
+  - go get github.com/lib/pq
+  - go get github.com/go-sql-driver/mysql
+  - go get github.com/mattn/go-sqlite3
+  - go get github.com/bradfitz/gomemcache/memcache
+  - go get github.com/gomodule/redigo/redis
+  - go get github.com/beego/x2j
+  - go get github.com/couchbase/go-couchbase
+  - go get github.com/beego/goyaml2
+  - go get gopkg.in/yaml.v2
+  - go get github.com/belogik/goes
+  - go get github.com/siddontang/ledisdb/config
+  - go get github.com/siddontang/ledisdb/ledis
+  - go get github.com/ssdb/gossdb/ssdb
+  - go get github.com/cloudflare/golz4
+  - go get github.com/gogo/protobuf/proto
+  - go get github.com/Knetic/govaluate
+  - go get github.com/casbin/casbin
+  - go get -u honnef.co/go/tools/cmd/gosimple
+  - go get -u github.com/mdempsky/unconvert
+  - go get -u github.com/gordonklaus/ineffassign
+  - go get -u github.com/golang/lint/golint
+  - go get -u github.com/go-redis/redis
+before_script:
+  - psql --version
+  - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
+  - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
+  - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
+  - sh -c "go get github.com/golang/lint/golint; golint ./...;"
+  - sh -c "go list ./... | grep -v vendor | xargs go vet -v"
+  - mkdir -p res/var
+  - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
+after_script:
+  -killall -w ssdb-server
+  - rm -rf ./res/var/*
+script:
+  - go test -v ./...
+  - gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/)
+  - unconvert $(go list ./... | grep -v /vendor/)
+  - ineffassign .
+  - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
+  - golint ./...
+addons:
+  postgresql: "9.6"

+ 52 - 0
vender/github.com/astaxie/beego/CONTRIBUTING.md

@@ -0,0 +1,52 @@
+# Contributing to beego
+
+beego is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+Here are instructions to get you started. They are probably not perfect, 
+please let us know if anything feels wrong or incomplete.
+
+## Contribution guidelines
+
+### Pull requests
+
+First of all. beego follow the gitflow. So please send you pull request 
+to **develop** branch. We will close the pull request to master branch.
+
+We are always happy to receive pull requests, and do our best to
+review them as fast as possible. Not sure if that typo is worth a pull
+request? Do it! We will appreciate it.
+
+If your pull request is not accepted on the first try, don't be
+discouraged! Sometimes we can make a mistake, please do more explaining 
+for us. We will appreciate it.
+
+We're trying very hard to keep beego simple and fast. We don't want it
+to do everything for everybody. This means that we might decide against
+incorporating a new feature. But we will give you some advice on how to 
+do it in other way.
+
+### Create issues
+
+Any significant improvement should be documented as [a GitHub
+issue](https://github.com/cnlh/nps/vender/github.com/astaxie/beego/issues) before anybody
+starts working on it. 
+
+Also when filing an issue, make sure to answer these five questions:
+
+- What version of beego are you using (bee version)?
+- What operating system and processor architecture are you using?
+- What did you do?
+- What did you expect to see?
+- What did you see instead?
+
+### but check existing issues and docs first!
+
+Please take a moment to check that an issue doesn't already exist
+documenting your bug report or improvement proposal. If it does, it
+never hurts to add a quick "+1" or "I have this problem too". This will
+help prioritize the most common problems and requests.
+
+Also if you don't know how to use it. please make sure you have read though
+the docs in http://beego.me/docs

+ 13 - 0
vender/github.com/astaxie/beego/LICENSE

@@ -0,0 +1,13 @@
+Copyright 2014 astaxie
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 63 - 0
vender/github.com/astaxie/beego/README.md

@@ -0,0 +1,63 @@
+# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/cnlh/nps/vender/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/cnlh/nps/vender/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/cnlh/nps/vender/github.com/astaxie/beego)](https://goreportcard.com/report/github.com/cnlh/nps/vender/github.com/astaxie/beego)
+
+
+beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
+It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
+
+ Response time ranking: [web-frameworks](https://github.com/the-benchmarker/web-frameworks).
+
+###### More info at [beego.me](http://beego.me).
+
+## Quick Start
+
+#### Download and install
+
+    go get github.com/cnlh/nps/vender/github.com/astaxie/beego
+
+#### Create file `hello.go`
+```go
+package main
+
+import "github.com/cnlh/nps/vender/github.com/astaxie/beego"
+
+func main(){
+    beego.Run()
+}
+```
+#### Build and run
+
+    go build hello.go
+    ./hello
+
+#### Go to [http://localhost:8080](http://localhost:8080)
+
+Congratulations! You've just built your first **beego** app.
+
+###### Please see [Documentation](http://beego.me/docs) for more.
+
+## Features
+
+* RESTful support
+* MVC architecture
+* Modularity
+* Auto API documents
+* Annotation router
+* Namespace
+* Powerful development tools
+* Full stack for Web & API
+
+## Documentation
+
+* [English](http://beego.me/docs/intro/)
+* [中文文档](http://beego.me/docs/intro/)
+* [Русский](http://beego.me/docs/intro/)
+
+## Community
+
+* [http://beego.me/community](http://beego.me/community)
+* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
+
+## License
+
+beego source code is licensed under the Apache Licence, Version 2.0
+(http://www.apache.org/licenses/LICENSE-2.0.html).

+ 416 - 0
vender/github.com/astaxie/beego/admin.go

@@ -0,0 +1,416 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"os"
+	"text/template"
+	"time"
+
+	"reflect"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/grace"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/toolbox"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
+)
+
+// BeeAdminApp is the default adminApp used by admin module.
+var beeAdminApp *adminApp
+
+// FilterMonitorFunc is default monitor filter when admin module is enable.
+// if this func returns, admin module records qbs for this request by condition of this function logic.
+// usage:
+// 	func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool {
+//	 	if method == "POST" {
+//			return false
+//	 	}
+//	 	if t.Nanoseconds() < 100 {
+//			return false
+//	 	}
+//	 	if strings.HasPrefix(requestPath, "/astaxie") {
+//			return false
+//	 	}
+//	 	return true
+// 	}
+// 	beego.FilterMonitorFunc = MyFilterMonitor.
+var FilterMonitorFunc func(string, string, time.Duration, string, int) bool
+
+func init() {
+	beeAdminApp = &adminApp{
+		routers: make(map[string]http.HandlerFunc),
+	}
+	beeAdminApp.Route("/", adminIndex)
+	beeAdminApp.Route("/qps", qpsIndex)
+	beeAdminApp.Route("/prof", profIndex)
+	beeAdminApp.Route("/healthcheck", healthcheck)
+	beeAdminApp.Route("/task", taskStatus)
+	beeAdminApp.Route("/listconf", listConf)
+	FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true }
+}
+
+// AdminIndex is the default http.Handler for admin module.
+// it matches url pattern "/".
+func adminIndex(rw http.ResponseWriter, _ *http.Request) {
+	execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
+}
+
+// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.
+// it's registered with url pattern "/qbs" in admin module.
+func qpsIndex(rw http.ResponseWriter, _ *http.Request) {
+	data := make(map[interface{}]interface{})
+	data["Content"] = toolbox.StatisticsMap.GetMap()
+
+	// do html escape before display path, avoid xss
+	if content, ok := (data["Content"]).(M); ok {
+		if resultLists, ok := (content["Data"]).([][]string); ok {
+			for i := range resultLists {
+				if len(resultLists[i]) > 0 {
+					resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0])
+				}
+			}
+		}
+	}
+
+	execTpl(rw, data, qpsTpl, defaultScriptsTpl)
+}
+
+// ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
+// it's registered with url pattern "/listconf" in admin module.
+func listConf(rw http.ResponseWriter, r *http.Request) {
+	r.ParseForm()
+	command := r.Form.Get("command")
+	if command == "" {
+		rw.Write([]byte("command not support"))
+		return
+	}
+
+	data := make(map[interface{}]interface{})
+	switch command {
+	case "conf":
+		m := make(M)
+		list("BConfig", BConfig, m)
+		m["AppConfigPath"] = appConfigPath
+		m["AppConfigProvider"] = appConfigProvider
+		tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
+		tmpl = template.Must(tmpl.Parse(configTpl))
+		tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
+
+		data["Content"] = m
+
+		tmpl.Execute(rw, data)
+
+	case "router":
+		content := PrintTree()
+		content["Fields"] = []string{
+			"Router Pattern",
+			"Methods",
+			"Controller",
+		}
+		data["Content"] = content
+		data["Title"] = "Routers"
+		execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
+	case "filter":
+		var (
+			content = M{
+				"Fields": []string{
+					"Router Pattern",
+					"Filter Function",
+				},
+			}
+			filterTypes    = []string{}
+			filterTypeData = make(M)
+		)
+
+		if BeeApp.Handlers.enableFilter {
+			var filterType string
+			for k, fr := range map[int]string{
+				BeforeStatic: "Before Static",
+				BeforeRouter: "Before Router",
+				BeforeExec:   "Before Exec",
+				AfterExec:    "After Exec",
+				FinishRouter: "Finish Router"} {
+				if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 {
+					filterType = fr
+					filterTypes = append(filterTypes, filterType)
+					resultList := new([][]string)
+					for _, f := range bf {
+						var result = []string{
+							f.pattern,
+							utils.GetFuncName(f.filterFunc),
+						}
+						*resultList = append(*resultList, result)
+					}
+					filterTypeData[filterType] = resultList
+				}
+			}
+		}
+
+		content["Data"] = filterTypeData
+		content["Methods"] = filterTypes
+
+		data["Content"] = content
+		data["Title"] = "Filters"
+		execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
+	default:
+		rw.Write([]byte("command not support"))
+	}
+}
+
+func list(root string, p interface{}, m M) {
+	pt := reflect.TypeOf(p)
+	pv := reflect.ValueOf(p)
+	if pt.Kind() == reflect.Ptr {
+		pt = pt.Elem()
+		pv = pv.Elem()
+	}
+	for i := 0; i < pv.NumField(); i++ {
+		var key string
+		if root == "" {
+			key = pt.Field(i).Name
+		} else {
+			key = root + "." + pt.Field(i).Name
+		}
+		if pv.Field(i).Kind() == reflect.Struct {
+			list(key, pv.Field(i).Interface(), m)
+		} else {
+			m[key] = pv.Field(i).Interface()
+		}
+	}
+}
+
+// PrintTree prints all registered routers.
+func PrintTree() M {
+	var (
+		content     = M{}
+		methods     = []string{}
+		methodsData = make(M)
+	)
+	for method, t := range BeeApp.Handlers.routers {
+
+		resultList := new([][]string)
+
+		printTree(resultList, t)
+
+		methods = append(methods, method)
+		methodsData[method] = resultList
+	}
+
+	content["Data"] = methodsData
+	content["Methods"] = methods
+	return content
+}
+
+func printTree(resultList *[][]string, t *Tree) {
+	for _, tr := range t.fixrouters {
+		printTree(resultList, tr)
+	}
+	if t.wildcard != nil {
+		printTree(resultList, t.wildcard)
+	}
+	for _, l := range t.leaves {
+		if v, ok := l.runObject.(*ControllerInfo); ok {
+			if v.routerType == routerTypeBeego {
+				var result = []string{
+					v.pattern,
+					fmt.Sprintf("%s", v.methods),
+					v.controllerType.String(),
+				}
+				*resultList = append(*resultList, result)
+			} else if v.routerType == routerTypeRESTFul {
+				var result = []string{
+					v.pattern,
+					fmt.Sprintf("%s", v.methods),
+					"",
+				}
+				*resultList = append(*resultList, result)
+			} else if v.routerType == routerTypeHandler {
+				var result = []string{
+					v.pattern,
+					"",
+					"",
+				}
+				*resultList = append(*resultList, result)
+			}
+		}
+	}
+}
+
+// ProfIndex is a http.Handler for showing profile command.
+// it's in url pattern "/prof" in admin module.
+func profIndex(rw http.ResponseWriter, r *http.Request) {
+	r.ParseForm()
+	command := r.Form.Get("command")
+	if command == "" {
+		return
+	}
+
+	var (
+		format = r.Form.Get("format")
+		data   = make(map[interface{}]interface{})
+		result bytes.Buffer
+	)
+	toolbox.ProcessInput(command, &result)
+	data["Content"] = result.String()
+
+	if format == "json" && command == "gc summary" {
+		dataJSON, err := json.Marshal(data)
+		if err != nil {
+			http.Error(rw, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		rw.Header().Set("Content-Type", "application/json")
+		rw.Write(dataJSON)
+		return
+	}
+
+	data["Title"] = command
+	defaultTpl := defaultScriptsTpl
+	if command == "gc summary" {
+		defaultTpl = gcAjaxTpl
+	}
+	execTpl(rw, data, profillingTpl, defaultTpl)
+}
+
+// Healthcheck is a http.Handler calling health checking and showing the result.
+// it's in "/healthcheck" pattern in admin module.
+func healthcheck(rw http.ResponseWriter, _ *http.Request) {
+	var (
+		result     []string
+		data       = make(map[interface{}]interface{})
+		resultList = new([][]string)
+		content    = M{
+			"Fields": []string{"Name", "Message", "Status"},
+		}
+	)
+
+	for name, h := range toolbox.AdminCheckList {
+		if err := h.Check(); err != nil {
+			result = []string{
+				"error",
+				name,
+				err.Error(),
+			}
+		} else {
+			result = []string{
+				"success",
+				name,
+				"OK",
+			}
+		}
+		*resultList = append(*resultList, result)
+	}
+
+	content["Data"] = resultList
+	data["Content"] = content
+	data["Title"] = "Health Check"
+	execTpl(rw, data, healthCheckTpl, defaultScriptsTpl)
+}
+
+// TaskStatus is a http.Handler with running task status (task name, status and the last execution).
+// it's in "/task" pattern in admin module.
+func taskStatus(rw http.ResponseWriter, req *http.Request) {
+	data := make(map[interface{}]interface{})
+
+	// Run Task
+	req.ParseForm()
+	taskname := req.Form.Get("taskname")
+	if taskname != "" {
+		if t, ok := toolbox.AdminTaskList[taskname]; ok {
+			if err := t.Run(); err != nil {
+				data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
+			}
+			data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
+		} else {
+			data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
+		}
+	}
+
+	// List Tasks
+	content := make(M)
+	resultList := new([][]string)
+	var fields = []string{
+		"Task Name",
+		"Task Spec",
+		"Task Status",
+		"Last Time",
+		"",
+	}
+	for tname, tk := range toolbox.AdminTaskList {
+		result := []string{
+			tname,
+			tk.GetSpec(),
+			tk.GetStatus(),
+			tk.GetPrev().String(),
+		}
+		*resultList = append(*resultList, result)
+	}
+
+	content["Fields"] = fields
+	content["Data"] = resultList
+	data["Content"] = content
+	data["Title"] = "Tasks"
+	execTpl(rw, data, tasksTpl, defaultScriptsTpl)
+}
+
+func execTpl(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) {
+	tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
+	for _, tpl := range tpls {
+		tmpl = template.Must(tmpl.Parse(tpl))
+	}
+	tmpl.Execute(rw, data)
+}
+
+// adminApp is an http.HandlerFunc map used as beeAdminApp.
+type adminApp struct {
+	routers map[string]http.HandlerFunc
+}
+
+// Route adds http.HandlerFunc to adminApp with url pattern.
+func (admin *adminApp) Route(pattern string, f http.HandlerFunc) {
+	admin.routers[pattern] = f
+}
+
+// Run adminApp http server.
+// Its addr is defined in configuration file as adminhttpaddr and adminhttpport.
+func (admin *adminApp) Run() {
+	if len(toolbox.AdminTaskList) > 0 {
+		toolbox.StartTask()
+	}
+	addr := BConfig.Listen.AdminAddr
+
+	if BConfig.Listen.AdminPort != 0 {
+		addr = fmt.Sprintf("%s:%d", BConfig.Listen.AdminAddr, BConfig.Listen.AdminPort)
+	}
+	for p, f := range admin.routers {
+		http.Handle(p, f)
+	}
+	logs.Info("Admin server Running on %s", addr)
+
+	var err error
+	if BConfig.Listen.Graceful {
+		err = grace.ListenAndServe(addr, nil)
+	} else {
+		err = http.ListenAndServe(addr, nil)
+	}
+	if err != nil {
+		logs.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
+	}
+}

+ 75 - 0
vender/github.com/astaxie/beego/admin_test.go

@@ -0,0 +1,75 @@
+package beego
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestList_01(t *testing.T) {
+	m := make(M)
+	list("BConfig", BConfig, m)
+	t.Log(m)
+	om := oldMap()
+	for k, v := range om {
+		if fmt.Sprint(m[k]) != fmt.Sprint(v) {
+			t.Log(k, "old-key", v, "new-key", m[k])
+			t.FailNow()
+		}
+	}
+}
+
+func oldMap() M {
+	m := make(M)
+	m["BConfig.AppName"] = BConfig.AppName
+	m["BConfig.RunMode"] = BConfig.RunMode
+	m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive
+	m["BConfig.ServerName"] = BConfig.ServerName
+	m["BConfig.RecoverPanic"] = BConfig.RecoverPanic
+	m["BConfig.CopyRequestBody"] = BConfig.CopyRequestBody
+	m["BConfig.EnableGzip"] = BConfig.EnableGzip
+	m["BConfig.MaxMemory"] = BConfig.MaxMemory
+	m["BConfig.EnableErrorsShow"] = BConfig.EnableErrorsShow
+	m["BConfig.Listen.Graceful"] = BConfig.Listen.Graceful
+	m["BConfig.Listen.ServerTimeOut"] = BConfig.Listen.ServerTimeOut
+	m["BConfig.Listen.ListenTCP4"] = BConfig.Listen.ListenTCP4
+	m["BConfig.Listen.EnableHTTP"] = BConfig.Listen.EnableHTTP
+	m["BConfig.Listen.HTTPAddr"] = BConfig.Listen.HTTPAddr
+	m["BConfig.Listen.HTTPPort"] = BConfig.Listen.HTTPPort
+	m["BConfig.Listen.EnableHTTPS"] = BConfig.Listen.EnableHTTPS
+	m["BConfig.Listen.HTTPSAddr"] = BConfig.Listen.HTTPSAddr
+	m["BConfig.Listen.HTTPSPort"] = BConfig.Listen.HTTPSPort
+	m["BConfig.Listen.HTTPSCertFile"] = BConfig.Listen.HTTPSCertFile
+	m["BConfig.Listen.HTTPSKeyFile"] = BConfig.Listen.HTTPSKeyFile
+	m["BConfig.Listen.EnableAdmin"] = BConfig.Listen.EnableAdmin
+	m["BConfig.Listen.AdminAddr"] = BConfig.Listen.AdminAddr
+	m["BConfig.Listen.AdminPort"] = BConfig.Listen.AdminPort
+	m["BConfig.Listen.EnableFcgi"] = BConfig.Listen.EnableFcgi
+	m["BConfig.Listen.EnableStdIo"] = BConfig.Listen.EnableStdIo
+	m["BConfig.WebConfig.AutoRender"] = BConfig.WebConfig.AutoRender
+	m["BConfig.WebConfig.EnableDocs"] = BConfig.WebConfig.EnableDocs
+	m["BConfig.WebConfig.FlashName"] = BConfig.WebConfig.FlashName
+	m["BConfig.WebConfig.FlashSeparator"] = BConfig.WebConfig.FlashSeparator
+	m["BConfig.WebConfig.DirectoryIndex"] = BConfig.WebConfig.DirectoryIndex
+	m["BConfig.WebConfig.StaticDir"] = BConfig.WebConfig.StaticDir
+	m["BConfig.WebConfig.StaticExtensionsToGzip"] = BConfig.WebConfig.StaticExtensionsToGzip
+	m["BConfig.WebConfig.TemplateLeft"] = BConfig.WebConfig.TemplateLeft
+	m["BConfig.WebConfig.TemplateRight"] = BConfig.WebConfig.TemplateRight
+	m["BConfig.WebConfig.ViewsPath"] = BConfig.WebConfig.ViewsPath
+	m["BConfig.WebConfig.EnableXSRF"] = BConfig.WebConfig.EnableXSRF
+	m["BConfig.WebConfig.XSRFExpire"] = BConfig.WebConfig.XSRFExpire
+	m["BConfig.WebConfig.Session.SessionOn"] = BConfig.WebConfig.Session.SessionOn
+	m["BConfig.WebConfig.Session.SessionProvider"] = BConfig.WebConfig.Session.SessionProvider
+	m["BConfig.WebConfig.Session.SessionName"] = BConfig.WebConfig.Session.SessionName
+	m["BConfig.WebConfig.Session.SessionGCMaxLifetime"] = BConfig.WebConfig.Session.SessionGCMaxLifetime
+	m["BConfig.WebConfig.Session.SessionProviderConfig"] = BConfig.WebConfig.Session.SessionProviderConfig
+	m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime
+	m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie
+	m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
+	m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
+	m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
+	m["BConfig.Log.EnableStaticLogs"] = BConfig.Log.EnableStaticLogs
+	m["BConfig.Log.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat
+	m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
+	m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
+	return m
+}

Fișier diff suprimat deoarece este prea mare
+ 286 - 0
vender/github.com/astaxie/beego/adminui.go


+ 497 - 0
vender/github.com/astaxie/beego/app.go

@@ -0,0 +1,497 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/http/fcgi"
+	"os"
+	"path"
+	"strings"
+	"time"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/grace"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
+	"golang.org/x/crypto/acme/autocert"
+)
+
+var (
+	// BeeApp is an application instance
+	BeeApp *App
+)
+
+func init() {
+	// create beego application
+	BeeApp = NewApp()
+}
+
+// App defines beego application with a new PatternServeMux.
+type App struct {
+	Handlers *ControllerRegister
+	Server   *http.Server
+}
+
+// NewApp returns a new beego application.
+func NewApp() *App {
+	cr := NewControllerRegister()
+	app := &App{Handlers: cr, Server: &http.Server{}}
+	return app
+}
+
+// MiddleWare function for http.Handler
+type MiddleWare func(http.Handler) http.Handler
+
+// Run beego application.
+func (app *App) Run(mws ...MiddleWare) {
+	addr := BConfig.Listen.HTTPAddr
+
+	if BConfig.Listen.HTTPPort != 0 {
+		addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPAddr, BConfig.Listen.HTTPPort)
+	}
+
+	var (
+		err        error
+		l          net.Listener
+		endRunning = make(chan bool, 1)
+	)
+
+	// run cgi server
+	if BConfig.Listen.EnableFcgi {
+		if BConfig.Listen.EnableStdIo {
+			if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O
+				logs.Info("Use FCGI via standard I/O")
+			} else {
+				logs.Critical("Cannot use FCGI via standard I/O", err)
+			}
+			return
+		}
+		if BConfig.Listen.HTTPPort == 0 {
+			// remove the Socket file before start
+			if utils.FileExists(addr) {
+				os.Remove(addr)
+			}
+			l, err = net.Listen("unix", addr)
+		} else {
+			l, err = net.Listen("tcp", addr)
+		}
+		if err != nil {
+			logs.Critical("Listen: ", err)
+		}
+		if err = fcgi.Serve(l, app.Handlers); err != nil {
+			logs.Critical("fcgi.Serve: ", err)
+		}
+		return
+	}
+
+	app.Server.Handler = app.Handlers
+	for i := len(mws) - 1; i >= 0; i-- {
+		if mws[i] == nil {
+			continue
+		}
+		app.Server.Handler = mws[i](app.Server.Handler)
+	}
+	app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
+	app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
+	app.Server.ErrorLog = logs.GetLogger("HTTP")
+
+	// run graceful mode
+	if BConfig.Listen.Graceful {
+		httpsAddr := BConfig.Listen.HTTPSAddr
+		app.Server.Addr = httpsAddr
+		if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
+			go func() {
+				time.Sleep(1000 * time.Microsecond)
+				if BConfig.Listen.HTTPSPort != 0 {
+					httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
+					app.Server.Addr = httpsAddr
+				}
+				server := grace.NewServer(httpsAddr, app.Handlers)
+				server.Server.ReadTimeout = app.Server.ReadTimeout
+				server.Server.WriteTimeout = app.Server.WriteTimeout
+				if BConfig.Listen.EnableMutualHTTPS {
+					if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
+						logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
+						time.Sleep(100 * time.Microsecond)
+						endRunning <- true
+					}
+				} else {
+					if BConfig.Listen.AutoTLS {
+						m := autocert.Manager{
+							Prompt:     autocert.AcceptTOS,
+							HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...),
+							Cache:      autocert.DirCache(BConfig.Listen.TLSCacheDir),
+						}
+						app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
+						BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", ""
+					}
+					if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
+						logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
+						time.Sleep(100 * time.Microsecond)
+						endRunning <- true
+					}
+				}
+			}()
+		}
+		if BConfig.Listen.EnableHTTP {
+			go func() {
+				server := grace.NewServer(addr, app.Handlers)
+				server.Server.ReadTimeout = app.Server.ReadTimeout
+				server.Server.WriteTimeout = app.Server.WriteTimeout
+				if BConfig.Listen.ListenTCP4 {
+					server.Network = "tcp4"
+				}
+				if err := server.ListenAndServe(); err != nil {
+					logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
+					time.Sleep(100 * time.Microsecond)
+					endRunning <- true
+				}
+			}()
+		}
+		<-endRunning
+		return
+	}
+
+	// run normal mode
+	if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
+		go func() {
+			time.Sleep(1000 * time.Microsecond)
+			if BConfig.Listen.HTTPSPort != 0 {
+				app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
+			} else if BConfig.Listen.EnableHTTP {
+				BeeLogger.Info("Start https server error, conflict with http. Please reset https port")
+				return
+			}
+			logs.Info("https server Running on https://%s", app.Server.Addr)
+			if BConfig.Listen.AutoTLS {
+				m := autocert.Manager{
+					Prompt:     autocert.AcceptTOS,
+					HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...),
+					Cache:      autocert.DirCache(BConfig.Listen.TLSCacheDir),
+				}
+				app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
+				BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", ""
+			} else if BConfig.Listen.EnableMutualHTTPS {
+				pool := x509.NewCertPool()
+				data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
+				if err != nil {
+					BeeLogger.Info("MutualHTTPS should provide TrustCaFile")
+					return
+				}
+				pool.AppendCertsFromPEM(data)
+				app.Server.TLSConfig = &tls.Config{
+					ClientCAs:  pool,
+					ClientAuth: tls.RequireAndVerifyClientCert,
+				}
+			}
+			if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
+				logs.Critical("ListenAndServeTLS: ", err)
+				time.Sleep(100 * time.Microsecond)
+				endRunning <- true
+			}
+		}()
+
+	}
+	if BConfig.Listen.EnableHTTP {
+		go func() {
+			app.Server.Addr = addr
+			logs.Info("http server Running on http://%s", app.Server.Addr)
+			if BConfig.Listen.ListenTCP4 {
+				ln, err := net.Listen("tcp4", app.Server.Addr)
+				if err != nil {
+					logs.Critical("ListenAndServe: ", err)
+					time.Sleep(100 * time.Microsecond)
+					endRunning <- true
+					return
+				}
+				if err = app.Server.Serve(ln); err != nil {
+					logs.Critical("ListenAndServe: ", err)
+					time.Sleep(100 * time.Microsecond)
+					endRunning <- true
+					return
+				}
+			} else {
+				if err := app.Server.ListenAndServe(); err != nil {
+					logs.Critical("ListenAndServe: ", err)
+					time.Sleep(100 * time.Microsecond)
+					endRunning <- true
+				}
+			}
+		}()
+	}
+	<-endRunning
+}
+
+// Router adds a patterned controller handler to BeeApp.
+// it's an alias method of App.Router.
+// usage:
+//  simple router
+//  beego.Router("/admin", &admin.UserController{})
+//  beego.Router("/admin/index", &admin.ArticleController{})
+//
+//  regex router
+//
+//  beego.Router("/api/:id([0-9]+)", &controllers.RController{})
+//
+//  custom rules
+//  beego.Router("/api/list",&RestController{},"*:ListFood")
+//  beego.Router("/api/create",&RestController{},"post:CreateFood")
+//  beego.Router("/api/update",&RestController{},"put:UpdateFood")
+//  beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
+func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
+	BeeApp.Handlers.Add(rootpath, c, mappingMethods...)
+	return BeeApp
+}
+
+// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful
+// in web applications that inherit most routes from a base webapp via the underscore
+// import, and aim to overwrite only certain paths.
+// The method parameter can be empty or "*" for all HTTP methods, or a particular
+// method type (e.g. "GET" or "POST") for selective removal.
+//
+// Usage (replace "GET" with "*" for all methods):
+//  beego.UnregisterFixedRoute("/yourpreviouspath", "GET")
+//  beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage")
+func UnregisterFixedRoute(fixedRoute string, method string) *App {
+	subPaths := splitPath(fixedRoute)
+	if method == "" || method == "*" {
+		for m := range HTTPMETHOD {
+			if _, ok := BeeApp.Handlers.routers[m]; !ok {
+				continue
+			}
+			if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") {
+				findAndRemoveSingleTree(BeeApp.Handlers.routers[m])
+				continue
+			}
+			findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m)
+		}
+		return BeeApp
+	}
+	// Single HTTP method
+	um := strings.ToUpper(method)
+	if _, ok := BeeApp.Handlers.routers[um]; ok {
+		if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") {
+			findAndRemoveSingleTree(BeeApp.Handlers.routers[um])
+			return BeeApp
+		}
+		findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um)
+	}
+	return BeeApp
+}
+
+func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) {
+	for i := range entryPointTree.fixrouters {
+		if entryPointTree.fixrouters[i].prefix == paths[0] {
+			if len(paths) == 1 {
+				if len(entryPointTree.fixrouters[i].fixrouters) > 0 {
+					// If the route had children subtrees, remove just the functional leaf,
+					// to allow children to function as before
+					if len(entryPointTree.fixrouters[i].leaves) > 0 {
+						entryPointTree.fixrouters[i].leaves[0] = nil
+						entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:]
+					}
+				} else {
+					// Remove the *Tree from the fixrouters slice
+					entryPointTree.fixrouters[i] = nil
+
+					if i == len(entryPointTree.fixrouters)-1 {
+						entryPointTree.fixrouters = entryPointTree.fixrouters[:i]
+					} else {
+						entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...)
+					}
+				}
+				return
+			}
+			findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method)
+		}
+	}
+}
+
+func findAndRemoveSingleTree(entryPointTree *Tree) {
+	if entryPointTree == nil {
+		return
+	}
+	if len(entryPointTree.fixrouters) > 0 {
+		// If the route had children subtrees, remove just the functional leaf,
+		// to allow children to function as before
+		if len(entryPointTree.leaves) > 0 {
+			entryPointTree.leaves[0] = nil
+			entryPointTree.leaves = entryPointTree.leaves[1:]
+		}
+	}
+}
+
+// Include will generate router file in the router/xxx.go from the controller's comments
+// usage:
+// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
+// type BankAccount struct{
+//   beego.Controller
+// }
+//
+// register the function
+// func (b *BankAccount)Mapping(){
+//  b.Mapping("ShowAccount" , b.ShowAccount)
+//  b.Mapping("ModifyAccount", b.ModifyAccount)
+//}
+//
+// //@router /account/:id  [get]
+// func (b *BankAccount) ShowAccount(){
+//    //logic
+// }
+//
+//
+// //@router /account/:id  [post]
+// func (b *BankAccount) ModifyAccount(){
+//    //logic
+// }
+//
+// the comments @router url methodlist
+// url support all the function Router's pattern
+// methodlist [get post head put delete options *]
+func Include(cList ...ControllerInterface) *App {
+	BeeApp.Handlers.Include(cList...)
+	return BeeApp
+}
+
+// RESTRouter adds a restful controller handler to BeeApp.
+// its' controller implements beego.ControllerInterface and
+// defines a param "pattern/:objectId" to visit each resource.
+func RESTRouter(rootpath string, c ControllerInterface) *App {
+	Router(rootpath, c)
+	Router(path.Join(rootpath, ":objectId"), c)
+	return BeeApp
+}
+
+// AutoRouter adds defined controller handler to BeeApp.
+// it's same to App.AutoRouter.
+// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
+// visit the url /main/list to exec List function or /main/page to exec Page function.
+func AutoRouter(c ControllerInterface) *App {
+	BeeApp.Handlers.AddAuto(c)
+	return BeeApp
+}
+
+// AutoPrefix adds controller handler to BeeApp with prefix.
+// it's same to App.AutoRouterWithPrefix.
+// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
+// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
+func AutoPrefix(prefix string, c ControllerInterface) *App {
+	BeeApp.Handlers.AddAutoPrefix(prefix, c)
+	return BeeApp
+}
+
+// Get used to register router for Get method
+// usage:
+//    beego.Get("/", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Get(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Get(rootpath, f)
+	return BeeApp
+}
+
+// Post used to register router for Post method
+// usage:
+//    beego.Post("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Post(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Post(rootpath, f)
+	return BeeApp
+}
+
+// Delete used to register router for Delete method
+// usage:
+//    beego.Delete("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Delete(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Delete(rootpath, f)
+	return BeeApp
+}
+
+// Put used to register router for Put method
+// usage:
+//    beego.Put("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Put(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Put(rootpath, f)
+	return BeeApp
+}
+
+// Head used to register router for Head method
+// usage:
+//    beego.Head("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Head(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Head(rootpath, f)
+	return BeeApp
+}
+
+// Options used to register router for Options method
+// usage:
+//    beego.Options("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Options(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Options(rootpath, f)
+	return BeeApp
+}
+
+// Patch used to register router for Patch method
+// usage:
+//    beego.Patch("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Patch(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Patch(rootpath, f)
+	return BeeApp
+}
+
+// Any used to register router for all methods
+// usage:
+//    beego.Any("/api", func(ctx *context.Context){
+//          ctx.Output.Body("hello world")
+//    })
+func Any(rootpath string, f FilterFunc) *App {
+	BeeApp.Handlers.Any(rootpath, f)
+	return BeeApp
+}
+
+// Handler used to register a Handler router
+// usage:
+//    beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
+//          fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
+//    }))
+func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
+	BeeApp.Handlers.Handler(rootpath, h, options...)
+	return BeeApp
+}
+
+// InsertFilter adds a FilterFunc with pattern condition and action constant.
+// The pos means action constant including
+// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
+// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
+func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App {
+	BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...)
+	return BeeApp
+}

+ 123 - 0
vender/github.com/astaxie/beego/beego.go

@@ -0,0 +1,123 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+)
+
+const (
+	// VERSION represent beego web framework version.
+	VERSION = "1.10.1"
+
+	// DEV is for develop
+	DEV = "dev"
+	// PROD is for production
+	PROD = "prod"
+)
+
+// Map shortcut
+type M map[string]interface{}
+
+// Hook function to run
+type hookfunc func() error
+
+var (
+	hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc
+)
+
+// AddAPPStartHook is used to register the hookfunc
+// The hookfuncs will run in beego.Run()
+// such as initiating session , starting middleware , building template, starting admin control and so on.
+func AddAPPStartHook(hf ...hookfunc) {
+	hooks = append(hooks, hf...)
+}
+
+// Run beego application.
+// beego.Run() default run on HttpPort
+// beego.Run("localhost")
+// beego.Run(":8089")
+// beego.Run("127.0.0.1:8089")
+func Run(params ...string) {
+
+	InitBeforeHTTPRun()
+
+	if len(params) > 0 && params[0] != "" {
+		strs := strings.Split(params[0], ":")
+		if len(strs) > 0 && strs[0] != "" {
+			BConfig.Listen.HTTPAddr = strs[0]
+		}
+		if len(strs) > 1 && strs[1] != "" {
+			BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
+		}
+
+		BConfig.Listen.Domains = params
+	}
+
+	BeeApp.Run()
+}
+
+// RunWithMiddleWares Run beego application with middlewares.
+func RunWithMiddleWares(addr string, mws ...MiddleWare) {
+	InitBeforeHTTPRun()
+
+	strs := strings.Split(addr, ":")
+	if len(strs) > 0 && strs[0] != "" {
+		BConfig.Listen.HTTPAddr = strs[0]
+		BConfig.Listen.Domains = []string{strs[0]}
+	}
+	if len(strs) > 1 && strs[1] != "" {
+		BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
+	}
+
+	BeeApp.Run(mws...)
+}
+
+func InitBeforeHTTPRun() {
+	//init hooks
+	AddAPPStartHook(
+		registerMime,
+		registerDefaultErrorHandler,
+		registerSession,
+		registerTemplate,
+		registerAdmin,
+		registerGzip,
+	)
+
+	for _, hk := range hooks {
+		if err := hk(); err != nil {
+			panic(err)
+		}
+	}
+}
+
+// TestBeegoInit is for test package init
+func TestBeegoInit(ap string) {
+	path := filepath.Join(ap, "conf", "app.conf")
+	os.Chdir(ap)
+	InitBeegoBeforeTest(path)
+}
+
+// InitBeegoBeforeTest is for test package init
+func InitBeegoBeforeTest(appConfigPath string) {
+	if err := LoadAppConfig(appConfigProvider, appConfigPath); err != nil {
+		panic(err)
+	}
+	BConfig.RunMode = "test"
+	InitBeforeHTTPRun()
+}

+ 510 - 0
vender/github.com/astaxie/beego/config.go

@@ -0,0 +1,510 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strings"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/session"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
+)
+
+// Config is the main struct for BConfig
+type Config struct {
+	AppName             string //Application name
+	RunMode             string //Running Mode: dev | prod
+	RouterCaseSensitive bool
+	ServerName          string
+	RecoverPanic        bool
+	RecoverFunc         func(*context.Context)
+	CopyRequestBody     bool
+	EnableGzip          bool
+	MaxMemory           int64
+	EnableErrorsShow    bool
+	EnableErrorsRender  bool
+	Listen              Listen
+	WebConfig           WebConfig
+	Log                 LogConfig
+}
+
+// Listen holds for http and https related config
+type Listen struct {
+	Graceful          bool // Graceful means use graceful module to start the server
+	ServerTimeOut     int64
+	ListenTCP4        bool
+	EnableHTTP        bool
+	HTTPAddr          string
+	HTTPPort          int
+	AutoTLS           bool
+	Domains           []string
+	TLSCacheDir       string
+	EnableHTTPS       bool
+	EnableMutualHTTPS bool
+	HTTPSAddr         string
+	HTTPSPort         int
+	HTTPSCertFile     string
+	HTTPSKeyFile      string
+	TrustCaFile       string
+	EnableAdmin       bool
+	AdminAddr         string
+	AdminPort         int
+	EnableFcgi        bool
+	EnableStdIo       bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O
+}
+
+// WebConfig holds web related config
+type WebConfig struct {
+	AutoRender             bool
+	EnableDocs             bool
+	FlashName              string
+	FlashSeparator         string
+	DirectoryIndex         bool
+	StaticDir              map[string]string
+	StaticExtensionsToGzip []string
+	TemplateLeft           string
+	TemplateRight          string
+	ViewsPath              string
+	EnableXSRF             bool
+	XSRFKey                string
+	XSRFExpire             int
+	Session                SessionConfig
+}
+
+// SessionConfig holds session related config
+type SessionConfig struct {
+	SessionOn                    bool
+	SessionProvider              string
+	SessionName                  string
+	SessionGCMaxLifetime         int64
+	SessionProviderConfig        string
+	SessionCookieLifeTime        int
+	SessionAutoSetCookie         bool
+	SessionDomain                string
+	SessionDisableHTTPOnly       bool // used to allow for cross domain cookies/javascript cookies.
+	SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
+	SessionNameInHTTPHeader      string
+	SessionEnableSidInURLQuery   bool // enable get the sessionId from Url Query params
+}
+
+// LogConfig holds Log related config
+type LogConfig struct {
+	AccessLogs       bool
+	EnableStaticLogs bool   //log static files requests default: false
+	AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
+	FileLineNum      bool
+	Outputs          map[string]string // Store Adaptor : config
+}
+
+var (
+	// BConfig is the default config for Application
+	BConfig *Config
+	// AppConfig is the instance of Config, store the config information from file
+	AppConfig *beegoAppConfig
+	// AppPath is the absolute path to the app
+	AppPath string
+	// GlobalSessions is the instance for the session manager
+	GlobalSessions *session.Manager
+
+	// appConfigPath is the path to the config files
+	appConfigPath string
+	// appConfigProvider is the provider for the config, default is ini
+	appConfigProvider = "ini"
+)
+
+func init() {
+	BConfig = newBConfig()
+	var err error
+	if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil {
+		panic(err)
+	}
+	workPath, err := os.Getwd()
+	if err != nil {
+		panic(err)
+	}
+	var filename = "app.conf"
+	if os.Getenv("BEEGO_RUNMODE") != "" {
+		filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
+	}
+	appConfigPath = filepath.Join(workPath, "conf", filename)
+	if !utils.FileExists(appConfigPath) {
+		appConfigPath = filepath.Join(AppPath, "conf", filename)
+		if !utils.FileExists(appConfigPath) {
+			AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()}
+			return
+		}
+	}
+	if err = parseConfig(appConfigPath); err != nil {
+		panic(err)
+	}
+}
+
+func recoverPanic(ctx *context.Context) {
+	if err := recover(); err != nil {
+		if err == ErrAbort {
+			return
+		}
+		if !BConfig.RecoverPanic {
+			panic(err)
+		}
+		if BConfig.EnableErrorsShow {
+			if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
+				exception(fmt.Sprint(err), ctx)
+				return
+			}
+		}
+		var stack string
+		logs.Critical("the request url is ", ctx.Input.URL())
+		logs.Critical("Handler crashed with error", err)
+		for i := 1; ; i++ {
+			_, file, line, ok := runtime.Caller(i)
+			if !ok {
+				break
+			}
+			logs.Critical(fmt.Sprintf("%s:%d", file, line))
+			stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
+		}
+		if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
+			showErr(err, ctx, stack)
+		}
+		if ctx.Output.Status != 0 {
+			ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
+		} else {
+			ctx.ResponseWriter.WriteHeader(500)
+		}
+	}
+}
+
+func newBConfig() *Config {
+	return &Config{
+		AppName:             "beego",
+		RunMode:             PROD,
+		RouterCaseSensitive: true,
+		ServerName:          "beegoServer:" + VERSION,
+		RecoverPanic:        true,
+		RecoverFunc:         recoverPanic,
+		CopyRequestBody:     false,
+		EnableGzip:          false,
+		MaxMemory:           1 << 26, //64MB
+		EnableErrorsShow:    true,
+		EnableErrorsRender:  true,
+		Listen: Listen{
+			Graceful:      false,
+			ServerTimeOut: 0,
+			ListenTCP4:    false,
+			EnableHTTP:    true,
+			AutoTLS:       false,
+			Domains:       []string{},
+			TLSCacheDir:   ".",
+			HTTPAddr:      "",
+			HTTPPort:      8080,
+			EnableHTTPS:   false,
+			HTTPSAddr:     "",
+			HTTPSPort:     10443,
+			HTTPSCertFile: "",
+			HTTPSKeyFile:  "",
+			EnableAdmin:   false,
+			AdminAddr:     "",
+			AdminPort:     8088,
+			EnableFcgi:    false,
+			EnableStdIo:   false,
+		},
+		WebConfig: WebConfig{
+			AutoRender:             true,
+			EnableDocs:             false,
+			FlashName:              "BEEGO_FLASH",
+			FlashSeparator:         "BEEGOFLASH",
+			DirectoryIndex:         false,
+			StaticDir:              map[string]string{"/static": "static"},
+			StaticExtensionsToGzip: []string{".css", ".js"},
+			TemplateLeft:           "{{",
+			TemplateRight:          "}}",
+			ViewsPath:              "views",
+			EnableXSRF:             false,
+			XSRFKey:                "beegoxsrf",
+			XSRFExpire:             0,
+			Session: SessionConfig{
+				SessionOn:                    false,
+				SessionProvider:              "memory",
+				SessionName:                  "beegosessionID",
+				SessionGCMaxLifetime:         3600,
+				SessionProviderConfig:        "",
+				SessionDisableHTTPOnly:       false,
+				SessionCookieLifeTime:        0, //set cookie default is the browser life
+				SessionAutoSetCookie:         true,
+				SessionDomain:                "",
+				SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
+				SessionNameInHTTPHeader:      "Beegosessionid",
+				SessionEnableSidInURLQuery:   false, // enable get the sessionId from Url Query params
+			},
+		},
+		Log: LogConfig{
+			AccessLogs:       false,
+			EnableStaticLogs: false,
+			AccessLogsFormat: "APACHE_FORMAT",
+			FileLineNum:      true,
+			Outputs:          map[string]string{"console": ""},
+		},
+	}
+}
+
+// now only support ini, next will support json.
+func parseConfig(appConfigPath string) (err error) {
+	AppConfig, err = newAppConfig(appConfigProvider, appConfigPath)
+	if err != nil {
+		return err
+	}
+	return assignConfig(AppConfig)
+}
+
+func assignConfig(ac config.Configer) error {
+	for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} {
+		assignSingleConfig(i, ac)
+	}
+	// set the run mode first
+	if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" {
+		BConfig.RunMode = envRunMode
+	} else if runMode := ac.String("RunMode"); runMode != "" {
+		BConfig.RunMode = runMode
+	}
+
+	if sd := ac.String("StaticDir"); sd != "" {
+		BConfig.WebConfig.StaticDir = map[string]string{}
+		sds := strings.Fields(sd)
+		for _, v := range sds {
+			if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
+				BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1]
+			} else {
+				BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0]
+			}
+		}
+	}
+
+	if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" {
+		extensions := strings.Split(sgz, ",")
+		fileExts := []string{}
+		for _, ext := range extensions {
+			ext = strings.TrimSpace(ext)
+			if ext == "" {
+				continue
+			}
+			if !strings.HasPrefix(ext, ".") {
+				ext = "." + ext
+			}
+			fileExts = append(fileExts, ext)
+		}
+		if len(fileExts) > 0 {
+			BConfig.WebConfig.StaticExtensionsToGzip = fileExts
+		}
+	}
+
+	if lo := ac.String("LogOutputs"); lo != "" {
+		// if lo is not nil or empty
+		// means user has set his own LogOutputs
+		// clear the default setting to BConfig.Log.Outputs
+		BConfig.Log.Outputs = make(map[string]string)
+		los := strings.Split(lo, ";")
+		for _, v := range los {
+			if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 {
+				BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1]
+			} else {
+				continue
+			}
+		}
+	}
+
+	//init log
+	//logs.Reset()
+	//for adaptor, config := range BConfig.Log.Outputs {
+	//	err := logs.SetLogger(adaptor, config)
+	//	if err != nil {
+	//		fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error()))
+	//	}
+	//}
+	//logs.SetLogFuncCall(BConfig.Log.FileLineNum)
+
+	return nil
+}
+
+func assignSingleConfig(p interface{}, ac config.Configer) {
+	pt := reflect.TypeOf(p)
+	if pt.Kind() != reflect.Ptr {
+		return
+	}
+	pt = pt.Elem()
+	if pt.Kind() != reflect.Struct {
+		return
+	}
+	pv := reflect.ValueOf(p).Elem()
+
+	for i := 0; i < pt.NumField(); i++ {
+		pf := pv.Field(i)
+		if !pf.CanSet() {
+			continue
+		}
+		name := pt.Field(i).Name
+		switch pf.Kind() {
+		case reflect.String:
+			pf.SetString(ac.DefaultString(name, pf.String()))
+		case reflect.Int, reflect.Int64:
+			pf.SetInt(ac.DefaultInt64(name, pf.Int()))
+		case reflect.Bool:
+			pf.SetBool(ac.DefaultBool(name, pf.Bool()))
+		case reflect.Struct:
+		default:
+			//do nothing here
+		}
+	}
+
+}
+
+// LoadAppConfig allow developer to apply a config file
+func LoadAppConfig(adapterName, configPath string) error {
+	absConfigPath, err := filepath.Abs(configPath)
+	if err != nil {
+		return err
+	}
+
+	if !utils.FileExists(absConfigPath) {
+		return fmt.Errorf("the target config file: %s don't exist", configPath)
+	}
+
+	appConfigPath = absConfigPath
+	appConfigProvider = adapterName
+
+	return parseConfig(appConfigPath)
+}
+
+type beegoAppConfig struct {
+	innerConfig config.Configer
+}
+
+func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) {
+	ac, err := config.NewConfig(appConfigProvider, appConfigPath)
+	if err != nil {
+		return nil, err
+	}
+	return &beegoAppConfig{ac}, nil
+}
+
+func (b *beegoAppConfig) Set(key, val string) error {
+	if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil {
+		return err
+	}
+	return b.innerConfig.Set(key, val)
+}
+
+func (b *beegoAppConfig) String(key string) string {
+	if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" {
+		return v
+	}
+	return b.innerConfig.String(key)
+}
+
+func (b *beegoAppConfig) Strings(key string) []string {
+	if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 {
+		return v
+	}
+	return b.innerConfig.Strings(key)
+}
+
+func (b *beegoAppConfig) Int(key string) (int, error) {
+	if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil {
+		return v, nil
+	}
+	return b.innerConfig.Int(key)
+}
+
+func (b *beegoAppConfig) Int64(key string) (int64, error) {
+	if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil {
+		return v, nil
+	}
+	return b.innerConfig.Int64(key)
+}
+
+func (b *beegoAppConfig) Bool(key string) (bool, error) {
+	if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil {
+		return v, nil
+	}
+	return b.innerConfig.Bool(key)
+}
+
+func (b *beegoAppConfig) Float(key string) (float64, error) {
+	if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil {
+		return v, nil
+	}
+	return b.innerConfig.Float(key)
+}
+
+func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string {
+	if v := b.String(key); v != "" {
+		return v
+	}
+	return defaultVal
+}
+
+func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string {
+	if v := b.Strings(key); len(v) != 0 {
+		return v
+	}
+	return defaultVal
+}
+
+func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int {
+	if v, err := b.Int(key); err == nil {
+		return v
+	}
+	return defaultVal
+}
+
+func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 {
+	if v, err := b.Int64(key); err == nil {
+		return v
+	}
+	return defaultVal
+}
+
+func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool {
+	if v, err := b.Bool(key); err == nil {
+		return v
+	}
+	return defaultVal
+}
+
+func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 {
+	if v, err := b.Float(key); err == nil {
+		return v
+	}
+	return defaultVal
+}
+
+func (b *beegoAppConfig) DIY(key string) (interface{}, error) {
+	return b.innerConfig.DIY(key)
+}
+
+func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) {
+	return b.innerConfig.GetSection(section)
+}
+
+func (b *beegoAppConfig) SaveConfigFile(filename string) error {
+	return b.innerConfig.SaveConfigFile(filename)
+}

+ 242 - 0
vender/github.com/astaxie/beego/config/config.go

@@ -0,0 +1,242 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package config is used to parse config.
+// Usage:
+//  import "github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+//Examples.
+//
+//  cnf, err := config.NewConfig("ini", "config.conf")
+//
+//  cnf APIS:
+//
+//  cnf.Set(key, val string) error
+//  cnf.String(key string) string
+//  cnf.Strings(key string) []string
+//  cnf.Int(key string) (int, error)
+//  cnf.Int64(key string) (int64, error)
+//  cnf.Bool(key string) (bool, error)
+//  cnf.Float(key string) (float64, error)
+//  cnf.DefaultString(key string, defaultVal string) string
+//  cnf.DefaultStrings(key string, defaultVal []string) []string
+//  cnf.DefaultInt(key string, defaultVal int) int
+//  cnf.DefaultInt64(key string, defaultVal int64) int64
+//  cnf.DefaultBool(key string, defaultVal bool) bool
+//  cnf.DefaultFloat(key string, defaultVal float64) float64
+//  cnf.DIY(key string) (interface{}, error)
+//  cnf.GetSection(section string) (map[string]string, error)
+//  cnf.SaveConfigFile(filename string) error
+//More docs http://beego.me/docs/module/config.md
+package config
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"time"
+)
+
+// Configer defines how to get and set value from configuration raw data.
+type Configer interface {
+	Set(key, val string) error   //support section::key type in given key when using ini type.
+	String(key string) string    //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
+	Strings(key string) []string //get string slice
+	Int(key string) (int, error)
+	Int64(key string) (int64, error)
+	Bool(key string) (bool, error)
+	Float(key string) (float64, error)
+	DefaultString(key string, defaultVal string) string      // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
+	DefaultStrings(key string, defaultVal []string) []string //get string slice
+	DefaultInt(key string, defaultVal int) int
+	DefaultInt64(key string, defaultVal int64) int64
+	DefaultBool(key string, defaultVal bool) bool
+	DefaultFloat(key string, defaultVal float64) float64
+	DIY(key string) (interface{}, error)
+	GetSection(section string) (map[string]string, error)
+	SaveConfigFile(filename string) error
+}
+
+// Config is the adapter interface for parsing config file to get raw data to Configer.
+type Config interface {
+	Parse(key string) (Configer, error)
+	ParseData(data []byte) (Configer, error)
+}
+
+var adapters = make(map[string]Config)
+
+// Register makes a config adapter available by the adapter name.
+// If Register is called twice with the same name or if driver is nil,
+// it panics.
+func Register(name string, adapter Config) {
+	if adapter == nil {
+		panic("config: Register adapter is nil")
+	}
+	if _, ok := adapters[name]; ok {
+		panic("config: Register called twice for adapter " + name)
+	}
+	adapters[name] = adapter
+}
+
+// NewConfig adapterName is ini/json/xml/yaml.
+// filename is the config file path.
+func NewConfig(adapterName, filename string) (Configer, error) {
+	adapter, ok := adapters[adapterName]
+	if !ok {
+		return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
+	}
+	return adapter.Parse(filename)
+}
+
+// NewConfigData adapterName is ini/json/xml/yaml.
+// data is the config data.
+func NewConfigData(adapterName string, data []byte) (Configer, error) {
+	adapter, ok := adapters[adapterName]
+	if !ok {
+		return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName)
+	}
+	return adapter.ParseData(data)
+}
+
+// ExpandValueEnvForMap convert all string value with environment variable.
+func ExpandValueEnvForMap(m map[string]interface{}) map[string]interface{} {
+	for k, v := range m {
+		switch value := v.(type) {
+		case string:
+			m[k] = ExpandValueEnv(value)
+		case map[string]interface{}:
+			m[k] = ExpandValueEnvForMap(value)
+		case map[string]string:
+			for k2, v2 := range value {
+				value[k2] = ExpandValueEnv(v2)
+			}
+			m[k] = value
+		}
+	}
+	return m
+}
+
+// ExpandValueEnv returns value of convert with environment variable.
+//
+// Return environment variable if value start with "${" and end with "}".
+// Return default value if environment variable is empty or not exist.
+//
+// It accept value formats "${env}" , "${env||}}" , "${env||defaultValue}" , "defaultvalue".
+// Examples:
+//	v1 := config.ExpandValueEnv("${GOPATH}")			// return the GOPATH environment variable.
+//	v2 := config.ExpandValueEnv("${GOAsta||/usr/local/go}")	// return the default value "/usr/local/go/".
+//	v3 := config.ExpandValueEnv("Astaxie")				// return the value "Astaxie".
+func ExpandValueEnv(value string) (realValue string) {
+	realValue = value
+
+	vLen := len(value)
+	// 3 = ${}
+	if vLen < 3 {
+		return
+	}
+	// Need start with "${" and end with "}", then return.
+	if value[0] != '$' || value[1] != '{' || value[vLen-1] != '}' {
+		return
+	}
+
+	key := ""
+	defaultV := ""
+	// value start with "${"
+	for i := 2; i < vLen; i++ {
+		if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') {
+			key = value[2:i]
+			defaultV = value[i+2 : vLen-1] // other string is default value.
+			break
+		} else if value[i] == '}' {
+			key = value[2:i]
+			break
+		}
+	}
+
+	realValue = os.Getenv(key)
+	if realValue == "" {
+		realValue = defaultV
+	}
+
+	return
+}
+
+// ParseBool returns the boolean value represented by the string.
+//
+// It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On,
+// 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off.
+// Any other value returns an error.
+func ParseBool(val interface{}) (value bool, err error) {
+	if val != nil {
+		switch v := val.(type) {
+		case bool:
+			return v, nil
+		case string:
+			switch v {
+			case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On":
+				return true, nil
+			case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off":
+				return false, nil
+			}
+		case int8, int32, int64:
+			strV := fmt.Sprintf("%d", v)
+			if strV == "1" {
+				return true, nil
+			} else if strV == "0" {
+				return false, nil
+			}
+		case float64:
+			if v == 1.0 {
+				return true, nil
+			} else if v == 0.0 {
+				return false, nil
+			}
+		}
+		return false, fmt.Errorf("parsing %q: invalid syntax", val)
+	}
+	return false, fmt.Errorf("parsing <nil>: invalid syntax")
+}
+
+// ToString converts values of any type to string.
+func ToString(x interface{}) string {
+	switch y := x.(type) {
+
+	// Handle dates with special logic
+	// This needs to come above the fmt.Stringer
+	// test since time.Time's have a .String()
+	// method
+	case time.Time:
+		return y.Format("A Monday")
+
+	// Handle type string
+	case string:
+		return y
+
+	// Handle type with .String() method
+	case fmt.Stringer:
+		return y.String()
+
+	// Handle type with .Error() method
+	case error:
+		return y.Error()
+
+	}
+
+	// Handle named string type
+	if v := reflect.ValueOf(x); v.Kind() == reflect.String {
+		return v.String()
+	}
+
+	// Fallback to fmt package for anything else like numeric types
+	return fmt.Sprint(x)
+}

+ 55 - 0
vender/github.com/astaxie/beego/config/config_test.go

@@ -0,0 +1,55 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"os"
+	"testing"
+)
+
+func TestExpandValueEnv(t *testing.T) {
+
+	testCases := []struct {
+		item string
+		want string
+	}{
+		{"", ""},
+		{"$", "$"},
+		{"{", "{"},
+		{"{}", "{}"},
+		{"${}", ""},
+		{"${|}", ""},
+		{"${}", ""},
+		{"${{}}", ""},
+		{"${{||}}", "}"},
+		{"${pwd||}", ""},
+		{"${pwd||}", ""},
+		{"${pwd||}", ""},
+		{"${pwd||}}", "}"},
+		{"${pwd||{{||}}}", "{{||}}"},
+		{"${GOPATH}", os.Getenv("GOPATH")},
+		{"${GOPATH||}", os.Getenv("GOPATH")},
+		{"${GOPATH||root}", os.Getenv("GOPATH")},
+		{"${GOPATH_NOT||root}", "root"},
+		{"${GOPATH_NOT||||root}", "||root"},
+	}
+
+	for _, c := range testCases {
+		if got := ExpandValueEnv(c.item); got != c.want {
+			t.Errorf("expand value error, item %q want %q, got %q", c.item, c.want, got)
+		}
+	}
+
+}

+ 87 - 0
vender/github.com/astaxie/beego/config/env/env.go

@@ -0,0 +1,87 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+// Copyright 2017 Faissal Elamraoui. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package env is used to parse environment.
+package env
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
+)
+
+var env *utils.BeeMap
+
+func init() {
+	env = utils.NewBeeMap()
+	for _, e := range os.Environ() {
+		splits := strings.Split(e, "=")
+		env.Set(splits[0], os.Getenv(splits[0]))
+	}
+}
+
+// Get returns a value by key.
+// If the key does not exist, the default value will be returned.
+func Get(key string, defVal string) string {
+	if val := env.Get(key); val != nil {
+		return val.(string)
+	}
+	return defVal
+}
+
+// MustGet returns a value by key.
+// If the key does not exist, it will return an error.
+func MustGet(key string) (string, error) {
+	if val := env.Get(key); val != nil {
+		return val.(string), nil
+	}
+	return "", fmt.Errorf("no env variable with %s", key)
+}
+
+// Set sets a value in the ENV copy.
+// This does not affect the child process environment.
+func Set(key string, value string) {
+	env.Set(key, value)
+}
+
+// MustSet sets a value in the ENV copy and the child process environment.
+// It returns an error in case the set operation failed.
+func MustSet(key string, value string) error {
+	err := os.Setenv(key, value)
+	if err != nil {
+		return err
+	}
+	env.Set(key, value)
+	return nil
+}
+
+// GetAll returns all keys/values in the current child process environment.
+func GetAll() map[string]string {
+	items := env.Items()
+	envs := make(map[string]string, env.Count())
+
+	for key, val := range items {
+		switch key := key.(type) {
+		case string:
+			switch val := val.(type) {
+			case string:
+				envs[key] = val
+			}
+		}
+	}
+	return envs
+}

+ 75 - 0
vender/github.com/astaxie/beego/config/env/env_test.go

@@ -0,0 +1,75 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+// Copyright 2017 Faissal Elamraoui. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package env
+
+import (
+	"os"
+	"testing"
+)
+
+func TestEnvGet(t *testing.T) {
+	gopath := Get("GOPATH", "")
+	if gopath != os.Getenv("GOPATH") {
+		t.Error("expected GOPATH not empty.")
+	}
+
+	noExistVar := Get("NOEXISTVAR", "foo")
+	if noExistVar != "foo" {
+		t.Errorf("expected NOEXISTVAR to equal foo, got %s.", noExistVar)
+	}
+}
+
+func TestEnvMustGet(t *testing.T) {
+	gopath, err := MustGet("GOPATH")
+	if err != nil {
+		t.Error(err)
+	}
+
+	if gopath != os.Getenv("GOPATH") {
+		t.Errorf("expected GOPATH to be the same, got %s.", gopath)
+	}
+
+	_, err = MustGet("NOEXISTVAR")
+	if err == nil {
+		t.Error("expected error to be non-nil")
+	}
+}
+
+func TestEnvSet(t *testing.T) {
+	Set("MYVAR", "foo")
+	myVar := Get("MYVAR", "bar")
+	if myVar != "foo" {
+		t.Errorf("expected MYVAR to equal foo, got %s.", myVar)
+	}
+}
+
+func TestEnvMustSet(t *testing.T) {
+	err := MustSet("FOO", "bar")
+	if err != nil {
+		t.Error(err)
+	}
+
+	fooVar := os.Getenv("FOO")
+	if fooVar != "bar" {
+		t.Errorf("expected FOO variable to equal bar, got %s.", fooVar)
+	}
+}
+
+func TestEnvGetAll(t *testing.T) {
+	envMap := GetAll()
+	if len(envMap) == 0 {
+		t.Error("expected environment not empty.")
+	}
+}

+ 134 - 0
vender/github.com/astaxie/beego/config/fake.go

@@ -0,0 +1,134 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"errors"
+	"strconv"
+	"strings"
+)
+
+type fakeConfigContainer struct {
+	data map[string]string
+}
+
+func (c *fakeConfigContainer) getData(key string) string {
+	return c.data[strings.ToLower(key)]
+}
+
+func (c *fakeConfigContainer) Set(key, val string) error {
+	c.data[strings.ToLower(key)] = val
+	return nil
+}
+
+func (c *fakeConfigContainer) String(key string) string {
+	return c.getData(key)
+}
+
+func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string {
+	v := c.String(key)
+	if v == "" {
+		return defaultval
+	}
+	return v
+}
+
+func (c *fakeConfigContainer) Strings(key string) []string {
+	v := c.String(key)
+	if v == "" {
+		return nil
+	}
+	return strings.Split(v, ";")
+}
+
+func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	v := c.Strings(key)
+	if v == nil {
+		return defaultval
+	}
+	return v
+}
+
+func (c *fakeConfigContainer) Int(key string) (int, error) {
+	return strconv.Atoi(c.getData(key))
+}
+
+func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int {
+	v, err := c.Int(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+func (c *fakeConfigContainer) Int64(key string) (int64, error) {
+	return strconv.ParseInt(c.getData(key), 10, 64)
+}
+
+func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	v, err := c.Int64(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+func (c *fakeConfigContainer) Bool(key string) (bool, error) {
+	return ParseBool(c.getData(key))
+}
+
+func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	v, err := c.Bool(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+func (c *fakeConfigContainer) Float(key string) (float64, error) {
+	return strconv.ParseFloat(c.getData(key), 64)
+}
+
+func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	v, err := c.Float(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+func (c *fakeConfigContainer) DIY(key string) (interface{}, error) {
+	if v, ok := c.data[strings.ToLower(key)]; ok {
+		return v, nil
+	}
+	return nil, errors.New("key not find")
+}
+
+func (c *fakeConfigContainer) GetSection(section string) (map[string]string, error) {
+	return nil, errors.New("not implement in the fakeConfigContainer")
+}
+
+func (c *fakeConfigContainer) SaveConfigFile(filename string) error {
+	return errors.New("not implement in the fakeConfigContainer")
+}
+
+var _ Configer = new(fakeConfigContainer)
+
+// NewFakeConfig return a fake Configer
+func NewFakeConfig() Configer {
+	return &fakeConfigContainer{
+		data: make(map[string]string),
+	}
+}

+ 504 - 0
vender/github.com/astaxie/beego/config/ini.go

@@ -0,0 +1,504 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/user"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+var (
+	defaultSection = "default"   // default section means if some ini items not in a section, make them in default section,
+	bNumComment    = []byte{'#'} // number signal
+	bSemComment    = []byte{';'} // semicolon signal
+	bEmpty         = []byte{}
+	bEqual         = []byte{'='} // equal signal
+	bDQuote        = []byte{'"'} // quote signal
+	sectionStart   = []byte{'['} // section start signal
+	sectionEnd     = []byte{']'} // section end signal
+	lineBreak      = "\n"
+)
+
+// IniConfig implements Config to parse ini file.
+type IniConfig struct {
+}
+
+// Parse creates a new Config and parses the file configuration from the named file.
+func (ini *IniConfig) Parse(name string) (Configer, error) {
+	return ini.parseFile(name)
+}
+
+func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
+	data, err := ioutil.ReadFile(name)
+	if err != nil {
+		return nil, err
+	}
+
+	return ini.parseData(filepath.Dir(name), data)
+}
+
+func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) {
+	cfg := &IniConfigContainer{
+		data:           make(map[string]map[string]string),
+		sectionComment: make(map[string]string),
+		keyComment:     make(map[string]string),
+		RWMutex:        sync.RWMutex{},
+	}
+	cfg.Lock()
+	defer cfg.Unlock()
+
+	var comment bytes.Buffer
+	buf := bufio.NewReader(bytes.NewBuffer(data))
+	// check the BOM
+	head, err := buf.Peek(3)
+	if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
+		for i := 1; i <= 3; i++ {
+			buf.ReadByte()
+		}
+	}
+	section := defaultSection
+	tmpBuf := bytes.NewBuffer(nil)
+	for {
+		tmpBuf.Reset()
+
+		shouldBreak := false
+		for {
+			tmp, isPrefix, err := buf.ReadLine()
+			if err == io.EOF {
+				shouldBreak = true
+				break
+			}
+
+			//It might be a good idea to throw a error on all unknonw errors?
+			if _, ok := err.(*os.PathError); ok {
+				return nil, err
+			}
+
+			tmpBuf.Write(tmp)
+			if isPrefix {
+				continue
+			}
+
+			if !isPrefix {
+				break
+			}
+		}
+		if shouldBreak {
+			break
+		}
+
+		line := tmpBuf.Bytes()
+		line = bytes.TrimSpace(line)
+		if bytes.Equal(line, bEmpty) {
+			continue
+		}
+		var bComment []byte
+		switch {
+		case bytes.HasPrefix(line, bNumComment):
+			bComment = bNumComment
+		case bytes.HasPrefix(line, bSemComment):
+			bComment = bSemComment
+		}
+		if bComment != nil {
+			line = bytes.TrimLeft(line, string(bComment))
+			// Need append to a new line if multi-line comments.
+			if comment.Len() > 0 {
+				comment.WriteByte('\n')
+			}
+			comment.Write(line)
+			continue
+		}
+
+		if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) {
+			section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive
+			if comment.Len() > 0 {
+				cfg.sectionComment[section] = comment.String()
+				comment.Reset()
+			}
+			if _, ok := cfg.data[section]; !ok {
+				cfg.data[section] = make(map[string]string)
+			}
+			continue
+		}
+
+		if _, ok := cfg.data[section]; !ok {
+			cfg.data[section] = make(map[string]string)
+		}
+		keyValue := bytes.SplitN(line, bEqual, 2)
+
+		key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
+		key = strings.ToLower(key)
+
+		// handle include "other.conf"
+		if len(keyValue) == 1 && strings.HasPrefix(key, "include") {
+
+			includefiles := strings.Fields(key)
+			if includefiles[0] == "include" && len(includefiles) == 2 {
+
+				otherfile := strings.Trim(includefiles[1], "\"")
+				if !filepath.IsAbs(otherfile) {
+					otherfile = filepath.Join(dir, otherfile)
+				}
+
+				i, err := ini.parseFile(otherfile)
+				if err != nil {
+					return nil, err
+				}
+
+				for sec, dt := range i.data {
+					if _, ok := cfg.data[sec]; !ok {
+						cfg.data[sec] = make(map[string]string)
+					}
+					for k, v := range dt {
+						cfg.data[sec][k] = v
+					}
+				}
+
+				for sec, comm := range i.sectionComment {
+					cfg.sectionComment[sec] = comm
+				}
+
+				for k, comm := range i.keyComment {
+					cfg.keyComment[k] = comm
+				}
+
+				continue
+			}
+		}
+
+		if len(keyValue) != 2 {
+			return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val")
+		}
+		val := bytes.TrimSpace(keyValue[1])
+		if bytes.HasPrefix(val, bDQuote) {
+			val = bytes.Trim(val, `"`)
+		}
+
+		cfg.data[section][key] = ExpandValueEnv(string(val))
+		if comment.Len() > 0 {
+			cfg.keyComment[section+"."+key] = comment.String()
+			comment.Reset()
+		}
+
+	}
+	return cfg, nil
+}
+
+// ParseData parse ini the data
+// When include other.conf,other.conf is either absolute directory
+// or under beego in default temporary directory(/tmp/beego[-username]).
+func (ini *IniConfig) ParseData(data []byte) (Configer, error) {
+	dir := "beego"
+	currentUser, err := user.Current()
+	if err == nil {
+		dir = "beego-" + currentUser.Username
+	}
+	dir = filepath.Join(os.TempDir(), dir)
+	if err = os.MkdirAll(dir, os.ModePerm); err != nil {
+		return nil, err
+	}
+
+	return ini.parseData(dir, data)
+}
+
+// IniConfigContainer A Config represents the ini configuration.
+// When set and get value, support key as section:name type.
+type IniConfigContainer struct {
+	data           map[string]map[string]string // section=> key:val
+	sectionComment map[string]string            // section : comment
+	keyComment     map[string]string            // id: []{comment, key...}; id 1 is for main comment.
+	sync.RWMutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *IniConfigContainer) Bool(key string) (bool, error) {
+	return ParseBool(c.getdata(key))
+}
+
+// DefaultBool returns the boolean value for a given key.
+// if err != nil return defaultval
+func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	v, err := c.Bool(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int returns the integer value for a given key.
+func (c *IniConfigContainer) Int(key string) (int, error) {
+	return strconv.Atoi(c.getdata(key))
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaultval
+func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int {
+	v, err := c.Int(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *IniConfigContainer) Int64(key string) (int64, error) {
+	return strconv.ParseInt(c.getdata(key), 10, 64)
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaultval
+func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	v, err := c.Int64(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Float returns the float value for a given key.
+func (c *IniConfigContainer) Float(key string) (float64, error) {
+	return strconv.ParseFloat(c.getdata(key), 64)
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaultval
+func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	v, err := c.Float(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// String returns the string value for a given key.
+func (c *IniConfigContainer) String(key string) string {
+	return c.getdata(key)
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaultval
+func (c *IniConfigContainer) DefaultString(key string, defaultval string) string {
+	v := c.String(key)
+	if v == "" {
+		return defaultval
+	}
+	return v
+}
+
+// Strings returns the []string value for a given key.
+// Return nil if config value does not exist or is empty.
+func (c *IniConfigContainer) Strings(key string) []string {
+	v := c.String(key)
+	if v == "" {
+		return nil
+	}
+	return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaultval
+func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	v := c.Strings(key)
+	if v == nil {
+		return defaultval
+	}
+	return v
+}
+
+// GetSection returns map for the given section
+func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) {
+	if v, ok := c.data[section]; ok {
+		return v, nil
+	}
+	return nil, errors.New("not exist section")
+}
+
+// SaveConfigFile save the config into file.
+//
+// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Function.
+func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
+	// Write configuration file by filename.
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	// Get section or key comments. Fixed #1607
+	getCommentStr := func(section, key string) string {
+		var (
+			comment string
+			ok      bool
+		)
+		if len(key) == 0 {
+			comment, ok = c.sectionComment[section]
+		} else {
+			comment, ok = c.keyComment[section+"."+key]
+		}
+
+		if ok {
+			// Empty comment
+			if len(comment) == 0 || len(strings.TrimSpace(comment)) == 0 {
+				return string(bNumComment)
+			}
+			prefix := string(bNumComment)
+			// Add the line head character "#"
+			return prefix + strings.Replace(comment, lineBreak, lineBreak+prefix, -1)
+		}
+		return ""
+	}
+
+	buf := bytes.NewBuffer(nil)
+	// Save default section at first place
+	if dt, ok := c.data[defaultSection]; ok {
+		for key, val := range dt {
+			if key != " " {
+				// Write key comments.
+				if v := getCommentStr(defaultSection, key); len(v) > 0 {
+					if _, err = buf.WriteString(v + lineBreak); err != nil {
+						return err
+					}
+				}
+
+				// Write key and value.
+				if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil {
+					return err
+				}
+			}
+		}
+
+		// Put a line between sections.
+		if _, err = buf.WriteString(lineBreak); err != nil {
+			return err
+		}
+	}
+	// Save named sections
+	for section, dt := range c.data {
+		if section != defaultSection {
+			// Write section comments.
+			if v := getCommentStr(section, ""); len(v) > 0 {
+				if _, err = buf.WriteString(v + lineBreak); err != nil {
+					return err
+				}
+			}
+
+			// Write section name.
+			if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil {
+				return err
+			}
+
+			for key, val := range dt {
+				if key != " " {
+					// Write key comments.
+					if v := getCommentStr(section, key); len(v) > 0 {
+						if _, err = buf.WriteString(v + lineBreak); err != nil {
+							return err
+						}
+					}
+
+					// Write key and value.
+					if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil {
+						return err
+					}
+				}
+			}
+
+			// Put a line between sections.
+			if _, err = buf.WriteString(lineBreak); err != nil {
+				return err
+			}
+		}
+	}
+	_, err = buf.WriteTo(f)
+	return err
+}
+
+// Set writes a new value for key.
+// if write to one section, the key need be "section::key".
+// if the section is not existed, it panics.
+func (c *IniConfigContainer) Set(key, value string) error {
+	c.Lock()
+	defer c.Unlock()
+	if len(key) == 0 {
+		return errors.New("key is empty")
+	}
+
+	var (
+		section, k string
+		sectionKey = strings.Split(strings.ToLower(key), "::")
+	)
+
+	if len(sectionKey) >= 2 {
+		section = sectionKey[0]
+		k = sectionKey[1]
+	} else {
+		section = defaultSection
+		k = sectionKey[0]
+	}
+
+	if _, ok := c.data[section]; !ok {
+		c.data[section] = make(map[string]string)
+	}
+	c.data[section][k] = value
+	return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) {
+	if v, ok := c.data[strings.ToLower(key)]; ok {
+		return v, nil
+	}
+	return v, errors.New("key not find")
+}
+
+// section.key or key
+func (c *IniConfigContainer) getdata(key string) string {
+	if len(key) == 0 {
+		return ""
+	}
+	c.RLock()
+	defer c.RUnlock()
+
+	var (
+		section, k string
+		sectionKey = strings.Split(strings.ToLower(key), "::")
+	)
+	if len(sectionKey) >= 2 {
+		section = sectionKey[0]
+		k = sectionKey[1]
+	} else {
+		section = defaultSection
+		k = sectionKey[0]
+	}
+	if v, ok := c.data[section]; ok {
+		if vv, ok := v[k]; ok {
+			return vv
+		}
+	}
+	return ""
+}
+
+func init() {
+	Register("ini", &IniConfig{})
+}

+ 190 - 0
vender/github.com/astaxie/beego/config/ini_test.go

@@ -0,0 +1,190 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+)
+
+func TestIni(t *testing.T) {
+
+	var (
+		inicontext = `
+;comment one
+#comment two
+appname = beeapi
+httpport = 8080
+mysqlport = 3600
+PI = 3.1415976
+runmode = "dev"
+autorender = false
+copyrequestbody = true
+session= on
+cookieon= off
+newreg = OFF
+needlogin = ON
+enableSession = Y
+enableCookie = N
+flag = 1
+path1 = ${GOPATH}
+path2 = ${GOPATH||/home/go}
+[demo]
+key1="asta"
+key2 = "xie"
+CaseInsensitive = true
+peers = one;two;three
+password = ${GOPATH}
+`
+
+		keyValue = map[string]interface{}{
+			"appname":               "beeapi",
+			"httpport":              8080,
+			"mysqlport":             int64(3600),
+			"pi":                    3.1415976,
+			"runmode":               "dev",
+			"autorender":            false,
+			"copyrequestbody":       true,
+			"session":               true,
+			"cookieon":              false,
+			"newreg":                false,
+			"needlogin":             true,
+			"enableSession":         true,
+			"enableCookie":          false,
+			"flag":                  true,
+			"path1":                 os.Getenv("GOPATH"),
+			"path2":                 os.Getenv("GOPATH"),
+			"demo::key1":            "asta",
+			"demo::key2":            "xie",
+			"demo::CaseInsensitive": true,
+			"demo::peers":           []string{"one", "two", "three"},
+			"demo::password":        os.Getenv("GOPATH"),
+			"null":                  "",
+			"demo2::key1":           "",
+			"error":                 "",
+			"emptystrings":          []string{},
+		}
+	)
+
+	f, err := os.Create("testini.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(inicontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testini.conf")
+	iniconf, err := NewConfig("ini", "testini.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	for k, v := range keyValue {
+		var err error
+		var value interface{}
+		switch v.(type) {
+		case int:
+			value, err = iniconf.Int(k)
+		case int64:
+			value, err = iniconf.Int64(k)
+		case float64:
+			value, err = iniconf.Float(k)
+		case bool:
+			value, err = iniconf.Bool(k)
+		case []string:
+			value = iniconf.Strings(k)
+		case string:
+			value = iniconf.String(k)
+		default:
+			value, err = iniconf.DIY(k)
+		}
+		if err != nil {
+			t.Fatalf("get key %q value fail,err %s", k, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Fatalf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+	if err = iniconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if iniconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+
+}
+
+func TestIniSave(t *testing.T) {
+
+	const (
+		inicontext = `
+app = app
+;comment one
+#comment two
+# comment three
+appname = beeapi
+httpport = 8080
+# DB Info
+# enable db
+[dbinfo]
+# db type name
+# suport mysql,sqlserver
+name = mysql
+`
+
+		saveResult = `
+app=app
+#comment one
+#comment two
+# comment three
+appname=beeapi
+httpport=8080
+
+# DB Info
+# enable db
+[dbinfo]
+# db type name
+# suport mysql,sqlserver
+name=mysql
+`
+	)
+	cfg, err := NewConfigData("ini", []byte(inicontext))
+	if err != nil {
+		t.Fatal(err)
+	}
+	name := "newIniConfig.ini"
+	if err := cfg.SaveConfigFile(name); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(name)
+
+	if data, err := ioutil.ReadFile(name); err != nil {
+		t.Fatal(err)
+	} else {
+		cfgData := string(data)
+		datas := strings.Split(saveResult, "\n")
+		for _, line := range datas {
+			if !strings.Contains(cfgData, line+"\n") {
+				t.Fatalf("different after save ini config file. need contains %q", line)
+			}
+		}
+
+	}
+}

+ 266 - 0
vender/github.com/astaxie/beego/config/json.go

@@ -0,0 +1,266 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+	"sync"
+)
+
+// JSONConfig is a json config parser and implements Config interface.
+type JSONConfig struct {
+}
+
+// Parse returns a ConfigContainer with parsed json config map.
+func (js *JSONConfig) Parse(filename string) (Configer, error) {
+	file, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+	content, err := ioutil.ReadAll(file)
+	if err != nil {
+		return nil, err
+	}
+
+	return js.ParseData(content)
+}
+
+// ParseData returns a ConfigContainer with json string
+func (js *JSONConfig) ParseData(data []byte) (Configer, error) {
+	x := &JSONConfigContainer{
+		data: make(map[string]interface{}),
+	}
+	err := json.Unmarshal(data, &x.data)
+	if err != nil {
+		var wrappingArray []interface{}
+		err2 := json.Unmarshal(data, &wrappingArray)
+		if err2 != nil {
+			return nil, err
+		}
+		x.data["rootArray"] = wrappingArray
+	}
+
+	x.data = ExpandValueEnvForMap(x.data)
+
+	return x, nil
+}
+
+// JSONConfigContainer A Config represents the json configuration.
+// Only when get value, support key as section:name type.
+type JSONConfigContainer struct {
+	data map[string]interface{}
+	sync.RWMutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *JSONConfigContainer) Bool(key string) (bool, error) {
+	val := c.getData(key)
+	if val != nil {
+		return ParseBool(val)
+	}
+	return false, fmt.Errorf("not exist key: %q", key)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *JSONConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	if v, err := c.Bool(key); err == nil {
+		return v
+	}
+	return defaultval
+}
+
+// Int returns the integer value for a given key.
+func (c *JSONConfigContainer) Int(key string) (int, error) {
+	val := c.getData(key)
+	if val != nil {
+		if v, ok := val.(float64); ok {
+			return int(v), nil
+		}
+		return 0, errors.New("not int value")
+	}
+	return 0, errors.New("not exist key:" + key)
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaultval
+func (c *JSONConfigContainer) DefaultInt(key string, defaultval int) int {
+	if v, err := c.Int(key); err == nil {
+		return v
+	}
+	return defaultval
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *JSONConfigContainer) Int64(key string) (int64, error) {
+	val := c.getData(key)
+	if val != nil {
+		if v, ok := val.(float64); ok {
+			return int64(v), nil
+		}
+		return 0, errors.New("not int64 value")
+	}
+	return 0, errors.New("not exist key:" + key)
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaultval
+func (c *JSONConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	if v, err := c.Int64(key); err == nil {
+		return v
+	}
+	return defaultval
+}
+
+// Float returns the float value for a given key.
+func (c *JSONConfigContainer) Float(key string) (float64, error) {
+	val := c.getData(key)
+	if val != nil {
+		if v, ok := val.(float64); ok {
+			return v, nil
+		}
+		return 0.0, errors.New("not float64 value")
+	}
+	return 0.0, errors.New("not exist key:" + key)
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaultval
+func (c *JSONConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	if v, err := c.Float(key); err == nil {
+		return v
+	}
+	return defaultval
+}
+
+// String returns the string value for a given key.
+func (c *JSONConfigContainer) String(key string) string {
+	val := c.getData(key)
+	if val != nil {
+		if v, ok := val.(string); ok {
+			return v
+		}
+	}
+	return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaultval
+func (c *JSONConfigContainer) DefaultString(key string, defaultval string) string {
+	// TODO FIXME should not use "" to replace non existence
+	if v := c.String(key); v != "" {
+		return v
+	}
+	return defaultval
+}
+
+// Strings returns the []string value for a given key.
+func (c *JSONConfigContainer) Strings(key string) []string {
+	stringVal := c.String(key)
+	if stringVal == "" {
+		return nil
+	}
+	return strings.Split(c.String(key), ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaultval
+func (c *JSONConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	if v := c.Strings(key); v != nil {
+		return v
+	}
+	return defaultval
+}
+
+// GetSection returns map for the given section
+func (c *JSONConfigContainer) GetSection(section string) (map[string]string, error) {
+	if v, ok := c.data[section]; ok {
+		return v.(map[string]string), nil
+	}
+	return nil, errors.New("nonexist section " + section)
+}
+
+// SaveConfigFile save the config into file
+func (c *JSONConfigContainer) SaveConfigFile(filename string) (err error) {
+	// Write configuration file by filename.
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	b, err := json.MarshalIndent(c.data, "", "  ")
+	if err != nil {
+		return err
+	}
+	_, err = f.Write(b)
+	return err
+}
+
+// Set writes a new value for key.
+func (c *JSONConfigContainer) Set(key, val string) error {
+	c.Lock()
+	defer c.Unlock()
+	c.data[key] = val
+	return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *JSONConfigContainer) DIY(key string) (v interface{}, err error) {
+	val := c.getData(key)
+	if val != nil {
+		return val, nil
+	}
+	return nil, errors.New("not exist key")
+}
+
+// section.key or key
+func (c *JSONConfigContainer) getData(key string) interface{} {
+	if len(key) == 0 {
+		return nil
+	}
+
+	c.RLock()
+	defer c.RUnlock()
+
+	sectionKeys := strings.Split(key, "::")
+	if len(sectionKeys) >= 2 {
+		curValue, ok := c.data[sectionKeys[0]]
+		if !ok {
+			return nil
+		}
+		for _, key := range sectionKeys[1:] {
+			if v, ok := curValue.(map[string]interface{}); ok {
+				if curValue, ok = v[key]; !ok {
+					return nil
+				}
+			}
+		}
+		return curValue
+	}
+	if v, ok := c.data[key]; ok {
+		return v
+	}
+	return nil
+}
+
+func init() {
+	Register("json", &JSONConfig{})
+}

+ 222 - 0
vender/github.com/astaxie/beego/config/json_test.go

@@ -0,0 +1,222 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"fmt"
+	"os"
+	"testing"
+)
+
+func TestJsonStartsWithArray(t *testing.T) {
+
+	const jsoncontextwitharray = `[
+	{
+		"url": "user",
+		"serviceAPI": "http://www.test.com/user"
+	},
+	{
+		"url": "employee",
+		"serviceAPI": "http://www.test.com/employee"
+	}
+]`
+	f, err := os.Create("testjsonWithArray.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(jsoncontextwitharray)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testjsonWithArray.conf")
+	jsonconf, err := NewConfig("json", "testjsonWithArray.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	rootArray, err := jsonconf.DIY("rootArray")
+	if err != nil {
+		t.Error("array does not exist as element")
+	}
+	rootArrayCasted := rootArray.([]interface{})
+	if rootArrayCasted == nil {
+		t.Error("array from root is nil")
+	} else {
+		elem := rootArrayCasted[0].(map[string]interface{})
+		if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
+			t.Error("array[0] values are not valid")
+		}
+
+		elem2 := rootArrayCasted[1].(map[string]interface{})
+		if elem2["url"] != "employee" || elem2["serviceAPI"] != "http://www.test.com/employee" {
+			t.Error("array[1] values are not valid")
+		}
+	}
+}
+
+func TestJson(t *testing.T) {
+
+	var (
+		jsoncontext = `{
+"appname": "beeapi",
+"testnames": "foo;bar",
+"httpport": 8080,
+"mysqlport": 3600,
+"PI": 3.1415976, 
+"runmode": "dev",
+"autorender": false,
+"copyrequestbody": true,
+"session": "on",
+"cookieon": "off",
+"newreg": "OFF",
+"needlogin": "ON",
+"enableSession": "Y",
+"enableCookie": "N",
+"flag": 1,
+"path1": "${GOPATH}",
+"path2": "${GOPATH||/home/go}",
+"database": {
+        "host": "host",
+        "port": "port",
+        "database": "database",
+        "username": "username",
+        "password": "${GOPATH}",
+		"conns":{
+			"maxconnection":12,
+			"autoconnect":true,
+			"connectioninfo":"info",
+			"root": "${GOPATH}"
+		}
+    }
+}`
+		keyValue = map[string]interface{}{
+			"appname":                         "beeapi",
+			"testnames":                       []string{"foo", "bar"},
+			"httpport":                        8080,
+			"mysqlport":                       int64(3600),
+			"PI":                              3.1415976,
+			"runmode":                         "dev",
+			"autorender":                      false,
+			"copyrequestbody":                 true,
+			"session":                         true,
+			"cookieon":                        false,
+			"newreg":                          false,
+			"needlogin":                       true,
+			"enableSession":                   true,
+			"enableCookie":                    false,
+			"flag":                            true,
+			"path1":                           os.Getenv("GOPATH"),
+			"path2":                           os.Getenv("GOPATH"),
+			"database::host":                  "host",
+			"database::port":                  "port",
+			"database::database":              "database",
+			"database::password":              os.Getenv("GOPATH"),
+			"database::conns::maxconnection":  12,
+			"database::conns::autoconnect":    true,
+			"database::conns::connectioninfo": "info",
+			"database::conns::root":           os.Getenv("GOPATH"),
+			"unknown":                         "",
+		}
+	)
+
+	f, err := os.Create("testjson.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(jsoncontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testjson.conf")
+	jsonconf, err := NewConfig("json", "testjson.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for k, v := range keyValue {
+		var err error
+		var value interface{}
+		switch v.(type) {
+		case int:
+			value, err = jsonconf.Int(k)
+		case int64:
+			value, err = jsonconf.Int64(k)
+		case float64:
+			value, err = jsonconf.Float(k)
+		case bool:
+			value, err = jsonconf.Bool(k)
+		case []string:
+			value = jsonconf.Strings(k)
+		case string:
+			value = jsonconf.String(k)
+		default:
+			value, err = jsonconf.DIY(k)
+		}
+		if err != nil {
+			t.Fatalf("get key %q value fatal,%v err %s", k, v, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Fatalf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+	if err = jsonconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if jsonconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+
+	if db, err := jsonconf.DIY("database"); err != nil {
+		t.Fatal(err)
+	} else if m, ok := db.(map[string]interface{}); !ok {
+		t.Log(db)
+		t.Fatal("db not map[string]interface{}")
+	} else {
+		if m["host"].(string) != "host" {
+			t.Fatal("get host err")
+		}
+	}
+
+	if _, err := jsonconf.Int("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting an Int")
+	}
+
+	if _, err := jsonconf.Int64("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting an Int64")
+	}
+
+	if _, err := jsonconf.Float("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting a Float")
+	}
+
+	if _, err := jsonconf.DIY("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting an interface{}")
+	}
+
+	if val := jsonconf.String("unknown"); val != "" {
+		t.Error("unknown keys should return an empty string when expecting a String")
+	}
+
+	if _, err := jsonconf.Bool("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting a Bool")
+	}
+
+	if !jsonconf.DefaultBool("unknown", true) {
+		t.Error("unknown keys with default value wrong")
+	}
+}

+ 228 - 0
vender/github.com/astaxie/beego/config/xml/xml.go

@@ -0,0 +1,228 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package xml for config provider.
+//
+// depend on github.com/beego/x2j.
+//
+// go install github.com/beego/x2j.
+//
+// Usage:
+//  import(
+//    _ "github.com/cnlh/nps/vender/github.com/astaxie/beego/config/xml"
+//      "github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+//  )
+//
+//  cnf, err := config.NewConfig("xml", "config.xml")
+//
+//More docs http://beego.me/docs/module/config.md
+package xml
+
+import (
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/beego/x2j"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+)
+
+// Config is a xml config parser and implements Config interface.
+// xml configurations should be included in <config></config> tag.
+// only support key/value pair as <key>value</key> as each item.
+type Config struct{}
+
+// Parse returns a ConfigContainer with parsed xml config map.
+func (xc *Config) Parse(filename string) (config.Configer, error) {
+	context, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	return xc.ParseData(context)
+}
+
+// ParseData xml data
+func (xc *Config) ParseData(data []byte) (config.Configer, error) {
+	x := &ConfigContainer{data: make(map[string]interface{})}
+
+	d, err := x2j.DocToMap(string(data))
+	if err != nil {
+		return nil, err
+	}
+
+	x.data = config.ExpandValueEnvForMap(d["config"].(map[string]interface{}))
+
+	return x, nil
+}
+
+// ConfigContainer A Config represents the xml configuration.
+type ConfigContainer struct {
+	data map[string]interface{}
+	sync.Mutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *ConfigContainer) Bool(key string) (bool, error) {
+	if v := c.data[key]; v != nil {
+		return config.ParseBool(v)
+	}
+	return false, fmt.Errorf("not exist key: %q", key)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	v, err := c.Bool(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int returns the integer value for a given key.
+func (c *ConfigContainer) Int(key string) (int, error) {
+	return strconv.Atoi(c.data[key].(string))
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
+	v, err := c.Int(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *ConfigContainer) Int64(key string) (int64, error) {
+	return strconv.ParseInt(c.data[key].(string), 10, 64)
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	v, err := c.Int64(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+
+}
+
+// Float returns the float value for a given key.
+func (c *ConfigContainer) Float(key string) (float64, error) {
+	return strconv.ParseFloat(c.data[key].(string), 64)
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	v, err := c.Float(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// String returns the string value for a given key.
+func (c *ConfigContainer) String(key string) string {
+	if v, ok := c.data[key].(string); ok {
+		return v
+	}
+	return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
+	v := c.String(key)
+	if v == "" {
+		return defaultval
+	}
+	return v
+}
+
+// Strings returns the []string value for a given key.
+func (c *ConfigContainer) Strings(key string) []string {
+	v := c.String(key)
+	if v == "" {
+		return nil
+	}
+	return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	v := c.Strings(key)
+	if v == nil {
+		return defaultval
+	}
+	return v
+}
+
+// GetSection returns map for the given section
+func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
+	if v, ok := c.data[section].(map[string]interface{}); ok {
+		mapstr := make(map[string]string)
+		for k, val := range v {
+			mapstr[k] = config.ToString(val)
+		}
+		return mapstr, nil
+	}
+	return nil, fmt.Errorf("section '%s' not found", section)
+}
+
+// SaveConfigFile save the config into file
+func (c *ConfigContainer) SaveConfigFile(filename string) (err error) {
+	// Write configuration file by filename.
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	b, err := xml.MarshalIndent(c.data, "  ", "    ")
+	if err != nil {
+		return err
+	}
+	_, err = f.Write(b)
+	return err
+}
+
+// Set writes a new value for key.
+func (c *ConfigContainer) Set(key, val string) error {
+	c.Lock()
+	defer c.Unlock()
+	c.data[key] = val
+	return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
+	if v, ok := c.data[key]; ok {
+		return v, nil
+	}
+	return nil, errors.New("not exist key")
+}
+
+func init() {
+	config.Register("xml", &Config{})
+}

+ 125 - 0
vender/github.com/astaxie/beego/config/xml/xml_test.go

@@ -0,0 +1,125 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package xml
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+)
+
+func TestXML(t *testing.T) {
+
+	var (
+		//xml parse should incluce in <config></config> tags
+		xmlcontext = `<?xml version="1.0" encoding="UTF-8"?>
+<config>
+<appname>beeapi</appname>
+<httpport>8080</httpport>
+<mysqlport>3600</mysqlport>
+<PI>3.1415976</PI>
+<runmode>dev</runmode>
+<autorender>false</autorender>
+<copyrequestbody>true</copyrequestbody>
+<path1>${GOPATH}</path1>
+<path2>${GOPATH||/home/go}</path2>
+<mysection>
+<id>1</id>
+<name>MySection</name>
+</mysection>
+</config>
+`
+		keyValue = map[string]interface{}{
+			"appname":         "beeapi",
+			"httpport":        8080,
+			"mysqlport":       int64(3600),
+			"PI":              3.1415976,
+			"runmode":         "dev",
+			"autorender":      false,
+			"copyrequestbody": true,
+			"path1":           os.Getenv("GOPATH"),
+			"path2":           os.Getenv("GOPATH"),
+			"error":           "",
+			"emptystrings":    []string{},
+		}
+	)
+
+	f, err := os.Create("testxml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(xmlcontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testxml.conf")
+
+	xmlconf, err := config.NewConfig("xml", "testxml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var xmlsection map[string]string
+	xmlsection, err = xmlconf.GetSection("mysection")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(xmlsection) == 0 {
+		t.Error("section should not be empty")
+	}
+
+	for k, v := range keyValue {
+
+		var (
+			value interface{}
+			err   error
+		)
+
+		switch v.(type) {
+		case int:
+			value, err = xmlconf.Int(k)
+		case int64:
+			value, err = xmlconf.Int64(k)
+		case float64:
+			value, err = xmlconf.Float(k)
+		case bool:
+			value, err = xmlconf.Bool(k)
+		case []string:
+			value = xmlconf.Strings(k)
+		case string:
+			value = xmlconf.String(k)
+		default:
+			value, err = xmlconf.DIY(k)
+		}
+		if err != nil {
+			t.Errorf("get key %q value fatal,%v err %s", k, v, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Errorf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+
+	if err = xmlconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if xmlconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+}

+ 316 - 0
vender/github.com/astaxie/beego/config/yaml/yaml.go

@@ -0,0 +1,316 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package yaml for config provider
+//
+// depend on github.com/beego/goyaml2
+//
+// go install github.com/beego/goyaml2
+//
+// Usage:
+//  import(
+//   _ "github.com/cnlh/nps/vender/github.com/astaxie/beego/config/yaml"
+//     "github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+//  )
+//
+//  cnf, err := config.NewConfig("yaml", "config.yaml")
+//
+//More docs http://beego.me/docs/module/config.md
+package yaml
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"strings"
+	"sync"
+
+	"github.com/beego/goyaml2"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+)
+
+// Config is a yaml config parser and implements Config interface.
+type Config struct{}
+
+// Parse returns a ConfigContainer with parsed yaml config map.
+func (yaml *Config) Parse(filename string) (y config.Configer, err error) {
+	cnf, err := ReadYmlReader(filename)
+	if err != nil {
+		return
+	}
+	y = &ConfigContainer{
+		data: cnf,
+	}
+	return
+}
+
+// ParseData parse yaml data
+func (yaml *Config) ParseData(data []byte) (config.Configer, error) {
+	cnf, err := parseYML(data)
+	if err != nil {
+		return nil, err
+	}
+
+	return &ConfigContainer{
+		data: cnf,
+	}, nil
+}
+
+// ReadYmlReader Read yaml file to map.
+// if json like, use json package, unless goyaml2 package.
+func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
+	buf, err := ioutil.ReadFile(path)
+	if err != nil {
+		return
+	}
+
+	return parseYML(buf)
+}
+
+// parseYML parse yaml formatted []byte to map.
+func parseYML(buf []byte) (cnf map[string]interface{}, err error) {
+	if len(buf) < 3 {
+		return
+	}
+
+	if string(buf[0:1]) == "{" {
+		log.Println("Look like a Json, try json umarshal")
+		err = json.Unmarshal(buf, &cnf)
+		if err == nil {
+			log.Println("It is Json Map")
+			return
+		}
+	}
+
+	data, err := goyaml2.Read(bytes.NewBuffer(buf))
+	if err != nil {
+		log.Println("Goyaml2 ERR>", string(buf), err)
+		return
+	}
+
+	if data == nil {
+		log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf))
+		return
+	}
+	cnf, ok := data.(map[string]interface{})
+	if !ok {
+		log.Println("Not a Map? >> ", string(buf), data)
+		cnf = nil
+	}
+	cnf = config.ExpandValueEnvForMap(cnf)
+	return
+}
+
+// ConfigContainer A Config represents the yaml configuration.
+type ConfigContainer struct {
+	data map[string]interface{}
+	sync.RWMutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *ConfigContainer) Bool(key string) (bool, error) {
+	v, err := c.getData(key)
+	if err != nil {
+		return false, err
+	}
+	return config.ParseBool(v)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	v, err := c.Bool(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int returns the integer value for a given key.
+func (c *ConfigContainer) Int(key string) (int, error) {
+	if v, err := c.getData(key); err != nil {
+		return 0, err
+	} else if vv, ok := v.(int); ok {
+		return vv, nil
+	} else if vv, ok := v.(int64); ok {
+		return int(vv), nil
+	}
+	return 0, errors.New("not int value")
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
+	v, err := c.Int(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *ConfigContainer) Int64(key string) (int64, error) {
+	if v, err := c.getData(key); err != nil {
+		return 0, err
+	} else if vv, ok := v.(int64); ok {
+		return vv, nil
+	}
+	return 0, errors.New("not bool value")
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	v, err := c.Int64(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Float returns the float value for a given key.
+func (c *ConfigContainer) Float(key string) (float64, error) {
+	if v, err := c.getData(key); err != nil {
+		return 0.0, err
+	} else if vv, ok := v.(float64); ok {
+		return vv, nil
+	} else if vv, ok := v.(int); ok {
+		return float64(vv), nil
+	} else if vv, ok := v.(int64); ok {
+		return float64(vv), nil
+	}
+	return 0.0, errors.New("not float64 value")
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	v, err := c.Float(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// String returns the string value for a given key.
+func (c *ConfigContainer) String(key string) string {
+	if v, err := c.getData(key); err == nil {
+		if vv, ok := v.(string); ok {
+			return vv
+		}
+	}
+	return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
+	v := c.String(key)
+	if v == "" {
+		return defaultval
+	}
+	return v
+}
+
+// Strings returns the []string value for a given key.
+func (c *ConfigContainer) Strings(key string) []string {
+	v := c.String(key)
+	if v == "" {
+		return nil
+	}
+	return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaultval
+func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	v := c.Strings(key)
+	if v == nil {
+		return defaultval
+	}
+	return v
+}
+
+// GetSection returns map for the given section
+func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
+
+	if v, ok := c.data[section]; ok {
+		return v.(map[string]string), nil
+	}
+	return nil, errors.New("not exist section")
+}
+
+// SaveConfigFile save the config into file
+func (c *ConfigContainer) SaveConfigFile(filename string) (err error) {
+	// Write configuration file by filename.
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	err = goyaml2.Write(f, c.data)
+	return err
+}
+
+// Set writes a new value for key.
+func (c *ConfigContainer) Set(key, val string) error {
+	c.Lock()
+	defer c.Unlock()
+	c.data[key] = val
+	return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
+	return c.getData(key)
+}
+
+func (c *ConfigContainer) getData(key string) (interface{}, error) {
+
+	if len(key) == 0 {
+		return nil, errors.New("key is empty")
+	}
+	c.RLock()
+	defer c.RUnlock()
+
+	keys := strings.Split(key, ".")
+	tmpData := c.data
+	for idx, k := range keys {
+		if v, ok := tmpData[k]; ok {
+			switch v.(type) {
+			case map[string]interface{}:
+				{
+					tmpData = v.(map[string]interface{})
+					if idx == len(keys)-1 {
+						return tmpData, nil
+					}
+				}
+			default:
+				{
+					return v, nil
+				}
+
+			}
+		}
+	}
+	return nil, fmt.Errorf("not exist key %q", key)
+}
+
+func init() {
+	config.Register("yaml", &Config{})
+}

+ 115 - 0
vender/github.com/astaxie/beego/config/yaml/yaml_test.go

@@ -0,0 +1,115 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package yaml
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+)
+
+func TestYaml(t *testing.T) {
+
+	var (
+		yamlcontext = `
+"appname": beeapi
+"httpport": 8080
+"mysqlport": 3600
+"PI": 3.1415976
+"runmode": dev
+"autorender": false
+"copyrequestbody": true
+"PATH": GOPATH
+"path1": ${GOPATH}
+"path2": ${GOPATH||/home/go}
+"empty": "" 
+`
+
+		keyValue = map[string]interface{}{
+			"appname":         "beeapi",
+			"httpport":        8080,
+			"mysqlport":       int64(3600),
+			"PI":              3.1415976,
+			"runmode":         "dev",
+			"autorender":      false,
+			"copyrequestbody": true,
+			"PATH":            "GOPATH",
+			"path1":           os.Getenv("GOPATH"),
+			"path2":           os.Getenv("GOPATH"),
+			"error":           "",
+			"emptystrings":    []string{},
+		}
+	)
+	f, err := os.Create("testyaml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(yamlcontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testyaml.conf")
+	yamlconf, err := config.NewConfig("yaml", "testyaml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if yamlconf.String("appname") != "beeapi" {
+		t.Fatal("appname not equal to beeapi")
+	}
+
+	for k, v := range keyValue {
+
+		var (
+			value interface{}
+			err   error
+		)
+
+		switch v.(type) {
+		case int:
+			value, err = yamlconf.Int(k)
+		case int64:
+			value, err = yamlconf.Int64(k)
+		case float64:
+			value, err = yamlconf.Float(k)
+		case bool:
+			value, err = yamlconf.Bool(k)
+		case []string:
+			value = yamlconf.Strings(k)
+		case string:
+			value = yamlconf.String(k)
+		default:
+			value, err = yamlconf.DIY(k)
+		}
+		if err != nil {
+			t.Errorf("get key %q value fatal,%v err %s", k, v, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Errorf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+
+	if err = yamlconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if yamlconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+
+}

+ 138 - 0
vender/github.com/astaxie/beego/config_test.go

@@ -0,0 +1,138 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/config"
+)
+
+func TestDefaults(t *testing.T) {
+	if BConfig.WebConfig.FlashName != "BEEGO_FLASH" {
+		t.Errorf("FlashName was not set to default.")
+	}
+
+	if BConfig.WebConfig.FlashSeparator != "BEEGOFLASH" {
+		t.Errorf("FlashName was not set to default.")
+	}
+}
+
+func TestAssignConfig_01(t *testing.T) {
+	_BConfig := &Config{}
+	_BConfig.AppName = "beego_test"
+	jcf := &config.JSONConfig{}
+	ac, _ := jcf.ParseData([]byte(`{"AppName":"beego_json"}`))
+	assignSingleConfig(_BConfig, ac)
+	if _BConfig.AppName != "beego_json" {
+		t.Log(_BConfig)
+		t.FailNow()
+	}
+}
+
+func TestAssignConfig_02(t *testing.T) {
+	_BConfig := &Config{}
+	bs, _ := json.Marshal(newBConfig())
+
+	jsonMap := M{}
+	json.Unmarshal(bs, &jsonMap)
+
+	configMap := M{}
+	for k, v := range jsonMap {
+		if reflect.TypeOf(v).Kind() == reflect.Map {
+			for k1, v1 := range v.(M) {
+				if reflect.TypeOf(v1).Kind() == reflect.Map {
+					for k2, v2 := range v1.(M) {
+						configMap[k2] = v2
+					}
+				} else {
+					configMap[k1] = v1
+				}
+			}
+		} else {
+			configMap[k] = v
+		}
+	}
+	configMap["MaxMemory"] = 1024
+	configMap["Graceful"] = true
+	configMap["XSRFExpire"] = 32
+	configMap["SessionProviderConfig"] = "file"
+	configMap["FileLineNum"] = true
+
+	jcf := &config.JSONConfig{}
+	bs, _ = json.Marshal(configMap)
+	ac, _ := jcf.ParseData(bs)
+
+	for _, i := range []interface{}{_BConfig, &_BConfig.Listen, &_BConfig.WebConfig, &_BConfig.Log, &_BConfig.WebConfig.Session} {
+		assignSingleConfig(i, ac)
+	}
+
+	if _BConfig.MaxMemory != 1024 {
+		t.Log(_BConfig.MaxMemory)
+		t.FailNow()
+	}
+
+	if !_BConfig.Listen.Graceful {
+		t.Log(_BConfig.Listen.Graceful)
+		t.FailNow()
+	}
+
+	if _BConfig.WebConfig.XSRFExpire != 32 {
+		t.Log(_BConfig.WebConfig.XSRFExpire)
+		t.FailNow()
+	}
+
+	if _BConfig.WebConfig.Session.SessionProviderConfig != "file" {
+		t.Log(_BConfig.WebConfig.Session.SessionProviderConfig)
+		t.FailNow()
+	}
+
+	if !_BConfig.Log.FileLineNum {
+		t.Log(_BConfig.Log.FileLineNum)
+		t.FailNow()
+	}
+
+}
+
+func TestAssignConfig_03(t *testing.T) {
+	jcf := &config.JSONConfig{}
+	ac, _ := jcf.ParseData([]byte(`{"AppName":"beego"}`))
+	ac.Set("AppName", "test_app")
+	ac.Set("RunMode", "online")
+	ac.Set("StaticDir", "download:down download2:down2")
+	ac.Set("StaticExtensionsToGzip", ".css,.js,.html,.jpg,.png")
+	assignConfig(ac)
+
+	t.Logf("%#v", BConfig)
+
+	if BConfig.AppName != "test_app" {
+		t.FailNow()
+	}
+
+	if BConfig.RunMode != "online" {
+		t.FailNow()
+	}
+	if BConfig.WebConfig.StaticDir["/download"] != "down" {
+		t.FailNow()
+	}
+	if BConfig.WebConfig.StaticDir["/download2"] != "down2" {
+		t.FailNow()
+	}
+	if len(BConfig.WebConfig.StaticExtensionsToGzip) != 5 {
+		t.FailNow()
+	}
+}

+ 232 - 0
vender/github.com/astaxie/beego/context/acceptencoder.go

@@ -0,0 +1,232 @@
+// Copyright 2015 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"bytes"
+	"compress/flate"
+	"compress/gzip"
+	"compress/zlib"
+	"io"
+	"net/http"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+var (
+	//Default size==20B same as nginx
+	defaultGzipMinLength = 20
+	//Content will only be compressed if content length is either unknown or greater than gzipMinLength.
+	gzipMinLength = defaultGzipMinLength
+	//The compression level used for deflate compression. (0-9).
+	gzipCompressLevel int
+	//List of HTTP methods to compress. If not set, only GET requests are compressed.
+	includedMethods map[string]bool
+	getMethodOnly   bool
+)
+
+// InitGzip init the gzipcompress
+func InitGzip(minLength, compressLevel int, methods []string) {
+	if minLength >= 0 {
+		gzipMinLength = minLength
+	}
+	gzipCompressLevel = compressLevel
+	if gzipCompressLevel < flate.NoCompression || gzipCompressLevel > flate.BestCompression {
+		gzipCompressLevel = flate.BestSpeed
+	}
+	getMethodOnly = (len(methods) == 0) || (len(methods) == 1 && strings.ToUpper(methods[0]) == "GET")
+	includedMethods = make(map[string]bool, len(methods))
+	for _, v := range methods {
+		includedMethods[strings.ToUpper(v)] = true
+	}
+}
+
+type resetWriter interface {
+	io.Writer
+	Reset(w io.Writer)
+}
+
+type nopResetWriter struct {
+	io.Writer
+}
+
+func (n nopResetWriter) Reset(w io.Writer) {
+	//do nothing
+}
+
+type acceptEncoder struct {
+	name                    string
+	levelEncode             func(int) resetWriter
+	customCompressLevelPool *sync.Pool
+	bestCompressionPool     *sync.Pool
+}
+
+func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter {
+	if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
+		return nopResetWriter{wr}
+	}
+	var rwr resetWriter
+	switch level {
+	case flate.BestSpeed:
+		rwr = ac.customCompressLevelPool.Get().(resetWriter)
+	case flate.BestCompression:
+		rwr = ac.bestCompressionPool.Get().(resetWriter)
+	default:
+		rwr = ac.levelEncode(level)
+	}
+	rwr.Reset(wr)
+	return rwr
+}
+
+func (ac acceptEncoder) put(wr resetWriter, level int) {
+	if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
+		return
+	}
+	wr.Reset(nil)
+
+	//notice
+	//compressionLevel==BestCompression DOES NOT MATTER
+	//sync.Pool will not memory leak
+
+	switch level {
+	case gzipCompressLevel:
+		ac.customCompressLevelPool.Put(wr)
+	case flate.BestCompression:
+		ac.bestCompressionPool.Put(wr)
+	}
+}
+
+var (
+	noneCompressEncoder = acceptEncoder{"", nil, nil, nil}
+	gzipCompressEncoder = acceptEncoder{
+		name:                    "gzip",
+		levelEncode:             func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr },
+		customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, gzipCompressLevel); return wr }},
+		bestCompressionPool:     &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }},
+	}
+
+	//according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed
+	//deflate
+	//The "zlib" format defined in RFC 1950 [31] in combination with
+	//the "deflate" compression mechanism described in RFC 1951 [29].
+	deflateCompressEncoder = acceptEncoder{
+		name:                    "deflate",
+		levelEncode:             func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr },
+		customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, gzipCompressLevel); return wr }},
+		bestCompressionPool:     &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr }},
+	}
+)
+
+var (
+	encoderMap = map[string]acceptEncoder{ // all the other compress methods will ignore
+		"gzip":     gzipCompressEncoder,
+		"deflate":  deflateCompressEncoder,
+		"*":        gzipCompressEncoder, // * means any compress will accept,we prefer gzip
+		"identity": noneCompressEncoder, // identity means none-compress
+	}
+)
+
+// WriteFile reads from file and writes to writer by the specific encoding(gzip/deflate)
+func WriteFile(encoding string, writer io.Writer, file *os.File) (bool, string, error) {
+	return writeLevel(encoding, writer, file, flate.BestCompression)
+}
+
+// WriteBody reads  writes content to writer by the specific encoding(gzip/deflate)
+func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) {
+	if encoding == "" || len(content) < gzipMinLength {
+		_, err := writer.Write(content)
+		return false, "", err
+	}
+	return writeLevel(encoding, writer, bytes.NewReader(content), gzipCompressLevel)
+}
+
+// writeLevel reads from reader,writes to writer by specific encoding and compress level
+// the compress level is defined by deflate package
+func writeLevel(encoding string, writer io.Writer, reader io.Reader, level int) (bool, string, error) {
+	var outputWriter resetWriter
+	var err error
+	var ce = noneCompressEncoder
+
+	if cf, ok := encoderMap[encoding]; ok {
+		ce = cf
+	}
+	encoding = ce.name
+	outputWriter = ce.encode(writer, level)
+	defer ce.put(outputWriter, level)
+
+	_, err = io.Copy(outputWriter, reader)
+	if err != nil {
+		return false, "", err
+	}
+
+	switch outputWriter.(type) {
+	case io.WriteCloser:
+		outputWriter.(io.WriteCloser).Close()
+	}
+	return encoding != "", encoding, nil
+}
+
+// ParseEncoding will extract the right encoding for response
+// the Accept-Encoding's sec is here:
+// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
+func ParseEncoding(r *http.Request) string {
+	if r == nil {
+		return ""
+	}
+	if (getMethodOnly && r.Method == "GET") || includedMethods[r.Method] {
+		return parseEncoding(r)
+	}
+	return ""
+}
+
+type q struct {
+	name  string
+	value float64
+}
+
+func parseEncoding(r *http.Request) string {
+	acceptEncoding := r.Header.Get("Accept-Encoding")
+	if acceptEncoding == "" {
+		return ""
+	}
+	var lastQ q
+	for _, v := range strings.Split(acceptEncoding, ",") {
+		v = strings.TrimSpace(v)
+		if v == "" {
+			continue
+		}
+		vs := strings.Split(v, ";")
+		var cf acceptEncoder
+		var ok bool
+		if cf, ok = encoderMap[vs[0]]; !ok {
+			continue
+		}
+		if len(vs) == 1 {
+			return cf.name
+		}
+		if len(vs) == 2 {
+			f, _ := strconv.ParseFloat(strings.Replace(vs[1], "q=", "", -1), 64)
+			if f == 0 {
+				continue
+			}
+			if f > lastQ.value {
+				lastQ = q{cf.name, f}
+			}
+		}
+	}
+	return lastQ.name
+}

+ 59 - 0
vender/github.com/astaxie/beego/context/acceptencoder_test.go

@@ -0,0 +1,59 @@
+// Copyright 2015 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"net/http"
+	"testing"
+)
+
+func Test_ExtractEncoding(t *testing.T) {
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip,deflate"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"deflate,gzip"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=.5,deflate"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=.5,deflate;q=0.3"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0,deflate"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"deflate;q=0.5,gzip;q=0.5,identity"}}}) != "" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"*"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"x,gzip,deflate"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip,x,deflate"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0.5,x,deflate"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"x"}}}) != "" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0.5,x;q=0.8"}}}) != "gzip" {
+		t.Fail()
+	}
+}

+ 262 - 0
vender/github.com/astaxie/beego/context/context.go

@@ -0,0 +1,262 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package context provide the context utils
+// Usage:
+//
+//	import "github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+//
+//	ctx := context.Context{Request:req,ResponseWriter:rw}
+//
+//  more docs http://beego.me/docs/module/context.md
+package context
+
+import (
+	"bufio"
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"net"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
+)
+
+//commonly used mime-types
+const (
+	ApplicationJSON = "application/json"
+	ApplicationXML  = "application/xml"
+	ApplicationYAML = "application/x-yaml"
+	TextXML         = "text/xml"
+)
+
+// NewContext return the Context with Input and Output
+func NewContext() *Context {
+	return &Context{
+		Input:  NewInput(),
+		Output: NewOutput(),
+	}
+}
+
+// Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
+// BeegoInput and BeegoOutput provides some api to operate request and response more easily.
+type Context struct {
+	Input          *BeegoInput
+	Output         *BeegoOutput
+	Request        *http.Request
+	ResponseWriter *Response
+	_xsrfToken     string
+}
+
+// Reset init Context, BeegoInput and BeegoOutput
+func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) {
+	ctx.Request = r
+	if ctx.ResponseWriter == nil {
+		ctx.ResponseWriter = &Response{}
+	}
+	ctx.ResponseWriter.reset(rw)
+	ctx.Input.Reset(ctx)
+	ctx.Output.Reset(ctx)
+	ctx._xsrfToken = ""
+}
+
+// Redirect does redirection to localurl with http header status code.
+func (ctx *Context) Redirect(status int, localurl string) {
+	http.Redirect(ctx.ResponseWriter, ctx.Request, localurl, status)
+}
+
+// Abort stops this request.
+// if beego.ErrorMaps exists, panic body.
+func (ctx *Context) Abort(status int, body string) {
+	ctx.Output.SetStatus(status)
+	panic(body)
+}
+
+// WriteString Write string to response body.
+// it sends response body.
+func (ctx *Context) WriteString(content string) {
+	ctx.ResponseWriter.Write([]byte(content))
+}
+
+// GetCookie Get cookie from request by a given key.
+// It's alias of BeegoInput.Cookie.
+func (ctx *Context) GetCookie(key string) string {
+	return ctx.Input.Cookie(key)
+}
+
+// SetCookie Set cookie for response.
+// It's alias of BeegoOutput.Cookie.
+func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
+	ctx.Output.Cookie(name, value, others...)
+}
+
+// GetSecureCookie Get secure cookie from request by a given key.
+func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
+	val := ctx.Input.Cookie(key)
+	if val == "" {
+		return "", false
+	}
+
+	parts := strings.SplitN(val, "|", 3)
+
+	if len(parts) != 3 {
+		return "", false
+	}
+
+	vs := parts[0]
+	timestamp := parts[1]
+	sig := parts[2]
+
+	h := hmac.New(sha1.New, []byte(Secret))
+	fmt.Fprintf(h, "%s%s", vs, timestamp)
+
+	if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
+		return "", false
+	}
+	res, _ := base64.URLEncoding.DecodeString(vs)
+	return string(res), true
+}
+
+// SetSecureCookie Set Secure cookie for response.
+func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
+	vs := base64.URLEncoding.EncodeToString([]byte(value))
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	h := hmac.New(sha1.New, []byte(Secret))
+	fmt.Fprintf(h, "%s%s", vs, timestamp)
+	sig := fmt.Sprintf("%02x", h.Sum(nil))
+	cookie := strings.Join([]string{vs, timestamp, sig}, "|")
+	ctx.Output.Cookie(name, cookie, others...)
+}
+
+// XSRFToken creates a xsrf token string and returns.
+func (ctx *Context) XSRFToken(key string, expire int64) string {
+	if ctx._xsrfToken == "" {
+		token, ok := ctx.GetSecureCookie(key, "_xsrf")
+		if !ok {
+			token = string(utils.RandomCreateBytes(32))
+			ctx.SetSecureCookie(key, "_xsrf", token, expire)
+		}
+		ctx._xsrfToken = token
+	}
+	return ctx._xsrfToken
+}
+
+// CheckXSRFCookie checks xsrf token in this request is valid or not.
+// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
+// or in form field value named as "_xsrf".
+func (ctx *Context) CheckXSRFCookie() bool {
+	token := ctx.Input.Query("_xsrf")
+	if token == "" {
+		token = ctx.Request.Header.Get("X-Xsrftoken")
+	}
+	if token == "" {
+		token = ctx.Request.Header.Get("X-Csrftoken")
+	}
+	if token == "" {
+		ctx.Abort(403, "'_xsrf' argument missing from POST")
+		return false
+	}
+	if ctx._xsrfToken != token {
+		ctx.Abort(403, "XSRF cookie does not match POST argument")
+		return false
+	}
+	return true
+}
+
+// RenderMethodResult renders the return value of a controller method to the output
+func (ctx *Context) RenderMethodResult(result interface{}) {
+	if result != nil {
+		renderer, ok := result.(Renderer)
+		if !ok {
+			err, ok := result.(error)
+			if ok {
+				renderer = errorRenderer(err)
+			} else {
+				renderer = jsonRenderer(result)
+			}
+		}
+		renderer.Render(ctx)
+	}
+}
+
+//Response is a wrapper for the http.ResponseWriter
+//started set to true if response was written to then don't execute other handler
+type Response struct {
+	http.ResponseWriter
+	Started bool
+	Status  int
+}
+
+func (r *Response) reset(rw http.ResponseWriter) {
+	r.ResponseWriter = rw
+	r.Status = 0
+	r.Started = false
+}
+
+// Write writes the data to the connection as part of an HTTP reply,
+// and sets `started` to true.
+// started means the response has sent out.
+func (r *Response) Write(p []byte) (int, error) {
+	r.Started = true
+	return r.ResponseWriter.Write(p)
+}
+
+// WriteHeader sends an HTTP response header with status code,
+// and sets `started` to true.
+func (r *Response) WriteHeader(code int) {
+	if r.Status > 0 {
+		//prevent multiple response.WriteHeader calls
+		return
+	}
+	r.Status = code
+	r.Started = true
+	r.ResponseWriter.WriteHeader(code)
+}
+
+// Hijack hijacker for http
+func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+	hj, ok := r.ResponseWriter.(http.Hijacker)
+	if !ok {
+		return nil, nil, errors.New("webserver doesn't support hijacking")
+	}
+	return hj.Hijack()
+}
+
+// Flush http.Flusher
+func (r *Response) Flush() {
+	if f, ok := r.ResponseWriter.(http.Flusher); ok {
+		f.Flush()
+	}
+}
+
+// CloseNotify http.CloseNotifier
+func (r *Response) CloseNotify() <-chan bool {
+	if cn, ok := r.ResponseWriter.(http.CloseNotifier); ok {
+		return cn.CloseNotify()
+	}
+	return nil
+}
+
+// Pusher http.Pusher
+func (r *Response) Pusher() (pusher http.Pusher) {
+	if pusher, ok := r.ResponseWriter.(http.Pusher); ok {
+		return pusher
+	}
+	return nil
+}

+ 47 - 0
vender/github.com/astaxie/beego/context/context_test.go

@@ -0,0 +1,47 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestXsrfReset_01(t *testing.T) {
+	r := &http.Request{}
+	c := NewContext()
+	c.Request = r
+	c.ResponseWriter = &Response{}
+	c.ResponseWriter.reset(httptest.NewRecorder())
+	c.Output.Reset(c)
+	c.Input.Reset(c)
+	c.XSRFToken("key", 16)
+	if c._xsrfToken == "" {
+		t.FailNow()
+	}
+	token := c._xsrfToken
+	c.Reset(&Response{ResponseWriter: httptest.NewRecorder()}, r)
+	if c._xsrfToken != "" {
+		t.FailNow()
+	}
+	c.XSRFToken("key", 16)
+	if c._xsrfToken == "" {
+		t.FailNow()
+	}
+	if token == c._xsrfToken {
+		t.FailNow()
+	}
+}

+ 668 - 0
vender/github.com/astaxie/beego/context/input.go

@@ -0,0 +1,668 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"bytes"
+	"compress/gzip"
+	"errors"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/session"
+)
+
+// Regexes for checking the accept headers
+// TODO make sure these are correct
+var (
+	acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
+	acceptsXMLRegex  = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
+	acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
+	acceptsYAMLRegex = regexp.MustCompile(`(application/x-yaml)(?:,|$)`)
+	maxParam         = 50
+)
+
+// BeegoInput operates the http request header, data, cookie and body.
+// it also contains router params and current session.
+type BeegoInput struct {
+	Context       *Context
+	CruSession    session.Store
+	pnames        []string
+	pvalues       []string
+	data          map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
+	RequestBody   []byte
+	RunMethod     string
+	RunController reflect.Type
+}
+
+// NewInput return BeegoInput generated by Context.
+func NewInput() *BeegoInput {
+	return &BeegoInput{
+		pnames:  make([]string, 0, maxParam),
+		pvalues: make([]string, 0, maxParam),
+		data:    make(map[interface{}]interface{}),
+	}
+}
+
+// Reset init the BeegoInput
+func (input *BeegoInput) Reset(ctx *Context) {
+	input.Context = ctx
+	input.CruSession = nil
+	input.pnames = input.pnames[:0]
+	input.pvalues = input.pvalues[:0]
+	input.data = nil
+	input.RequestBody = []byte{}
+}
+
+// Protocol returns request protocol name, such as HTTP/1.1 .
+func (input *BeegoInput) Protocol() string {
+	return input.Context.Request.Proto
+}
+
+// URI returns full request url with query string, fragment.
+func (input *BeegoInput) URI() string {
+	return input.Context.Request.RequestURI
+}
+
+// URL returns request url path (without query string, fragment).
+func (input *BeegoInput) URL() string {
+	return input.Context.Request.URL.Path
+}
+
+// Site returns base site url as scheme://domain type.
+func (input *BeegoInput) Site() string {
+	return input.Scheme() + "://" + input.Domain()
+}
+
+// Scheme returns request scheme as "http" or "https".
+func (input *BeegoInput) Scheme() string {
+	if scheme := input.Header("X-Forwarded-Proto"); scheme != "" {
+		return scheme
+	}
+	if input.Context.Request.URL.Scheme != "" {
+		return input.Context.Request.URL.Scheme
+	}
+	if input.Context.Request.TLS == nil {
+		return "http"
+	}
+	return "https"
+}
+
+// Domain returns host name.
+// Alias of Host method.
+func (input *BeegoInput) Domain() string {
+	return input.Host()
+}
+
+// Host returns host name.
+// if no host info in request, return localhost.
+func (input *BeegoInput) Host() string {
+	if input.Context.Request.Host != "" {
+		if hostPart, _, err := net.SplitHostPort(input.Context.Request.Host); err == nil {
+			return hostPart
+		}
+		return input.Context.Request.Host
+	}
+	return "localhost"
+}
+
+// Method returns http request method.
+func (input *BeegoInput) Method() string {
+	return input.Context.Request.Method
+}
+
+// Is returns boolean of this request is on given method, such as Is("POST").
+func (input *BeegoInput) Is(method string) bool {
+	return input.Method() == method
+}
+
+// IsGet Is this a GET method request?
+func (input *BeegoInput) IsGet() bool {
+	return input.Is("GET")
+}
+
+// IsPost Is this a POST method request?
+func (input *BeegoInput) IsPost() bool {
+	return input.Is("POST")
+}
+
+// IsHead Is this a Head method request?
+func (input *BeegoInput) IsHead() bool {
+	return input.Is("HEAD")
+}
+
+// IsOptions Is this a OPTIONS method request?
+func (input *BeegoInput) IsOptions() bool {
+	return input.Is("OPTIONS")
+}
+
+// IsPut Is this a PUT method request?
+func (input *BeegoInput) IsPut() bool {
+	return input.Is("PUT")
+}
+
+// IsDelete Is this a DELETE method request?
+func (input *BeegoInput) IsDelete() bool {
+	return input.Is("DELETE")
+}
+
+// IsPatch Is this a PATCH method request?
+func (input *BeegoInput) IsPatch() bool {
+	return input.Is("PATCH")
+}
+
+// IsAjax returns boolean of this request is generated by ajax.
+func (input *BeegoInput) IsAjax() bool {
+	return input.Header("X-Requested-With") == "XMLHttpRequest"
+}
+
+// IsSecure returns boolean of this request is in https.
+func (input *BeegoInput) IsSecure() bool {
+	return input.Scheme() == "https"
+}
+
+// IsWebsocket returns boolean of this request is in webSocket.
+func (input *BeegoInput) IsWebsocket() bool {
+	return input.Header("Upgrade") == "websocket"
+}
+
+// IsUpload returns boolean of whether file uploads in this request or not..
+func (input *BeegoInput) IsUpload() bool {
+	return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
+}
+
+// AcceptsHTML Checks if request accepts html response
+func (input *BeegoInput) AcceptsHTML() bool {
+	return acceptsHTMLRegex.MatchString(input.Header("Accept"))
+}
+
+// AcceptsXML Checks if request accepts xml response
+func (input *BeegoInput) AcceptsXML() bool {
+	return acceptsXMLRegex.MatchString(input.Header("Accept"))
+}
+
+// AcceptsJSON Checks if request accepts json response
+func (input *BeegoInput) AcceptsJSON() bool {
+	return acceptsJSONRegex.MatchString(input.Header("Accept"))
+}
+
+// AcceptsYAML Checks if request accepts json response
+func (input *BeegoInput) AcceptsYAML() bool {
+	return acceptsYAMLRegex.MatchString(input.Header("Accept"))
+}
+
+// IP returns request client ip.
+// if in proxy, return first proxy id.
+// if error, return RemoteAddr.
+func (input *BeegoInput) IP() string {
+	ips := input.Proxy()
+	if len(ips) > 0 && ips[0] != "" {
+		rip, _, err := net.SplitHostPort(ips[0])
+		if err != nil {
+			rip = ips[0]
+		}
+		return rip
+	}
+	if ip, _, err := net.SplitHostPort(input.Context.Request.RemoteAddr); err == nil {
+		return ip
+	}
+	return input.Context.Request.RemoteAddr
+}
+
+// Proxy returns proxy client ips slice.
+func (input *BeegoInput) Proxy() []string {
+	if ips := input.Header("X-Forwarded-For"); ips != "" {
+		return strings.Split(ips, ",")
+	}
+	return []string{}
+}
+
+// Referer returns http referer header.
+func (input *BeegoInput) Referer() string {
+	return input.Header("Referer")
+}
+
+// Refer returns http referer header.
+func (input *BeegoInput) Refer() string {
+	return input.Referer()
+}
+
+// SubDomains returns sub domain string.
+// if aa.bb.domain.com, returns aa.bb .
+func (input *BeegoInput) SubDomains() string {
+	parts := strings.Split(input.Host(), ".")
+	if len(parts) >= 3 {
+		return strings.Join(parts[:len(parts)-2], ".")
+	}
+	return ""
+}
+
+// Port returns request client port.
+// when error or empty, return 80.
+func (input *BeegoInput) Port() int {
+	if _, portPart, err := net.SplitHostPort(input.Context.Request.Host); err == nil {
+		port, _ := strconv.Atoi(portPart)
+		return port
+	}
+	return 80
+}
+
+// UserAgent returns request client user agent string.
+func (input *BeegoInput) UserAgent() string {
+	return input.Header("User-Agent")
+}
+
+// ParamsLen return the length of the params
+func (input *BeegoInput) ParamsLen() int {
+	return len(input.pnames)
+}
+
+// Param returns router param by a given key.
+func (input *BeegoInput) Param(key string) string {
+	for i, v := range input.pnames {
+		if v == key && i <= len(input.pvalues) {
+			return input.pvalues[i]
+		}
+	}
+	return ""
+}
+
+// Params returns the map[key]value.
+func (input *BeegoInput) Params() map[string]string {
+	m := make(map[string]string)
+	for i, v := range input.pnames {
+		if i <= len(input.pvalues) {
+			m[v] = input.pvalues[i]
+		}
+	}
+	return m
+}
+
+// SetParam will set the param with key and value
+func (input *BeegoInput) SetParam(key, val string) {
+	// check if already exists
+	for i, v := range input.pnames {
+		if v == key && i <= len(input.pvalues) {
+			input.pvalues[i] = val
+			return
+		}
+	}
+	input.pvalues = append(input.pvalues, val)
+	input.pnames = append(input.pnames, key)
+}
+
+// ResetParams clears any of the input's Params
+// This function is used to clear parameters so they may be reset between filter
+// passes.
+func (input *BeegoInput) ResetParams() {
+	input.pnames = input.pnames[:0]
+	input.pvalues = input.pvalues[:0]
+}
+
+// Query returns input data item string by a given string.
+func (input *BeegoInput) Query(key string) string {
+	if val := input.Param(key); val != "" {
+		return val
+	}
+	if input.Context.Request.Form == nil {
+		input.Context.Request.ParseForm()
+	}
+	return input.Context.Request.Form.Get(key)
+}
+
+// Header returns request header item string by a given string.
+// if non-existed, return empty string.
+func (input *BeegoInput) Header(key string) string {
+	return input.Context.Request.Header.Get(key)
+}
+
+// Cookie returns request cookie item string by a given key.
+// if non-existed, return empty string.
+func (input *BeegoInput) Cookie(key string) string {
+	ck, err := input.Context.Request.Cookie(key)
+	if err != nil {
+		return ""
+	}
+	return ck.Value
+}
+
+// Session returns current session item value by a given key.
+// if non-existed, return nil.
+func (input *BeegoInput) Session(key interface{}) interface{} {
+	return input.CruSession.Get(key)
+}
+
+// CopyBody returns the raw request body data as bytes.
+func (input *BeegoInput) CopyBody(MaxMemory int64) []byte {
+	if input.Context.Request.Body == nil {
+		return []byte{}
+	}
+
+	var requestbody []byte
+	safe := &io.LimitedReader{R: input.Context.Request.Body, N: MaxMemory}
+	if input.Header("Content-Encoding") == "gzip" {
+		reader, err := gzip.NewReader(safe)
+		if err != nil {
+			return nil
+		}
+		requestbody, _ = ioutil.ReadAll(reader)
+	} else {
+		requestbody, _ = ioutil.ReadAll(safe)
+	}
+
+	input.Context.Request.Body.Close()
+	bf := bytes.NewBuffer(requestbody)
+	input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory)
+	input.RequestBody = requestbody
+	return requestbody
+}
+
+// Data return the implicit data in the input
+func (input *BeegoInput) Data() map[interface{}]interface{} {
+	if input.data == nil {
+		input.data = make(map[interface{}]interface{})
+	}
+	return input.data
+}
+
+// GetData returns the stored data in this context.
+func (input *BeegoInput) GetData(key interface{}) interface{} {
+	if v, ok := input.data[key]; ok {
+		return v
+	}
+	return nil
+}
+
+// SetData stores data with given key in this context.
+// This data are only available in this context.
+func (input *BeegoInput) SetData(key, val interface{}) {
+	if input.data == nil {
+		input.data = make(map[interface{}]interface{})
+	}
+	input.data[key] = val
+}
+
+// ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type
+func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
+	// Parse the body depending on the content type.
+	if strings.Contains(input.Header("Content-Type"), "multipart/form-data") {
+		if err := input.Context.Request.ParseMultipartForm(maxMemory); err != nil {
+			return errors.New("Error parsing request body:" + err.Error())
+		}
+	} else if err := input.Context.Request.ParseForm(); err != nil {
+		return errors.New("Error parsing request body:" + err.Error())
+	}
+	return nil
+}
+
+// Bind data from request.Form[key] to dest
+// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
+// var id int  beegoInput.Bind(&id, "id")  id ==123
+// var isok bool  beegoInput.Bind(&isok, "isok")  isok ==true
+// var ft float64  beegoInput.Bind(&ft, "ft")  ft ==1.2
+// ol := make([]int, 0, 2)  beegoInput.Bind(&ol, "ol")  ol ==[1 2]
+// ul := make([]string, 0, 2)  beegoInput.Bind(&ul, "ul")  ul ==[str array]
+// user struct{Name}  beegoInput.Bind(&user, "user")  user == {Name:"astaxie"}
+func (input *BeegoInput) Bind(dest interface{}, key string) error {
+	value := reflect.ValueOf(dest)
+	if value.Kind() != reflect.Ptr {
+		return errors.New("beego: non-pointer passed to Bind: " + key)
+	}
+	value = value.Elem()
+	if !value.CanSet() {
+		return errors.New("beego: non-settable variable passed to Bind: " + key)
+	}
+	typ := value.Type()
+	// Get real type if dest define with interface{}.
+	// e.g  var dest interface{} dest=1.0
+	if value.Kind() == reflect.Interface {
+		typ = value.Elem().Type()
+	}
+	rv := input.bind(key, typ)
+	if !rv.IsValid() {
+		return errors.New("beego: reflect value is empty")
+	}
+	value.Set(rv)
+	return nil
+}
+
+func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
+	if input.Context.Request.Form == nil {
+		input.Context.Request.ParseForm()
+	}
+	rv := reflect.Zero(typ)
+	switch typ.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		val := input.Query(key)
+		if len(val) == 0 {
+			return rv
+		}
+		rv = input.bindInt(val, typ)
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		val := input.Query(key)
+		if len(val) == 0 {
+			return rv
+		}
+		rv = input.bindUint(val, typ)
+	case reflect.Float32, reflect.Float64:
+		val := input.Query(key)
+		if len(val) == 0 {
+			return rv
+		}
+		rv = input.bindFloat(val, typ)
+	case reflect.String:
+		val := input.Query(key)
+		if len(val) == 0 {
+			return rv
+		}
+		rv = input.bindString(val, typ)
+	case reflect.Bool:
+		val := input.Query(key)
+		if len(val) == 0 {
+			return rv
+		}
+		rv = input.bindBool(val, typ)
+	case reflect.Slice:
+		rv = input.bindSlice(&input.Context.Request.Form, key, typ)
+	case reflect.Struct:
+		rv = input.bindStruct(&input.Context.Request.Form, key, typ)
+	case reflect.Ptr:
+		rv = input.bindPoint(key, typ)
+	case reflect.Map:
+		rv = input.bindMap(&input.Context.Request.Form, key, typ)
+	}
+	return rv
+}
+
+func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value {
+	rv := reflect.Zero(typ)
+	switch typ.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		rv = input.bindInt(val, typ)
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		rv = input.bindUint(val, typ)
+	case reflect.Float32, reflect.Float64:
+		rv = input.bindFloat(val, typ)
+	case reflect.String:
+		rv = input.bindString(val, typ)
+	case reflect.Bool:
+		rv = input.bindBool(val, typ)
+	case reflect.Slice:
+		rv = input.bindSlice(&url.Values{"": {val}}, "", typ)
+	case reflect.Struct:
+		rv = input.bindStruct(&url.Values{"": {val}}, "", typ)
+	case reflect.Ptr:
+		rv = input.bindPoint(val, typ)
+	case reflect.Map:
+		rv = input.bindMap(&url.Values{"": {val}}, "", typ)
+	}
+	return rv
+}
+
+func (input *BeegoInput) bindInt(val string, typ reflect.Type) reflect.Value {
+	intValue, err := strconv.ParseInt(val, 10, 64)
+	if err != nil {
+		return reflect.Zero(typ)
+	}
+	pValue := reflect.New(typ)
+	pValue.Elem().SetInt(intValue)
+	return pValue.Elem()
+}
+
+func (input *BeegoInput) bindUint(val string, typ reflect.Type) reflect.Value {
+	uintValue, err := strconv.ParseUint(val, 10, 64)
+	if err != nil {
+		return reflect.Zero(typ)
+	}
+	pValue := reflect.New(typ)
+	pValue.Elem().SetUint(uintValue)
+	return pValue.Elem()
+}
+
+func (input *BeegoInput) bindFloat(val string, typ reflect.Type) reflect.Value {
+	floatValue, err := strconv.ParseFloat(val, 64)
+	if err != nil {
+		return reflect.Zero(typ)
+	}
+	pValue := reflect.New(typ)
+	pValue.Elem().SetFloat(floatValue)
+	return pValue.Elem()
+}
+
+func (input *BeegoInput) bindString(val string, typ reflect.Type) reflect.Value {
+	return reflect.ValueOf(val)
+}
+
+func (input *BeegoInput) bindBool(val string, typ reflect.Type) reflect.Value {
+	val = strings.TrimSpace(strings.ToLower(val))
+	switch val {
+	case "true", "on", "1":
+		return reflect.ValueOf(true)
+	}
+	return reflect.ValueOf(false)
+}
+
+type sliceValue struct {
+	index int           // Index extracted from brackets.  If -1, no index was provided.
+	value reflect.Value // the bound value for this slice element.
+}
+
+func (input *BeegoInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value {
+	maxIndex := -1
+	numNoIndex := 0
+	sliceValues := []sliceValue{}
+	for reqKey, vals := range *params {
+		if !strings.HasPrefix(reqKey, key+"[") {
+			continue
+		}
+		// Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
+		index := -1
+		leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key)
+		if rightBracket > leftBracket+1 {
+			index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket])
+		}
+		subKeyIndex := rightBracket + 1
+
+		// Handle the indexed case.
+		if index > -1 {
+			if index > maxIndex {
+				maxIndex = index
+			}
+			sliceValues = append(sliceValues, sliceValue{
+				index: index,
+				value: input.bind(reqKey[:subKeyIndex], typ.Elem()),
+			})
+			continue
+		}
+
+		// It's an un-indexed element.  (e.g. element[])
+		numNoIndex += len(vals)
+		for _, val := range vals {
+			// Unindexed values can only be direct-bound.
+			sliceValues = append(sliceValues, sliceValue{
+				index: -1,
+				value: input.bindValue(val, typ.Elem()),
+			})
+		}
+	}
+	resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
+	for _, sv := range sliceValues {
+		if sv.index != -1 {
+			resultArray.Index(sv.index).Set(sv.value)
+		} else {
+			resultArray = reflect.Append(resultArray, sv.value)
+		}
+	}
+	return resultArray
+}
+
+func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value {
+	result := reflect.New(typ).Elem()
+	fieldValues := make(map[string]reflect.Value)
+	for reqKey, val := range *params {
+		var fieldName string
+		if strings.HasPrefix(reqKey, key+".") {
+			fieldName = reqKey[len(key)+1:]
+		} else if strings.HasPrefix(reqKey, key+"[") && reqKey[len(reqKey)-1] == ']' {
+			fieldName = reqKey[len(key)+1 : len(reqKey)-1]
+		} else {
+			continue
+		}
+
+		if _, ok := fieldValues[fieldName]; !ok {
+			// Time to bind this field.  Get it and make sure we can set it.
+			fieldValue := result.FieldByName(fieldName)
+			if !fieldValue.IsValid() {
+				continue
+			}
+			if !fieldValue.CanSet() {
+				continue
+			}
+			boundVal := input.bindValue(val[0], fieldValue.Type())
+			fieldValue.Set(boundVal)
+			fieldValues[fieldName] = boundVal
+		}
+	}
+
+	return result
+}
+
+func (input *BeegoInput) bindPoint(key string, typ reflect.Type) reflect.Value {
+	return input.bind(key, typ.Elem()).Addr()
+}
+
+func (input *BeegoInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value {
+	var (
+		result    = reflect.MakeMap(typ)
+		keyType   = typ.Key()
+		valueType = typ.Elem()
+	)
+	for paramName, values := range *params {
+		if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' {
+			continue
+		}
+
+		key := paramName[len(key)+1 : len(paramName)-1]
+		result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType))
+	}
+	return result
+}

+ 207 - 0
vender/github.com/astaxie/beego/context/input_test.go

@@ -0,0 +1,207 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"testing"
+)
+
+func TestBind(t *testing.T) {
+	type testItem struct {
+		field string
+		empty interface{}
+		want  interface{}
+	}
+	type Human struct {
+		ID   int
+		Nick string
+		Pwd  string
+		Ms   bool
+	}
+
+	cases := []struct {
+		request string
+		valueGp []testItem
+	}{
+		{"/?p=str", []testItem{{"p", interface{}(""), interface{}("str")}}},
+
+		{"/?p=", []testItem{{"p", "", ""}}},
+		{"/?p=str", []testItem{{"p", "", "str"}}},
+
+		{"/?p=123", []testItem{{"p", 0, 123}}},
+		{"/?p=123", []testItem{{"p", uint(0), uint(123)}}},
+
+		{"/?p=1.0", []testItem{{"p", 0.0, 1.0}}},
+		{"/?p=1", []testItem{{"p", false, true}}},
+
+		{"/?p=true", []testItem{{"p", false, true}}},
+		{"/?p=ON", []testItem{{"p", false, true}}},
+		{"/?p=on", []testItem{{"p", false, true}}},
+		{"/?p=1", []testItem{{"p", false, true}}},
+		{"/?p=2", []testItem{{"p", false, false}}},
+		{"/?p=false", []testItem{{"p", false, false}}},
+
+		{"/?p[a]=1&p[b]=2&p[c]=3", []testItem{{"p", map[string]int{}, map[string]int{"a": 1, "b": 2, "c": 3}}}},
+		{"/?p[a]=v1&p[b]=v2&p[c]=v3", []testItem{{"p", map[string]string{}, map[string]string{"a": "v1", "b": "v2", "c": "v3"}}}},
+
+		{"/?p[]=8&p[]=9&p[]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}},
+		{"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}},
+		{"/?p[0]=8&p[1]=9&p[2]=10&p[5]=14", []testItem{{"p", []int{}, []int{8, 9, 10, 0, 0, 14}}}},
+		{"/?p[0]=8.0&p[1]=9.0&p[2]=10.0", []testItem{{"p", []float64{}, []float64{8.0, 9.0, 10.0}}}},
+
+		{"/?p[]=10&p[]=9&p[]=8", []testItem{{"p", []string{}, []string{"10", "9", "8"}}}},
+		{"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []string{}, []string{"8", "9", "10"}}}},
+
+		{"/?p[0]=true&p[1]=false&p[2]=true&p[5]=1&p[6]=ON&p[7]=other", []testItem{{"p", []bool{}, []bool{true, false, true, false, false, true, true, false}}}},
+
+		{"/?human.Nick=astaxie", []testItem{{"human", Human{}, Human{Nick: "astaxie"}}}},
+		{"/?human.ID=888&human.Nick=astaxie&human.Ms=true&human[Pwd]=pass", []testItem{{"human", Human{}, Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass"}}}},
+		{"/?human[0].ID=888&human[0].Nick=astaxie&human[0].Ms=true&human[0][Pwd]=pass01&human[1].ID=999&human[1].Nick=ysqi&human[1].Ms=On&human[1].Pwd=pass02",
+			[]testItem{{"human", []Human{}, []Human{
+				{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"},
+				{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"},
+			}}}},
+
+		{
+			"/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&human.Nick=astaxie",
+			[]testItem{
+				{"id", 0, 123},
+				{"isok", false, true},
+				{"ft", 0.0, 1.2},
+				{"ol", []int{}, []int{1, 2}},
+				{"ul", []string{}, []string{"str", "array"}},
+				{"human", Human{}, Human{Nick: "astaxie"}},
+			},
+		},
+	}
+	for _, c := range cases {
+		r, _ := http.NewRequest("GET", c.request, nil)
+		beegoInput := NewInput()
+		beegoInput.Context = NewContext()
+		beegoInput.Context.Reset(httptest.NewRecorder(), r)
+
+		for _, item := range c.valueGp {
+			got := item.empty
+			err := beegoInput.Bind(&got, item.field)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !reflect.DeepEqual(got, item.want) {
+				t.Fatalf("Bind %q error,should be:\n%#v \ngot:\n%#v", item.field, item.want, got)
+			}
+		}
+
+	}
+}
+
+func TestSubDomain(t *testing.T) {
+	r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
+	beegoInput := NewInput()
+	beegoInput.Context = NewContext()
+	beegoInput.Context.Reset(httptest.NewRecorder(), r)
+
+	subdomain := beegoInput.SubDomains()
+	if subdomain != "www" {
+		t.Fatal("Subdomain parse error, got" + subdomain)
+	}
+
+	r, _ = http.NewRequest("GET", "http://localhost/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "" {
+		t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
+	}
+
+	r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "aa.bb" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+
+	/* TODO Fix this
+	r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+	*/
+
+	r, _ = http.NewRequest("GET", "http://example.com/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+
+	r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "aa.bb.cc.dd" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+}
+
+func TestParams(t *testing.T) {
+	inp := NewInput()
+
+	inp.SetParam("p1", "val1_ver1")
+	inp.SetParam("p2", "val2_ver1")
+	inp.SetParam("p3", "val3_ver1")
+	if l := inp.ParamsLen(); l != 3 {
+		t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
+	}
+
+	if val := inp.Param("p1"); val != "val1_ver1" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver1")
+	}
+	if val := inp.Param("p3"); val != "val3_ver1" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val3_ver1")
+	}
+	vals := inp.Params()
+	expected := map[string]string{
+		"p1": "val1_ver1",
+		"p2": "val2_ver1",
+		"p3": "val3_ver1",
+	}
+	if !reflect.DeepEqual(vals, expected) {
+		t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
+	}
+
+	// overwriting existing params
+	inp.SetParam("p1", "val1_ver2")
+	inp.SetParam("p2", "val2_ver2")
+	expected = map[string]string{
+		"p1": "val1_ver2",
+		"p2": "val2_ver2",
+		"p3": "val3_ver1",
+	}
+	vals = inp.Params()
+	if !reflect.DeepEqual(vals, expected) {
+		t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
+	}
+
+	if l := inp.ParamsLen(); l != 3 {
+		t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
+	}
+
+	if val := inp.Param("p1"); val != "val1_ver2" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
+	}
+
+	if val := inp.Param("p2"); val != "val2_ver2" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
+	}
+
+}

+ 395 - 0
vender/github.com/astaxie/beego/context/output.go

@@ -0,0 +1,395 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"bytes"
+	"encoding/json"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"html/template"
+	"io"
+	"mime"
+	"net/http"
+	"net/url"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+	"gopkg.in/yaml.v2"
+)
+
+// BeegoOutput does work for sending response header.
+type BeegoOutput struct {
+	Context    *Context
+	Status     int
+	EnableGzip bool
+}
+
+// NewOutput returns new BeegoOutput.
+// it contains nothing now.
+func NewOutput() *BeegoOutput {
+	return &BeegoOutput{}
+}
+
+// Reset init BeegoOutput
+func (output *BeegoOutput) Reset(ctx *Context) {
+	output.Context = ctx
+	output.Status = 0
+}
+
+// Header sets response header item string via given key.
+func (output *BeegoOutput) Header(key, val string) {
+	output.Context.ResponseWriter.Header().Set(key, val)
+}
+
+// Body sets response body content.
+// if EnableGzip, compress content string.
+// it sends out response body directly.
+func (output *BeegoOutput) Body(content []byte) error {
+	var encoding string
+	var buf = &bytes.Buffer{}
+	if output.EnableGzip {
+		encoding = ParseEncoding(output.Context.Request)
+	}
+	if b, n, _ := WriteBody(encoding, buf, content); b {
+		output.Header("Content-Encoding", n)
+		output.Header("Content-Length", strconv.Itoa(buf.Len()))
+	} else {
+		output.Header("Content-Length", strconv.Itoa(len(content)))
+	}
+	// Write status code if it has been set manually
+	// Set it to 0 afterwards to prevent "multiple response.WriteHeader calls"
+	if output.Status != 0 {
+		output.Context.ResponseWriter.WriteHeader(output.Status)
+		output.Status = 0
+	} else {
+		output.Context.ResponseWriter.Started = true
+	}
+	io.Copy(output.Context.ResponseWriter, buf)
+	return nil
+}
+
+// Cookie sets cookie value via given key.
+// others are ordered as cookie's max age time, path,domain, secure and httponly.
+func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) {
+	var b bytes.Buffer
+	fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value))
+
+	//fix cookie not work in IE
+	if len(others) > 0 {
+		var maxAge int64
+
+		switch v := others[0].(type) {
+		case int:
+			maxAge = int64(v)
+		case int32:
+			maxAge = int64(v)
+		case int64:
+			maxAge = v
+		}
+
+		switch {
+		case maxAge > 0:
+			fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(maxAge)*time.Second).UTC().Format(time.RFC1123), maxAge)
+		case maxAge < 0:
+			fmt.Fprintf(&b, "; Max-Age=0")
+		}
+	}
+
+	// the settings below
+	// Path, Domain, Secure, HttpOnly
+	// can use nil skip set
+
+	// default "/"
+	if len(others) > 1 {
+		if v, ok := others[1].(string); ok && len(v) > 0 {
+			fmt.Fprintf(&b, "; Path=%s", sanitizeValue(v))
+		}
+	} else {
+		fmt.Fprintf(&b, "; Path=%s", "/")
+	}
+
+	// default empty
+	if len(others) > 2 {
+		if v, ok := others[2].(string); ok && len(v) > 0 {
+			fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(v))
+		}
+	}
+
+	// default empty
+	if len(others) > 3 {
+		var secure bool
+		switch v := others[3].(type) {
+		case bool:
+			secure = v
+		default:
+			if others[3] != nil {
+				secure = true
+			}
+		}
+		if secure {
+			fmt.Fprintf(&b, "; Secure")
+		}
+	}
+
+	// default false. for session cookie default true
+	if len(others) > 4 {
+		if v, ok := others[4].(bool); ok && v {
+			fmt.Fprintf(&b, "; HttpOnly")
+		}
+	}
+
+	output.Context.ResponseWriter.Header().Add("Set-Cookie", b.String())
+}
+
+var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")
+
+func sanitizeName(n string) string {
+	return cookieNameSanitizer.Replace(n)
+}
+
+var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ")
+
+func sanitizeValue(v string) string {
+	return cookieValueSanitizer.Replace(v)
+}
+
+func jsonRenderer(value interface{}) Renderer {
+	return rendererFunc(func(ctx *Context) {
+		ctx.Output.JSON(value, false, false)
+	})
+}
+
+func errorRenderer(err error) Renderer {
+	return rendererFunc(func(ctx *Context) {
+		ctx.Output.SetStatus(500)
+		ctx.Output.Body([]byte(err.Error()))
+	})
+}
+
+// JSON writes json to response body.
+// if encoding is true, it converts utf-8 to \u0000 type.
+func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool) error {
+	output.Header("Content-Type", "application/json; charset=utf-8")
+	var content []byte
+	var err error
+	if hasIndent {
+		content, err = json.MarshalIndent(data, "", "  ")
+	} else {
+		content, err = json.Marshal(data)
+	}
+	if err != nil {
+		http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	if encoding {
+		content = []byte(stringsToJSON(string(content)))
+	}
+	return output.Body(content)
+}
+
+
+// YAML writes yaml to response body.
+func (output *BeegoOutput) YAML(data interface{}) error {
+	output.Header("Content-Type", "application/x-yaml; charset=utf-8")
+	var content []byte
+	var err error
+	content, err = yaml.Marshal(data)
+	if err != nil {
+		http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	return output.Body(content)
+}
+
+// JSONP writes jsonp to response body.
+func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error {
+	output.Header("Content-Type", "application/javascript; charset=utf-8")
+	var content []byte
+	var err error
+	if hasIndent {
+		content, err = json.MarshalIndent(data, "", "  ")
+	} else {
+		content, err = json.Marshal(data)
+	}
+	if err != nil {
+		http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	callback := output.Context.Input.Query("callback")
+	if callback == "" {
+		return errors.New(`"callback" parameter required`)
+	}
+	callback = template.JSEscapeString(callback)
+	callbackContent := bytes.NewBufferString(" if(window." + callback + ")" + callback)
+	callbackContent.WriteString("(")
+	callbackContent.Write(content)
+	callbackContent.WriteString(");\r\n")
+	return output.Body(callbackContent.Bytes())
+}
+
+// XML writes xml string to response body.
+func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error {
+	output.Header("Content-Type", "application/xml; charset=utf-8")
+	var content []byte
+	var err error
+	if hasIndent {
+		content, err = xml.MarshalIndent(data, "", "  ")
+	} else {
+		content, err = xml.Marshal(data)
+	}
+	if err != nil {
+		http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	return output.Body(content)
+}
+
+// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
+func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) {
+	accept := output.Context.Input.Header("Accept")
+	switch accept {
+	case ApplicationYAML:
+		output.YAML(data)
+	case ApplicationXML, TextXML:
+		output.XML(data, hasIndent)
+	default:
+		output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0])
+	}
+}
+
+// Download forces response for download file.
+// it prepares the download response header automatically.
+func (output *BeegoOutput) Download(file string, filename ...string) {
+	// check get file error, file not found or other error.
+	if _, err := os.Stat(file); err != nil {
+		http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file)
+		return
+	}
+
+	var fName string
+	if len(filename) > 0 && filename[0] != "" {
+		fName = filename[0]
+	} else {
+		fName = filepath.Base(file)
+	}
+	output.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fName))
+	output.Header("Content-Description", "File Transfer")
+	output.Header("Content-Type", "application/octet-stream")
+	output.Header("Content-Transfer-Encoding", "binary")
+	output.Header("Expires", "0")
+	output.Header("Cache-Control", "must-revalidate")
+	output.Header("Pragma", "public")
+	http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file)
+}
+
+// ContentType sets the content type from ext string.
+// MIME type is given in mime package.
+func (output *BeegoOutput) ContentType(ext string) {
+	if !strings.HasPrefix(ext, ".") {
+		ext = "." + ext
+	}
+	ctype := mime.TypeByExtension(ext)
+	if ctype != "" {
+		output.Header("Content-Type", ctype)
+	}
+}
+
+// SetStatus sets response status code.
+// It writes response header directly.
+func (output *BeegoOutput) SetStatus(status int) {
+	output.Status = status
+}
+
+// IsCachable returns boolean of this request is cached.
+// HTTP 304 means cached.
+func (output *BeegoOutput) IsCachable() bool {
+	return output.Status >= 200 && output.Status < 300 || output.Status == 304
+}
+
+// IsEmpty returns boolean of this request is empty.
+// HTTP 201,204 and 304 means empty.
+func (output *BeegoOutput) IsEmpty() bool {
+	return output.Status == 201 || output.Status == 204 || output.Status == 304
+}
+
+// IsOk returns boolean of this request runs well.
+// HTTP 200 means ok.
+func (output *BeegoOutput) IsOk() bool {
+	return output.Status == 200
+}
+
+// IsSuccessful returns boolean of this request runs successfully.
+// HTTP 2xx means ok.
+func (output *BeegoOutput) IsSuccessful() bool {
+	return output.Status >= 200 && output.Status < 300
+}
+
+// IsRedirect returns boolean of this request is redirection header.
+// HTTP 301,302,307 means redirection.
+func (output *BeegoOutput) IsRedirect() bool {
+	return output.Status == 301 || output.Status == 302 || output.Status == 303 || output.Status == 307
+}
+
+// IsForbidden returns boolean of this request is forbidden.
+// HTTP 403 means forbidden.
+func (output *BeegoOutput) IsForbidden() bool {
+	return output.Status == 403
+}
+
+// IsNotFound returns boolean of this request is not found.
+// HTTP 404 means not found.
+func (output *BeegoOutput) IsNotFound() bool {
+	return output.Status == 404
+}
+
+// IsClientError returns boolean of this request client sends error data.
+// HTTP 4xx means client error.
+func (output *BeegoOutput) IsClientError() bool {
+	return output.Status >= 400 && output.Status < 500
+}
+
+// IsServerError returns boolean of this server handler errors.
+// HTTP 5xx means server internal error.
+func (output *BeegoOutput) IsServerError() bool {
+	return output.Status >= 500 && output.Status < 600
+}
+
+func stringsToJSON(str string) string {
+	var jsons bytes.Buffer
+	for _, r := range str {
+		rint := int(r)
+		if rint < 128 {
+			jsons.WriteRune(r)
+		} else {
+			jsons.WriteString("\\u")
+			if rint < 0x100 {
+				jsons.WriteString("00")
+			} else if rint < 0x1000 {
+				jsons.WriteString("0")
+			}
+			jsons.WriteString(strconv.FormatInt(int64(rint), 16))
+		}
+	}
+	return jsons.String()
+}
+
+// Session sets session item value with given key.
+func (output *BeegoOutput) Session(name interface{}, value interface{}) {
+	output.Context.Input.CruSession.Set(name, value)
+}

+ 78 - 0
vender/github.com/astaxie/beego/context/param/conv.go

@@ -0,0 +1,78 @@
+package param
+
+import (
+	"fmt"
+	"reflect"
+
+	beecontext "github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+)
+
+// ConvertParams converts http method params to values that will be passed to the method controller as arguments
+func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) {
+	result = make([]reflect.Value, 0, len(methodParams))
+	for i := 0; i < len(methodParams); i++ {
+		reflectValue := convertParam(methodParams[i], methodType.In(i), ctx)
+		result = append(result, reflectValue)
+	}
+	return
+}
+
+func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) {
+	paramValue := getParamValue(param, ctx)
+	if paramValue == "" {
+		if param.required {
+			ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name))
+		} else {
+			paramValue = param.defaultValue
+		}
+	}
+
+	reflectValue, err := parseValue(param, paramValue, paramType)
+	if err != nil {
+		logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err))
+		ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType))
+	}
+
+	return reflectValue
+}
+
+func getParamValue(param *MethodParam, ctx *beecontext.Context) string {
+	switch param.in {
+	case body:
+		return string(ctx.Input.RequestBody)
+	case header:
+		return ctx.Input.Header(param.name)
+	case path:
+		return ctx.Input.Query(":" + param.name)
+	default:
+		return ctx.Input.Query(param.name)
+	}
+}
+
+func parseValue(param *MethodParam, paramValue string, paramType reflect.Type) (result reflect.Value, err error) {
+	if paramValue == "" {
+		return reflect.Zero(paramType), nil
+	}
+	parser := getParser(param, paramType)
+	value, err := parser.parse(paramValue, paramType)
+	if err != nil {
+		return result, err
+	}
+
+	return safeConvert(reflect.ValueOf(value), paramType)
+}
+
+func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			var ok bool
+			err, ok = r.(error)
+			if !ok {
+				err = fmt.Errorf("%v", r)
+			}
+		}
+	}()
+	result = value.Convert(t)
+	return
+}

+ 69 - 0
vender/github.com/astaxie/beego/context/param/methodparams.go

@@ -0,0 +1,69 @@
+package param
+
+import (
+	"fmt"
+	"strings"
+)
+
+//MethodParam keeps param information to be auto passed to controller methods
+type MethodParam struct {
+	name         string
+	in           paramType
+	required     bool
+	defaultValue string
+}
+
+type paramType byte
+
+const (
+	param paramType = iota
+	path
+	body
+	header
+)
+
+//New creates a new MethodParam with name and specific options
+func New(name string, opts ...MethodParamOption) *MethodParam {
+	return newParam(name, nil, opts)
+}
+
+func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) {
+	param = &MethodParam{name: name}
+	for _, option := range opts {
+		option(param)
+	}
+	return
+}
+
+//Make creates an array of MethodParmas or an empty array
+func Make(list ...*MethodParam) []*MethodParam {
+	if len(list) > 0 {
+		return list
+	}
+	return nil
+}
+
+func (mp *MethodParam) String() string {
+	options := []string{}
+	result := "param.New(\"" + mp.name + "\""
+	if mp.required {
+		options = append(options, "param.IsRequired")
+	}
+	switch mp.in {
+	case path:
+		options = append(options, "param.InPath")
+	case body:
+		options = append(options, "param.InBody")
+	case header:
+		options = append(options, "param.InHeader")
+	}
+	if mp.defaultValue != "" {
+		options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defaultValue))
+	}
+	if len(options) > 0 {
+		result += ", "
+	}
+	result += strings.Join(options, ", ")
+	result += ")"
+	return result
+}

+ 37 - 0
vender/github.com/astaxie/beego/context/param/options.go

@@ -0,0 +1,37 @@
+package param
+
+import (
+	"fmt"
+)
+
+// MethodParamOption defines a func which apply options on a MethodParam
+type MethodParamOption func(*MethodParam)
+
+// IsRequired indicates that this param is required and can not be omitted from the http request
+var IsRequired MethodParamOption = func(p *MethodParam) {
+	p.required = true
+}
+
+// InHeader indicates that this param is passed via an http header
+var InHeader MethodParamOption = func(p *MethodParam) {
+	p.in = header
+}
+
+// InPath indicates that this param is part of the URL path
+var InPath MethodParamOption = func(p *MethodParam) {
+	p.in = path
+}
+
+// InBody indicates that this param is passed as an http request body
+var InBody MethodParamOption = func(p *MethodParam) {
+	p.in = body
+}
+
+// Default provides a default value for the http param
+func Default(defaultValue interface{}) MethodParamOption {
+	return func(p *MethodParam) {
+		if defaultValue != nil {
+			p.defaultValue = fmt.Sprint(defaultValue)
+		}
+	}
+}

+ 149 - 0
vender/github.com/astaxie/beego/context/param/parsers.go

@@ -0,0 +1,149 @@
+package param
+
+import (
+	"encoding/json"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type paramParser interface {
+	parse(value string, toType reflect.Type) (interface{}, error)
+}
+
+func getParser(param *MethodParam, t reflect.Type) paramParser {
+	switch t.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		return intParser{}
+	case reflect.Slice:
+		if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string
+			return stringParser{}
+		}
+		if param.in == body {
+			return jsonParser{}
+		}
+		elemParser := getParser(param, t.Elem())
+		if elemParser == (jsonParser{}) {
+			return elemParser
+		}
+		return sliceParser(elemParser)
+	case reflect.Bool:
+		return boolParser{}
+	case reflect.String:
+		return stringParser{}
+	case reflect.Float32, reflect.Float64:
+		return floatParser{}
+	case reflect.Ptr:
+		elemParser := getParser(param, t.Elem())
+		if elemParser == (jsonParser{}) {
+			return elemParser
+		}
+		return ptrParser(elemParser)
+	default:
+		if t.PkgPath() == "time" && t.Name() == "Time" {
+			return timeParser{}
+		}
+		return jsonParser{}
+	}
+}
+
+type parserFunc func(value string, toType reflect.Type) (interface{}, error)
+
+func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) {
+	return f(value, toType)
+}
+
+type boolParser struct {
+}
+
+func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) {
+	return strconv.ParseBool(value)
+}
+
+type stringParser struct {
+}
+
+func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) {
+	return value, nil
+}
+
+type intParser struct {
+}
+
+func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) {
+	return strconv.Atoi(value)
+}
+
+type floatParser struct {
+}
+
+func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) {
+	if toType.Kind() == reflect.Float32 {
+		res, err := strconv.ParseFloat(value, 32)
+		if err != nil {
+			return nil, err
+		}
+		return float32(res), nil
+	}
+	return strconv.ParseFloat(value, 64)
+}
+
+type timeParser struct {
+}
+
+func (p timeParser) parse(value string, toType reflect.Type) (result interface{}, err error) {
+	result, err = time.Parse(time.RFC3339, value)
+	if err != nil {
+		result, err = time.Parse("2006-01-02", value)
+	}
+	return
+}
+
+type jsonParser struct {
+}
+
+func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error) {
+	pResult := reflect.New(toType)
+	v := pResult.Interface()
+	err := json.Unmarshal([]byte(value), v)
+	if err != nil {
+		return nil, err
+	}
+	return pResult.Elem().Interface(), nil
+}
+
+func sliceParser(elemParser paramParser) paramParser {
+	return parserFunc(func(value string, toType reflect.Type) (interface{}, error) {
+		values := strings.Split(value, ",")
+		result := reflect.MakeSlice(toType, 0, len(values))
+		elemType := toType.Elem()
+		for _, v := range values {
+			parsedValue, err := elemParser.parse(v, elemType)
+			if err != nil {
+				return nil, err
+			}
+			result = reflect.Append(result, reflect.ValueOf(parsedValue))
+		}
+		return result.Interface(), nil
+	})
+}
+
+func ptrParser(elemParser paramParser) paramParser {
+	return parserFunc(func(value string, toType reflect.Type) (interface{}, error) {
+		parsedValue, err := elemParser.parse(value, toType.Elem())
+		if err != nil {
+			return nil, err
+		}
+		newValPtr := reflect.New(toType.Elem())
+		newVal := reflect.Indirect(newValPtr)
+		convertedVal, err := safeConvert(reflect.ValueOf(parsedValue), toType.Elem())
+		if err != nil {
+			return nil, err
+		}
+
+		newVal.Set(convertedVal)
+		return newValPtr.Interface(), nil
+	})
+}

+ 84 - 0
vender/github.com/astaxie/beego/context/param/parsers_test.go

@@ -0,0 +1,84 @@
+package param
+
+import "testing"
+import "reflect"
+import "time"
+
+type testDefinition struct {
+	strValue       string
+	expectedValue  interface{}
+	expectedParser paramParser
+}
+
+func Test_Parsers(t *testing.T) {
+
+	//ints
+	checkParser(testDefinition{"1", 1, intParser{}}, t)
+	checkParser(testDefinition{"-1", int64(-1), intParser{}}, t)
+	checkParser(testDefinition{"1", uint64(1), intParser{}}, t)
+
+	//floats
+	checkParser(testDefinition{"1.0", float32(1.0), floatParser{}}, t)
+	checkParser(testDefinition{"-1.0", float64(-1.0), floatParser{}}, t)
+
+	//strings
+	checkParser(testDefinition{"AB", "AB", stringParser{}}, t)
+	checkParser(testDefinition{"AB", []byte{65, 66}, stringParser{}}, t)
+
+	//bools
+	checkParser(testDefinition{"true", true, boolParser{}}, t)
+	checkParser(testDefinition{"0", false, boolParser{}}, t)
+
+	//timeParser
+	checkParser(testDefinition{"2017-05-30T13:54:53Z", time.Date(2017, 5, 30, 13, 54, 53, 0, time.UTC), timeParser{}}, t)
+	checkParser(testDefinition{"2017-05-30", time.Date(2017, 5, 30, 0, 0, 0, 0, time.UTC), timeParser{}}, t)
+
+	//json
+	checkParser(testDefinition{`{"X": 5, "Y":"Z"}`, struct {
+		X int
+		Y string
+	}{5, "Z"}, jsonParser{}}, t)
+
+	//slice in query is parsed as comma delimited
+	checkParser(testDefinition{`1,2`, []int{1, 2}, sliceParser(intParser{})}, t)
+
+	//slice in body is parsed as json
+	checkParser(testDefinition{`["a","b"]`, []string{"a", "b"}, jsonParser{}}, t, MethodParam{in: body})
+
+	//pointers
+	var someInt = 1
+	checkParser(testDefinition{`1`, &someInt, ptrParser(intParser{})}, t)
+
+	var someStruct = struct{ X int }{5}
+	checkParser(testDefinition{`{"X": 5}`, &someStruct, jsonParser{}}, t)
+
+}
+
+func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) {
+	toType := reflect.TypeOf(def.expectedValue)
+	var mp MethodParam
+	if len(methodParam) == 0 {
+		mp = MethodParam{}
+	} else {
+		mp = methodParam[0]
+	}
+	parser := getParser(&mp, toType)
+
+	if reflect.TypeOf(parser) != reflect.TypeOf(def.expectedParser) {
+		t.Errorf("Invalid parser for value %v. Expected: %v, actual: %v", def.strValue, reflect.TypeOf(def.expectedParser).Name(), reflect.TypeOf(parser).Name())
+		return
+	}
+	result, err := parser.parse(def.strValue, toType)
+	if err != nil {
+		t.Errorf("Parsing error for value %v. Expected result: %v, error: %v", def.strValue, def.expectedValue, err)
+		return
+	}
+	convResult, err := safeConvert(reflect.ValueOf(result), toType)
+	if err != nil {
+		t.Errorf("Conversion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err)
+		return
+	}
+	if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) {
+		t.Errorf("Parsing error for value %v. Expected result: %v, actual: %v", def.strValue, def.expectedValue, result)
+	}
+}

+ 12 - 0
vender/github.com/astaxie/beego/context/renderer.go

@@ -0,0 +1,12 @@
+package context
+
+// Renderer defines an http response renderer
+type Renderer interface {
+	Render(ctx *Context)
+}
+
+type rendererFunc func(ctx *Context)
+
+func (f rendererFunc) Render(ctx *Context) {
+	f(ctx)
+}

+ 27 - 0
vender/github.com/astaxie/beego/context/response.go

@@ -0,0 +1,27 @@
+package context
+
+import (
+	"strconv"
+
+	"net/http"
+)
+
+const (
+	//BadRequest indicates http error 400
+	BadRequest StatusCode = http.StatusBadRequest
+
+	//NotFound indicates http error 404
+	NotFound StatusCode = http.StatusNotFound
+)
+
+// StatusCode sets the http response status code
+type StatusCode int
+
+func (s StatusCode) Error() string {
+	return strconv.Itoa(int(s))
+}
+
+// Render sets the http status code
+func (s StatusCode) Render(ctx *Context) {
+	ctx.Output.SetStatus(int(s))
+}

+ 683 - 0
vender/github.com/astaxie/beego/controller.go

@@ -0,0 +1,683 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"bytes"
+	"errors"
+	"html/template"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"net/url"
+	"os"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context/param"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/session"
+)
+
+var (
+	// ErrAbort custom error when user stop request handler manually.
+	ErrAbort = errors.New("User stop run")
+	// GlobalControllerRouter store comments with controller. pkgpath+controller:comments
+	GlobalControllerRouter = make(map[string][]ControllerComments)
+)
+
+// ControllerFilter store the filter for controller
+type ControllerFilter struct {
+	Pattern        string
+	Pos            int
+	Filter         FilterFunc
+	ReturnOnOutput bool
+	ResetParams    bool
+}
+
+// ControllerFilterComments store the comment for controller level filter
+type ControllerFilterComments struct {
+	Pattern        string
+	Pos            int
+	Filter         string // NOQA
+	ReturnOnOutput bool
+	ResetParams    bool
+}
+
+// ControllerImportComments store the import comment for controller needed
+type ControllerImportComments struct {
+	ImportPath  string
+	ImportAlias string
+}
+
+// ControllerComments store the comment for the controller method
+type ControllerComments struct {
+	Method           string
+	Router           string
+	Filters          []*ControllerFilter
+	ImportComments   []*ControllerImportComments
+	FilterComments   []*ControllerFilterComments
+	AllowHTTPMethods []string
+	Params           []map[string]string
+	MethodParams     []*param.MethodParam
+}
+
+// ControllerCommentsSlice implements the sort interface
+type ControllerCommentsSlice []ControllerComments
+
+func (p ControllerCommentsSlice) Len() int           { return len(p) }
+func (p ControllerCommentsSlice) Less(i, j int) bool { return p[i].Router < p[j].Router }
+func (p ControllerCommentsSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+// Controller defines some basic http request handler operations, such as
+// http context, template and view, session and xsrf.
+type Controller struct {
+	// context data
+	Ctx  *context.Context
+	Data map[interface{}]interface{}
+
+	// route controller info
+	controllerName string
+	actionName     string
+	methodMapping  map[string]func() //method:routertree
+	gotofunc       string
+	AppController  interface{}
+
+	// template data
+	TplName        string
+	ViewPath       string
+	Layout         string
+	LayoutSections map[string]string // the key is the section name and the value is the template name
+	TplPrefix      string
+	TplExt         string
+	EnableRender   bool
+
+	// xsrf data
+	_xsrfToken string
+	XSRFExpire int
+	EnableXSRF bool
+
+	// session
+	CruSession session.Store
+}
+
+// ControllerInterface is an interface to uniform all controller handler.
+type ControllerInterface interface {
+	Init(ct *context.Context, controllerName, actionName string, app interface{})
+	Prepare()
+	Get()
+	Post()
+	Delete()
+	Put()
+	Head()
+	Patch()
+	Options()
+	Finish()
+	Render() error
+	XSRFToken() string
+	CheckXSRFCookie() bool
+	HandlerFunc(fn string) bool
+	URLMapping()
+}
+
+// Init generates default values of controller operations.
+func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	c.Layout = ""
+	c.TplName = ""
+	c.controllerName = controllerName
+	c.actionName = actionName
+	c.Ctx = ctx
+	c.TplExt = "tpl"
+	c.AppController = app
+	c.EnableRender = true
+	c.EnableXSRF = true
+	c.Data = ctx.Input.Data()
+	c.methodMapping = make(map[string]func())
+}
+
+// Prepare runs after Init before request function execution.
+func (c *Controller) Prepare() {}
+
+// Finish runs after request function execution.
+func (c *Controller) Finish() {}
+
+// Get adds a request function to handle GET request.
+func (c *Controller) Get() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// Post adds a request function to handle POST request.
+func (c *Controller) Post() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// Delete adds a request function to handle DELETE request.
+func (c *Controller) Delete() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// Put adds a request function to handle PUT request.
+func (c *Controller) Put() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// Head adds a request function to handle HEAD request.
+func (c *Controller) Head() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// Patch adds a request function to handle PATCH request.
+func (c *Controller) Patch() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// Options adds a request function to handle OPTIONS request.
+func (c *Controller) Options() {
+	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
+}
+
+// HandlerFunc call function with the name
+func (c *Controller) HandlerFunc(fnname string) bool {
+	if v, ok := c.methodMapping[fnname]; ok {
+		v()
+		return true
+	}
+	return false
+}
+
+// URLMapping register the internal Controller router.
+func (c *Controller) URLMapping() {}
+
+// Mapping the method to function
+func (c *Controller) Mapping(method string, fn func()) {
+	c.methodMapping[method] = fn
+}
+
+// Render sends the response with rendered template bytes as text/html type.
+func (c *Controller) Render() error {
+	if !c.EnableRender {
+		return nil
+	}
+	rb, err := c.RenderBytes()
+	if err != nil {
+		return err
+	}
+
+	if c.Ctx.ResponseWriter.Header().Get("Content-Type") == "" {
+		c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
+	}
+
+	return c.Ctx.Output.Body(rb)
+}
+
+// RenderString returns the rendered template string. Do not send out response.
+func (c *Controller) RenderString() (string, error) {
+	b, e := c.RenderBytes()
+	return string(b), e
+}
+
+// RenderBytes returns the bytes of rendered template string. Do not send out response.
+func (c *Controller) RenderBytes() ([]byte, error) {
+	buf, err := c.renderTemplate()
+	//if the controller has set layout, then first get the tplName's content set the content to the layout
+	if err == nil && c.Layout != "" {
+		c.Data["LayoutContent"] = template.HTML(buf.String())
+
+		if c.LayoutSections != nil {
+			for sectionName, sectionTpl := range c.LayoutSections {
+				if sectionTpl == "" {
+					c.Data[sectionName] = ""
+					continue
+				}
+				buf.Reset()
+				err = ExecuteViewPathTemplate(&buf, sectionTpl, c.viewPath(), c.Data)
+				if err != nil {
+					return nil, err
+				}
+				c.Data[sectionName] = template.HTML(buf.String())
+			}
+		}
+
+		buf.Reset()
+		ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
+	}
+	return buf.Bytes(), err
+}
+
+func (c *Controller) renderTemplate() (bytes.Buffer, error) {
+	var buf bytes.Buffer
+	if c.TplName == "" {
+		c.TplName = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
+	}
+	if c.TplPrefix != "" {
+		c.TplName = c.TplPrefix + c.TplName
+	}
+	if BConfig.RunMode == DEV {
+		buildFiles := []string{c.TplName}
+		if c.Layout != "" {
+			buildFiles = append(buildFiles, c.Layout)
+			if c.LayoutSections != nil {
+				for _, sectionTpl := range c.LayoutSections {
+					if sectionTpl == "" {
+						continue
+					}
+					buildFiles = append(buildFiles, sectionTpl)
+				}
+			}
+		}
+		BuildTemplate(c.viewPath(), buildFiles...)
+	}
+	return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data)
+}
+
+func (c *Controller) viewPath() string {
+	if c.ViewPath == "" {
+		return BConfig.WebConfig.ViewsPath
+	}
+	return c.ViewPath
+}
+
+// Redirect sends the redirection response to url with status code.
+func (c *Controller) Redirect(url string, code int) {
+	logAccess(c.Ctx, nil, code)
+	c.Ctx.Redirect(code, url)
+}
+
+// Set the data depending on the accepted
+func (c *Controller) SetData(data interface{}) {
+	accept := c.Ctx.Input.Header("Accept")
+	switch accept {
+	case context.ApplicationYAML:
+		c.Data["yaml"] = data
+	case context.ApplicationXML, context.TextXML:
+		c.Data["xml"] = data
+	default:
+		c.Data["json"] = data
+	}
+}
+
+// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string.
+func (c *Controller) Abort(code string) {
+	status, err := strconv.Atoi(code)
+	if err != nil {
+		status = 200
+	}
+	c.CustomAbort(status, code)
+}
+
+// CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body.
+func (c *Controller) CustomAbort(status int, body string) {
+	// first panic from ErrorMaps, it is user defined error functions.
+	if _, ok := ErrorMaps[body]; ok {
+		c.Ctx.Output.Status = status
+		panic(body)
+	}
+	// last panic user string
+	c.Ctx.ResponseWriter.WriteHeader(status)
+	c.Ctx.ResponseWriter.Write([]byte(body))
+	panic(ErrAbort)
+}
+
+// StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
+func (c *Controller) StopRun() {
+	panic(ErrAbort)
+}
+
+// URLFor does another controller handler in this request function.
+// it goes to this controller method if endpoint is not clear.
+func (c *Controller) URLFor(endpoint string, values ...interface{}) string {
+	if len(endpoint) == 0 {
+		return ""
+	}
+	if endpoint[0] == '.' {
+		return URLFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...)
+	}
+	return URLFor(endpoint, values...)
+}
+
+// ServeJSON sends a json response with encoding charset.
+func (c *Controller) ServeJSON(encoding ...bool) {
+	var (
+		hasIndent   = BConfig.RunMode != PROD
+		hasEncoding = len(encoding) > 0 && encoding[0]
+	)
+
+	c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
+}
+
+// ServeJSONP sends a jsonp response.
+func (c *Controller) ServeJSONP() {
+	hasIndent := BConfig.RunMode != PROD
+	c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent)
+}
+
+// ServeXML sends xml response.
+func (c *Controller) ServeXML() {
+	hasIndent := BConfig.RunMode != PROD
+	c.Ctx.Output.XML(c.Data["xml"], hasIndent)
+}
+
+// ServeXML sends xml response.
+func (c *Controller) ServeYAML() {
+	c.Ctx.Output.YAML(c.Data["yaml"])
+}
+
+// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
+func (c *Controller) ServeFormatted(encoding ...bool) {
+	hasIndent := BConfig.RunMode != PROD
+	hasEncoding := len(encoding) > 0 && encoding[0]
+	c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding)
+}
+
+// Input returns the input data map from POST or PUT request body and query string.
+func (c *Controller) Input() url.Values {
+	if c.Ctx.Request.Form == nil {
+		c.Ctx.Request.ParseForm()
+	}
+	return c.Ctx.Request.Form
+}
+
+// ParseForm maps input data map to obj struct.
+func (c *Controller) ParseForm(obj interface{}) error {
+	return ParseForm(c.Input(), obj)
+}
+
+// GetString returns the input value by key string or the default value while it's present and input is blank
+func (c *Controller) GetString(key string, def ...string) string {
+	if v := c.Ctx.Input.Query(key); v != "" {
+		return v
+	}
+	if len(def) > 0 {
+		return def[0]
+	}
+	return ""
+}
+
+// GetStrings returns the input string slice by key string or the default value while it's present and input is blank
+// it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection.
+func (c *Controller) GetStrings(key string, def ...[]string) []string {
+	var defv []string
+	if len(def) > 0 {
+		defv = def[0]
+	}
+
+	if f := c.Input(); f == nil {
+		return defv
+	} else if vs := f[key]; len(vs) > 0 {
+		return vs
+	}
+
+	return defv
+}
+
+// GetInt returns input as an int or the default value while it's present and input is blank
+func (c *Controller) GetInt(key string, def ...int) (int, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	return strconv.Atoi(strv)
+}
+
+// GetInt8 return input as an int8 or the default value while it's present and input is blank
+func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	i64, err := strconv.ParseInt(strv, 10, 8)
+	return int8(i64), err
+}
+
+// GetUint8 return input as an uint8 or the default value while it's present and input is blank
+func (c *Controller) GetUint8(key string, def ...uint8) (uint8, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	u64, err := strconv.ParseUint(strv, 10, 8)
+	return uint8(u64), err
+}
+
+// GetInt16 returns input as an int16 or the default value while it's present and input is blank
+func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	i64, err := strconv.ParseInt(strv, 10, 16)
+	return int16(i64), err
+}
+
+// GetUint16 returns input as an uint16 or the default value while it's present and input is blank
+func (c *Controller) GetUint16(key string, def ...uint16) (uint16, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	u64, err := strconv.ParseUint(strv, 10, 16)
+	return uint16(u64), err
+}
+
+// GetInt32 returns input as an int32 or the default value while it's present and input is blank
+func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	i64, err := strconv.ParseInt(strv, 10, 32)
+	return int32(i64), err
+}
+
+// GetUint32 returns input as an uint32 or the default value while it's present and input is blank
+func (c *Controller) GetUint32(key string, def ...uint32) (uint32, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	u64, err := strconv.ParseUint(strv, 10, 32)
+	return uint32(u64), err
+}
+
+// GetInt64 returns input value as int64 or the default value while it's present and input is blank.
+func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	return strconv.ParseInt(strv, 10, 64)
+}
+
+// GetUint64 returns input value as uint64 or the default value while it's present and input is blank.
+func (c *Controller) GetUint64(key string, def ...uint64) (uint64, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	return strconv.ParseUint(strv, 10, 64)
+}
+
+// GetBool returns input value as bool or the default value while it's present and input is blank.
+func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	return strconv.ParseBool(strv)
+}
+
+// GetFloat returns input value as float64 or the default value while it's present and input is blank.
+func (c *Controller) GetFloat(key string, def ...float64) (float64, error) {
+	strv := c.Ctx.Input.Query(key)
+	if len(strv) == 0 && len(def) > 0 {
+		return def[0], nil
+	}
+	return strconv.ParseFloat(strv, 64)
+}
+
+// GetFile returns the file data in file upload field named as key.
+// it returns the first one of multi-uploaded files.
+func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
+	return c.Ctx.Request.FormFile(key)
+}
+
+// GetFiles return multi-upload files
+// files, err:=c.GetFiles("myfiles")
+//	if err != nil {
+//		http.Error(w, err.Error(), http.StatusNoContent)
+//		return
+//	}
+// for i, _ := range files {
+//	//for each fileheader, get a handle to the actual file
+//	file, err := files[i].Open()
+//	defer file.Close()
+//	if err != nil {
+//		http.Error(w, err.Error(), http.StatusInternalServerError)
+//		return
+//	}
+//	//create destination file making sure the path is writeable.
+//	dst, err := os.Create("upload/" + files[i].Filename)
+//	defer dst.Close()
+//	if err != nil {
+//		http.Error(w, err.Error(), http.StatusInternalServerError)
+//		return
+//	}
+//	//copy the uploaded file to the destination file
+//	if _, err := io.Copy(dst, file); err != nil {
+//		http.Error(w, err.Error(), http.StatusInternalServerError)
+//		return
+//	}
+// }
+func (c *Controller) GetFiles(key string) ([]*multipart.FileHeader, error) {
+	if files, ok := c.Ctx.Request.MultipartForm.File[key]; ok {
+		return files, nil
+	}
+	return nil, http.ErrMissingFile
+}
+
+// SaveToFile saves uploaded file to new path.
+// it only operates the first one of mutil-upload form file field.
+func (c *Controller) SaveToFile(fromfile, tofile string) error {
+	file, _, err := c.Ctx.Request.FormFile(fromfile)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	f, err := os.OpenFile(tofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	io.Copy(f, file)
+	return nil
+}
+
+// StartSession starts session and load old session data info this controller.
+func (c *Controller) StartSession() session.Store {
+	if c.CruSession == nil {
+		c.CruSession = c.Ctx.Input.CruSession
+	}
+	return c.CruSession
+}
+
+// SetSession puts value into session.
+func (c *Controller) SetSession(name interface{}, value interface{}) {
+	if c.CruSession == nil {
+		c.StartSession()
+	}
+	c.CruSession.Set(name, value)
+}
+
+// GetSession gets value from session.
+func (c *Controller) GetSession(name interface{}) interface{} {
+	if c.CruSession == nil {
+		c.StartSession()
+	}
+	return c.CruSession.Get(name)
+}
+
+// DelSession removes value from session.
+func (c *Controller) DelSession(name interface{}) {
+	if c.CruSession == nil {
+		c.StartSession()
+	}
+	c.CruSession.Delete(name)
+}
+
+// SessionRegenerateID regenerates session id for this session.
+// the session data have no changes.
+func (c *Controller) SessionRegenerateID() {
+	if c.CruSession != nil {
+		c.CruSession.SessionRelease(c.Ctx.ResponseWriter)
+	}
+	c.CruSession = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request)
+	c.Ctx.Input.CruSession = c.CruSession
+}
+
+// DestroySession cleans session data and session cookie.
+func (c *Controller) DestroySession() {
+	c.Ctx.Input.CruSession.Flush()
+	c.Ctx.Input.CruSession = nil
+	GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
+}
+
+// IsAjax returns this request is ajax or not.
+func (c *Controller) IsAjax() bool {
+	return c.Ctx.Input.IsAjax()
+}
+
+// GetSecureCookie returns decoded cookie value from encoded browser cookie values.
+func (c *Controller) GetSecureCookie(Secret, key string) (string, bool) {
+	return c.Ctx.GetSecureCookie(Secret, key)
+}
+
+// SetSecureCookie puts value into cookie after encoded the value.
+func (c *Controller) SetSecureCookie(Secret, name, value string, others ...interface{}) {
+	c.Ctx.SetSecureCookie(Secret, name, value, others...)
+}
+
+// XSRFToken creates a CSRF token string and returns.
+func (c *Controller) XSRFToken() string {
+	if c._xsrfToken == "" {
+		expire := int64(BConfig.WebConfig.XSRFExpire)
+		if c.XSRFExpire > 0 {
+			expire = int64(c.XSRFExpire)
+		}
+		c._xsrfToken = c.Ctx.XSRFToken(BConfig.WebConfig.XSRFKey, expire)
+	}
+	return c._xsrfToken
+}
+
+// CheckXSRFCookie checks xsrf token in this request is valid or not.
+// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
+// or in form field value named as "_xsrf".
+func (c *Controller) CheckXSRFCookie() bool {
+	if !c.EnableXSRF {
+		return true
+	}
+	return c.Ctx.CheckXSRFCookie()
+}
+
+// XSRFFormHTML writes an input field contains xsrf token value.
+func (c *Controller) XSRFFormHTML() string {
+	return `<input type="hidden" name="_xsrf" value="` +
+		c.XSRFToken() + `" />`
+}
+
+// GetControllerAndAction gets the executing controller name and action name.
+func (c *Controller) GetControllerAndAction() (string, string) {
+	return c.controllerName, c.actionName
+}

+ 181 - 0
vender/github.com/astaxie/beego/controller_test.go

@@ -0,0 +1,181 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"math"
+	"strconv"
+	"testing"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+	"os"
+	"path/filepath"
+)
+
+func TestGetInt(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt("age")
+	if val != 40 {
+		t.Errorf("TestGetInt expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetInt8(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt8("age")
+	if val != 40 {
+		t.Errorf("TestGetInt8 expect 40,get %T,%v", val, val)
+	}
+	//Output: int8
+}
+
+func TestGetInt16(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt16("age")
+	if val != 40 {
+		t.Errorf("TestGetInt16 expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetInt32(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt32("age")
+	if val != 40 {
+		t.Errorf("TestGetInt32 expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetInt64(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt64("age")
+	if val != 40 {
+		t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetUint8(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint8, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint8("age")
+	if val != math.MaxUint8 {
+		t.Errorf("TestGetUint8 expect %v,get %T,%v", math.MaxUint8, val, val)
+	}
+}
+
+func TestGetUint16(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint16, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint16("age")
+	if val != math.MaxUint16 {
+		t.Errorf("TestGetUint16 expect %v,get %T,%v", math.MaxUint16, val, val)
+	}
+}
+
+func TestGetUint32(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint32, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint32("age")
+	if val != math.MaxUint32 {
+		t.Errorf("TestGetUint32 expect %v,get %T,%v", math.MaxUint32, val, val)
+	}
+}
+
+func TestGetUint64(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint64, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint64("age")
+	if val != math.MaxUint64 {
+		t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val)
+	}
+}
+
+func TestAdditionalViewPaths(t *testing.T) {
+	dir1 := "_beeTmp"
+	dir2 := "_beeTmp2"
+	defer os.RemoveAll(dir1)
+	defer os.RemoveAll(dir2)
+
+	dir1file := "file1.tpl"
+	dir2file := "file2.tpl"
+
+	genFile := func(dir string, name string, content string) {
+		os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
+		if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+			t.Fatal(err)
+		} else {
+			defer f.Close()
+			f.WriteString(content)
+			f.Close()
+		}
+
+	}
+	genFile(dir1, dir1file, `<div>{{.Content}}</div>`)
+	genFile(dir2, dir2file, `<html>{{.Content}}</html>`)
+
+	AddViewPath(dir1)
+	AddViewPath(dir2)
+
+	ctrl := Controller{
+		TplName:  "file1.tpl",
+		ViewPath: dir1,
+	}
+	ctrl.Data = map[interface{}]interface{}{
+		"Content": "value2",
+	}
+	if result, err := ctrl.RenderString(); err != nil {
+		t.Fatal(err)
+	} else {
+		if result != "<div>value2</div>" {
+			t.Fatalf("TestAdditionalViewPaths expect %s got %s", "<div>value2</div>", result)
+		}
+	}
+
+	func() {
+		ctrl.TplName = "file2.tpl"
+		defer func() {
+			if r := recover(); r == nil {
+				t.Fatal("TestAdditionalViewPaths expected error")
+			}
+		}()
+		ctrl.RenderString()
+	}()
+
+	ctrl.TplName = "file2.tpl"
+	ctrl.ViewPath = dir2
+	ctrl.RenderString()
+}

+ 17 - 0
vender/github.com/astaxie/beego/doc.go

@@ -0,0 +1,17 @@
+/*
+Package beego provide a MVC framework
+beego: an open-source, high-performance, modular, full-stack web framework
+
+It is used for rapid development of RESTful APIs, web apps and backend services in Go.
+beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding.
+
+	package main
+	import "github.com/cnlh/nps/vender/github.com/astaxie/beego"
+
+	func main() {
+	 beego.Run()
+	}
+
+more information: http://beego.me
+*/
+package beego

+ 474 - 0
vender/github.com/astaxie/beego/error.go

@@ -0,0 +1,474 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"fmt"
+	"html/template"
+	"net/http"
+	"reflect"
+	"runtime"
+	"strconv"
+	"strings"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
+)
+
+const (
+	errorTypeHandler = iota
+	errorTypeController
+)
+
+var tpl = `
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>beego application error</title>
+    <style>
+        html, body, body * {padding: 0; margin: 0;}
+        #header {background:#ffd; border-bottom:solid 2px #A31515; padding: 20px 10px;}
+        #header h2{ }
+        #footer {border-top:solid 1px #aaa; padding: 5px 10px; font-size: 12px; color:green;}
+        #content {padding: 5px;}
+        #content .stack b{ font-size: 13px; color: red;}
+        #content .stack pre{padding-left: 10px;}
+        table {}
+        td.t {text-align: right; padding-right: 5px; color: #888;}
+    </style>
+    <script type="text/javascript">
+    </script>
+</head>
+<body>
+    <div id="header">
+        <h2>{{.AppError}}</h2>
+    </div>
+    <div id="content">
+        <table>
+            <tr>
+                <td class="t">Request Method: </td><td>{{.RequestMethod}}</td>
+            </tr>
+            <tr>
+                <td class="t">Request URL: </td><td>{{.RequestURL}}</td>
+            </tr>
+            <tr>
+                <td class="t">RemoteAddr: </td><td>{{.RemoteAddr }}</td>
+            </tr>
+        </table>
+        <div class="stack">
+            <b>Stack</b>
+            <pre>{{.Stack}}</pre>
+        </div>
+    </div>
+    <div id="footer">
+        <p>beego {{ .BeegoVersion }} (beego framework)</p>
+        <p>golang version: {{.GoVersion}}</p>
+    </div>
+</body>
+</html>
+`
+
+// render default application error page with error and stack string.
+func showErr(err interface{}, ctx *context.Context, stack string) {
+	t, _ := template.New("beegoerrortemp").Parse(tpl)
+	data := map[string]string{
+		"AppError":      fmt.Sprintf("%s:%v", BConfig.AppName, err),
+		"RequestMethod": ctx.Input.Method(),
+		"RequestURL":    ctx.Input.URI(),
+		"RemoteAddr":    ctx.Input.IP(),
+		"Stack":         stack,
+		"BeegoVersion":  VERSION,
+		"GoVersion":     runtime.Version(),
+	}
+	t.Execute(ctx.ResponseWriter, data)
+}
+
+var errtpl = `
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+		<title>{{.Title}}</title>
+		<style type="text/css">
+			* {
+				margin:0;
+				padding:0;
+			}
+
+			body {
+				background-color:#EFEFEF;
+				font: .9em "Lucida Sans Unicode", "Lucida Grande", sans-serif;
+			}
+
+			#wrapper{
+				width:600px;
+				margin:40px auto 0;
+				text-align:center;
+				-moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
+				-webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
+				box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
+			}
+
+			#wrapper h1{
+				color:#FFF;
+				text-align:center;
+				margin-bottom:20px;
+			}
+
+			#wrapper a{
+				display:block;
+				font-size:.9em;
+				padding-top:20px;
+				color:#FFF;
+				text-decoration:none;
+				text-align:center;
+			}
+
+			#container {
+				width:600px;
+				padding-bottom:15px;
+				background-color:#FFFFFF;
+			}
+
+			.navtop{
+				height:40px;
+				background-color:#24B2EB;
+				padding:13px;
+			}
+
+			.content {
+				padding:10px 10px 25px;
+				background: #FFFFFF;
+				margin:;
+				color:#333;
+			}
+
+			a.button{
+				color:white;
+				padding:15px 20px;
+				text-shadow:1px 1px 0 #00A5FF;
+				font-weight:bold;
+				text-align:center;
+				border:1px solid #24B2EB;
+				margin:0px 200px;
+				clear:both;
+				background-color: #24B2EB;
+				border-radius:100px;
+				-moz-border-radius:100px;
+				-webkit-border-radius:100px;
+			}
+
+			a.button:hover{
+				text-decoration:none;
+				background-color: #24B2EB;
+			}
+
+		</style>
+	</head>
+	<body>
+		<div id="wrapper">
+			<div id="container">
+				<div class="navtop">
+					<h1>{{.Title}}</h1>
+				</div>
+				<div id="content">
+					{{.Content}}
+					<a href="/" title="Home" class="button">Go Home</a><br />
+
+					<br>Powered by beego {{.BeegoVersion}}
+				</div>
+			</div>
+		</div>
+	</body>
+</html>
+`
+
+type errorInfo struct {
+	controllerType reflect.Type
+	handler        http.HandlerFunc
+	method         string
+	errorType      int
+}
+
+// ErrorMaps holds map of http handlers for each error string.
+// there is 10 kinds default error(40x and 50x)
+var ErrorMaps = make(map[string]*errorInfo, 10)
+
+// show 401 unauthorized error.
+func unauthorized(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		401,
+		"<br>The page you have requested can't be authorized."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>The credentials you supplied are incorrect"+
+			"<br>There are errors in the website address"+
+			"</ul>",
+	)
+}
+
+// show 402 Payment Required
+func paymentRequired(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		402,
+		"<br>The page you have requested Payment Required."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>The credentials you supplied are incorrect"+
+			"<br>There are errors in the website address"+
+			"</ul>",
+	)
+}
+
+// show 403 forbidden error.
+func forbidden(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		403,
+		"<br>The page you have requested is forbidden."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>Your address may be blocked"+
+			"<br>The site may be disabled"+
+			"<br>You need to log in"+
+			"</ul>",
+	)
+}
+
+// show 422 missing xsrf token
+func missingxsrf(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		422,
+		"<br>The page you have requested is forbidden."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>'_xsrf' argument missing from POST"+
+			"</ul>",
+	)
+}
+
+// show 417 invalid xsrf token
+func invalidxsrf(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		417,
+		"<br>The page you have requested is forbidden."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>expected XSRF not found"+
+			"</ul>",
+	)
+}
+
+// show 404 not found error.
+func notFound(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		404,
+		"<br>The page you have requested has flown the coop."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>The page has moved"+
+			"<br>The page no longer exists"+
+			"<br>You were looking for your puppy and got lost"+
+			"<br>You like 404 pages"+
+			"</ul>",
+	)
+}
+
+// show 405 Method Not Allowed
+func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		405,
+		"<br>The method you have requested Not Allowed."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI"+
+			"<br>The response MUST include an Allow header containing a list of valid methods for the requested resource."+
+			"</ul>",
+	)
+}
+
+// show 500 internal server error.
+func internalServerError(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		500,
+		"<br>The page you have requested is down right now."+
+			"<br><br><ul>"+
+			"<br>Please try again later and report the error to the website administrator"+
+			"<br></ul>",
+	)
+}
+
+// show 501 Not Implemented.
+func notImplemented(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		501,
+		"<br>The page you have requested is Not Implemented."+
+			"<br><br><ul>"+
+			"<br>Please try again later and report the error to the website administrator"+
+			"<br></ul>",
+	)
+}
+
+// show 502 Bad Gateway.
+func badGateway(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		502,
+		"<br>The page you have requested is down right now."+
+			"<br><br><ul>"+
+			"<br>The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."+
+			"<br>Please try again later and report the error to the website administrator"+
+			"<br></ul>",
+	)
+}
+
+// show 503 service unavailable error.
+func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		503,
+		"<br>The page you have requested is unavailable."+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br><br>The page is overloaded"+
+			"<br>Please try again later."+
+			"</ul>",
+	)
+}
+
+// show 504 Gateway Timeout.
+func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
+	responseError(rw, r,
+		504,
+		"<br>The page you have requested is unavailable"+
+			"<br>Perhaps you are here because:"+
+			"<br><br><ul>"+
+			"<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI."+
+			"<br>Please try again later."+
+			"</ul>",
+	)
+}
+
+func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
+	t, _ := template.New("beegoerrortemp").Parse(errtpl)
+	data := M{
+		"Title":        http.StatusText(errCode),
+		"BeegoVersion": VERSION,
+		"Content":      template.HTML(errContent),
+	}
+	t.Execute(rw, data)
+}
+
+// ErrorHandler registers http.HandlerFunc to each http err code string.
+// usage:
+// 	beego.ErrorHandler("404",NotFound)
+//	beego.ErrorHandler("500",InternalServerError)
+func ErrorHandler(code string, h http.HandlerFunc) *App {
+	ErrorMaps[code] = &errorInfo{
+		errorType: errorTypeHandler,
+		handler:   h,
+		method:    code,
+	}
+	return BeeApp
+}
+
+// ErrorController registers ControllerInterface to each http err code string.
+// usage:
+// 	beego.ErrorController(&controllers.ErrorController{})
+func ErrorController(c ControllerInterface) *App {
+	reflectVal := reflect.ValueOf(c)
+	rt := reflectVal.Type()
+	ct := reflect.Indirect(reflectVal).Type()
+	for i := 0; i < rt.NumMethod(); i++ {
+		methodName := rt.Method(i).Name
+		if !utils.InSlice(methodName, exceptMethod) && strings.HasPrefix(methodName, "Error") {
+			errName := strings.TrimPrefix(methodName, "Error")
+			ErrorMaps[errName] = &errorInfo{
+				errorType:      errorTypeController,
+				controllerType: ct,
+				method:         methodName,
+			}
+		}
+	}
+	return BeeApp
+}
+
+// Exception Write HttpStatus with errCode and Exec error handler if exist.
+func Exception(errCode uint64, ctx *context.Context) {
+	exception(strconv.FormatUint(errCode, 10), ctx)
+}
+
+// show error string as simple text message.
+// if error string is empty, show 503 or 500 error as default.
+func exception(errCode string, ctx *context.Context) {
+	atoi := func(code string) int {
+		v, err := strconv.Atoi(code)
+		if err == nil {
+			return v
+		}
+		if ctx.Output.Status == 0 {
+			return 503
+		}
+		return ctx.Output.Status
+	}
+
+	for _, ec := range []string{errCode, "503", "500"} {
+		if h, ok := ErrorMaps[ec]; ok {
+			executeError(h, ctx, atoi(ec))
+			return
+		}
+	}
+	//if 50x error has been removed from errorMap
+	ctx.ResponseWriter.WriteHeader(atoi(errCode))
+	ctx.WriteString(errCode)
+}
+
+func executeError(err *errorInfo, ctx *context.Context, code int) {
+	//make sure to log the error in the access log
+	logAccess(ctx, nil, code)
+
+	if err.errorType == errorTypeHandler {
+		ctx.ResponseWriter.WriteHeader(code)
+		err.handler(ctx.ResponseWriter, ctx.Request)
+		return
+	}
+	if err.errorType == errorTypeController {
+		ctx.Output.SetStatus(code)
+		//Invoke the request handler
+		vc := reflect.New(err.controllerType)
+		execController, ok := vc.Interface().(ControllerInterface)
+		if !ok {
+			panic("controller is not ControllerInterface")
+		}
+		//call the controller init function
+		execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface())
+
+		//call prepare function
+		execController.Prepare()
+
+		execController.URLMapping()
+
+		method := vc.MethodByName(err.method)
+		method.Call([]reflect.Value{})
+
+		//render template
+		if BConfig.WebConfig.AutoRender {
+			if err := execController.Render(); err != nil {
+				panic(err)
+			}
+		}
+
+		// finish all runrouter. release resource
+		execController.Finish()
+	}
+}

+ 88 - 0
vender/github.com/astaxie/beego/error_test.go

@@ -0,0 +1,88 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+type errorTestController struct {
+	Controller
+}
+
+const parseCodeError = "parse code error"
+
+func (ec *errorTestController) Get() {
+	errorCode, err := ec.GetInt("code")
+	if err != nil {
+		ec.Abort(parseCodeError)
+	}
+	if errorCode != 0 {
+		ec.CustomAbort(errorCode, ec.GetString("code"))
+	}
+	ec.Abort("404")
+}
+
+func TestErrorCode_01(t *testing.T) {
+	registerDefaultErrorHandler()
+	for k := range ErrorMaps {
+		r, _ := http.NewRequest("GET", "/error?code="+k, nil)
+		w := httptest.NewRecorder()
+
+		handler := NewControllerRegister()
+		handler.Add("/error", &errorTestController{})
+		handler.ServeHTTP(w, r)
+		code, _ := strconv.Atoi(k)
+		if w.Code != code {
+			t.Fail()
+		}
+		if !strings.Contains(w.Body.String(), http.StatusText(code)) {
+			t.Fail()
+		}
+	}
+}
+
+func TestErrorCode_02(t *testing.T) {
+	registerDefaultErrorHandler()
+	r, _ := http.NewRequest("GET", "/error?code=0", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/error", &errorTestController{})
+	handler.ServeHTTP(w, r)
+	if w.Code != 404 {
+		t.Fail()
+	}
+}
+
+func TestErrorCode_03(t *testing.T) {
+	registerDefaultErrorHandler()
+	r, _ := http.NewRequest("GET", "/error?code=panic", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/error", &errorTestController{})
+	handler.ServeHTTP(w, r)
+	if w.Code != 200 {
+		t.Fail()
+	}
+	if w.Body.String() != parseCodeError {
+		t.Fail()
+	}
+}

+ 44 - 0
vender/github.com/astaxie/beego/filter.go

@@ -0,0 +1,44 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import "github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+
+// FilterFunc defines a filter function which is invoked before the controller handler is executed.
+type FilterFunc func(*context.Context)
+
+// FilterRouter defines a filter operation which is invoked before the controller handler is executed.
+// It can match the URL against a pattern, and execute a filter function
+// when a request with a matching URL arrives.
+type FilterRouter struct {
+	filterFunc     FilterFunc
+	tree           *Tree
+	pattern        string
+	returnOnOutput bool
+	resetParams    bool
+}
+
+// ValidRouter checks if the current request is matched by this filter.
+// If the request is matched, the values of the URL parameters defined
+// by the filter pattern are also returned.
+func (f *FilterRouter) ValidRouter(url string, ctx *context.Context) bool {
+	isOk := f.tree.Match(url, ctx)
+	if isOk != nil {
+		if b, ok := isOk.(bool); ok {
+			return b
+		}
+	}
+	return false
+}

+ 68 - 0
vender/github.com/astaxie/beego/filter_test.go

@@ -0,0 +1,68 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+)
+
+var FilterUser = func(ctx *context.Context) {
+	ctx.Output.Body([]byte("i am " + ctx.Input.Param(":last") + ctx.Input.Param(":first")))
+}
+
+func TestFilter(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
+	w := httptest.NewRecorder()
+	handler := NewControllerRegister()
+	handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
+	handler.Add("/person/:last/:first", &TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am astaXie" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+var FilterAdminUser = func(ctx *context.Context) {
+	ctx.Output.Body([]byte("i am admin"))
+}
+
+// Filter pattern /admin/:all
+// all url like    /admin/    /admin/xie    will all get filter
+
+func TestPatternTwo(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/admin/", nil)
+	w := httptest.NewRecorder()
+	handler := NewControllerRegister()
+	handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am admin" {
+		t.Errorf("filter /admin/ can't run")
+	}
+}
+
+func TestPatternThree(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
+	w := httptest.NewRecorder()
+	handler := NewControllerRegister()
+	handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am admin" {
+		t.Errorf("filter /admin/astaxie can't run")
+	}
+}

+ 110 - 0
vender/github.com/astaxie/beego/flash.go

@@ -0,0 +1,110 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"fmt"
+	"net/url"
+	"strings"
+)
+
+// FlashData is a tools to maintain data when using across request.
+type FlashData struct {
+	Data map[string]string
+}
+
+// NewFlash return a new empty FlashData struct.
+func NewFlash() *FlashData {
+	return &FlashData{
+		Data: make(map[string]string),
+	}
+}
+
+// Set message to flash
+func (fd *FlashData) Set(key string, msg string, args ...interface{}) {
+	if len(args) == 0 {
+		fd.Data[key] = msg
+	} else {
+		fd.Data[key] = fmt.Sprintf(msg, args...)
+	}
+}
+
+// Success writes success message to flash.
+func (fd *FlashData) Success(msg string, args ...interface{}) {
+	if len(args) == 0 {
+		fd.Data["success"] = msg
+	} else {
+		fd.Data["success"] = fmt.Sprintf(msg, args...)
+	}
+}
+
+// Notice writes notice message to flash.
+func (fd *FlashData) Notice(msg string, args ...interface{}) {
+	if len(args) == 0 {
+		fd.Data["notice"] = msg
+	} else {
+		fd.Data["notice"] = fmt.Sprintf(msg, args...)
+	}
+}
+
+// Warning writes warning message to flash.
+func (fd *FlashData) Warning(msg string, args ...interface{}) {
+	if len(args) == 0 {
+		fd.Data["warning"] = msg
+	} else {
+		fd.Data["warning"] = fmt.Sprintf(msg, args...)
+	}
+}
+
+// Error writes error message to flash.
+func (fd *FlashData) Error(msg string, args ...interface{}) {
+	if len(args) == 0 {
+		fd.Data["error"] = msg
+	} else {
+		fd.Data["error"] = fmt.Sprintf(msg, args...)
+	}
+}
+
+// Store does the saving operation of flash data.
+// the data are encoded and saved in cookie.
+func (fd *FlashData) Store(c *Controller) {
+	c.Data["flash"] = fd.Data
+	var flashValue string
+	for key, value := range fd.Data {
+		flashValue += "\x00" + key + "\x23" + BConfig.WebConfig.FlashSeparator + "\x23" + value + "\x00"
+	}
+	c.Ctx.SetCookie(BConfig.WebConfig.FlashName, url.QueryEscape(flashValue), 0, "/")
+}
+
+// ReadFromRequest parsed flash data from encoded values in cookie.
+func ReadFromRequest(c *Controller) *FlashData {
+	flash := NewFlash()
+	if cookie, err := c.Ctx.Request.Cookie(BConfig.WebConfig.FlashName); err == nil {
+		v, _ := url.QueryUnescape(cookie.Value)
+		vals := strings.Split(v, "\x00")
+		for _, v := range vals {
+			if len(v) > 0 {
+				kv := strings.Split(v, "\x23"+BConfig.WebConfig.FlashSeparator+"\x23")
+				if len(kv) == 2 {
+					flash.Data[kv[0]] = kv[1]
+				}
+			}
+		}
+		//read one time then delete it
+		c.Ctx.SetCookie(BConfig.WebConfig.FlashName, "", -1, "/")
+	}
+	c.Data["flash"] = flash.Data
+	return flash
+}

+ 54 - 0
vender/github.com/astaxie/beego/flash_test.go

@@ -0,0 +1,54 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+type TestFlashController struct {
+	Controller
+}
+
+func (t *TestFlashController) TestWriteFlash() {
+	flash := NewFlash()
+	flash.Notice("TestFlashString")
+	flash.Store(&t.Controller)
+	// we choose to serve json because we don't want to load a template html file
+	t.ServeJSON(true)
+}
+
+func TestFlashHeader(t *testing.T) {
+	// create fake GET request
+	r, _ := http.NewRequest("GET", "/", nil)
+	w := httptest.NewRecorder()
+
+	// setup the handler
+	handler := NewControllerRegister()
+	handler.Add("/", &TestFlashController{}, "get:TestWriteFlash")
+	handler.ServeHTTP(w, r)
+
+	// get the Set-Cookie value
+	sc := w.Header().Get("Set-Cookie")
+	// match for the expected header
+	res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00")
+	// validate the assertion
+	if !res {
+		t.Errorf("TestFlashHeader() unable to validate flash message")
+	}
+}

+ 8 - 0
vender/github.com/astaxie/beego/go.mod

@@ -0,0 +1,8 @@
+module github.com/cnlh/nps/vender/github.com/astaxie/beego
+
+require (
+	golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb
+	golang.org/x/net v0.0.0-20170920234330-b60f3a92103d
+	google.golang.org/appengine v1.1.0
+	gopkg.in/yaml.v2 v2.2.1
+)

+ 7 - 0
vender/github.com/astaxie/beego/go.sum

@@ -0,0 +1,7 @@
+golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb h1:Ah9YqXLj6fEgeKqcmBuLCbAsrF3ScD7dJ/bYM0C6tXI=
+golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20170920234330-b60f3a92103d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 39 - 0
vender/github.com/astaxie/beego/grace/conn.go

@@ -0,0 +1,39 @@
+package grace
+
+import (
+	"errors"
+	"net"
+	"sync"
+)
+
+type graceConn struct {
+	net.Conn
+	server *Server
+	m      sync.Mutex
+	closed bool
+}
+
+func (c *graceConn) Close() (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			switch x := r.(type) {
+			case string:
+				err = errors.New(x)
+			case error:
+				err = x
+			default:
+				err = errors.New("Unknown panic")
+			}
+		}
+	}()
+
+	c.m.Lock()
+	if c.closed {
+		c.m.Unlock()
+		return
+	}
+	c.server.wg.Done()
+	c.closed = true
+	c.m.Unlock()
+	return c.Conn.Close()
+}

+ 166 - 0
vender/github.com/astaxie/beego/grace/grace.go

@@ -0,0 +1,166 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package grace use to hot reload
+// Description: http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/
+//
+// Usage:
+//
+// import(
+//   "log"
+//	 "net/http"
+//	 "os"
+//
+//   "github.com/cnlh/nps/vender/github.com/astaxie/beego/grace"
+// )
+//
+//  func handler(w http.ResponseWriter, r *http.Request) {
+//	  w.Write([]byte("WORLD!"))
+//  }
+//
+//  func main() {
+//      mux := http.NewServeMux()
+//      mux.HandleFunc("/hello", handler)
+//
+//	    err := grace.ListenAndServe("localhost:8080", mux)
+//      if err != nil {
+//		   log.Println(err)
+//	    }
+//      log.Println("Server on 8080 stopped")
+//	     os.Exit(0)
+//    }
+package grace
+
+import (
+	"flag"
+	"net/http"
+	"os"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+)
+
+const (
+	// PreSignal is the position to add filter before signal
+	PreSignal = iota
+	// PostSignal is the position to add filter after signal
+	PostSignal
+	// StateInit represent the application inited
+	StateInit
+	// StateRunning represent the application is running
+	StateRunning
+	// StateShuttingDown represent the application is shutting down
+	StateShuttingDown
+	// StateTerminate represent the application is killed
+	StateTerminate
+)
+
+var (
+	regLock              *sync.Mutex
+	runningServers       map[string]*Server
+	runningServersOrder  []string
+	socketPtrOffsetMap   map[string]uint
+	runningServersForked bool
+
+	// DefaultReadTimeOut is the HTTP read timeout
+	DefaultReadTimeOut time.Duration
+	// DefaultWriteTimeOut is the HTTP Write timeout
+	DefaultWriteTimeOut time.Duration
+	// DefaultMaxHeaderBytes is the Max HTTP Herder size, default is 0, no limit
+	DefaultMaxHeaderBytes int
+	// DefaultTimeout is the shutdown server's timeout. default is 60s
+	DefaultTimeout = 60 * time.Second
+
+	isChild     bool
+	socketOrder string
+
+	hookableSignals []os.Signal
+)
+
+func init() {
+	flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)")
+	flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started")
+
+	regLock = &sync.Mutex{}
+	runningServers = make(map[string]*Server)
+	runningServersOrder = []string{}
+	socketPtrOffsetMap = make(map[string]uint)
+
+	hookableSignals = []os.Signal{
+		syscall.SIGHUP,
+		syscall.SIGINT,
+		syscall.SIGTERM,
+	}
+}
+
+// NewServer returns a new graceServer.
+func NewServer(addr string, handler http.Handler) (srv *Server) {
+	regLock.Lock()
+	defer regLock.Unlock()
+
+	if !flag.Parsed() {
+		flag.Parse()
+	}
+	if len(socketOrder) > 0 {
+		for i, addr := range strings.Split(socketOrder, ",") {
+			socketPtrOffsetMap[addr] = uint(i)
+		}
+	} else {
+		socketPtrOffsetMap[addr] = uint(len(runningServersOrder))
+	}
+
+	srv = &Server{
+		wg:      sync.WaitGroup{},
+		sigChan: make(chan os.Signal),
+		isChild: isChild,
+		SignalHooks: map[int]map[os.Signal][]func(){
+			PreSignal: {
+				syscall.SIGHUP:  {},
+				syscall.SIGINT:  {},
+				syscall.SIGTERM: {},
+			},
+			PostSignal: {
+				syscall.SIGHUP:  {},
+				syscall.SIGINT:  {},
+				syscall.SIGTERM: {},
+			},
+		},
+		state:   StateInit,
+		Network: "tcp",
+	}
+	srv.Server = &http.Server{}
+	srv.Server.Addr = addr
+	srv.Server.ReadTimeout = DefaultReadTimeOut
+	srv.Server.WriteTimeout = DefaultWriteTimeOut
+	srv.Server.MaxHeaderBytes = DefaultMaxHeaderBytes
+	srv.Server.Handler = handler
+
+	runningServersOrder = append(runningServersOrder, addr)
+	runningServers[addr] = srv
+
+	return
+}
+
+// ListenAndServe refer http.ListenAndServe
+func ListenAndServe(addr string, handler http.Handler) error {
+	server := NewServer(addr, handler)
+	return server.ListenAndServe()
+}
+
+// ListenAndServeTLS refer http.ListenAndServeTLS
+func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
+	server := NewServer(addr, handler)
+	return server.ListenAndServeTLS(certFile, keyFile)
+}

+ 62 - 0
vender/github.com/astaxie/beego/grace/listener.go

@@ -0,0 +1,62 @@
+package grace
+
+import (
+	"net"
+	"os"
+	"syscall"
+	"time"
+)
+
+type graceListener struct {
+	net.Listener
+	stop    chan error
+	stopped bool
+	server  *Server
+}
+
+func newGraceListener(l net.Listener, srv *Server) (el *graceListener) {
+	el = &graceListener{
+		Listener: l,
+		stop:     make(chan error),
+		server:   srv,
+	}
+	go func() {
+		<-el.stop
+		el.stopped = true
+		el.stop <- el.Listener.Close()
+	}()
+	return
+}
+
+func (gl *graceListener) Accept() (c net.Conn, err error) {
+	tc, err := gl.Listener.(*net.TCPListener).AcceptTCP()
+	if err != nil {
+		return
+	}
+
+	tc.SetKeepAlive(true)
+	tc.SetKeepAlivePeriod(3 * time.Minute)
+
+	c = &graceConn{
+		Conn:   tc,
+		server: gl.server,
+	}
+
+	gl.server.wg.Add(1)
+	return
+}
+
+func (gl *graceListener) Close() error {
+	if gl.stopped {
+		return syscall.EINVAL
+	}
+	gl.stop <- nil
+	return <-gl.stop
+}
+
+func (gl *graceListener) File() *os.File {
+	// returns a dup(2) - FD_CLOEXEC flag *not* set
+	tl := gl.Listener.(*net.TCPListener)
+	fl, _ := tl.File()
+	return fl
+}

+ 363 - 0
vender/github.com/astaxie/beego/grace/server.go

@@ -0,0 +1,363 @@
+package grace
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net"
+	"net/http"
+	"os"
+	"os/exec"
+	"os/signal"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+)
+
+// Server embedded http.Server
+type Server struct {
+	*http.Server
+	GraceListener    net.Listener
+	SignalHooks      map[int]map[os.Signal][]func()
+	tlsInnerListener *graceListener
+	wg               sync.WaitGroup
+	sigChan          chan os.Signal
+	isChild          bool
+	state            uint8
+	Network          string
+}
+
+// Serve accepts incoming connections on the Listener l,
+// creating a new service goroutine for each.
+// The service goroutines read requests and then call srv.Handler to reply to them.
+func (srv *Server) Serve() (err error) {
+	srv.state = StateRunning
+	err = srv.Server.Serve(srv.GraceListener)
+	log.Println(syscall.Getpid(), "Waiting for connections to finish...")
+	srv.wg.Wait()
+	srv.state = StateTerminate
+	return
+}
+
+// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve
+// to handle requests on incoming connections. If srv.Addr is blank, ":http" is
+// used.
+func (srv *Server) ListenAndServe() (err error) {
+	addr := srv.Addr
+	if addr == "" {
+		addr = ":http"
+	}
+
+	go srv.handleSignals()
+
+	l, err := srv.getListener(addr)
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	srv.GraceListener = newGraceListener(l, srv)
+
+	if srv.isChild {
+		process, err := os.FindProcess(os.Getppid())
+		if err != nil {
+			log.Println(err)
+			return err
+		}
+		err = process.Signal(syscall.SIGTERM)
+		if err != nil {
+			return err
+		}
+	}
+
+	log.Println(os.Getpid(), srv.Addr)
+	return srv.Serve()
+}
+
+// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls
+// Serve to handle requests on incoming TLS connections.
+//
+// Filenames containing a certificate and matching private key for the server must
+// be provided. If the certificate is signed by a certificate authority, the
+// certFile should be the concatenation of the server's certificate followed by the
+// CA's certificate.
+//
+// If srv.Addr is blank, ":https" is used.
+func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
+	addr := srv.Addr
+	if addr == "" {
+		addr = ":https"
+	}
+
+	if srv.TLSConfig == nil {
+		srv.TLSConfig = &tls.Config{}
+	}
+	if srv.TLSConfig.NextProtos == nil {
+		srv.TLSConfig.NextProtos = []string{"http/1.1"}
+	}
+
+	srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
+	srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+	if err != nil {
+		return
+	}
+
+	go srv.handleSignals()
+
+	l, err := srv.getListener(addr)
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	srv.tlsInnerListener = newGraceListener(l, srv)
+	srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
+
+	if srv.isChild {
+		process, err := os.FindProcess(os.Getppid())
+		if err != nil {
+			log.Println(err)
+			return err
+		}
+		err = process.Signal(syscall.SIGTERM)
+		if err != nil {
+			return err
+		}
+	}
+	log.Println(os.Getpid(), srv.Addr)
+	return srv.Serve()
+}
+
+// ListenAndServeMutualTLS listens on the TCP network address srv.Addr and then calls
+// Serve to handle requests on incoming mutual TLS connections.
+func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) (err error) {
+	addr := srv.Addr
+	if addr == "" {
+		addr = ":https"
+	}
+
+	if srv.TLSConfig == nil {
+		srv.TLSConfig = &tls.Config{}
+	}
+	if srv.TLSConfig.NextProtos == nil {
+		srv.TLSConfig.NextProtos = []string{"http/1.1"}
+	}
+
+	srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
+	srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+	if err != nil {
+		return
+	}
+	srv.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
+	pool := x509.NewCertPool()
+	data, err := ioutil.ReadFile(trustFile)
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+	pool.AppendCertsFromPEM(data)
+	srv.TLSConfig.ClientCAs = pool
+	log.Println("Mutual HTTPS")
+	go srv.handleSignals()
+
+	l, err := srv.getListener(addr)
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	srv.tlsInnerListener = newGraceListener(l, srv)
+	srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
+
+	if srv.isChild {
+		process, err := os.FindProcess(os.Getppid())
+		if err != nil {
+			log.Println(err)
+			return err
+		}
+		err = process.Kill()
+		if err != nil {
+			return err
+		}
+	}
+	log.Println(os.Getpid(), srv.Addr)
+	return srv.Serve()
+}
+
+// getListener either opens a new socket to listen on, or takes the acceptor socket
+// it got passed when restarted.
+func (srv *Server) getListener(laddr string) (l net.Listener, err error) {
+	if srv.isChild {
+		var ptrOffset uint
+		if len(socketPtrOffsetMap) > 0 {
+			ptrOffset = socketPtrOffsetMap[laddr]
+			log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr])
+		}
+
+		f := os.NewFile(uintptr(3+ptrOffset), "")
+		l, err = net.FileListener(f)
+		if err != nil {
+			err = fmt.Errorf("net.FileListener error: %v", err)
+			return
+		}
+	} else {
+		l, err = net.Listen(srv.Network, laddr)
+		if err != nil {
+			err = fmt.Errorf("net.Listen error: %v", err)
+			return
+		}
+	}
+	return
+}
+
+// handleSignals listens for os Signals and calls any hooked in function that the
+// user had registered with the signal.
+func (srv *Server) handleSignals() {
+	var sig os.Signal
+
+	signal.Notify(
+		srv.sigChan,
+		hookableSignals...,
+	)
+
+	pid := syscall.Getpid()
+	for {
+		sig = <-srv.sigChan
+		srv.signalHooks(PreSignal, sig)
+		switch sig {
+		case syscall.SIGHUP:
+			log.Println(pid, "Received SIGHUP. forking.")
+			err := srv.fork()
+			if err != nil {
+				log.Println("Fork err:", err)
+			}
+		case syscall.SIGINT:
+			log.Println(pid, "Received SIGINT.")
+			srv.shutdown()
+		case syscall.SIGTERM:
+			log.Println(pid, "Received SIGTERM.")
+			srv.shutdown()
+		default:
+			log.Printf("Received %v: nothing i care about...\n", sig)
+		}
+		srv.signalHooks(PostSignal, sig)
+	}
+}
+
+func (srv *Server) signalHooks(ppFlag int, sig os.Signal) {
+	if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet {
+		return
+	}
+	for _, f := range srv.SignalHooks[ppFlag][sig] {
+		f()
+	}
+}
+
+// shutdown closes the listener so that no new connections are accepted. it also
+// starts a goroutine that will serverTimeout (stop all running requests) the server
+// after DefaultTimeout.
+func (srv *Server) shutdown() {
+	if srv.state != StateRunning {
+		return
+	}
+
+	srv.state = StateShuttingDown
+	if DefaultTimeout >= 0 {
+		go srv.serverTimeout(DefaultTimeout)
+	}
+	err := srv.GraceListener.Close()
+	if err != nil {
+		log.Println(syscall.Getpid(), "Listener.Close() error:", err)
+	} else {
+		log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.")
+	}
+}
+
+// serverTimeout forces the server to shutdown in a given timeout - whether it
+// finished outstanding requests or not. if Read/WriteTimeout are not set or the
+// max header size is very big a connection could hang
+func (srv *Server) serverTimeout(d time.Duration) {
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("WaitGroup at 0", r)
+		}
+	}()
+	if srv.state != StateShuttingDown {
+		return
+	}
+	time.Sleep(d)
+	log.Println("[STOP - Hammer Time] Forcefully shutting down parent")
+	for {
+		if srv.state == StateTerminate {
+			break
+		}
+		srv.wg.Done()
+	}
+}
+
+func (srv *Server) fork() (err error) {
+	regLock.Lock()
+	defer regLock.Unlock()
+	if runningServersForked {
+		return
+	}
+	runningServersForked = true
+
+	var files = make([]*os.File, len(runningServers))
+	var orderArgs = make([]string, len(runningServers))
+	for _, srvPtr := range runningServers {
+		switch srvPtr.GraceListener.(type) {
+		case *graceListener:
+			files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File()
+		default:
+			files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File()
+		}
+		orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr
+	}
+
+	log.Println(files)
+	path := os.Args[0]
+	var args []string
+	if len(os.Args) > 1 {
+		for _, arg := range os.Args[1:] {
+			if arg == "-graceful" {
+				break
+			}
+			args = append(args, arg)
+		}
+	}
+	args = append(args, "-graceful")
+	if len(runningServers) > 1 {
+		args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ",")))
+		log.Println(args)
+	}
+	cmd := exec.Command(path, args...)
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	cmd.ExtraFiles = files
+	err = cmd.Start()
+	if err != nil {
+		log.Fatalf("Restart: Failed to launch, error: %v", err)
+	}
+
+	return
+}
+
+// RegisterSignalHook registers a function to be run PreSignal or PostSignal for a given signal.
+func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err error) {
+	if ppFlag != PreSignal && ppFlag != PostSignal {
+		err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal")
+		return
+	}
+	for _, s := range hookableSignals {
+		if s == sig {
+			srv.SignalHooks[ppFlag][sig] = append(srv.SignalHooks[ppFlag][sig], f)
+			return
+		}
+	}
+	err = fmt.Errorf("Signal '%v' is not supported", sig)
+	return
+}

+ 103 - 0
vender/github.com/astaxie/beego/hooks.go

@@ -0,0 +1,103 @@
+package beego
+
+import (
+	"encoding/json"
+	"mime"
+	"net/http"
+	"path/filepath"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/session"
+)
+
+//
+func registerMime() error {
+	for k, v := range mimemaps {
+		mime.AddExtensionType(k, v)
+	}
+	return nil
+}
+
+// register default error http handlers, 404,401,403,500 and 503.
+func registerDefaultErrorHandler() error {
+	m := map[string]func(http.ResponseWriter, *http.Request){
+		"401": unauthorized,
+		"402": paymentRequired,
+		"403": forbidden,
+		"404": notFound,
+		"405": methodNotAllowed,
+		"500": internalServerError,
+		"501": notImplemented,
+		"502": badGateway,
+		"503": serviceUnavailable,
+		"504": gatewayTimeout,
+		"417": invalidxsrf,
+		"422": missingxsrf,
+	}
+	for e, h := range m {
+		if _, ok := ErrorMaps[e]; !ok {
+			ErrorHandler(e, h)
+		}
+	}
+	return nil
+}
+
+func registerSession() error {
+	if BConfig.WebConfig.Session.SessionOn {
+		var err error
+		sessionConfig := AppConfig.String("sessionConfig")
+		conf := new(session.ManagerConfig)
+		if sessionConfig == "" {
+			conf.CookieName = BConfig.WebConfig.Session.SessionName
+			conf.EnableSetCookie = BConfig.WebConfig.Session.SessionAutoSetCookie
+			conf.Gclifetime = BConfig.WebConfig.Session.SessionGCMaxLifetime
+			conf.Secure = BConfig.Listen.EnableHTTPS
+			conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime
+			conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig)
+			conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly
+			conf.Domain = BConfig.WebConfig.Session.SessionDomain
+			conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
+			conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
+			conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
+		} else {
+			if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
+				return err
+			}
+		}
+		if GlobalSessions, err = session.NewManager(BConfig.WebConfig.Session.SessionProvider, conf); err != nil {
+			return err
+		}
+		go GlobalSessions.GC()
+	}
+	return nil
+}
+
+func registerTemplate() error {
+	defer lockViewPaths()
+	if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil {
+		if BConfig.RunMode == DEV {
+			logs.Warn(err)
+		}
+		return err
+	}
+	return nil
+}
+
+func registerAdmin() error {
+	if BConfig.Listen.EnableAdmin {
+		go beeAdminApp.Run()
+	}
+	return nil
+}
+
+func registerGzip() error {
+	if BConfig.EnableGzip {
+		context.InitGzip(
+			AppConfig.DefaultInt("gzipMinLength", -1),
+			AppConfig.DefaultInt("gzipCompressLevel", -1),
+			AppConfig.DefaultStrings("includedMethods", []string{"GET"}),
+		)
+	}
+	return nil
+}

+ 97 - 0
vender/github.com/astaxie/beego/httplib/README.md

@@ -0,0 +1,97 @@
+# httplib
+httplib is an libs help you to curl remote url.
+
+# How to use?
+
+## GET
+you can use Get to crawl data.
+
+	import "github.com/cnlh/nps/vender/github.com/astaxie/beego/httplib"
+	
+	str, err := httplib.Get("http://beego.me/").String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+	
+## POST
+POST data to remote url
+
+	req := httplib.Post("http://beego.me/")
+	req.Param("username","astaxie")
+	req.Param("password","123456")
+	str, err := req.String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+
+## Set timeout
+
+The default timeout is `60` seconds, function prototype:
+
+	SetTimeout(connectTimeout, readWriteTimeout time.Duration)
+
+Example:
+
+	// GET
+	httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+	
+	// POST
+	httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+
+
+## Debug
+
+If you want to debug the request info, set the debug on
+
+	httplib.Get("http://beego.me/").Debug(true)
+	
+## Set HTTP Basic Auth
+
+	str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+	
+## Set HTTPS
+
+If request url is https, You can set the client support TSL:
+
+	httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
+	
+More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config	
+
+## Set HTTP Version
+
+some servers need to specify the protocol version of HTTP
+
+	httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
+	
+## Set Cookie
+
+some http request need setcookie. So set it like this:
+
+	cookie := &http.Cookie{}
+	cookie.Name = "username"
+	cookie.Value  = "astaxie"
+	httplib.Get("http://beego.me/").SetCookie(cookie)
+
+## Upload file
+
+httplib support mutil file upload, use `req.PostFile()`
+
+	req := httplib.Post("http://beego.me/")
+	req.Param("username","astaxie")
+	req.PostFile("uploadfile1", "httplib.pdf")
+	str, err := req.String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+
+
+See godoc for further documentation and examples.
+
+* [godoc.org/github.com/cnlh/nps/vender/github.com/astaxie/beego/httplib](https://godoc.org/github.com/cnlh/nps/vender/github.com/astaxie/beego/httplib)

+ 624 - 0
vender/github.com/astaxie/beego/httplib/httplib.go

@@ -0,0 +1,624 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package httplib is used as http.Client
+// Usage:
+//
+// import "github.com/cnlh/nps/vender/github.com/astaxie/beego/httplib"
+//
+//	b := httplib.Post("http://beego.me/")
+//	b.Param("username","astaxie")
+//	b.Param("password","123456")
+//	b.PostFile("uploadfile1", "httplib.pdf")
+//	b.PostFile("uploadfile2", "httplib.txt")
+//	str, err := b.String()
+//	if err != nil {
+//		t.Fatal(err)
+//	}
+//	fmt.Println(str)
+//
+//  more docs http://beego.me/docs/module/httplib.md
+package httplib
+
+import (
+	"bytes"
+	"compress/gzip"
+	"crypto/tls"
+	"encoding/json"
+	"encoding/xml"
+	"gopkg.in/yaml.v2"
+	"io"
+	"io/ioutil"
+	"log"
+	"mime/multipart"
+	"net"
+	"net/http"
+	"net/http/cookiejar"
+	"net/http/httputil"
+	"net/url"
+	"os"
+	"strings"
+	"sync"
+	"time"
+)
+
+var defaultSetting = BeegoHTTPSettings{
+	UserAgent:        "beegoServer",
+	ConnectTimeout:   60 * time.Second,
+	ReadWriteTimeout: 60 * time.Second,
+	Gzip:             true,
+	DumpBody:         true,
+}
+
+var defaultCookieJar http.CookieJar
+var settingMutex sync.Mutex
+
+// createDefaultCookie creates a global cookiejar to store cookies.
+func createDefaultCookie() {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+	defaultCookieJar, _ = cookiejar.New(nil)
+}
+
+// SetDefaultSetting Overwrite default settings
+func SetDefaultSetting(setting BeegoHTTPSettings) {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+	defaultSetting = setting
+}
+
+// NewBeegoRequest return *BeegoHttpRequest with specific method
+func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest {
+	var resp http.Response
+	u, err := url.Parse(rawurl)
+	if err != nil {
+		log.Println("Httplib:", err)
+	}
+	req := http.Request{
+		URL:        u,
+		Method:     method,
+		Header:     make(http.Header),
+		Proto:      "HTTP/1.1",
+		ProtoMajor: 1,
+		ProtoMinor: 1,
+	}
+	return &BeegoHTTPRequest{
+		url:     rawurl,
+		req:     &req,
+		params:  map[string][]string{},
+		files:   map[string]string{},
+		setting: defaultSetting,
+		resp:    &resp,
+	}
+}
+
+// Get returns *BeegoHttpRequest with GET method.
+func Get(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "GET")
+}
+
+// Post returns *BeegoHttpRequest with POST method.
+func Post(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "POST")
+}
+
+// Put returns *BeegoHttpRequest with PUT method.
+func Put(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "PUT")
+}
+
+// Delete returns *BeegoHttpRequest DELETE method.
+func Delete(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "DELETE")
+}
+
+// Head returns *BeegoHttpRequest with HEAD method.
+func Head(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "HEAD")
+}
+
+// BeegoHTTPSettings is the http.Client setting
+type BeegoHTTPSettings struct {
+	ShowDebug        bool
+	UserAgent        string
+	ConnectTimeout   time.Duration
+	ReadWriteTimeout time.Duration
+	TLSClientConfig  *tls.Config
+	Proxy            func(*http.Request) (*url.URL, error)
+	Transport        http.RoundTripper
+	CheckRedirect    func(req *http.Request, via []*http.Request) error
+	EnableCookie     bool
+	Gzip             bool
+	DumpBody         bool
+	Retries          int // if set to -1 means will retry forever
+}
+
+// BeegoHTTPRequest provides more useful methods for requesting one url than http.Request.
+type BeegoHTTPRequest struct {
+	url     string
+	req     *http.Request
+	params  map[string][]string
+	files   map[string]string
+	setting BeegoHTTPSettings
+	resp    *http.Response
+	body    []byte
+	dump    []byte
+}
+
+// GetRequest return the request object
+func (b *BeegoHTTPRequest) GetRequest() *http.Request {
+	return b.req
+}
+
+// Setting Change request settings
+func (b *BeegoHTTPRequest) Setting(setting BeegoHTTPSettings) *BeegoHTTPRequest {
+	b.setting = setting
+	return b
+}
+
+// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
+func (b *BeegoHTTPRequest) SetBasicAuth(username, password string) *BeegoHTTPRequest {
+	b.req.SetBasicAuth(username, password)
+	return b
+}
+
+// SetEnableCookie sets enable/disable cookiejar
+func (b *BeegoHTTPRequest) SetEnableCookie(enable bool) *BeegoHTTPRequest {
+	b.setting.EnableCookie = enable
+	return b
+}
+
+// SetUserAgent sets User-Agent header field
+func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest {
+	b.setting.UserAgent = useragent
+	return b
+}
+
+// Debug sets show debug or not when executing request.
+func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
+	b.setting.ShowDebug = isdebug
+	return b
+}
+
+// Retries sets Retries times.
+// default is 0 means no retried.
+// -1 means retried forever.
+// others means retried times.
+func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest {
+	b.setting.Retries = times
+	return b
+}
+
+// DumpBody setting whether need to Dump the Body.
+func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
+	b.setting.DumpBody = isdump
+	return b
+}
+
+// DumpRequest return the DumpRequest
+func (b *BeegoHTTPRequest) DumpRequest() []byte {
+	return b.dump
+}
+
+// SetTimeout sets connect time out and read-write time out for BeegoRequest.
+func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest {
+	b.setting.ConnectTimeout = connectTimeout
+	b.setting.ReadWriteTimeout = readWriteTimeout
+	return b
+}
+
+// SetTLSClientConfig sets tls connection configurations if visiting https url.
+func (b *BeegoHTTPRequest) SetTLSClientConfig(config *tls.Config) *BeegoHTTPRequest {
+	b.setting.TLSClientConfig = config
+	return b
+}
+
+// Header add header item string in request.
+func (b *BeegoHTTPRequest) Header(key, value string) *BeegoHTTPRequest {
+	b.req.Header.Set(key, value)
+	return b
+}
+
+// SetHost set the request host
+func (b *BeegoHTTPRequest) SetHost(host string) *BeegoHTTPRequest {
+	b.req.Host = host
+	return b
+}
+
+// SetProtocolVersion Set the protocol version for incoming requests.
+// Client requests always use HTTP/1.1.
+func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest {
+	if len(vers) == 0 {
+		vers = "HTTP/1.1"
+	}
+
+	major, minor, ok := http.ParseHTTPVersion(vers)
+	if ok {
+		b.req.Proto = vers
+		b.req.ProtoMajor = major
+		b.req.ProtoMinor = minor
+	}
+
+	return b
+}
+
+// SetCookie add cookie into request.
+func (b *BeegoHTTPRequest) SetCookie(cookie *http.Cookie) *BeegoHTTPRequest {
+	b.req.Header.Add("Cookie", cookie.String())
+	return b
+}
+
+// SetTransport set the setting transport
+func (b *BeegoHTTPRequest) SetTransport(transport http.RoundTripper) *BeegoHTTPRequest {
+	b.setting.Transport = transport
+	return b
+}
+
+// SetProxy set the http proxy
+// example:
+//
+//	func(req *http.Request) (*url.URL, error) {
+// 		u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
+// 		return u, nil
+// 	}
+func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHTTPRequest {
+	b.setting.Proxy = proxy
+	return b
+}
+
+// SetCheckRedirect specifies the policy for handling redirects.
+//
+// If CheckRedirect is nil, the Client uses its default policy,
+// which is to stop after 10 consecutive requests.
+func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest {
+	b.setting.CheckRedirect = redirect
+	return b
+}
+
+// Param adds query param in to request.
+// params build query string as ?key1=value1&key2=value2...
+func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest {
+	if param, ok := b.params[key]; ok {
+		b.params[key] = append(param, value)
+	} else {
+		b.params[key] = []string{value}
+	}
+	return b
+}
+
+// PostFile add a post file to the request
+func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest {
+	b.files[formname] = filename
+	return b
+}
+
+// Body adds request raw body.
+// it supports string and []byte.
+func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
+	switch t := data.(type) {
+	case string:
+		bf := bytes.NewBufferString(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	case []byte:
+		bf := bytes.NewBuffer(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	}
+	return b
+}
+
+// XMLBody adds request raw body encoding by XML.
+func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
+	if b.req.Body == nil && obj != nil {
+		byts, err := xml.Marshal(obj)
+		if err != nil {
+			return b, err
+		}
+		b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
+		b.req.ContentLength = int64(len(byts))
+		b.req.Header.Set("Content-Type", "application/xml")
+	}
+	return b, nil
+}
+
+// YAMLBody adds request raw body encoding by YAML.
+func (b *BeegoHTTPRequest) YAMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
+	if b.req.Body == nil && obj != nil {
+		byts, err := yaml.Marshal(obj)
+		if err != nil {
+			return b, err
+		}
+		b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
+		b.req.ContentLength = int64(len(byts))
+		b.req.Header.Set("Content-Type", "application/x+yaml")
+	}
+	return b, nil
+}
+
+// JSONBody adds request raw body encoding by JSON.
+func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
+	if b.req.Body == nil && obj != nil {
+		byts, err := json.Marshal(obj)
+		if err != nil {
+			return b, err
+		}
+		b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
+		b.req.ContentLength = int64(len(byts))
+		b.req.Header.Set("Content-Type", "application/json")
+	}
+	return b, nil
+}
+
+func (b *BeegoHTTPRequest) buildURL(paramBody string) {
+	// build GET url with query string
+	if b.req.Method == "GET" && len(paramBody) > 0 {
+		if strings.Contains(b.url, "?") {
+			b.url += "&" + paramBody
+		} else {
+			b.url = b.url + "?" + paramBody
+		}
+		return
+	}
+
+	// build POST/PUT/PATCH url and body
+	if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil {
+		// with files
+		if len(b.files) > 0 {
+			pr, pw := io.Pipe()
+			bodyWriter := multipart.NewWriter(pw)
+			go func() {
+				for formname, filename := range b.files {
+					fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
+					if err != nil {
+						log.Println("Httplib:", err)
+					}
+					fh, err := os.Open(filename)
+					if err != nil {
+						log.Println("Httplib:", err)
+					}
+					//iocopy
+					_, err = io.Copy(fileWriter, fh)
+					fh.Close()
+					if err != nil {
+						log.Println("Httplib:", err)
+					}
+				}
+				for k, v := range b.params {
+					for _, vv := range v {
+						bodyWriter.WriteField(k, vv)
+					}
+				}
+				bodyWriter.Close()
+				pw.Close()
+			}()
+			b.Header("Content-Type", bodyWriter.FormDataContentType())
+			b.req.Body = ioutil.NopCloser(pr)
+			return
+		}
+
+		// with params
+		if len(paramBody) > 0 {
+			b.Header("Content-Type", "application/x-www-form-urlencoded")
+			b.Body(paramBody)
+		}
+	}
+}
+
+func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) {
+	if b.resp.StatusCode != 0 {
+		return b.resp, nil
+	}
+	resp, err := b.DoRequest()
+	if err != nil {
+		return nil, err
+	}
+	b.resp = resp
+	return resp, nil
+}
+
+// DoRequest will do the client.Do
+func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
+	var paramBody string
+	if len(b.params) > 0 {
+		var buf bytes.Buffer
+		for k, v := range b.params {
+			for _, vv := range v {
+				buf.WriteString(url.QueryEscape(k))
+				buf.WriteByte('=')
+				buf.WriteString(url.QueryEscape(vv))
+				buf.WriteByte('&')
+			}
+		}
+		paramBody = buf.String()
+		paramBody = paramBody[0 : len(paramBody)-1]
+	}
+
+	b.buildURL(paramBody)
+	urlParsed, err := url.Parse(b.url)
+	if err != nil {
+		return nil, err
+	}
+
+	b.req.URL = urlParsed
+
+	trans := b.setting.Transport
+
+	if trans == nil {
+		// create default transport
+		trans = &http.Transport{
+			TLSClientConfig:     b.setting.TLSClientConfig,
+			Proxy:               b.setting.Proxy,
+			Dial:                TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
+			MaxIdleConnsPerHost: 100,
+		}
+	} else {
+		// if b.transport is *http.Transport then set the settings.
+		if t, ok := trans.(*http.Transport); ok {
+			if t.TLSClientConfig == nil {
+				t.TLSClientConfig = b.setting.TLSClientConfig
+			}
+			if t.Proxy == nil {
+				t.Proxy = b.setting.Proxy
+			}
+			if t.Dial == nil {
+				t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
+			}
+		}
+	}
+
+	var jar http.CookieJar
+	if b.setting.EnableCookie {
+		if defaultCookieJar == nil {
+			createDefaultCookie()
+		}
+		jar = defaultCookieJar
+	}
+
+	client := &http.Client{
+		Transport: trans,
+		Jar:       jar,
+	}
+
+	if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
+		b.req.Header.Set("User-Agent", b.setting.UserAgent)
+	}
+
+	if b.setting.CheckRedirect != nil {
+		client.CheckRedirect = b.setting.CheckRedirect
+	}
+
+	if b.setting.ShowDebug {
+		dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
+		if err != nil {
+			log.Println(err.Error())
+		}
+		b.dump = dump
+	}
+	// retries default value is 0, it will run once.
+	// retries equal to -1, it will run forever until success
+	// retries is setted, it will retries fixed times.
+	for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
+		resp, err = client.Do(b.req)
+		if err == nil {
+			break
+		}
+	}
+	return resp, err
+}
+
+// String returns the body string in response.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) String() (string, error) {
+	data, err := b.Bytes()
+	if err != nil {
+		return "", err
+	}
+
+	return string(data), nil
+}
+
+// Bytes returns the body []byte in response.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
+	if b.body != nil {
+		return b.body, nil
+	}
+	resp, err := b.getResponse()
+	if err != nil {
+		return nil, err
+	}
+	if resp.Body == nil {
+		return nil, nil
+	}
+	defer resp.Body.Close()
+	if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
+		reader, err := gzip.NewReader(resp.Body)
+		if err != nil {
+			return nil, err
+		}
+		b.body, err = ioutil.ReadAll(reader)
+		return b.body, err
+	}
+	b.body, err = ioutil.ReadAll(resp.Body)
+	return b.body, err
+}
+
+// ToFile saves the body data in response to one file.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToFile(filename string) error {
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	resp, err := b.getResponse()
+	if err != nil {
+		return err
+	}
+	if resp.Body == nil {
+		return nil
+	}
+	defer resp.Body.Close()
+	_, err = io.Copy(f, resp.Body)
+	return err
+}
+
+// ToJSON returns the map that marshals from the body bytes as json in response .
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToJSON(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(data, v)
+}
+
+// ToXML returns the map that marshals from the body bytes as xml in response .
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToXML(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return xml.Unmarshal(data, v)
+}
+
+// ToYAML returns the map that marshals from the body bytes as yaml in response .
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToYAML(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return yaml.Unmarshal(data, v)
+}
+
+// Response executes request client gets response mannually.
+func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
+	return b.getResponse()
+}
+
+// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
+func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
+	return func(netw, addr string) (net.Conn, error) {
+		conn, err := net.DialTimeout(netw, addr, cTimeout)
+		if err != nil {
+			return nil, err
+		}
+		err = conn.SetDeadline(time.Now().Add(rwTimeout))
+		return conn, err
+	}
+}

+ 226 - 0
vender/github.com/astaxie/beego/httplib/httplib_test.go

@@ -0,0 +1,226 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package httplib
+
+import (
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+	"time"
+)
+
+func TestResponse(t *testing.T) {
+	req := Get("http://httpbin.org/get")
+	resp, err := req.Response()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(resp)
+}
+
+func TestGet(t *testing.T) {
+	req := Get("http://httpbin.org/get")
+	b, err := req.Bytes()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(b)
+
+	s, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(s)
+
+	if string(b) != s {
+		t.Fatal("request data not match")
+	}
+}
+
+func TestSimplePost(t *testing.T) {
+	v := "smallfish"
+	req := Post("http://httpbin.org/post")
+	req.Param("username", v)
+
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in post")
+	}
+}
+
+//func TestPostFile(t *testing.T) {
+//	v := "smallfish"
+//	req := Post("http://httpbin.org/post")
+//	req.Debug(true)
+//	req.Param("username", v)
+//	req.PostFile("uploadfile", "httplib_test.go")
+
+//	str, err := req.String()
+//	if err != nil {
+//		t.Fatal(err)
+//	}
+//	t.Log(str)
+
+//	n := strings.Index(str, v)
+//	if n == -1 {
+//		t.Fatal(v + " not found in post")
+//	}
+//}
+
+func TestSimplePut(t *testing.T) {
+	str, err := Put("http://httpbin.org/put").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestSimpleDelete(t *testing.T) {
+	str, err := Delete("http://httpbin.org/delete").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestSimpleDeleteParam(t *testing.T) {
+	str, err := Delete("http://httpbin.org/delete").Param("key", "val").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestWithCookie(t *testing.T) {
+	v := "smallfish"
+	str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in cookie")
+	}
+}
+
+func TestWithBasicAuth(t *testing.T) {
+	str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+	n := strings.Index(str, "authenticated")
+	if n == -1 {
+		t.Fatal("authenticated not found in response")
+	}
+}
+
+func TestWithUserAgent(t *testing.T) {
+	v := "beego"
+	str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in user-agent")
+	}
+}
+
+func TestWithSetting(t *testing.T) {
+	v := "beego"
+	var setting BeegoHTTPSettings
+	setting.EnableCookie = true
+	setting.UserAgent = v
+	setting.Transport = nil
+	setting.ReadWriteTimeout = 5 * time.Second
+	SetDefaultSetting(setting)
+
+	str, err := Get("http://httpbin.org/get").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in user-agent")
+	}
+}
+
+func TestToJson(t *testing.T) {
+	req := Get("http://httpbin.org/ip")
+	resp, err := req.Response()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(resp)
+
+	// httpbin will return http remote addr
+	type IP struct {
+		Origin string `json:"origin"`
+	}
+	var ip IP
+	err = req.ToJSON(&ip)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(ip.Origin)
+
+	if n := strings.Count(ip.Origin, "."); n != 3 {
+		t.Fatal("response is not valid ip")
+	}
+}
+
+func TestToFile(t *testing.T) {
+	f := "beego_testfile"
+	req := Get("http://httpbin.org/ip")
+	err := req.ToFile(f)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(f)
+	b, err := ioutil.ReadFile(f)
+	if n := strings.Index(string(b), "origin"); n == -1 {
+		t.Fatal(err)
+	}
+}
+
+func TestHeader(t *testing.T) {
+	req := Get("http://httpbin.org/headers")
+	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}

+ 111 - 0
vender/github.com/astaxie/beego/log.go

@@ -0,0 +1,111 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"strings"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+)
+
+// Log levels to control the logging output.
+const (
+	LevelEmergency = iota
+	LevelAlert
+	LevelCritical
+	LevelError
+	LevelWarning
+	LevelNotice
+	LevelInformational
+	LevelDebug
+)
+
+// BeeLogger references the used application logger.
+var BeeLogger = logs.GetBeeLogger()
+
+// SetLevel sets the global log level used by the simple logger.
+func SetLevel(l int) {
+	logs.SetLevel(l)
+}
+
+// SetLogFuncCall set the CallDepth, default is 3
+func SetLogFuncCall(b bool) {
+	logs.SetLogFuncCall(b)
+}
+
+// SetLogger sets a new logger.
+func SetLogger(adaptername string, config string) error {
+	return logs.SetLogger(adaptername, config)
+}
+
+// Emergency logs a message at emergency level.
+func Emergency(v ...interface{}) {
+	logs.Emergency(generateFmtStr(len(v)), v...)
+}
+
+// Alert logs a message at alert level.
+func Alert(v ...interface{}) {
+	logs.Alert(generateFmtStr(len(v)), v...)
+}
+
+// Critical logs a message at critical level.
+func Critical(v ...interface{}) {
+	logs.Critical(generateFmtStr(len(v)), v...)
+}
+
+// Error logs a message at error level.
+func Error(v ...interface{}) {
+	logs.Error(generateFmtStr(len(v)), v...)
+}
+
+// Warning logs a message at warning level.
+func Warning(v ...interface{}) {
+	logs.Warning(generateFmtStr(len(v)), v...)
+}
+
+// Warn compatibility alias for Warning()
+func Warn(v ...interface{}) {
+	logs.Warn(generateFmtStr(len(v)), v...)
+}
+
+// Notice logs a message at notice level.
+func Notice(v ...interface{}) {
+	logs.Notice(generateFmtStr(len(v)), v...)
+}
+
+// Informational logs a message at info level.
+func Informational(v ...interface{}) {
+	logs.Informational(generateFmtStr(len(v)), v...)
+}
+
+// Info compatibility alias for Warning()
+func Info(v ...interface{}) {
+	logs.Info(generateFmtStr(len(v)), v...)
+}
+
+// Debug logs a message at debug level.
+func Debug(v ...interface{}) {
+	logs.Debug(generateFmtStr(len(v)), v...)
+}
+
+// Trace logs a message at trace level.
+// compatibility alias for Warning()
+func Trace(v ...interface{}) {
+	logs.Trace(generateFmtStr(len(v)), v...)
+}
+
+func generateFmtStr(n int) string {
+	return strings.Repeat("%v ", n)
+}

+ 83 - 0
vender/github.com/astaxie/beego/logs/accesslog.go

@@ -0,0 +1,83 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package logs
+
+import (
+	"bytes"
+	"strings"
+	"encoding/json"
+	"fmt"
+	"time"
+)
+
+const (
+	apacheFormatPattern = "%s - - [%s] \"%s %d %d\" %f %s %s"
+	apacheFormat        = "APACHE_FORMAT"
+	jsonFormat          = "JSON_FORMAT"
+)
+
+// AccessLogRecord struct for holding access log data.
+type AccessLogRecord struct {
+	RemoteAddr     string        `json:"remote_addr"`
+	RequestTime    time.Time     `json:"request_time"`
+	RequestMethod  string        `json:"request_method"`
+	Request        string        `json:"request"`
+	ServerProtocol string        `json:"server_protocol"`
+	Host           string        `json:"host"`
+	Status         int           `json:"status"`
+	BodyBytesSent  int64         `json:"body_bytes_sent"`
+	ElapsedTime    time.Duration `json:"elapsed_time"`
+	HTTPReferrer   string        `json:"http_referrer"`
+	HTTPUserAgent  string        `json:"http_user_agent"`
+	RemoteUser     string        `json:"remote_user"`
+}
+
+func (r *AccessLogRecord) json() ([]byte, error) {
+	buffer := &bytes.Buffer{}
+	encoder := json.NewEncoder(buffer)
+	disableEscapeHTML(encoder)
+
+	err := encoder.Encode(r)
+	return buffer.Bytes(), err
+}
+
+func disableEscapeHTML(i interface{}) {
+	if e, ok := i.(interface {
+		SetEscapeHTML(bool)
+	}); ok {
+		e.SetEscapeHTML(false)
+	}
+}
+
+// AccessLog - Format and print access log.
+func AccessLog(r *AccessLogRecord, format string) {
+	var msg string
+	switch format {
+	case apacheFormat:
+		timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05")
+		msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent,
+			r.ElapsedTime.Seconds(), r.HTTPReferrer, r.HTTPUserAgent)
+	case jsonFormat:
+		fallthrough
+	default:
+		jsonData, err := r.json()
+		if err != nil {
+			msg = fmt.Sprintf(`{"Error": "%s"}`, err)
+		} else {
+			msg = string(jsonData)
+		}
+	}
+	beeLogger.writeMsg(levelLoggerImpl, strings.TrimSpace(msg))
+}

+ 186 - 0
vender/github.com/astaxie/beego/logs/alils/alils.go

@@ -0,0 +1,186 @@
+package alils
+
+import (
+	"encoding/json"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
+	"github.com/gogo/protobuf/proto"
+)
+
+const (
+	// CacheSize set the flush size
+	CacheSize int = 64
+	// Delimiter define the topic delimiter
+	Delimiter string = "##"
+)
+
+// Config is the Config for Ali Log
+type Config struct {
+	Project   string   `json:"project"`
+	Endpoint  string   `json:"endpoint"`
+	KeyID     string   `json:"key_id"`
+	KeySecret string   `json:"key_secret"`
+	LogStore  string   `json:"log_store"`
+	Topics    []string `json:"topics"`
+	Source    string   `json:"source"`
+	Level     int      `json:"level"`
+	FlushWhen int      `json:"flush_when"`
+}
+
+// aliLSWriter implements LoggerInterface.
+// it writes messages in keep-live tcp connection.
+type aliLSWriter struct {
+	store    *LogStore
+	group    []*LogGroup
+	withMap  bool
+	groupMap map[string]*LogGroup
+	lock     *sync.Mutex
+	Config
+}
+
+// NewAliLS create a new Logger
+func NewAliLS() logs.Logger {
+	alils := new(aliLSWriter)
+	alils.Level = logs.LevelTrace
+	return alils
+}
+
+// Init parse config and init struct
+func (c *aliLSWriter) Init(jsonConfig string) (err error) {
+
+	json.Unmarshal([]byte(jsonConfig), c)
+
+	if c.FlushWhen > CacheSize {
+		c.FlushWhen = CacheSize
+	}
+
+	prj := &LogProject{
+		Name:            c.Project,
+		Endpoint:        c.Endpoint,
+		AccessKeyID:     c.KeyID,
+		AccessKeySecret: c.KeySecret,
+	}
+
+	c.store, err = prj.GetLogStore(c.LogStore)
+	if err != nil {
+		return err
+	}
+
+	// Create default Log Group
+	c.group = append(c.group, &LogGroup{
+		Topic:  proto.String(""),
+		Source: proto.String(c.Source),
+		Logs:   make([]*Log, 0, c.FlushWhen),
+	})
+
+	// Create other Log Group
+	c.groupMap = make(map[string]*LogGroup)
+	for _, topic := range c.Topics {
+
+		lg := &LogGroup{
+			Topic:  proto.String(topic),
+			Source: proto.String(c.Source),
+			Logs:   make([]*Log, 0, c.FlushWhen),
+		}
+
+		c.group = append(c.group, lg)
+		c.groupMap[topic] = lg
+	}
+
+	if len(c.group) == 1 {
+		c.withMap = false
+	} else {
+		c.withMap = true
+	}
+
+	c.lock = &sync.Mutex{}
+
+	return nil
+}
+
+// WriteMsg write message in connection.
+// if connection is down, try to re-connect.
+func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error) {
+
+	if level > c.Level {
+		return nil
+	}
+
+	var topic string
+	var content string
+	var lg *LogGroup
+	if c.withMap {
+
+		// Topic,LogGroup
+		strs := strings.SplitN(msg, Delimiter, 2)
+		if len(strs) == 2 {
+			pos := strings.LastIndex(strs[0], " ")
+			topic = strs[0][pos+1 : len(strs[0])]
+			content = strs[0][0:pos] + strs[1]
+			lg = c.groupMap[topic]
+		}
+
+		// send to empty Topic
+		if lg == nil {
+			content = msg
+			lg = c.group[0]
+		}
+	} else {
+		content = msg
+		lg = c.group[0]
+	}
+
+	c1 := &LogContent{
+		Key:   proto.String("msg"),
+		Value: proto.String(content),
+	}
+
+	l := &Log{
+		Time: proto.Uint32(uint32(when.Unix())),
+		Contents: []*LogContent{
+			c1,
+		},
+	}
+
+	c.lock.Lock()
+	lg.Logs = append(lg.Logs, l)
+	c.lock.Unlock()
+
+	if len(lg.Logs) >= c.FlushWhen {
+		c.flush(lg)
+	}
+
+	return nil
+}
+
+// Flush implementing method. empty.
+func (c *aliLSWriter) Flush() {
+
+	// flush all group
+	for _, lg := range c.group {
+		c.flush(lg)
+	}
+}
+
+// Destroy destroy connection writer and close tcp listener.
+func (c *aliLSWriter) Destroy() {
+}
+
+func (c *aliLSWriter) flush(lg *LogGroup) {
+
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	err := c.store.PutLogs(lg)
+	if err != nil {
+		return
+	}
+
+	lg.Logs = make([]*Log, 0, c.FlushWhen)
+}
+
+func init() {
+	logs.Register(logs.AdapterAliLS, NewAliLS)
+}

+ 13 - 0
vender/github.com/astaxie/beego/logs/alils/config.go

@@ -0,0 +1,13 @@
+package alils
+
+const (
+	version         = "0.5.0"     // SDK version
+	signatureMethod = "hmac-sha1" // Signature method
+
+	// OffsetNewest stands for the log head offset, i.e. the offset that will be
+	// assigned to the next message that will be produced to the shard.
+	OffsetNewest = "end"
+	// OffsetOldest stands for the oldest offset available on the logstore for a
+	// shard.
+	OffsetOldest = "begin"
+)

+ 1038 - 0
vender/github.com/astaxie/beego/logs/alils/log.pb.go

@@ -0,0 +1,1038 @@
+package alils
+
+import (
+	"fmt"
+	"io"
+	"math"
+
+	"github.com/gogo/protobuf/proto"
+	github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+var (
+	// ErrInvalidLengthLog invalid proto
+	ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling")
+	// ErrIntOverflowLog overflow
+	ErrIntOverflowLog = fmt.Errorf("proto: integer overflow")
+)
+
+// Log define the proto Log
+type Log struct {
+	Time            *uint32       `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"`
+	Contents        []*LogContent `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"`
+	XXXUnrecognized []byte        `json:"-"`
+}
+
+// Reset the Log
+func (m *Log) Reset() { *m = Log{} }
+
+// String return the Compact Log
+func (m *Log) String() string { return proto.CompactTextString(m) }
+
+// ProtoMessage not implemented
+func (*Log) ProtoMessage() {}
+
+// GetTime return the Log's Time
+func (m *Log) GetTime() uint32 {
+	if m != nil && m.Time != nil {
+		return *m.Time
+	}
+	return 0
+}
+
+// GetContents return the Log's Contents
+func (m *Log) GetContents() []*LogContent {
+	if m != nil {
+		return m.Contents
+	}
+	return nil
+}
+
+// LogContent define the Log content struct
+type LogContent struct {
+	Key             *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"`
+	Value           *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"`
+	XXXUnrecognized []byte  `json:"-"`
+}
+
+// Reset LogContent
+func (m *LogContent) Reset() { *m = LogContent{} }
+
+// String return the compact text
+func (m *LogContent) String() string { return proto.CompactTextString(m) }
+
+// ProtoMessage not implemented
+func (*LogContent) ProtoMessage() {}
+
+// GetKey return the Key
+func (m *LogContent) GetKey() string {
+	if m != nil && m.Key != nil {
+		return *m.Key
+	}
+	return ""
+}
+
+// GetValue return the Value
+func (m *LogContent) GetValue() string {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return ""
+}
+
+// LogGroup define the logs struct
+type LogGroup struct {
+	Logs            []*Log  `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"`
+	Reserved        *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"`
+	Topic           *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"`
+	Source          *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"`
+	XXXUnrecognized []byte  `json:"-"`
+}
+
+// Reset LogGroup
+func (m *LogGroup) Reset() { *m = LogGroup{} }
+
+// String return the compact text
+func (m *LogGroup) String() string { return proto.CompactTextString(m) }
+
+// ProtoMessage not implemented
+func (*LogGroup) ProtoMessage() {}
+
+// GetLogs return the loggroup logs
+func (m *LogGroup) GetLogs() []*Log {
+	if m != nil {
+		return m.Logs
+	}
+	return nil
+}
+
+// GetReserved return Reserved
+func (m *LogGroup) GetReserved() string {
+	if m != nil && m.Reserved != nil {
+		return *m.Reserved
+	}
+	return ""
+}
+
+// GetTopic return Topic
+func (m *LogGroup) GetTopic() string {
+	if m != nil && m.Topic != nil {
+		return *m.Topic
+	}
+	return ""
+}
+
+// GetSource return Source
+func (m *LogGroup) GetSource() string {
+	if m != nil && m.Source != nil {
+		return *m.Source
+	}
+	return ""
+}
+
+// LogGroupList define the LogGroups
+type LogGroupList struct {
+	LogGroups       []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"`
+	XXXUnrecognized []byte      `json:"-"`
+}
+
+// Reset LogGroupList
+func (m *LogGroupList) Reset() { *m = LogGroupList{} }
+
+// String return compact text
+func (m *LogGroupList) String() string { return proto.CompactTextString(m) }
+
+// ProtoMessage not implemented
+func (*LogGroupList) ProtoMessage() {}
+
+// GetLogGroups return the LogGroups
+func (m *LogGroupList) GetLogGroups() []*LogGroup {
+	if m != nil {
+		return m.LogGroups
+	}
+	return nil
+}
+
+// Marshal the logs to byte slice
+func (m *Log) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+// MarshalTo data
+func (m *Log) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Time == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time")
+	}
+	data[i] = 0x8
+	i++
+	i = encodeVarintLog(data, i, uint64(*m.Time))
+	if len(m.Contents) > 0 {
+		for _, msg := range m.Contents {
+			data[i] = 0x12
+			i++
+			i = encodeVarintLog(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.XXXUnrecognized != nil {
+		i += copy(data[i:], m.XXXUnrecognized)
+	}
+	return i, nil
+}
+
+// Marshal LogContent
+func (m *LogContent) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+// MarshalTo logcontent to data
+func (m *LogContent) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Key == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key")
+	}
+	data[i] = 0xa
+	i++
+	i = encodeVarintLog(data, i, uint64(len(*m.Key)))
+	i += copy(data[i:], *m.Key)
+
+	if m.Value == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value")
+	}
+	data[i] = 0x12
+	i++
+	i = encodeVarintLog(data, i, uint64(len(*m.Value)))
+	i += copy(data[i:], *m.Value)
+	if m.XXXUnrecognized != nil {
+		i += copy(data[i:], m.XXXUnrecognized)
+	}
+	return i, nil
+}
+
+// Marshal LogGroup
+func (m *LogGroup) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+// MarshalTo LogGroup to data
+func (m *LogGroup) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Logs) > 0 {
+		for _, msg := range m.Logs {
+			data[i] = 0xa
+			i++
+			i = encodeVarintLog(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.Reserved != nil {
+		data[i] = 0x12
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Reserved)))
+		i += copy(data[i:], *m.Reserved)
+	}
+	if m.Topic != nil {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Topic)))
+		i += copy(data[i:], *m.Topic)
+	}
+	if m.Source != nil {
+		data[i] = 0x22
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Source)))
+		i += copy(data[i:], *m.Source)
+	}
+	if m.XXXUnrecognized != nil {
+		i += copy(data[i:], m.XXXUnrecognized)
+	}
+	return i, nil
+}
+
+// Marshal LogGroupList
+func (m *LogGroupList) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+// MarshalTo LogGroupList to data
+func (m *LogGroupList) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.LogGroups) > 0 {
+		for _, msg := range m.LogGroups {
+			data[i] = 0xa
+			i++
+			i = encodeVarintLog(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.XXXUnrecognized != nil {
+		i += copy(data[i:], m.XXXUnrecognized)
+	}
+	return i, nil
+}
+
+func encodeFixed64Log(data []byte, offset int, v uint64) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	data[offset+4] = uint8(v >> 32)
+	data[offset+5] = uint8(v >> 40)
+	data[offset+6] = uint8(v >> 48)
+	data[offset+7] = uint8(v >> 56)
+	return offset + 8
+}
+func encodeFixed32Log(data []byte, offset int, v uint32) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	return offset + 4
+}
+func encodeVarintLog(data []byte, offset int, v uint64) int {
+	for v >= 1<<7 {
+		data[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	data[offset] = uint8(v)
+	return offset + 1
+}
+
+// Size return the log's size
+func (m *Log) Size() (n int) {
+	var l int
+	_ = l
+	if m.Time != nil {
+		n += 1 + sovLog(uint64(*m.Time))
+	}
+	if len(m.Contents) > 0 {
+		for _, e := range m.Contents {
+			l = e.Size()
+			n += 1 + l + sovLog(uint64(l))
+		}
+	}
+	if m.XXXUnrecognized != nil {
+		n += len(m.XXXUnrecognized)
+	}
+	return n
+}
+
+// Size return LogContent size based on Key and Value
+func (m *LogContent) Size() (n int) {
+	var l int
+	_ = l
+	if m.Key != nil {
+		l = len(*m.Key)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.Value != nil {
+		l = len(*m.Value)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.XXXUnrecognized != nil {
+		n += len(m.XXXUnrecognized)
+	}
+	return n
+}
+
+// Size return LogGroup size based on Logs
+func (m *LogGroup) Size() (n int) {
+	var l int
+	_ = l
+	if len(m.Logs) > 0 {
+		for _, e := range m.Logs {
+			l = e.Size()
+			n += 1 + l + sovLog(uint64(l))
+		}
+	}
+	if m.Reserved != nil {
+		l = len(*m.Reserved)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.Topic != nil {
+		l = len(*m.Topic)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.Source != nil {
+		l = len(*m.Source)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.XXXUnrecognized != nil {
+		n += len(m.XXXUnrecognized)
+	}
+	return n
+}
+
+// Size return LogGroupList size
+func (m *LogGroupList) Size() (n int) {
+	var l int
+	_ = l
+	if len(m.LogGroups) > 0 {
+		for _, e := range m.LogGroups {
+			l = e.Size()
+			n += 1 + l + sovLog(uint64(l))
+		}
+	}
+	if m.XXXUnrecognized != nil {
+		n += len(m.XXXUnrecognized)
+	}
+	return n
+}
+
+func sovLog(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozLog(x uint64) (n int) {
+	return sovLog((x << 1) ^ (x >> 63))
+}
+
+// Unmarshal data to log
+func (m *Log) Unmarshal(data []byte) error {
+	var hasFields [1]uint64
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Log: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType)
+			}
+			var v uint32
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (uint32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Time = &v
+			hasFields[0] |= uint64(0x00000001)
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Contents = append(m.Contents, &LogContent{})
+			if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+	if hasFields[0]&uint64(0x00000001) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time")
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+
+// Unmarshal data to LogContent
+func (m *LogContent) Unmarshal(data []byte) error {
+	var hasFields [1]uint64
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Content: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Key = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000001)
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Value = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000002)
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+	if hasFields[0]&uint64(0x00000001) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key")
+	}
+	if hasFields[0]&uint64(0x00000002) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value")
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+
+// Unmarshal data to LogGroup
+func (m *LogGroup) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LogGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LogGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Logs = append(m.Logs, &Log{})
+			if err := m.Logs[len(m.Logs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Reserved", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Reserved = &s
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Topic = &s
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Source = &s
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+
+// Unmarshal data to LogGroupList
+func (m *LogGroupList) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LogGroupList: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LogGroupList: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LogGroups", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.LogGroups = append(m.LogGroups, &LogGroup{})
+			if err := m.LogGroups[len(m.LogGroups)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+
+func skipLog(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if data[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+			return iNdEx, nil
+		case 1:
+			iNdEx += 8
+			return iNdEx, nil
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			iNdEx += length
+			if length < 0 {
+				return 0, ErrInvalidLengthLog
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowLog
+					}
+					if iNdEx >= l {
+						return 0, io.ErrUnexpectedEOF
+					}
+					b := data[iNdEx]
+					iNdEx++
+					innerWire |= (uint64(b) & 0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				innerWireType := int(innerWire & 0x7)
+				if innerWireType == 4 {
+					break
+				}
+				next, err := skipLog(data[start:])
+				if err != nil {
+					return 0, err
+				}
+				iNdEx = start + next
+			}
+			return iNdEx, nil
+		case 4:
+			return iNdEx, nil
+		case 5:
+			iNdEx += 4
+			return iNdEx, nil
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+	}
+	panic("unreachable")
+}

+ 42 - 0
vender/github.com/astaxie/beego/logs/alils/log_config.go

@@ -0,0 +1,42 @@
+package alils
+
+// InputDetail define log detail
+type InputDetail struct {
+	LogType       string   `json:"logType"`
+	LogPath       string   `json:"logPath"`
+	FilePattern   string   `json:"filePattern"`
+	LocalStorage  bool     `json:"localStorage"`
+	TimeFormat    string   `json:"timeFormat"`
+	LogBeginRegex string   `json:"logBeginRegex"`
+	Regex         string   `json:"regex"`
+	Keys          []string `json:"key"`
+	FilterKeys    []string `json:"filterKey"`
+	FilterRegex   []string `json:"filterRegex"`
+	TopicFormat   string   `json:"topicFormat"`
+}
+
+// OutputDetail define the output detail
+type OutputDetail struct {
+	Endpoint     string `json:"endpoint"`
+	LogStoreName string `json:"logstoreName"`
+}
+
+// LogConfig define Log Config
+type LogConfig struct {
+	Name         string       `json:"configName"`
+	InputType    string       `json:"inputType"`
+	InputDetail  InputDetail  `json:"inputDetail"`
+	OutputType   string       `json:"outputType"`
+	OutputDetail OutputDetail `json:"outputDetail"`
+
+	CreateTime     uint32
+	LastModifyTime uint32
+
+	project *LogProject
+}
+
+// GetAppliedMachineGroup returns applied machine group of this config.
+func (c *LogConfig) GetAppliedMachineGroup(confName string) (groupNames []string, err error) {
+	groupNames, err = c.project.GetAppliedMachineGroups(c.Name)
+	return
+}

+ 819 - 0
vender/github.com/astaxie/beego/logs/alils/log_project.go

@@ -0,0 +1,819 @@
+/*
+Package alils implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS).
+
+For more description about SLS, please read this article:
+http://gitlab.alibaba-inc.com/sls/doc.
+*/
+package alils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httputil"
+)
+
+// Error message in SLS HTTP response.
+type errorMessage struct {
+	Code    string `json:"errorCode"`
+	Message string `json:"errorMessage"`
+}
+
+// LogProject Define the Ali Project detail
+type LogProject struct {
+	Name            string // Project name
+	Endpoint        string // IP or hostname of SLS endpoint
+	AccessKeyID     string
+	AccessKeySecret string
+}
+
+// NewLogProject creates a new SLS project.
+func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogProject, err error) {
+	p = &LogProject{
+		Name:            name,
+		Endpoint:        endpoint,
+		AccessKeyID:     AccessKeyID,
+		AccessKeySecret: accessKeySecret,
+	}
+	return p, nil
+}
+
+// ListLogStore returns all logstore names of project p.
+func (p *LogProject) ListLogStore() (storeNames []string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/logstores")
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to list logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Count     int
+		LogStores []string
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	storeNames = body.LogStores
+
+	return
+}
+
+// GetLogStore returns logstore according by logstore name.
+func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "GET", "/logstores/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	s = &LogStore{}
+	err = json.Unmarshal(buf, s)
+	if err != nil {
+		return
+	}
+	s.project = p
+	return
+}
+
+// CreateLogStore creates a new logstore in SLS,
+// where name is logstore name,
+// and ttl is time-to-live(in day) of logs,
+// and shardCnt is the number of shards.
+func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error) {
+
+	type Body struct {
+		Name       string `json:"logstoreName"`
+		TTL        int    `json:"ttl"`
+		ShardCount int    `json:"shardCount"`
+	}
+
+	store := &Body{
+		Name:       name,
+		TTL:        ttl,
+		ShardCount: shardCnt,
+	}
+
+	body, err := json.Marshal(store)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "POST", "/logstores", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to create logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// DeleteLogStore deletes a logstore according by logstore name.
+func (p *LogProject) DeleteLogStore(name string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "DELETE", "/logstores/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// UpdateLogStore updates a logstore according by logstore name,
+// obviously we can't modify the logstore name itself.
+func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error) {
+
+	type Body struct {
+		Name       string `json:"logstoreName"`
+		TTL        int    `json:"ttl"`
+		ShardCount int    `json:"shardCount"`
+	}
+
+	store := &Body{
+		Name:       name,
+		TTL:        ttl,
+		ShardCount: shardCnt,
+	}
+
+	body, err := json.Marshal(store)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "PUT", "/logstores", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// ListMachineGroup returns machine group name list and the total number of machine groups.
+// The offset starts from 0 and the size is the max number of machine groups could be returned.
+func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	if size <= 0 {
+		size = 500
+	}
+
+	uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to list machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		MachineGroups []string
+		Count         int
+		Total         int
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	m = body.MachineGroups
+	total = body.Total
+
+	return
+}
+
+// GetMachineGroup retruns machine group according by machine group name.
+func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "GET", "/machinegroups/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get machine group:%v", name)
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	m = &MachineGroup{}
+	err = json.Unmarshal(buf, m)
+	if err != nil {
+		return
+	}
+	m.project = p
+	return
+}
+
+// CreateMachineGroup creates a new machine group in SLS.
+func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) {
+
+	body, err := json.Marshal(m)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "POST", "/machinegroups", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to create machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// UpdateMachineGroup updates a machine group.
+func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) {
+
+	body, err := json.Marshal(m)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// DeleteMachineGroup deletes machine group according machine group name.
+func (p *LogProject) DeleteMachineGroup(name string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// ListConfig returns config names list and the total number of configs.
+// The offset starts from 0 and the size is the max number of configs could be returned.
+func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	if size <= 0 {
+		size = 100
+	}
+
+	uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Total   int
+		Configs []string
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	cfgNames = body.Configs
+	total = body.Total
+	return
+}
+
+// GetConfig returns config according by config name.
+func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "GET", "/configs/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	c = &LogConfig{}
+	err = json.Unmarshal(buf, c)
+	if err != nil {
+		return
+	}
+	c.project = p
+	return
+}
+
+// UpdateConfig updates a config.
+func (p *LogProject) UpdateConfig(c *LogConfig) (err error) {
+
+	body, err := json.Marshal(c)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "PUT", "/configs/"+c.Name, h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// CreateConfig creates a new config in SLS.
+func (p *LogProject) CreateConfig(c *LogConfig) (err error) {
+
+	body, err := json.Marshal(c)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "POST", "/configs", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// DeleteConfig deletes a config according by config name.
+func (p *LogProject) DeleteConfig(name string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "DELETE", "/configs/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// GetAppliedMachineGroups returns applied machine group names list according config name.
+func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/configs/%v/machinegroups", confName)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get applied machine groups")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Count         int
+		Machinegroups []string
+	}
+
+	body := &Body{}
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	groupNames = body.Machinegroups
+	return
+}
+
+// GetAppliedConfigs returns applied config names list according machine group name groupName.
+func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/configs", groupName)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to applied configs")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Cfg struct {
+		Count   int      `json:"count"`
+		Configs []string `json:"configs"`
+	}
+
+	body := &Cfg{}
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	confNames = body.Configs
+	return
+}
+
+// ApplyConfigToMachineGroup applies config to machine group.
+func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
+	r, err := request(p, "PUT", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to apply config to machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// RemoveConfigFromMachineGroup removes config from machine group.
+func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
+	r, err := request(p, "DELETE", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to remove config from machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}

+ 271 - 0
vender/github.com/astaxie/beego/logs/alils/log_store.go

@@ -0,0 +1,271 @@
+package alils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httputil"
+	"strconv"
+
+	lz4 "github.com/cloudflare/golz4"
+	"github.com/gogo/protobuf/proto"
+)
+
+// LogStore Store the logs
+type LogStore struct {
+	Name       string `json:"logstoreName"`
+	TTL        int
+	ShardCount int
+
+	CreateTime     uint32
+	LastModifyTime uint32
+
+	project *LogProject
+}
+
+// Shard define the Log Shard
+type Shard struct {
+	ShardID int `json:"shardID"`
+}
+
+// ListShards returns shard id list of this logstore.
+func (s *LogStore) ListShards() (shardIDs []int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v/shards", s.Name)
+	r, err := request(s.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to list logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	var shards []*Shard
+	err = json.Unmarshal(buf, &shards)
+	if err != nil {
+		return
+	}
+
+	for _, v := range shards {
+		shardIDs = append(shardIDs, v.ShardID)
+	}
+	return
+}
+
+// PutLogs put logs into logstore.
+// The callers should transform user logs into LogGroup.
+func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
+	body, err := proto.Marshal(lg)
+	if err != nil {
+		return
+	}
+
+	// Compresse body with lz4
+	out := make([]byte, lz4.CompressBound(body))
+	n, err := lz4.Compress(body, out)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-compresstype": "lz4",
+		"x-sls-bodyrawsize":  fmt.Sprintf("%v", len(body)),
+		"Content-Type":       "application/x-protobuf",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v", s.Name)
+	r, err := request(s.project, "POST", uri, h, out[:n])
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to put logs")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// GetCursor gets log cursor of one shard specified by shardID.
+// The from can be in three form: a) unix timestamp in seccond, b) "begin", c) "end".
+// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore
+func (s *LogStore) GetCursor(shardID int, from string) (cursor string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v",
+		s.Name, shardID, from)
+
+	r, err := request(s.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get cursor")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Cursor string
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+	cursor = body.Cursor
+	return
+}
+
+// GetLogsBytes gets logs binary data from shard specified by shardID according cursor.
+// The logGroupMaxCount is the max number of logGroup could be returned.
+// The nextCursor is the next curosr can be used to read logs at next time.
+func (s *LogStore) GetLogsBytes(shardID int, cursor string,
+	logGroupMaxCount int) (out []byte, nextCursor string, err error) {
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+		"Accept":            "application/x-protobuf",
+		"Accept-Encoding":   "lz4",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v",
+		s.Name, shardID, cursor, logGroupMaxCount)
+
+	r, err := request(s.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get cursor")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	v, ok := r.Header["X-Sls-Compresstype"]
+	if !ok || len(v) == 0 {
+		err = fmt.Errorf("can't find 'x-sls-compresstype' header")
+		return
+	}
+	if v[0] != "lz4" {
+		err = fmt.Errorf("unexpected compress type:%v", v[0])
+		return
+	}
+
+	v, ok = r.Header["X-Sls-Cursor"]
+	if !ok || len(v) == 0 {
+		err = fmt.Errorf("can't find 'x-sls-cursor' header")
+		return
+	}
+	nextCursor = v[0]
+
+	v, ok = r.Header["X-Sls-Bodyrawsize"]
+	if !ok || len(v) == 0 {
+		err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header")
+		return
+	}
+	bodyRawSize, err := strconv.Atoi(v[0])
+	if err != nil {
+		return
+	}
+
+	out = make([]byte, bodyRawSize)
+	err = lz4.Uncompress(buf, out)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// LogsBytesDecode decodes logs binary data retruned by GetLogsBytes API
+func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) {
+
+	gl = &LogGroupList{}
+	err = proto.Unmarshal(data, gl)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// GetLogs gets logs from shard specified by shardID according cursor.
+// The logGroupMaxCount is the max number of logGroup could be returned.
+// The nextCursor is the next curosr can be used to read logs at next time.
+func (s *LogStore) GetLogs(shardID int, cursor string,
+	logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) {
+
+	out, nextCursor, err := s.GetLogsBytes(shardID, cursor, logGroupMaxCount)
+	if err != nil {
+		return
+	}
+
+	gl, err = LogsBytesDecode(out)
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 91 - 0
vender/github.com/astaxie/beego/logs/alils/machine_group.go

@@ -0,0 +1,91 @@
+package alils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httputil"
+)
+
+// MachineGroupAttribute define the Attribute
+type MachineGroupAttribute struct {
+	ExternalName string `json:"externalName"`
+	TopicName    string `json:"groupTopic"`
+}
+
+// MachineGroup define the machine Group
+type MachineGroup struct {
+	Name          string   `json:"groupName"`
+	Type          string   `json:"groupType"`
+	MachineIDType string   `json:"machineIdentifyType"`
+	MachineIDList []string `json:"machineList"`
+
+	Attribute MachineGroupAttribute `json:"groupAttribute"`
+
+	CreateTime     uint32
+	LastModifyTime uint32
+
+	project *LogProject
+}
+
+// Machine define the Machine
+type Machine struct {
+	IP            string
+	UniqueID      string `json:"machine-uniqueid"`
+	UserdefinedID string `json:"userdefined-id"`
+}
+
+// MachineList define the Machine List
+type MachineList struct {
+	Total    int
+	Machines []*Machine
+}
+
+// ListMachines returns machine list of this machine group.
+func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name)
+	r, err := request(m.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to remove config from machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	body := &MachineList{}
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	ms = body.Machines
+	total = body.Total
+
+	return
+}
+
+// GetAppliedConfigs returns applied configs of this machine group.
+func (m *MachineGroup) GetAppliedConfigs() (confNames []string, err error) {
+	confNames, err = m.project.GetAppliedConfigs(m.Name)
+	return
+}

+ 62 - 0
vender/github.com/astaxie/beego/logs/alils/request.go

@@ -0,0 +1,62 @@
+package alils
+
+import (
+	"bytes"
+	"crypto/md5"
+	"fmt"
+	"net/http"
+)
+
+// request sends a request to SLS.
+func request(project *LogProject, method, uri string, headers map[string]string,
+	body []byte) (resp *http.Response, err error) {
+
+	// The caller should provide 'x-sls-bodyrawsize' header
+	if _, ok := headers["x-sls-bodyrawsize"]; !ok {
+		err = fmt.Errorf("Can't find 'x-sls-bodyrawsize' header")
+		return
+	}
+
+	// SLS public request headers
+	headers["Host"] = project.Name + "." + project.Endpoint
+	headers["Date"] = nowRFC1123()
+	headers["x-sls-apiversion"] = version
+	headers["x-sls-signaturemethod"] = signatureMethod
+	if body != nil {
+		bodyMD5 := fmt.Sprintf("%X", md5.Sum(body))
+		headers["Content-MD5"] = bodyMD5
+
+		if _, ok := headers["Content-Type"]; !ok {
+			err = fmt.Errorf("Can't find 'Content-Type' header")
+			return
+		}
+	}
+
+	// Calc Authorization
+	// Authorization = "SLS <AccessKeyID>:<Signature>"
+	digest, err := signature(project, method, uri, headers)
+	if err != nil {
+		return
+	}
+	auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyID, digest)
+	headers["Authorization"] = auth
+
+	// Initialize http request
+	reader := bytes.NewReader(body)
+	urlStr := fmt.Sprintf("http://%v.%v%v", project.Name, project.Endpoint, uri)
+	req, err := http.NewRequest(method, urlStr, reader)
+	if err != nil {
+		return
+	}
+	for k, v := range headers {
+		req.Header.Add(k, v)
+	}
+
+	// Get ready to do request
+	resp, err = http.DefaultClient.Do(req)
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 111 - 0
vender/github.com/astaxie/beego/logs/alils/signature.go

@@ -0,0 +1,111 @@
+package alils
+
+import (
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"sort"
+	"strings"
+	"time"
+)
+
+// GMT location
+var gmtLoc = time.FixedZone("GMT", 0)
+
+// NowRFC1123 returns now time in RFC1123 format with GMT timezone,
+// eg. "Mon, 02 Jan 2006 15:04:05 GMT".
+func nowRFC1123() string {
+	return time.Now().In(gmtLoc).Format(time.RFC1123)
+}
+
+// signature calculates a request's signature digest.
+func signature(project *LogProject, method, uri string,
+	headers map[string]string) (digest string, err error) {
+	var contentMD5, contentType, date, canoHeaders, canoResource string
+	var slsHeaderKeys sort.StringSlice
+
+	// SignString = VERB + "\n"
+	//              + CONTENT-MD5 + "\n"
+	//              + CONTENT-TYPE + "\n"
+	//              + DATE + "\n"
+	//              + CanonicalizedSLSHeaders + "\n"
+	//              + CanonicalizedResource
+
+	if val, ok := headers["Content-MD5"]; ok {
+		contentMD5 = val
+	}
+
+	if val, ok := headers["Content-Type"]; ok {
+		contentType = val
+	}
+
+	date, ok := headers["Date"]
+	if !ok {
+		err = fmt.Errorf("Can't find 'Date' header")
+		return
+	}
+
+	// Calc CanonicalizedSLSHeaders
+	slsHeaders := make(map[string]string, len(headers))
+	for k, v := range headers {
+		l := strings.TrimSpace(strings.ToLower(k))
+		if strings.HasPrefix(l, "x-sls-") {
+			slsHeaders[l] = strings.TrimSpace(v)
+			slsHeaderKeys = append(slsHeaderKeys, l)
+		}
+	}
+
+	sort.Sort(slsHeaderKeys)
+	for i, k := range slsHeaderKeys {
+		canoHeaders += k + ":" + slsHeaders[k]
+		if i+1 < len(slsHeaderKeys) {
+			canoHeaders += "\n"
+		}
+	}
+
+	// Calc CanonicalizedResource
+	u, err := url.Parse(uri)
+	if err != nil {
+		return
+	}
+
+	canoResource += url.QueryEscape(u.Path)
+	if u.RawQuery != "" {
+		var keys sort.StringSlice
+
+		vals := u.Query()
+		for k := range vals {
+			keys = append(keys, k)
+		}
+
+		sort.Sort(keys)
+		canoResource += "?"
+		for i, k := range keys {
+			if i > 0 {
+				canoResource += "&"
+			}
+
+			for _, v := range vals[k] {
+				canoResource += k + "=" + v
+			}
+		}
+	}
+
+	signStr := method + "\n" +
+		contentMD5 + "\n" +
+		contentType + "\n" +
+		date + "\n" +
+		canoHeaders + "\n" +
+		canoResource
+
+	// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
+	mac := hmac.New(sha1.New, []byte(project.AccessKeySecret))
+	_, err = mac.Write([]byte(signStr))
+	if err != nil {
+		return
+	}
+	digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
+	return
+}

+ 28 - 0
vender/github.com/astaxie/beego/logs/color.go

@@ -0,0 +1,28 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package logs
+
+import "io"
+
+type ansiColorWriter struct {
+	w    io.Writer
+	mode outputMode
+}
+
+func (cw *ansiColorWriter) Write(p []byte) (int, error) {
+	return cw.w.Write(p)
+}

+ 428 - 0
vender/github.com/astaxie/beego/logs/color_windows.go

@@ -0,0 +1,428 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build windows
+
+package logs
+
+import (
+	"bytes"
+	"io"
+	"strings"
+	"syscall"
+	"unsafe"
+)
+
+type (
+	csiState    int
+	parseResult int
+)
+
+const (
+	outsideCsiCode csiState = iota
+	firstCsiCode
+	secondCsiCode
+)
+
+const (
+	noConsole parseResult = iota
+	changedColor
+	unknown
+)
+
+type ansiColorWriter struct {
+	w             io.Writer
+	mode          outputMode
+	state         csiState
+	paramStartBuf bytes.Buffer
+	paramBuf      bytes.Buffer
+}
+
+const (
+	firstCsiChar   byte = '\x1b'
+	secondeCsiChar byte = '['
+	separatorChar  byte = ';'
+	sgrCode        byte = 'm'
+)
+
+const (
+	foregroundBlue      = uint16(0x0001)
+	foregroundGreen     = uint16(0x0002)
+	foregroundRed       = uint16(0x0004)
+	foregroundIntensity = uint16(0x0008)
+	backgroundBlue      = uint16(0x0010)
+	backgroundGreen     = uint16(0x0020)
+	backgroundRed       = uint16(0x0040)
+	backgroundIntensity = uint16(0x0080)
+	underscore          = uint16(0x8000)
+
+	foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
+	backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
+)
+
+const (
+	ansiReset        = "0"
+	ansiIntensityOn  = "1"
+	ansiIntensityOff = "21"
+	ansiUnderlineOn  = "4"
+	ansiUnderlineOff = "24"
+	ansiBlinkOn      = "5"
+	ansiBlinkOff     = "25"
+
+	ansiForegroundBlack   = "30"
+	ansiForegroundRed     = "31"
+	ansiForegroundGreen   = "32"
+	ansiForegroundYellow  = "33"
+	ansiForegroundBlue    = "34"
+	ansiForegroundMagenta = "35"
+	ansiForegroundCyan    = "36"
+	ansiForegroundWhite   = "37"
+	ansiForegroundDefault = "39"
+
+	ansiBackgroundBlack   = "40"
+	ansiBackgroundRed     = "41"
+	ansiBackgroundGreen   = "42"
+	ansiBackgroundYellow  = "43"
+	ansiBackgroundBlue    = "44"
+	ansiBackgroundMagenta = "45"
+	ansiBackgroundCyan    = "46"
+	ansiBackgroundWhite   = "47"
+	ansiBackgroundDefault = "49"
+
+	ansiLightForegroundGray    = "90"
+	ansiLightForegroundRed     = "91"
+	ansiLightForegroundGreen   = "92"
+	ansiLightForegroundYellow  = "93"
+	ansiLightForegroundBlue    = "94"
+	ansiLightForegroundMagenta = "95"
+	ansiLightForegroundCyan    = "96"
+	ansiLightForegroundWhite   = "97"
+
+	ansiLightBackgroundGray    = "100"
+	ansiLightBackgroundRed     = "101"
+	ansiLightBackgroundGreen   = "102"
+	ansiLightBackgroundYellow  = "103"
+	ansiLightBackgroundBlue    = "104"
+	ansiLightBackgroundMagenta = "105"
+	ansiLightBackgroundCyan    = "106"
+	ansiLightBackgroundWhite   = "107"
+)
+
+type drawType int
+
+const (
+	foreground drawType = iota
+	background
+)
+
+type winColor struct {
+	code     uint16
+	drawType drawType
+}
+
+var colorMap = map[string]winColor{
+	ansiForegroundBlack:   {0, foreground},
+	ansiForegroundRed:     {foregroundRed, foreground},
+	ansiForegroundGreen:   {foregroundGreen, foreground},
+	ansiForegroundYellow:  {foregroundRed | foregroundGreen, foreground},
+	ansiForegroundBlue:    {foregroundBlue, foreground},
+	ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
+	ansiForegroundCyan:    {foregroundGreen | foregroundBlue, foreground},
+	ansiForegroundWhite:   {foregroundRed | foregroundGreen | foregroundBlue, foreground},
+	ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
+
+	ansiBackgroundBlack:   {0, background},
+	ansiBackgroundRed:     {backgroundRed, background},
+	ansiBackgroundGreen:   {backgroundGreen, background},
+	ansiBackgroundYellow:  {backgroundRed | backgroundGreen, background},
+	ansiBackgroundBlue:    {backgroundBlue, background},
+	ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
+	ansiBackgroundCyan:    {backgroundGreen | backgroundBlue, background},
+	ansiBackgroundWhite:   {backgroundRed | backgroundGreen | backgroundBlue, background},
+	ansiBackgroundDefault: {0, background},
+
+	ansiLightForegroundGray:    {foregroundIntensity, foreground},
+	ansiLightForegroundRed:     {foregroundIntensity | foregroundRed, foreground},
+	ansiLightForegroundGreen:   {foregroundIntensity | foregroundGreen, foreground},
+	ansiLightForegroundYellow:  {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
+	ansiLightForegroundBlue:    {foregroundIntensity | foregroundBlue, foreground},
+	ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
+	ansiLightForegroundCyan:    {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
+	ansiLightForegroundWhite:   {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},
+
+	ansiLightBackgroundGray:    {backgroundIntensity, background},
+	ansiLightBackgroundRed:     {backgroundIntensity | backgroundRed, background},
+	ansiLightBackgroundGreen:   {backgroundIntensity | backgroundGreen, background},
+	ansiLightBackgroundYellow:  {backgroundIntensity | backgroundRed | backgroundGreen, background},
+	ansiLightBackgroundBlue:    {backgroundIntensity | backgroundBlue, background},
+	ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
+	ansiLightBackgroundCyan:    {backgroundIntensity | backgroundGreen | backgroundBlue, background},
+	ansiLightBackgroundWhite:   {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
+}
+
+var (
+	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
+	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
+	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+	defaultAttr                    *textAttributes
+)
+
+func init() {
+	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
+	if screenInfo != nil {
+		colorMap[ansiForegroundDefault] = winColor{
+			screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
+			foreground,
+		}
+		colorMap[ansiBackgroundDefault] = winColor{
+			screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
+			background,
+		}
+		defaultAttr = convertTextAttr(screenInfo.WAttributes)
+	}
+}
+
+type coord struct {
+	X, Y int16
+}
+
+type smallRect struct {
+	Left, Top, Right, Bottom int16
+}
+
+type consoleScreenBufferInfo struct {
+	DwSize              coord
+	DwCursorPosition    coord
+	WAttributes         uint16
+	SrWindow            smallRect
+	DwMaximumWindowSize coord
+}
+
+func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
+	var csbi consoleScreenBufferInfo
+	ret, _, _ := procGetConsoleScreenBufferInfo.Call(
+		hConsoleOutput,
+		uintptr(unsafe.Pointer(&csbi)))
+	if ret == 0 {
+		return nil
+	}
+	return &csbi
+}
+
+func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
+	ret, _, _ := procSetConsoleTextAttribute.Call(
+		hConsoleOutput,
+		uintptr(wAttributes))
+	return ret != 0
+}
+
+type textAttributes struct {
+	foregroundColor     uint16
+	backgroundColor     uint16
+	foregroundIntensity uint16
+	backgroundIntensity uint16
+	underscore          uint16
+	otherAttributes     uint16
+}
+
+func convertTextAttr(winAttr uint16) *textAttributes {
+	fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
+	bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
+	fgIntensity := winAttr & foregroundIntensity
+	bgIntensity := winAttr & backgroundIntensity
+	underline := winAttr & underscore
+	otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
+	return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
+}
+
+func convertWinAttr(textAttr *textAttributes) uint16 {
+	var winAttr uint16
+	winAttr |= textAttr.foregroundColor
+	winAttr |= textAttr.backgroundColor
+	winAttr |= textAttr.foregroundIntensity
+	winAttr |= textAttr.backgroundIntensity
+	winAttr |= textAttr.underscore
+	winAttr |= textAttr.otherAttributes
+	return winAttr
+}
+
+func changeColor(param []byte) parseResult {
+	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
+	if screenInfo == nil {
+		return noConsole
+	}
+
+	winAttr := convertTextAttr(screenInfo.WAttributes)
+	strParam := string(param)
+	if len(strParam) <= 0 {
+		strParam = "0"
+	}
+	csiParam := strings.Split(strParam, string(separatorChar))
+	for _, p := range csiParam {
+		c, ok := colorMap[p]
+		switch {
+		case !ok:
+			switch p {
+			case ansiReset:
+				winAttr.foregroundColor = defaultAttr.foregroundColor
+				winAttr.backgroundColor = defaultAttr.backgroundColor
+				winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
+				winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
+				winAttr.underscore = 0
+				winAttr.otherAttributes = 0
+			case ansiIntensityOn:
+				winAttr.foregroundIntensity = foregroundIntensity
+			case ansiIntensityOff:
+				winAttr.foregroundIntensity = 0
+			case ansiUnderlineOn:
+				winAttr.underscore = underscore
+			case ansiUnderlineOff:
+				winAttr.underscore = 0
+			case ansiBlinkOn:
+				winAttr.backgroundIntensity = backgroundIntensity
+			case ansiBlinkOff:
+				winAttr.backgroundIntensity = 0
+			default:
+				// unknown code
+			}
+		case c.drawType == foreground:
+			winAttr.foregroundColor = c.code
+		case c.drawType == background:
+			winAttr.backgroundColor = c.code
+		}
+	}
+	winTextAttribute := convertWinAttr(winAttr)
+	setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)
+
+	return changedColor
+}
+
+func parseEscapeSequence(command byte, param []byte) parseResult {
+	if defaultAttr == nil {
+		return noConsole
+	}
+
+	switch command {
+	case sgrCode:
+		return changeColor(param)
+	default:
+		return unknown
+	}
+}
+
+func (cw *ansiColorWriter) flushBuffer() (int, error) {
+	return cw.flushTo(cw.w)
+}
+
+func (cw *ansiColorWriter) resetBuffer() (int, error) {
+	return cw.flushTo(nil)
+}
+
+func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) {
+	var n1, n2 int
+	var err error
+
+	startBytes := cw.paramStartBuf.Bytes()
+	cw.paramStartBuf.Reset()
+	if w != nil {
+		n1, err = cw.w.Write(startBytes)
+		if err != nil {
+			return n1, err
+		}
+	} else {
+		n1 = len(startBytes)
+	}
+	paramBytes := cw.paramBuf.Bytes()
+	cw.paramBuf.Reset()
+	if w != nil {
+		n2, err = cw.w.Write(paramBytes)
+		if err != nil {
+			return n1 + n2, err
+		}
+	} else {
+		n2 = len(paramBytes)
+	}
+	return n1 + n2, nil
+}
+
+func isParameterChar(b byte) bool {
+	return ('0' <= b && b <= '9') || b == separatorChar
+}
+
+func (cw *ansiColorWriter) Write(p []byte) (int, error) {
+	var r, nw, first, last int
+	if cw.mode != DiscardNonColorEscSeq {
+		cw.state = outsideCsiCode
+		cw.resetBuffer()
+	}
+
+	var err error
+	for i, ch := range p {
+		switch cw.state {
+		case outsideCsiCode:
+			if ch == firstCsiChar {
+				cw.paramStartBuf.WriteByte(ch)
+				cw.state = firstCsiCode
+			}
+		case firstCsiCode:
+			switch ch {
+			case firstCsiChar:
+				cw.paramStartBuf.WriteByte(ch)
+				break
+			case secondeCsiChar:
+				cw.paramStartBuf.WriteByte(ch)
+				cw.state = secondCsiCode
+				last = i - 1
+			default:
+				cw.resetBuffer()
+				cw.state = outsideCsiCode
+			}
+		case secondCsiCode:
+			if isParameterChar(ch) {
+				cw.paramBuf.WriteByte(ch)
+			} else {
+				nw, err = cw.w.Write(p[first:last])
+				r += nw
+				if err != nil {
+					return r, err
+				}
+				first = i + 1
+				result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
+				if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) {
+					cw.paramBuf.WriteByte(ch)
+					nw, err := cw.flushBuffer()
+					if err != nil {
+						return r, err
+					}
+					r += nw
+				} else {
+					n, _ := cw.resetBuffer()
+					// Add one more to the size of the buffer for the last ch
+					r += n + 1
+				}
+
+				cw.state = outsideCsiCode
+			}
+		default:
+			cw.state = outsideCsiCode
+		}
+	}
+
+	if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
+		nw, err = cw.w.Write(p[first:])
+		r += nw
+	}
+
+	return r, err
+}

+ 294 - 0
vender/github.com/astaxie/beego/logs/color_windows_test.go

@@ -0,0 +1,294 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build windows
+
+package logs
+
+import (
+	"bytes"
+	"fmt"
+	"syscall"
+	"testing"
+)
+
+var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo
+
+func ChangeColor(color uint16) {
+	setConsoleTextAttribute(uintptr(syscall.Stdout), color)
+}
+
+func ResetColor() {
+	ChangeColor(uint16(0x0007))
+}
+
+func TestWritePlanText(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+	expected := "plain text"
+	fmt.Fprintf(w, expected)
+	actual := inner.String()
+	if actual != expected {
+		t.Errorf("Get %q, want %q", actual, expected)
+	}
+}
+
+func TestWriteParseText(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+
+	inputTail := "\x1b[0mtail text"
+	expectedTail := "tail text"
+	fmt.Fprintf(w, inputTail)
+	actualTail := inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputHead := "head text\x1b[0m"
+	expectedHead := "head text"
+	fmt.Fprintf(w, inputHead)
+	actualHead := inner.String()
+	inner.Reset()
+	if actualHead != expectedHead {
+		t.Errorf("Get %q, want %q", actualHead, expectedHead)
+	}
+
+	inputBothEnds := "both ends \x1b[0m text"
+	expectedBothEnds := "both ends  text"
+	fmt.Fprintf(w, inputBothEnds)
+	actualBothEnds := inner.String()
+	inner.Reset()
+	if actualBothEnds != expectedBothEnds {
+		t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds)
+	}
+
+	inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc"
+	expectedManyEsc := "\x1b\x1b\x1b many esc"
+	fmt.Fprintf(w, inputManyEsc)
+	actualManyEsc := inner.String()
+	inner.Reset()
+	if actualManyEsc != expectedManyEsc {
+		t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc)
+	}
+
+	expectedSplit := "split  text"
+	for _, ch := range "split \x1b[0m text" {
+		fmt.Fprintf(w, string(ch))
+	}
+	actualSplit := inner.String()
+	inner.Reset()
+	if actualSplit != expectedSplit {
+		t.Errorf("Get %q, want %q", actualSplit, expectedSplit)
+	}
+}
+
+type screenNotFoundError struct {
+	error
+}
+
+func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+	fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText)
+
+	actualText = inner.String()
+	screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
+	if screenInfo != nil {
+		actualAttributes = screenInfo.WAttributes
+	} else {
+		err = &screenNotFoundError{}
+	}
+	return
+}
+
+type testParam struct {
+	text       string
+	attributes uint16
+	ansiColor  string
+}
+
+func TestWriteAnsiColorText(t *testing.T) {
+	screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
+	if screenInfo == nil {
+		t.Fatal("Could not get ConsoleScreenBufferInfo")
+	}
+	defer ChangeColor(screenInfo.WAttributes)
+	defaultFgColor := screenInfo.WAttributes & uint16(0x0007)
+	defaultBgColor := screenInfo.WAttributes & uint16(0x0070)
+	defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008)
+	defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080)
+
+	fgParam := []testParam{
+		{"foreground black  ", uint16(0x0000 | 0x0000), "30"},
+		{"foreground red    ", uint16(0x0004 | 0x0000), "31"},
+		{"foreground green  ", uint16(0x0002 | 0x0000), "32"},
+		{"foreground yellow ", uint16(0x0006 | 0x0000), "33"},
+		{"foreground blue   ", uint16(0x0001 | 0x0000), "34"},
+		{"foreground magenta", uint16(0x0005 | 0x0000), "35"},
+		{"foreground cyan   ", uint16(0x0003 | 0x0000), "36"},
+		{"foreground white  ", uint16(0x0007 | 0x0000), "37"},
+		{"foreground default", defaultFgColor | 0x0000, "39"},
+		{"foreground light gray   ", uint16(0x0000 | 0x0008 | 0x0000), "90"},
+		{"foreground light red    ", uint16(0x0004 | 0x0008 | 0x0000), "91"},
+		{"foreground light green  ", uint16(0x0002 | 0x0008 | 0x0000), "92"},
+		{"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"},
+		{"foreground light blue   ", uint16(0x0001 | 0x0008 | 0x0000), "94"},
+		{"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"},
+		{"foreground light cyan   ", uint16(0x0003 | 0x0008 | 0x0000), "96"},
+		{"foreground light white  ", uint16(0x0007 | 0x0008 | 0x0000), "97"},
+	}
+
+	bgParam := []testParam{
+		{"background black  ", uint16(0x0007 | 0x0000), "40"},
+		{"background red    ", uint16(0x0007 | 0x0040), "41"},
+		{"background green  ", uint16(0x0007 | 0x0020), "42"},
+		{"background yellow ", uint16(0x0007 | 0x0060), "43"},
+		{"background blue   ", uint16(0x0007 | 0x0010), "44"},
+		{"background magenta", uint16(0x0007 | 0x0050), "45"},
+		{"background cyan   ", uint16(0x0007 | 0x0030), "46"},
+		{"background white  ", uint16(0x0007 | 0x0070), "47"},
+		{"background default", uint16(0x0007) | defaultBgColor, "49"},
+		{"background light gray   ", uint16(0x0007 | 0x0000 | 0x0080), "100"},
+		{"background light red    ", uint16(0x0007 | 0x0040 | 0x0080), "101"},
+		{"background light green  ", uint16(0x0007 | 0x0020 | 0x0080), "102"},
+		{"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"},
+		{"background light blue   ", uint16(0x0007 | 0x0010 | 0x0080), "104"},
+		{"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"},
+		{"background light cyan   ", uint16(0x0007 | 0x0030 | 0x0080), "106"},
+		{"background light white  ", uint16(0x0007 | 0x0070 | 0x0080), "107"},
+	}
+
+	resetParam := []testParam{
+		{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"},
+		{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""},
+	}
+
+	boldParam := []testParam{
+		{"bold on", uint16(0x0007 | 0x0008), "1"},
+		{"bold off", uint16(0x0007), "21"},
+	}
+
+	underscoreParam := []testParam{
+		{"underscore on", uint16(0x0007 | 0x8000), "4"},
+		{"underscore off", uint16(0x0007), "24"},
+	}
+
+	blinkParam := []testParam{
+		{"blink on", uint16(0x0007 | 0x0080), "5"},
+		{"blink off", uint16(0x0007), "25"},
+	}
+
+	mixedParam := []testParam{
+		{"both black,   bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"},
+		{"both red,     bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"},
+		{"both green,   bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"},
+		{"both yellow,  bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"},
+		{"both blue,    bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"},
+		{"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"},
+		{"both cyan,    bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"},
+		{"both white,   bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"},
+		{"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"},
+	}
+
+	assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) {
+		actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor)
+		if actualText != expectedText {
+			t.Errorf("Get %q, want %q", actualText, expectedText)
+		}
+		if err != nil {
+			t.Fatal("Could not get ConsoleScreenBufferInfo")
+		}
+		if actualAttributes != expectedAttributes {
+			t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes)
+		}
+	}
+
+	for _, v := range fgParam {
+		ResetColor()
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	for _, v := range bgParam {
+		ChangeColor(uint16(0x0070 | 0x0007))
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	for _, v := range resetParam {
+		ChangeColor(uint16(0x0000 | 0x0070 | 0x0008))
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	ResetColor()
+	for _, v := range boldParam {
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	ResetColor()
+	for _, v := range underscoreParam {
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	ResetColor()
+	for _, v := range blinkParam {
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	for _, v := range mixedParam {
+		ResetColor()
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+}
+
+func TestIgnoreUnknownSequences(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewModeAnsiColorWriter(inner, OutputNonColorEscSeq)
+
+	inputText := "\x1b[=decpath mode"
+	expectedTail := inputText
+	fmt.Fprintf(w, inputText)
+	actualTail := inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputText = "\x1b[=tailing esc and bracket\x1b["
+	expectedTail = inputText
+	fmt.Fprintf(w, inputText)
+	actualTail = inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputText = "\x1b[?tailing esc\x1b"
+	expectedTail = inputText
+	fmt.Fprintf(w, inputText)
+	actualTail = inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputText = "\x1b[1h;3punended color code invalid\x1b3"
+	expectedTail = inputText
+	fmt.Fprintf(w, inputText)
+	actualTail = inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+}

+ 117 - 0
vender/github.com/astaxie/beego/logs/conn.go

@@ -0,0 +1,117 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package logs
+
+import (
+	"encoding/json"
+	"io"
+	"net"
+	"time"
+)
+
+// connWriter implements LoggerInterface.
+// it writes messages in keep-live tcp connection.
+type connWriter struct {
+	lg             *logWriter
+	innerWriter    io.WriteCloser
+	ReconnectOnMsg bool   `json:"reconnectOnMsg"`
+	Reconnect      bool   `json:"reconnect"`
+	Net            string `json:"net"`
+	Addr           string `json:"addr"`
+	Level          int    `json:"level"`
+}
+
+// NewConn create new ConnWrite returning as LoggerInterface.
+func NewConn() Logger {
+	conn := new(connWriter)
+	conn.Level = LevelTrace
+	return conn
+}
+
+// Init init connection writer with json config.
+// json config only need key "level".
+func (c *connWriter) Init(jsonConfig string) error {
+	return json.Unmarshal([]byte(jsonConfig), c)
+}
+
+// WriteMsg write message in connection.
+// if connection is down, try to re-connect.
+func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error {
+	if level > c.Level {
+		return nil
+	}
+	if c.needToConnectOnMsg() {
+		err := c.connect()
+		if err != nil {
+			return err
+		}
+	}
+
+	if c.ReconnectOnMsg {
+		defer c.innerWriter.Close()
+	}
+
+	c.lg.println(when, msg)
+	return nil
+}
+
+// Flush implementing method. empty.
+func (c *connWriter) Flush() {
+
+}
+
+// Destroy destroy connection writer and close tcp listener.
+func (c *connWriter) Destroy() {
+	if c.innerWriter != nil {
+		c.innerWriter.Close()
+	}
+}
+
+func (c *connWriter) connect() error {
+	if c.innerWriter != nil {
+		c.innerWriter.Close()
+		c.innerWriter = nil
+	}
+
+	conn, err := net.Dial(c.Net, c.Addr)
+	if err != nil {
+		return err
+	}
+
+	if tcpConn, ok := conn.(*net.TCPConn); ok {
+		tcpConn.SetKeepAlive(true)
+	}
+
+	c.innerWriter = conn
+	c.lg = newLogWriter(conn)
+	return nil
+}
+
+func (c *connWriter) needToConnectOnMsg() bool {
+	if c.Reconnect {
+		c.Reconnect = false
+		return true
+	}
+
+	if c.innerWriter == nil {
+		return true
+	}
+
+	return c.ReconnectOnMsg
+}
+
+func init() {
+	Register(AdapterConn, NewConn)
+}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff