mirror of https://github.com/fatedier/frp
				
				
				
			
						commit
						793624c379
					
				| 
						 | 
				
			
			@ -22,3 +22,7 @@ _testmain.go
 | 
			
		|||
*.exe
 | 
			
		||||
*.test
 | 
			
		||||
*.prof
 | 
			
		||||
 | 
			
		||||
# Self
 | 
			
		||||
bin/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
{
 | 
			
		||||
	"ImportPath": "frp",
 | 
			
		||||
	"GoVersion": "go1.4",
 | 
			
		||||
	"Packages": [
 | 
			
		||||
		"./..."
 | 
			
		||||
	],
 | 
			
		||||
	"Deps": [
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/astaxie/beego/logs",
 | 
			
		||||
			"Comment": "v1.5.0-9-gfb7314f",
 | 
			
		||||
			"Rev": "fb7314f8ac86b83ccd34386518d97cf2363e2ae5"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/vaughan0/go-ini",
 | 
			
		||||
			"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
This directory tree is generated automatically by godep.
 | 
			
		||||
 | 
			
		||||
Please do not edit.
 | 
			
		||||
 | 
			
		||||
See https://github.com/tools/godep for more information.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
/pkg
 | 
			
		||||
/bin
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
## logs
 | 
			
		||||
logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` .
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## How to install?
 | 
			
		||||
 | 
			
		||||
	go get github.com/astaxie/beego/logs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## What adapters are supported?
 | 
			
		||||
 | 
			
		||||
As of now this logs support console, file,smtp and conn.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## How to use it?
 | 
			
		||||
 | 
			
		||||
First you must import it
 | 
			
		||||
 | 
			
		||||
	import (
 | 
			
		||||
		"github.com/astaxie/beego/logs"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
Then init a Log (example with console adapter)
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(10000)
 | 
			
		||||
	log.SetLogger("console", "")	
 | 
			
		||||
 | 
			
		||||
> the first params stand for how many channel
 | 
			
		||||
 | 
			
		||||
Use it like this:	
 | 
			
		||||
	
 | 
			
		||||
	log.Trace("trace")
 | 
			
		||||
	log.Info("info")
 | 
			
		||||
	log.Warn("warning")
 | 
			
		||||
	log.Debug("debug")
 | 
			
		||||
	log.Critical("critical")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## File adapter
 | 
			
		||||
 | 
			
		||||
Configure file adapter like this:
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(10000)
 | 
			
		||||
	log.SetLogger("file", `{"filename":"test.log"}`)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Conn adapter
 | 
			
		||||
 | 
			
		||||
Configure like this:
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(1000)
 | 
			
		||||
	log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
 | 
			
		||||
	log.Info("info")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Smtp adapter
 | 
			
		||||
 | 
			
		||||
Configure like this:
 | 
			
		||||
 | 
			
		||||
	log := NewLogger(10000)
 | 
			
		||||
	log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
 | 
			
		||||
	log.Critical("sendmail critical")
 | 
			
		||||
	time.Sleep(time.Second * 30)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
// 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"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConnWriter implements LoggerInterface.
 | 
			
		||||
// it writes messages in keep-live tcp connection.
 | 
			
		||||
type ConnWriter struct {
 | 
			
		||||
	lg             *log.Logger
 | 
			
		||||
	innerWriter    io.WriteCloser
 | 
			
		||||
	ReconnectOnMsg bool   `json:"reconnectOnMsg"`
 | 
			
		||||
	Reconnect      bool   `json:"reconnect"`
 | 
			
		||||
	Net            string `json:"net"`
 | 
			
		||||
	Addr           string `json:"addr"`
 | 
			
		||||
	Level          int    `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// create new ConnWrite returning as LoggerInterface.
 | 
			
		||||
func NewConn() LoggerInterface {
 | 
			
		||||
	conn := new(ConnWriter)
 | 
			
		||||
	conn.Level = LevelTrace
 | 
			
		||||
	return conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write message in connection.
 | 
			
		||||
// if connection is down, try to re-connect.
 | 
			
		||||
func (c *ConnWriter) WriteMsg(msg string, level int) error {
 | 
			
		||||
	if level > c.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if c.neddedConnectOnMsg() {
 | 
			
		||||
		err := c.connect()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.ReconnectOnMsg {
 | 
			
		||||
		defer c.innerWriter.Close()
 | 
			
		||||
	}
 | 
			
		||||
	c.lg.Println(msg)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// implementing method. empty.
 | 
			
		||||
func (c *ConnWriter) Flush() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 = log.New(conn, "", log.Ldate|log.Ltime)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *ConnWriter) neddedConnectOnMsg() bool {
 | 
			
		||||
	if c.Reconnect {
 | 
			
		||||
		c.Reconnect = false
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.innerWriter == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.ReconnectOnMsg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register("conn", NewConn)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
// 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"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Brush func(string) string
 | 
			
		||||
 | 
			
		||||
func NewBrush(color string) Brush {
 | 
			
		||||
	pre := "\033["
 | 
			
		||||
	reset := "\033[0m"
 | 
			
		||||
	return func(text string) string {
 | 
			
		||||
		return pre + color + "m" + text + reset
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var colors = []Brush{
 | 
			
		||||
	NewBrush("1;37"), // Emergency	white
 | 
			
		||||
	NewBrush("1;36"), // Alert			cyan
 | 
			
		||||
	NewBrush("1;35"), // Critical   magenta
 | 
			
		||||
	NewBrush("1;31"), // Error      red
 | 
			
		||||
	NewBrush("1;33"), // Warning    yellow
 | 
			
		||||
	NewBrush("1;32"), // Notice			green
 | 
			
		||||
	NewBrush("1;34"), // Informational	blue
 | 
			
		||||
	NewBrush("1;34"), // Debug      blue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
 | 
			
		||||
type ConsoleWriter struct {
 | 
			
		||||
	lg    *log.Logger
 | 
			
		||||
	Level int `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// create ConsoleWriter returning as LoggerInterface.
 | 
			
		||||
func NewConsole() LoggerInterface {
 | 
			
		||||
	cw := &ConsoleWriter{
 | 
			
		||||
		lg:    log.New(os.Stdout, "", log.Ldate|log.Ltime),
 | 
			
		||||
		Level: LevelDebug,
 | 
			
		||||
	}
 | 
			
		||||
	return cw
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// init console logger.
 | 
			
		||||
// jsonconfig like '{"level":LevelTrace}'.
 | 
			
		||||
func (c *ConsoleWriter) Init(jsonconfig string) error {
 | 
			
		||||
	if len(jsonconfig) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return json.Unmarshal([]byte(jsonconfig), c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write message in console.
 | 
			
		||||
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
 | 
			
		||||
	if level > c.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if goos := runtime.GOOS; goos == "windows" {
 | 
			
		||||
		c.lg.Println(msg)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	c.lg.Println(colors[level](msg))
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// implementing method. empty.
 | 
			
		||||
func (c *ConsoleWriter) Destroy() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// implementing method. empty.
 | 
			
		||||
func (c *ConsoleWriter) Flush() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register("console", NewConsole)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
package es
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/astaxie/beego/logs"
 | 
			
		||||
	"github.com/belogik/goes"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewES() logs.LoggerInterface {
 | 
			
		||||
	cw := &esLogger{
 | 
			
		||||
		Level: logs.LevelDebug,
 | 
			
		||||
	}
 | 
			
		||||
	return cw
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type esLogger struct {
 | 
			
		||||
	*goes.Connection
 | 
			
		||||
	DSN   string `json:"dsn"`
 | 
			
		||||
	Level int    `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// {"dsn":"http://localhost:9200/","level":1}
 | 
			
		||||
func (el *esLogger) Init(jsonconfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonconfig), el)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if el.DSN == "" {
 | 
			
		||||
		return errors.New("empty dsn")
 | 
			
		||||
	} else if u, err := url.Parse(el.DSN); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if u.Path == "" {
 | 
			
		||||
		return errors.New("missing prefix")
 | 
			
		||||
	} else if host, port, err := net.SplitHostPort(u.Host); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else {
 | 
			
		||||
		conn := goes.NewConnection(host, port)
 | 
			
		||||
		el.Connection = conn
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (el *esLogger) WriteMsg(msg string, level int) error {
 | 
			
		||||
	if level > el.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	t := time.Now()
 | 
			
		||||
	vals := make(map[string]interface{})
 | 
			
		||||
	vals["@timestamp"] = t.Format(time.RFC3339)
 | 
			
		||||
	vals["@msg"] = msg
 | 
			
		||||
	d := goes.Document{
 | 
			
		||||
		Index:  fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()),
 | 
			
		||||
		Type:   "logs",
 | 
			
		||||
		Fields: vals,
 | 
			
		||||
	}
 | 
			
		||||
	_, err := el.Index(d, nil)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (el *esLogger) Destroy() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (el *esLogger) Flush() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	logs.Register("es", NewES)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,283 @@
 | 
			
		|||
// 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"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FileLogWriter implements LoggerInterface.
 | 
			
		||||
// It writes messages by lines limit, file size limit, or time frequency.
 | 
			
		||||
type FileLogWriter struct {
 | 
			
		||||
	*log.Logger
 | 
			
		||||
	mw *MuxWriter
 | 
			
		||||
	// The opened file
 | 
			
		||||
	Filename string `json:"filename"`
 | 
			
		||||
 | 
			
		||||
	Maxlines          int `json:"maxlines"`
 | 
			
		||||
	maxlines_curlines int
 | 
			
		||||
 | 
			
		||||
	// Rotate at size
 | 
			
		||||
	Maxsize         int `json:"maxsize"`
 | 
			
		||||
	maxsize_cursize int
 | 
			
		||||
 | 
			
		||||
	// Rotate daily
 | 
			
		||||
	Daily          bool  `json:"daily"`
 | 
			
		||||
	Maxdays        int64 `json:"maxdays"`
 | 
			
		||||
	daily_opendate int
 | 
			
		||||
 | 
			
		||||
	Rotate bool `json:"rotate"`
 | 
			
		||||
 | 
			
		||||
	startLock sync.Mutex // Only one log can write to the file
 | 
			
		||||
 | 
			
		||||
	Level int `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// an *os.File writer with locker.
 | 
			
		||||
type MuxWriter struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	fd *os.File
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write to os.File.
 | 
			
		||||
func (l *MuxWriter) Write(b []byte) (int, error) {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
	return l.fd.Write(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// set os.File in writer.
 | 
			
		||||
func (l *MuxWriter) SetFd(fd *os.File) {
 | 
			
		||||
	if l.fd != nil {
 | 
			
		||||
		l.fd.Close()
 | 
			
		||||
	}
 | 
			
		||||
	l.fd = fd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// create a FileLogWriter returning as LoggerInterface.
 | 
			
		||||
func NewFileWriter() LoggerInterface {
 | 
			
		||||
	w := &FileLogWriter{
 | 
			
		||||
		Filename: "",
 | 
			
		||||
		Maxlines: 1000000,
 | 
			
		||||
		Maxsize:  1 << 28, //256 MB
 | 
			
		||||
		Daily:    true,
 | 
			
		||||
		Maxdays:  7,
 | 
			
		||||
		Rotate:   true,
 | 
			
		||||
		Level:    LevelTrace,
 | 
			
		||||
	}
 | 
			
		||||
	// use MuxWriter instead direct use os.File for lock write when rotate
 | 
			
		||||
	w.mw = new(MuxWriter)
 | 
			
		||||
	// set MuxWriter as Logger's io.Writer
 | 
			
		||||
	w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
 | 
			
		||||
	return w
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init file logger with json config.
 | 
			
		||||
// jsonconfig like:
 | 
			
		||||
//	{
 | 
			
		||||
//	"filename":"logs/beego.log",
 | 
			
		||||
//	"maxlines":10000,
 | 
			
		||||
//	"maxsize":1<<30,
 | 
			
		||||
//	"daily":true,
 | 
			
		||||
//	"maxdays":15,
 | 
			
		||||
//	"rotate":true
 | 
			
		||||
//	}
 | 
			
		||||
func (w *FileLogWriter) Init(jsonconfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonconfig), w)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if len(w.Filename) == 0 {
 | 
			
		||||
		return errors.New("jsonconfig must have filename")
 | 
			
		||||
	}
 | 
			
		||||
	err = w.startLogger()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start file logger. create log file and set to locker-inside file writer.
 | 
			
		||||
func (w *FileLogWriter) startLogger() error {
 | 
			
		||||
	fd, err := w.createLogFile()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	w.mw.SetFd(fd)
 | 
			
		||||
	return w.initFd()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *FileLogWriter) docheck(size int) {
 | 
			
		||||
	w.startLock.Lock()
 | 
			
		||||
	defer w.startLock.Unlock()
 | 
			
		||||
	if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
 | 
			
		||||
		(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
 | 
			
		||||
		(w.Daily && time.Now().Day() != w.daily_opendate)) {
 | 
			
		||||
		if err := w.DoRotate(); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	w.maxlines_curlines++
 | 
			
		||||
	w.maxsize_cursize += size
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write logger message into file.
 | 
			
		||||
func (w *FileLogWriter) WriteMsg(msg string, level int) error {
 | 
			
		||||
	if level > w.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
 | 
			
		||||
	w.docheck(n)
 | 
			
		||||
	w.Logger.Println(msg)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *FileLogWriter) createLogFile() (*os.File, error) {
 | 
			
		||||
	// Open the log file
 | 
			
		||||
	fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
 | 
			
		||||
	return fd, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *FileLogWriter) initFd() error {
 | 
			
		||||
	fd := w.mw.fd
 | 
			
		||||
	finfo, err := fd.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("get stat err: %s\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	w.maxsize_cursize = int(finfo.Size())
 | 
			
		||||
	w.daily_opendate = time.Now().Day()
 | 
			
		||||
	w.maxlines_curlines = 0
 | 
			
		||||
	if finfo.Size() > 0 {
 | 
			
		||||
		count, err := w.lines()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		w.maxlines_curlines = count
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *FileLogWriter) lines() (int, error) {
 | 
			
		||||
	fd, err := os.Open(w.Filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	defer fd.Close()
 | 
			
		||||
 | 
			
		||||
	buf := make([]byte, 32768) // 32k
 | 
			
		||||
	count := 0
 | 
			
		||||
	lineSep := []byte{'\n'}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		c, err := fd.Read(buf)
 | 
			
		||||
		if err != nil && err != io.EOF {
 | 
			
		||||
			return count, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		count += bytes.Count(buf[:c], lineSep)
 | 
			
		||||
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return count, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DoRotate means it need to write file in new file.
 | 
			
		||||
// new file name like xx.log.2013-01-01.2
 | 
			
		||||
func (w *FileLogWriter) DoRotate() error {
 | 
			
		||||
	_, err := os.Lstat(w.Filename)
 | 
			
		||||
	if err == nil { // file exists
 | 
			
		||||
		// Find the next available number
 | 
			
		||||
		num := 1
 | 
			
		||||
		fname := ""
 | 
			
		||||
		for ; err == nil && num <= 999; num++ {
 | 
			
		||||
			fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
 | 
			
		||||
			_, err = os.Lstat(fname)
 | 
			
		||||
		}
 | 
			
		||||
		// return error if the last file checked still existed
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// block Logger's io.Writer
 | 
			
		||||
		w.mw.Lock()
 | 
			
		||||
		defer w.mw.Unlock()
 | 
			
		||||
 | 
			
		||||
		fd := w.mw.fd
 | 
			
		||||
		fd.Close()
 | 
			
		||||
 | 
			
		||||
		// close fd before rename
 | 
			
		||||
		// Rename the file to its newfound home
 | 
			
		||||
		err = os.Rename(w.Filename, fname)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Rotate: %s\n", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// re-start logger
 | 
			
		||||
		err = w.startLogger()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Rotate StartLogger: %s\n", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		go w.deleteOldLog()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *FileLogWriter) deleteOldLog() {
 | 
			
		||||
	dir := filepath.Dir(w.Filename)
 | 
			
		||||
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if r := recover(); r != nil {
 | 
			
		||||
				returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
 | 
			
		||||
				fmt.Println(returnErr)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
 | 
			
		||||
			if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
 | 
			
		||||
				os.Remove(path)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// destroy file logger, close file writer.
 | 
			
		||||
func (w *FileLogWriter) Destroy() {
 | 
			
		||||
	w.mw.fd.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// flush file logger.
 | 
			
		||||
// there are no buffering messages in file logger in memory.
 | 
			
		||||
// flush file means sync file from disk.
 | 
			
		||||
func (w *FileLogWriter) Flush() {
 | 
			
		||||
	w.mw.fd.Sync()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register("file", NewFileWriter)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,350 @@
 | 
			
		|||
// 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.
 | 
			
		||||
 | 
			
		||||
// Usage:
 | 
			
		||||
//
 | 
			
		||||
// import "github.com/astaxie/beego/logs"
 | 
			
		||||
//
 | 
			
		||||
//	log := NewLogger(10000)
 | 
			
		||||
//	log.SetLogger("console", "")
 | 
			
		||||
//
 | 
			
		||||
//	> the first params stand for how many channel
 | 
			
		||||
//
 | 
			
		||||
// Use it like this:
 | 
			
		||||
//
 | 
			
		||||
//	log.Trace("trace")
 | 
			
		||||
//	log.Info("info")
 | 
			
		||||
//	log.Warn("warning")
 | 
			
		||||
//	log.Debug("debug")
 | 
			
		||||
//	log.Critical("critical")
 | 
			
		||||
//
 | 
			
		||||
//  more docs http://beego.me/docs/module/logs.md
 | 
			
		||||
package logs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RFC5424 log message levels.
 | 
			
		||||
const (
 | 
			
		||||
	LevelEmergency = iota
 | 
			
		||||
	LevelAlert
 | 
			
		||||
	LevelCritical
 | 
			
		||||
	LevelError
 | 
			
		||||
	LevelWarning
 | 
			
		||||
	LevelNotice
 | 
			
		||||
	LevelInformational
 | 
			
		||||
	LevelDebug
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Legacy loglevel constants to ensure backwards compatibility.
 | 
			
		||||
//
 | 
			
		||||
// Deprecated: will be removed in 1.5.0.
 | 
			
		||||
const (
 | 
			
		||||
	LevelInfo  = LevelInformational
 | 
			
		||||
	LevelTrace = LevelDebug
 | 
			
		||||
	LevelWarn  = LevelWarning
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type loggerType func() LoggerInterface
 | 
			
		||||
 | 
			
		||||
// LoggerInterface defines the behavior of a log provider.
 | 
			
		||||
type LoggerInterface interface {
 | 
			
		||||
	Init(config string) error
 | 
			
		||||
	WriteMsg(msg string, level int) error
 | 
			
		||||
	Destroy()
 | 
			
		||||
	Flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var adapters = make(map[string]loggerType)
 | 
			
		||||
 | 
			
		||||
// Register makes a log provide available by the provided name.
 | 
			
		||||
// If Register is called twice with the same name or if driver is nil,
 | 
			
		||||
// it panics.
 | 
			
		||||
func Register(name string, log loggerType) {
 | 
			
		||||
	if log == nil {
 | 
			
		||||
		panic("logs: Register provide is nil")
 | 
			
		||||
	}
 | 
			
		||||
	if _, dup := adapters[name]; dup {
 | 
			
		||||
		panic("logs: Register called twice for provider " + name)
 | 
			
		||||
	}
 | 
			
		||||
	adapters[name] = log
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BeeLogger is default logger in beego application.
 | 
			
		||||
// it can contain several providers and log message into all providers.
 | 
			
		||||
type BeeLogger struct {
 | 
			
		||||
	lock                sync.Mutex
 | 
			
		||||
	level               int
 | 
			
		||||
	enableFuncCallDepth bool
 | 
			
		||||
	loggerFuncCallDepth int
 | 
			
		||||
	asynchronous        bool
 | 
			
		||||
	msg                 chan *logMsg
 | 
			
		||||
	outputs             map[string]LoggerInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type logMsg struct {
 | 
			
		||||
	level int
 | 
			
		||||
	msg   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewLogger returns a new BeeLogger.
 | 
			
		||||
// channellen means the number of messages in chan.
 | 
			
		||||
// if the buffering chan is full, logger adapters write to file or other way.
 | 
			
		||||
func NewLogger(channellen int64) *BeeLogger {
 | 
			
		||||
	bl := new(BeeLogger)
 | 
			
		||||
	bl.level = LevelDebug
 | 
			
		||||
	bl.loggerFuncCallDepth = 2
 | 
			
		||||
	bl.msg = make(chan *logMsg, channellen)
 | 
			
		||||
	bl.outputs = make(map[string]LoggerInterface)
 | 
			
		||||
	return bl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bl *BeeLogger) Async() *BeeLogger {
 | 
			
		||||
	bl.asynchronous = true
 | 
			
		||||
	go bl.startLogger()
 | 
			
		||||
	return bl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogger provides a given logger adapter into BeeLogger with config string.
 | 
			
		||||
// config need to be correct JSON as string: {"interval":360}.
 | 
			
		||||
func (bl *BeeLogger) SetLogger(adaptername string, config string) error {
 | 
			
		||||
	bl.lock.Lock()
 | 
			
		||||
	defer bl.lock.Unlock()
 | 
			
		||||
	if log, ok := adapters[adaptername]; ok {
 | 
			
		||||
		lg := log()
 | 
			
		||||
		err := lg.Init(config)
 | 
			
		||||
		bl.outputs[adaptername] = lg
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("logs.BeeLogger.SetLogger: " + err.Error())
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// remove a logger adapter in BeeLogger.
 | 
			
		||||
func (bl *BeeLogger) DelLogger(adaptername string) error {
 | 
			
		||||
	bl.lock.Lock()
 | 
			
		||||
	defer bl.lock.Unlock()
 | 
			
		||||
	if lg, ok := bl.outputs[adaptername]; ok {
 | 
			
		||||
		lg.Destroy()
 | 
			
		||||
		delete(bl.outputs, adaptername)
 | 
			
		||||
		return nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
 | 
			
		||||
	lm := new(logMsg)
 | 
			
		||||
	lm.level = loglevel
 | 
			
		||||
	if bl.enableFuncCallDepth {
 | 
			
		||||
		_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			file = "???"
 | 
			
		||||
			line = 0
 | 
			
		||||
		}
 | 
			
		||||
		_, filename := path.Split(file)
 | 
			
		||||
		lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
 | 
			
		||||
	} else {
 | 
			
		||||
		lm.msg = msg
 | 
			
		||||
	}
 | 
			
		||||
	if bl.asynchronous {
 | 
			
		||||
		bl.msg <- lm
 | 
			
		||||
	} else {
 | 
			
		||||
		for name, l := range bl.outputs {
 | 
			
		||||
			err := l.WriteMsg(lm.msg, lm.level)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("unable to WriteMsg to adapter:", name, err)
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set log message level.
 | 
			
		||||
//
 | 
			
		||||
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
 | 
			
		||||
// log providers will not even be sent the message.
 | 
			
		||||
func (bl *BeeLogger) SetLevel(l int) {
 | 
			
		||||
	bl.level = l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// set log funcCallDepth
 | 
			
		||||
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
 | 
			
		||||
	bl.loggerFuncCallDepth = d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// get log funcCallDepth for wrapper
 | 
			
		||||
func (bl *BeeLogger) GetLogFuncCallDepth() int {
 | 
			
		||||
	return bl.loggerFuncCallDepth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// enable log funcCallDepth
 | 
			
		||||
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
 | 
			
		||||
	bl.enableFuncCallDepth = b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start logger chan reading.
 | 
			
		||||
// when chan is not empty, write logs.
 | 
			
		||||
func (bl *BeeLogger) startLogger() {
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case bm := <-bl.msg:
 | 
			
		||||
			for _, l := range bl.outputs {
 | 
			
		||||
				err := l.WriteMsg(bm.msg, bm.level)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Println("ERROR, unable to WriteMsg:", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log EMERGENCY level message.
 | 
			
		||||
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
 | 
			
		||||
	if LevelEmergency > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[M] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelEmergency, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log ALERT level message.
 | 
			
		||||
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
 | 
			
		||||
	if LevelAlert > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[A] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelAlert, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log CRITICAL level message.
 | 
			
		||||
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
 | 
			
		||||
	if LevelCritical > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[C] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelCritical, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log ERROR level message.
 | 
			
		||||
func (bl *BeeLogger) Error(format string, v ...interface{}) {
 | 
			
		||||
	if LevelError > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[E] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelError, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log WARNING level message.
 | 
			
		||||
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
 | 
			
		||||
	if LevelWarning > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[W] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelWarning, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log NOTICE level message.
 | 
			
		||||
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
 | 
			
		||||
	if LevelNotice > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[N] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelNotice, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log INFORMATIONAL level message.
 | 
			
		||||
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
 | 
			
		||||
	if LevelInformational > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[I] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelInformational, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log DEBUG level message.
 | 
			
		||||
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
 | 
			
		||||
	if LevelDebug > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[D] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelDebug, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log WARN level message.
 | 
			
		||||
// compatibility alias for Warning()
 | 
			
		||||
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
 | 
			
		||||
	if LevelWarning > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[W] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelWarning, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log INFO level message.
 | 
			
		||||
// compatibility alias for Informational()
 | 
			
		||||
func (bl *BeeLogger) Info(format string, v ...interface{}) {
 | 
			
		||||
	if LevelInformational > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[I] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelInformational, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Log TRACE level message.
 | 
			
		||||
// compatibility alias for Debug()
 | 
			
		||||
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
 | 
			
		||||
	if LevelDebug > bl.level {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf("[D] "+format, v...)
 | 
			
		||||
	bl.writerMsg(LevelDebug, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// flush all chan data.
 | 
			
		||||
func (bl *BeeLogger) Flush() {
 | 
			
		||||
	for _, l := range bl.outputs {
 | 
			
		||||
		l.Flush()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// close logger, flush all chan data and destroy all adapters in BeeLogger.
 | 
			
		||||
func (bl *BeeLogger) Close() {
 | 
			
		||||
	for {
 | 
			
		||||
		if len(bl.msg) > 0 {
 | 
			
		||||
			bm := <-bl.msg
 | 
			
		||||
			for _, l := range bl.outputs {
 | 
			
		||||
				err := l.WriteMsg(bm.msg, bm.level)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
	for _, l := range bl.outputs {
 | 
			
		||||
		l.Flush()
 | 
			
		||||
		l.Destroy()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,165 @@
 | 
			
		|||
// 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 (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/smtp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
// no usage
 | 
			
		||||
// subjectPhrase = "Diagnostic message from server"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
 | 
			
		||||
type SmtpWriter struct {
 | 
			
		||||
	Username           string   `json:"Username"`
 | 
			
		||||
	Password           string   `json:"password"`
 | 
			
		||||
	Host               string   `json:"Host"`
 | 
			
		||||
	Subject            string   `json:"subject"`
 | 
			
		||||
	FromAddress        string   `json:"fromAddress"`
 | 
			
		||||
	RecipientAddresses []string `json:"sendTos"`
 | 
			
		||||
	Level              int      `json:"level"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// create smtp writer.
 | 
			
		||||
func NewSmtpWriter() LoggerInterface {
 | 
			
		||||
	return &SmtpWriter{Level: LevelTrace}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// init smtp writer with json config.
 | 
			
		||||
// config like:
 | 
			
		||||
//	{
 | 
			
		||||
//		"Username":"example@gmail.com",
 | 
			
		||||
//		"password:"password",
 | 
			
		||||
//		"host":"smtp.gmail.com:465",
 | 
			
		||||
//		"subject":"email title",
 | 
			
		||||
//		"fromAddress":"from@example.com",
 | 
			
		||||
//		"sendTos":["email1","email2"],
 | 
			
		||||
//		"level":LevelError
 | 
			
		||||
//	}
 | 
			
		||||
func (s *SmtpWriter) Init(jsonconfig string) error {
 | 
			
		||||
	err := json.Unmarshal([]byte(jsonconfig), s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SmtpWriter) GetSmtpAuth(host string) smtp.Auth {
 | 
			
		||||
	if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return smtp.PlainAuth(
 | 
			
		||||
		"",
 | 
			
		||||
		s.Username,
 | 
			
		||||
		s.Password,
 | 
			
		||||
		host,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SmtpWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
 | 
			
		||||
	client, err := smtp.Dial(hostAddressWithPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, _, _ := net.SplitHostPort(hostAddressWithPort)
 | 
			
		||||
	tlsConn := &tls.Config{
 | 
			
		||||
		InsecureSkipVerify: true,
 | 
			
		||||
		ServerName:         host,
 | 
			
		||||
	}
 | 
			
		||||
	if err = client.StartTLS(tlsConn); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if auth != nil {
 | 
			
		||||
		if err = client.Auth(auth); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = client.Mail(fromAddress); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rec := range recipients {
 | 
			
		||||
		if err = client.Rcpt(rec); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w, err := client.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = w.Write([]byte(msgContent))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = client.Quit()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write message in smtp writer.
 | 
			
		||||
// it will send an email with subject and only this message.
 | 
			
		||||
func (s *SmtpWriter) WriteMsg(msg string, level int) error {
 | 
			
		||||
	if level > s.Level {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hp := strings.Split(s.Host, ":")
 | 
			
		||||
 | 
			
		||||
	// Set up authentication information.
 | 
			
		||||
	auth := s.GetSmtpAuth(hp[0])
 | 
			
		||||
 | 
			
		||||
	// Connect to the server, authenticate, set the sender and recipient,
 | 
			
		||||
	// and send the email all in one step.
 | 
			
		||||
	content_type := "Content-Type: text/plain" + "; charset=UTF-8"
 | 
			
		||||
	mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
 | 
			
		||||
		">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
 | 
			
		||||
 | 
			
		||||
	return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// implementing method. empty.
 | 
			
		||||
func (s *SmtpWriter) Flush() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// implementing method. empty.
 | 
			
		||||
func (s *SmtpWriter) Destroy() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Register("smtp", NewSmtpWriter)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/LICENSE
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										19
									
								
								Godeps/_workspace/src/github.com/astaxie/beego/utils/captcha/LICENSE
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
Copyright (c) 2011-2014 Dmitry Chestnykh <dmitry@codingrobots.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
Copyright (c) 2013 Vaughan Newton
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 | 
			
		||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 | 
			
		||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 | 
			
		||||
persons to whom the Software is furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 | 
			
		||||
Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 | 
			
		||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
			
		||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 | 
			
		||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
go-ini
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
INI parsing library for Go (golang).
 | 
			
		||||
 | 
			
		||||
View the API documentation [here](http://godoc.org/github.com/vaughan0/go-ini).
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Parse an INI file:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
import "github.com/vaughan0/go-ini"
 | 
			
		||||
 | 
			
		||||
file, err := ini.LoadFile("myfile.ini")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Get data from the parsed file:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
name, ok := file.Get("person", "name")
 | 
			
		||||
if !ok {
 | 
			
		||||
  panic("'name' variable missing from 'person' section")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Iterate through values in a section:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
for key, value := range file["mysection"] {
 | 
			
		||||
  fmt.Printf("%s => %s\n", key, value)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Iterate through sections in a file:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
for name, section := range file {
 | 
			
		||||
  fmt.Printf("Section name: %s\n", name)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
File Format
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
INI files are parsed by go-ini line-by-line. Each line may be one of the following:
 | 
			
		||||
 | 
			
		||||
  * A section definition: [section-name]
 | 
			
		||||
  * A property: key = value
 | 
			
		||||
  * A comment: #blahblah _or_ ;blahblah
 | 
			
		||||
  * Blank. The line will be ignored.
 | 
			
		||||
 | 
			
		||||
Properties defined before any section headers are placed in the default section, which has
 | 
			
		||||
the empty string as it's key.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
# I am a comment
 | 
			
		||||
; So am I!
 | 
			
		||||
 | 
			
		||||
[apples]
 | 
			
		||||
colour = red or green
 | 
			
		||||
shape = applish
 | 
			
		||||
 | 
			
		||||
[oranges]
 | 
			
		||||
shape = square
 | 
			
		||||
colour = blue
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,123 @@
 | 
			
		|||
// Package ini provides functions for parsing INI configuration files.
 | 
			
		||||
package ini
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	sectionRegex = regexp.MustCompile(`^\[(.*)\]$`)
 | 
			
		||||
	assignRegex  = regexp.MustCompile(`^([^=]+)=(.*)$`)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrSyntax is returned when there is a syntax error in an INI file.
 | 
			
		||||
type ErrSyntax struct {
 | 
			
		||||
	Line   int
 | 
			
		||||
	Source string // The contents of the erroneous line, without leading or trailing whitespace
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e ErrSyntax) Error() string {
 | 
			
		||||
	return fmt.Sprintf("invalid INI syntax on line %d: %s", e.Line, e.Source)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A File represents a parsed INI file.
 | 
			
		||||
type File map[string]Section
 | 
			
		||||
 | 
			
		||||
// A Section represents a single section of an INI file.
 | 
			
		||||
type Section map[string]string
 | 
			
		||||
 | 
			
		||||
// Returns a named Section. A Section will be created if one does not already exist for the given name.
 | 
			
		||||
func (f File) Section(name string) Section {
 | 
			
		||||
	section := f[name]
 | 
			
		||||
	if section == nil {
 | 
			
		||||
		section = make(Section)
 | 
			
		||||
		f[name] = section
 | 
			
		||||
	}
 | 
			
		||||
	return section
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup.
 | 
			
		||||
func (f File) Get(section, key string) (value string, ok bool) {
 | 
			
		||||
	if s := f[section]; s != nil {
 | 
			
		||||
		value, ok = s[key]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loads INI data from a reader and stores the data in the File.
 | 
			
		||||
func (f File) Load(in io.Reader) (err error) {
 | 
			
		||||
	bufin, ok := in.(*bufio.Reader)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		bufin = bufio.NewReader(in)
 | 
			
		||||
	}
 | 
			
		||||
	return parseFile(bufin, f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loads INI data from a named file and stores the data in the File.
 | 
			
		||||
func (f File) LoadFile(file string) (err error) {
 | 
			
		||||
	in, err := os.Open(file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer in.Close()
 | 
			
		||||
	return f.Load(in)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseFile(in *bufio.Reader, file File) (err error) {
 | 
			
		||||
	section := ""
 | 
			
		||||
	lineNum := 0
 | 
			
		||||
	for done := false; !done; {
 | 
			
		||||
		var line string
 | 
			
		||||
		if line, err = in.ReadString('\n'); err != nil {
 | 
			
		||||
			if err == io.EOF {
 | 
			
		||||
				done = true
 | 
			
		||||
			} else {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		lineNum++
 | 
			
		||||
		line = strings.TrimSpace(line)
 | 
			
		||||
		if len(line) == 0 {
 | 
			
		||||
			// Skip blank lines
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if line[0] == ';' || line[0] == '#' {
 | 
			
		||||
			// Skip comments
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
 | 
			
		||||
			key, val := groups[1], groups[2]
 | 
			
		||||
			key, val = strings.TrimSpace(key), strings.TrimSpace(val)
 | 
			
		||||
			file.Section(section)[key] = val
 | 
			
		||||
		} else if groups := sectionRegex.FindStringSubmatch(line); groups != nil {
 | 
			
		||||
			name := strings.TrimSpace(groups[1])
 | 
			
		||||
			section = name
 | 
			
		||||
			// Create the section if it does not exist
 | 
			
		||||
			file.Section(section)
 | 
			
		||||
		} else {
 | 
			
		||||
			return ErrSyntax{lineNum, line}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loads and returns a File from a reader.
 | 
			
		||||
func Load(in io.Reader) (File, error) {
 | 
			
		||||
	file := make(File)
 | 
			
		||||
	err := file.Load(in)
 | 
			
		||||
	return file, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loads and returns an INI File from a file on disk.
 | 
			
		||||
func LoadFile(filename string) (File, error) {
 | 
			
		||||
	file := make(File)
 | 
			
		||||
	err := file.LoadFile(filename)
 | 
			
		||||
	return file, err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
[default]
 | 
			
		||||
stuff = things
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
export PATH := $(GOPATH)/bin:$(PATH)
 | 
			
		||||
 | 
			
		||||
all: build
 | 
			
		||||
 | 
			
		||||
build: godep frps frpc
 | 
			
		||||
 | 
			
		||||
godep:
 | 
			
		||||
	@go get github.com/tools/godep
 | 
			
		||||
	godep restore
 | 
			
		||||
 | 
			
		||||
frps:
 | 
			
		||||
	godep go build -o bin/frps ./cmd/frps
 | 
			
		||||
 | 
			
		||||
frpc:
 | 
			
		||||
	godep go build -o bin/frpc ./cmd/frpc
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/models"
 | 
			
		||||
 | 
			
		||||
	ini "github.com/vaughan0/go-ini"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// common config
 | 
			
		||||
var (
 | 
			
		||||
	ServerAddr	string = "0.0.0.0"
 | 
			
		||||
	ServerPort	int64  = 7000
 | 
			
		||||
	LogFile		string = "./frpc.log"
 | 
			
		||||
	LogLevel	string = "warn"
 | 
			
		||||
	LogWay		string = "file"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ProxyClients map[string]*models.ProxyClient = make(map[string]*models.ProxyClient)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func LoadConf(confFile string) (err error) {
 | 
			
		||||
	var tmpStr string
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
	conf, err := ini.LoadFile(confFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// common
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "server_addr")
 | 
			
		||||
	if ok {
 | 
			
		||||
		ServerAddr = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "server_port")
 | 
			
		||||
	if ok {
 | 
			
		||||
		ServerPort, _ = strconv.ParseInt(tmpStr, 10, 64)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "log_file")
 | 
			
		||||
	if ok {
 | 
			
		||||
		LogFile = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "log_level")
 | 
			
		||||
	if ok {
 | 
			
		||||
		LogLevel = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "log_way")
 | 
			
		||||
	if ok {
 | 
			
		||||
		LogWay = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// servers
 | 
			
		||||
	for name, section := range conf {
 | 
			
		||||
		if name != "common" {
 | 
			
		||||
			proxyClient := &models.ProxyClient{}
 | 
			
		||||
			proxyClient.Name = name
 | 
			
		||||
 | 
			
		||||
			proxyClient.Passwd, ok = section["passwd"]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return fmt.Errorf("Parse ini file error: proxy [%s] no passwd found", proxyClient.Name)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			portStr, ok := section["local_port"]
 | 
			
		||||
			if ok {
 | 
			
		||||
				proxyClient.LocalPort, err = strconv.ParseInt(portStr, 10, 64)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return fmt.Errorf("Parse ini file error: proxy [%s] local_port error", proxyClient.Name)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return fmt.Errorf("Parse ini file error: proxy [%s] local_port not found", proxyClient.Name)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ProxyClients[proxyClient.Name] = proxyClient
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ProxyClients) == 0 {
 | 
			
		||||
		return fmt.Errorf("Parse ini file error: no proxy config found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/models"
 | 
			
		||||
	"frp/pkg/utils/conn"
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ControlProcess(cli *models.ProxyClient, wait *sync.WaitGroup) {
 | 
			
		||||
	defer wait.Done()
 | 
			
		||||
 | 
			
		||||
	c := &conn.Conn{}
 | 
			
		||||
	err := c.ConnectServer(ServerAddr, ServerPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", cli.Name, ServerAddr, ServerPort, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	req := &models.ClientCtlReq{
 | 
			
		||||
		Type:		models.ControlConn,
 | 
			
		||||
		ProxyName:	cli.Name,
 | 
			
		||||
		Passwd:		cli.Passwd,
 | 
			
		||||
	}
 | 
			
		||||
	buf, _ := json.Marshal(req)
 | 
			
		||||
	err = c.Write(string(buf) + "\n")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], write to server error, %v", cli.Name, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res, err := c.ReadLine()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], read from server error, %v", cli.Name, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Debug("ProxyName [%s], read [%s]", cli.Name, res)
 | 
			
		||||
 | 
			
		||||
	clientCtlRes := &models.ClientCtlRes{}
 | 
			
		||||
	if err = json.Unmarshal([]byte(res), &clientCtlRes); err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], format server response error, %v", cli.Name, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if clientCtlRes.Code != 0 {
 | 
			
		||||
		log.Error("ProxyName [%s], start proxy error, %s", cli.Name, clientCtlRes.Msg)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		// ignore response content now
 | 
			
		||||
		_, err := c.ReadLine()
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			log.Debug("ProxyName [%s], server close this control conn", cli.Name)
 | 
			
		||||
			break
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			log.Warn("ProxyName [%s], read from server error, %v", cli.Name, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cli.StartTunnel(ServerAddr, ServerPort)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
	
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	err := LoadConf("./frpc.ini")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		os.Exit(-1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.InitLog(LogWay, LogFile, LogLevel)
 | 
			
		||||
 | 
			
		||||
	// wait until all control goroutine exit
 | 
			
		||||
	var wait sync.WaitGroup
 | 
			
		||||
	wait.Add(len(ProxyClients))
 | 
			
		||||
 | 
			
		||||
	for _, client := range ProxyClients {
 | 
			
		||||
		go ControlProcess(client, &wait)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Info("Start frpc success")
 | 
			
		||||
 | 
			
		||||
	wait.Wait()
 | 
			
		||||
	log.Warn("All proxy exit!")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/models"
 | 
			
		||||
 | 
			
		||||
	ini "github.com/vaughan0/go-ini"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// common config
 | 
			
		||||
var (
 | 
			
		||||
	BindAddr	string = "0.0.0.0"
 | 
			
		||||
	BindPort	int64  = 9527
 | 
			
		||||
	LogFile		string = "./frps.log"
 | 
			
		||||
	LogLevel	string = "warn"
 | 
			
		||||
	LogWay		string = "file"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ProxyServers map[string]*models.ProxyServer = make(map[string]*models.ProxyServer)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func LoadConf(confFile string) (err error) {
 | 
			
		||||
	var tmpStr string
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
	conf, err := ini.LoadFile(confFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// common
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "bind_addr")
 | 
			
		||||
	if ok {
 | 
			
		||||
		BindAddr = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "bind_port")
 | 
			
		||||
	if ok {
 | 
			
		||||
		BindPort, _ = strconv.ParseInt(tmpStr, 10, 64)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "log_file")
 | 
			
		||||
	if ok {
 | 
			
		||||
		LogFile = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "log_level")
 | 
			
		||||
	if ok {
 | 
			
		||||
		LogLevel = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpStr, ok = conf.Get("common", "log_way")
 | 
			
		||||
	if ok {
 | 
			
		||||
		LogWay = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// servers
 | 
			
		||||
	for name, section := range conf {
 | 
			
		||||
		if name != "common" {
 | 
			
		||||
			proxyServer := &models.ProxyServer{}
 | 
			
		||||
			proxyServer.Name = name
 | 
			
		||||
 | 
			
		||||
			proxyServer.Passwd, ok = section["passwd"]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return fmt.Errorf("Parse ini file error: proxy [%s] no passwd found", proxyServer.Name)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			proxyServer.BindAddr, ok = section["bind_addr"]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				proxyServer.BindAddr = "0.0.0.0"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			portStr, ok := section["listen_port"]
 | 
			
		||||
			if ok {
 | 
			
		||||
				proxyServer.ListenPort, err = strconv.ParseInt(portStr, 10, 64)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return fmt.Errorf("Parse ini file error: proxy [%s] listen_port error", proxyServer.Name)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return fmt.Errorf("Parse ini file error: proxy [%s] listen_port not found", proxyServer.Name)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			proxyServer.Init()
 | 
			
		||||
			ProxyServers[proxyServer.Name] = proxyServer
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ProxyServers) == 0 {
 | 
			
		||||
		return fmt.Errorf("Parse ini file error: no proxy config found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
	"frp/pkg/utils/conn"
 | 
			
		||||
	"frp/pkg/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ProcessControlConn(l *conn.Listener) {
 | 
			
		||||
	for {
 | 
			
		||||
		c := l.GetConn()
 | 
			
		||||
		log.Debug("Get one new conn, %v", c.GetRemoteAddr())
 | 
			
		||||
		go controlWorker(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// control connection from every client and server
 | 
			
		||||
func controlWorker(c *conn.Conn) {
 | 
			
		||||
	// the first message is from client to server
 | 
			
		||||
	// if error, close connection
 | 
			
		||||
	res, err := c.ReadLine()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Warn("Read error, %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Debug("get: %s", res)
 | 
			
		||||
 | 
			
		||||
	clientCtlReq := &models.ClientCtlReq{}
 | 
			
		||||
	clientCtlRes := &models.ClientCtlRes{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(res), &clientCtlReq); err != nil {
 | 
			
		||||
		log.Warn("Parse err: %v : %s", err, res)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check
 | 
			
		||||
	succ, msg, needRes := checkProxy(clientCtlReq, c)
 | 
			
		||||
	if !succ {
 | 
			
		||||
		clientCtlRes.Code = 1
 | 
			
		||||
		clientCtlRes.Msg = msg
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if needRes {
 | 
			
		||||
		buf, _ := json.Marshal(clientCtlRes)
 | 
			
		||||
		err = c.Write(string(buf) + "\n")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Warn("Write error, %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
	// work conn, just return
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
	// others is from server to client
 | 
			
		||||
	server, ok := ProxyServers[clientCtlReq.ProxyName]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		log.Warn("ProxyName [%s] is not exist", clientCtlReq.ProxyName)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	serverCtlReq := &models.ClientCtlReq{}
 | 
			
		||||
	serverCtlReq.Type = models.WorkConn
 | 
			
		||||
	for {
 | 
			
		||||
		server.WaitUserConn()
 | 
			
		||||
		buf, _ := json.Marshal(serverCtlReq)
 | 
			
		||||
		err = c.Write(string(buf) + "\n")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Warn("ProxyName [%s], write to client error, proxy exit", server.Name)
 | 
			
		||||
			server.Close()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Debug("ProxyName [%s], write to client to add work conn success", server.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkProxy(req *models.ClientCtlReq, c *conn.Conn) (succ bool, msg string, needRes bool) {
 | 
			
		||||
	succ = false
 | 
			
		||||
	needRes = true
 | 
			
		||||
	// check if proxy name exist
 | 
			
		||||
	server, ok := ProxyServers[req.ProxyName]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		msg = fmt.Sprintf("ProxyName [%s] is not exist", req.ProxyName)
 | 
			
		||||
		log.Warn(msg)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check password
 | 
			
		||||
	if req.Passwd != server.Passwd {
 | 
			
		||||
		msg = fmt.Sprintf("ProxyName [%s], password is not correct", req.ProxyName)
 | 
			
		||||
		log.Warn(msg)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// control conn
 | 
			
		||||
	if req.Type == models.ControlConn {
 | 
			
		||||
		if server.Status != models.Idle {
 | 
			
		||||
			msg = fmt.Sprintf("ProxyName [%s], already in use", req.ProxyName)
 | 
			
		||||
			log.Warn(msg)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// start proxy and listen for user conn, no block
 | 
			
		||||
		err := server.Start()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			msg = fmt.Sprintf("ProxyName [%s], start proxy error: %v", req.ProxyName, err.Error())
 | 
			
		||||
			log.Warn(msg)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Info("ProxyName [%s], start proxy success", req.ProxyName)
 | 
			
		||||
	} else if req.Type == models.WorkConn {
 | 
			
		||||
	// work conn
 | 
			
		||||
		needRes = false
 | 
			
		||||
		if server.Status != models.Working {
 | 
			
		||||
			log.Warn("ProxyName [%s], is not working when it gets one new work conn", req.ProxyName)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		server.CliConnChan <- c
 | 
			
		||||
	} else {
 | 
			
		||||
		msg = fmt.Sprintf("ProxyName [%s], type [%d] unsupport", req.ProxyName)
 | 
			
		||||
		log.Warn(msg)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	succ = true
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
	"frp/pkg/utils/conn"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	err := LoadConf("./frps.ini")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		os.Exit(-1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.InitLog(LogWay, LogFile, LogLevel)
 | 
			
		||||
 | 
			
		||||
	l, err := conn.Listen(BindAddr, BindPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("Create listener error, %v", err)
 | 
			
		||||
		os.Exit(-1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Info("Start frps success")
 | 
			
		||||
	ProcessControlConn(l)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
# common是必须的section
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
bind_port = 7000
 | 
			
		||||
log_file = ./frpc.log
 | 
			
		||||
# debug, info, warn, error
 | 
			
		||||
log_level = info
 | 
			
		||||
# file, console
 | 
			
		||||
log_way = file
 | 
			
		||||
 | 
			
		||||
# test1即为name
 | 
			
		||||
[test1]
 | 
			
		||||
passwd = 123
 | 
			
		||||
local_port = 22
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
# common是必须的section
 | 
			
		||||
[common]
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 7000
 | 
			
		||||
log_file = ./frps.log
 | 
			
		||||
# debug, info, warn, error
 | 
			
		||||
log_level = info
 | 
			
		||||
# file, console
 | 
			
		||||
log_way = file
 | 
			
		||||
 | 
			
		||||
# test1即为name
 | 
			
		||||
[test1]
 | 
			
		||||
passwd = 123
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
listen_port = 6000
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/utils/conn"
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProxyClient struct {
 | 
			
		||||
	Name		string
 | 
			
		||||
	Passwd		string
 | 
			
		||||
	LocalPort	int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) {
 | 
			
		||||
	c = &conn.Conn{}
 | 
			
		||||
	err = c.ConnectServer("127.0.0.1", p.LocalPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], connect to local port error, %v", p.Name, err)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err error) {
 | 
			
		||||
	c = &conn.Conn{}
 | 
			
		||||
	defer func(){
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Close()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	err = c.ConnectServer(addr, port)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", p.Name, addr, port, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req := &ClientCtlReq{
 | 
			
		||||
		Type:		WorkConn,
 | 
			
		||||
		ProxyName:	p.Name,
 | 
			
		||||
		Passwd:		p.Passwd,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf, _ := json.Marshal(req)
 | 
			
		||||
	err = c.Write(string(buf) + "\n")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("ProxyName [%s], write to server error, %v", p.Name, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = nil
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyClient) StartTunnel(serverAddr string, serverPort int64) (err error) {
 | 
			
		||||
	localConn, err := p.GetLocalConn()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	remoteConn, err := p.GetRemoteConn(serverAddr, serverPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debug("Join two conns, (l[%s] r[%s]) (l[%s] r[%s])", localConn.GetLocalAddr(), localConn.GetRemoteAddr(),
 | 
			
		||||
			remoteConn.GetLocalAddr(), remoteConn.GetRemoteAddr())
 | 
			
		||||
	go conn.Join(localConn, remoteConn)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
package models
 | 
			
		||||
 | 
			
		||||
type GeneralRes struct {
 | 
			
		||||
	Code			int64	`json:"code"`
 | 
			
		||||
	Msg				string	`json:"msg"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// type
 | 
			
		||||
const (
 | 
			
		||||
	ControlConn = iota
 | 
			
		||||
	WorkConn
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ClientCtlReq struct {
 | 
			
		||||
	Type			int64	`json:"type"`
 | 
			
		||||
	ProxyName		string	`json:"proxy_name"`
 | 
			
		||||
	Passwd			string	`json:"passwd"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ClientCtlRes struct {
 | 
			
		||||
	GeneralRes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type ServerCtlReq struct {
 | 
			
		||||
	Type			int64	`json:"type"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"container/list"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/utils/conn"
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Idle = iota
 | 
			
		||||
	Working
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProxyServer struct {
 | 
			
		||||
	Name			string
 | 
			
		||||
	Passwd			string
 | 
			
		||||
	BindAddr		string
 | 
			
		||||
	ListenPort		int64
 | 
			
		||||
 | 
			
		||||
	Status			int64
 | 
			
		||||
	Listener		*conn.Listener		// accept new connection from remote users
 | 
			
		||||
	CtlMsgChan		chan int64			// every time accept a new user conn, put "1" to the channel
 | 
			
		||||
	CliConnChan		chan *conn.Conn		// get client conns from control goroutine
 | 
			
		||||
	UserConnList	*list.List			// store user conns
 | 
			
		||||
	Mutex			sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyServer) Init() {
 | 
			
		||||
	p.Status = Idle
 | 
			
		||||
	p.CtlMsgChan = make(chan int64)
 | 
			
		||||
	p.CliConnChan = make(chan *conn.Conn)
 | 
			
		||||
	p.UserConnList = list.New()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyServer) Lock() {
 | 
			
		||||
	p.Mutex.Lock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyServer) Unlock() {
 | 
			
		||||
	p.Mutex.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start listening for user conns
 | 
			
		||||
func (p *ProxyServer) Start() (err error) {
 | 
			
		||||
	p.Listener, err = conn.Listen(p.BindAddr, p.ListenPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.Status = Working
 | 
			
		||||
 | 
			
		||||
	// start a goroutine for listener
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			// block
 | 
			
		||||
			c := p.Listener.GetConn()	
 | 
			
		||||
			log.Debug("ProxyName [%s], get one new user conn [%s]", p.Name, c.GetRemoteAddr())
 | 
			
		||||
 | 
			
		||||
			// put to list
 | 
			
		||||
			p.Lock()
 | 
			
		||||
			if p.Status != Working {
 | 
			
		||||
				log.Debug("ProxyName [%s] is not working, new user conn close", p.Name)
 | 
			
		||||
				c.Close()
 | 
			
		||||
				p.Unlock()
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			p.UserConnList.PushBack(c)
 | 
			
		||||
			p.Unlock()
 | 
			
		||||
 | 
			
		||||
			// put msg to control conn
 | 
			
		||||
			p.CtlMsgChan <- 1
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// start another goroutine for join two conns from client and user
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			cliConn := <-p.CliConnChan
 | 
			
		||||
			p.Lock()
 | 
			
		||||
			element := p.UserConnList.Front()
 | 
			
		||||
 | 
			
		||||
			var userConn *conn.Conn
 | 
			
		||||
			if element != nil {
 | 
			
		||||
				userConn = element.Value.(*conn.Conn)
 | 
			
		||||
				p.UserConnList.Remove(element)
 | 
			
		||||
			} else {
 | 
			
		||||
				cliConn.Close()
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			p.Unlock()
 | 
			
		||||
 | 
			
		||||
			// msg will transfer to another without modifying
 | 
			
		||||
			log.Debug("Join two conns, (l[%s] r[%s]) (l[%s] r[%s])", cliConn.GetLocalAddr(), cliConn.GetRemoteAddr(),
 | 
			
		||||
					userConn.GetLocalAddr(), userConn.GetRemoteAddr())
 | 
			
		||||
			go conn.Join(cliConn, userConn)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyServer) Close() {
 | 
			
		||||
	p.Lock()
 | 
			
		||||
	p.Status = Idle
 | 
			
		||||
	p.CtlMsgChan = make(chan int64)
 | 
			
		||||
	p.CliConnChan = make(chan *conn.Conn)
 | 
			
		||||
	p.UserConnList = list.New()
 | 
			
		||||
	p.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *ProxyServer) WaitUserConn() (res int64) {
 | 
			
		||||
	res = <-p.CtlMsgChan
 | 
			
		||||
	return 
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
package conn
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"frp/pkg/utils/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Listener struct {
 | 
			
		||||
	Addr	net.Addr
 | 
			
		||||
	Conns	chan *Conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// wait util get one
 | 
			
		||||
func (l *Listener) GetConn() (conn *Conn) {
 | 
			
		||||
	conn = <-l.Conns
 | 
			
		||||
	return conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Conn struct {
 | 
			
		||||
	TcpConn		*net.TCPConn
 | 
			
		||||
	Reader		*bufio.Reader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) ConnectServer(host string, port int64) (err error) {
 | 
			
		||||
	servertAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", host, port))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	conn, err := net.DialTCP("tcp", nil, servertAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	c.TcpConn = conn
 | 
			
		||||
	c.Reader = bufio.NewReader(c.TcpConn)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) GetRemoteAddr() (addr string) {
 | 
			
		||||
	return c.TcpConn.RemoteAddr().String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) GetLocalAddr() (addr string) {
 | 
			
		||||
	return c.TcpConn.LocalAddr().String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) ReadLine() (buff string, err error) {
 | 
			
		||||
	buff, err = c.Reader.ReadString('\n')
 | 
			
		||||
	return buff, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) Write(content string) (err error) {
 | 
			
		||||
	_, err = c.TcpConn.Write([]byte(content))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Conn) Close() {
 | 
			
		||||
	c.TcpConn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Listen(bindAddr string, bindPort int64) (l *Listener, err error) {
 | 
			
		||||
	tcpAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", bindAddr, bindPort))
 | 
			
		||||
	listener, err := net.ListenTCP("tcp", tcpAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return l, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l = &Listener{
 | 
			
		||||
		Addr:	listener.Addr(),
 | 
			
		||||
		Conns:	make(chan *Conn),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			conn, err := listener.AcceptTCP()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("Accept new tcp connection error, %v", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			c := &Conn{
 | 
			
		||||
				TcpConn:	conn,
 | 
			
		||||
			}
 | 
			
		||||
			c.Reader = bufio.NewReader(c.TcpConn)
 | 
			
		||||
			l.Conns <- c
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return l, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// will block until conn close
 | 
			
		||||
func Join(c1 *Conn, c2 *Conn) {
 | 
			
		||||
	var wait sync.WaitGroup
 | 
			
		||||
	pipe := func(to *Conn, from *Conn) {
 | 
			
		||||
		defer to.Close()
 | 
			
		||||
		defer from.Close()
 | 
			
		||||
		defer wait.Done()
 | 
			
		||||
 | 
			
		||||
		var err error
 | 
			
		||||
		_, err = io.Copy(to.TcpConn, from.TcpConn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Warn("join conns error, %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wait.Add(2)
 | 
			
		||||
	go pipe(c1, c2)
 | 
			
		||||
	go pipe(c2, c1)
 | 
			
		||||
	wait.Wait()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/astaxie/beego/logs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Log *logs.BeeLogger
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Log = logs.NewLogger(1000)
 | 
			
		||||
	Log.EnableFuncCallDepth(true)
 | 
			
		||||
	Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InitLog(logWay string, logFile string, logLevel string) {
 | 
			
		||||
	SetLogFile(logWay, logFile)
 | 
			
		||||
	SetLogLevel(logLevel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// logWay: such as file or console
 | 
			
		||||
func SetLogFile(logWay string, logFile string) {
 | 
			
		||||
	if logWay == "console" {
 | 
			
		||||
		Log.SetLogger("console", "")
 | 
			
		||||
	} else {
 | 
			
		||||
		Log.SetLogger("file", `{"filename": "` + logFile + `"}`)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// value: error, warning, info, debug
 | 
			
		||||
func SetLogLevel(logLevel string) {
 | 
			
		||||
	level := 4 // warning
 | 
			
		||||
 | 
			
		||||
	switch logLevel {
 | 
			
		||||
	case "error":
 | 
			
		||||
		level = 3
 | 
			
		||||
	case "warn":
 | 
			
		||||
		level = 4
 | 
			
		||||
	case "info":
 | 
			
		||||
		level = 6
 | 
			
		||||
	case "debug":
 | 
			
		||||
		level = 7
 | 
			
		||||
	default:
 | 
			
		||||
		level = 4
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Log.SetLevel(level)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// wrap log
 | 
			
		||||
func Error(format string, v ...interface{}) {
 | 
			
		||||
	Log.Error(format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Warn(format string, v ...interface{}) {
 | 
			
		||||
	Log.Warn(format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Info(format string, v ...interface{}) {
 | 
			
		||||
	Log.Info(format, v...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Debug(format string, v ...interface{}) {
 | 
			
		||||
	Log.Debug(format, v...)
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue