Skip to content

Commit

Permalink
Merge branch 'develop', version 0.9.1
Browse files Browse the repository at this point in the history
  • Loading branch information
cyfdecyf committed Dec 21, 2013
2 parents 45981a8 + f9fa178 commit 5092cd8
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
0.9.1 (2013-12-20)
* Fix can't save site stat bug
* Improve install and startup script

0.9 (2013-12-02)
* New feature: two COW servers can be connected using encrypted
connection, thus we have an encrypted HTTP proxy chain that can
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

COW 是一个简化穿墙的 HTTP 代理服务器。它能自动检测被墙网站,仅对这些网站使用二级代理。

当前版本:0.9 [CHANGELOG](CHANGELOG)
当前版本:0.9.1 [CHANGELOG](CHANGELOG)
[![Build Status](https://travis-ci.org/cyfdecyf/cow.png?branch=master)](https://travis-ci.org/cyfdecyf/cow)

**欢迎在 develop branch 进行开发并发送 pull request :)**
Expand Down Expand Up @@ -125,6 +125,7 @@ COW 默认配置下检测到被墙后,过两分钟再次尝试直连也是为

- @tevino: http parent proxy basic authentication
- @xupefei: 提供 cow-hide.exe 以在 windows 上在后台执行 cow.exe
- @sunteya: 改进启动和安装脚本

Bug reporter:

Expand Down
20 changes: 10 additions & 10 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

const (
version = "0.9"
version = "0.9.1"
defaultListenAddr = "127.0.0.1:7777"
)

Expand Down Expand Up @@ -70,7 +70,7 @@ type Config struct {
var config Config
var configNeedUpgrade bool // whether should upgrade config file

var dsFile struct {
var configPath struct {
dir string // directory containing config file and blocked site list
alwaysBlocked string // blocked sites specified by user
alwaysDirect string // direct sites specified by user
Expand All @@ -85,9 +85,9 @@ func init() {
initConfigDir()
// fmt.Println("home dir:", homeDir)

dsFile.alwaysBlocked = path.Join(dsFile.dir, alwaysBlockedFname)
dsFile.alwaysDirect = path.Join(dsFile.dir, alwaysDirectFname)
dsFile.stat = path.Join(dsFile.dir, statFname)
configPath.alwaysBlocked = path.Join(configPath.dir, alwaysBlockedFname)
configPath.alwaysDirect = path.Join(configPath.dir, alwaysDirectFname)
configPath.stat = path.Join(configPath.dir, statFname)

config.DetectSSLErr = false
config.AlwaysProxy = false
Expand All @@ -109,7 +109,7 @@ func parseCmdLineConfig() *Config {
var c Config
var listenAddr string

flag.StringVar(&c.RcFile, "rc", path.Join(dsFile.dir, rcFname), "configuration file")
flag.StringVar(&c.RcFile, "rc", path.Join(configPath.dir, rcFname), "configuration file")
// Specifying listen default value to StringVar would override config file options
flag.StringVar(&listenAddr, "listen", "", "listen address, disables listen in config")
flag.IntVar(&c.Core, "core", 2, "number of cores to use")
Expand Down Expand Up @@ -722,19 +722,19 @@ func checkConfig() {
}

func mkConfigDir() (err error) {
if dsFile.dir == "" {
if configPath.dir == "" {
return os.ErrNotExist
}
exists, err := isDirExists(dsFile.dir)
exists, err := isDirExists(configPath.dir)
if err != nil {
errl.Printf("Error checking config directory: %v\n", err)
return
}
if exists {
return
}
if err = os.Mkdir(dsFile.dir, 0755); err != nil {
errl.Printf("Error create config directory %s: %v\n", dsFile.dir, err)
if err = os.Mkdir(configPath.dir, 0755); err != nil {
errl.Printf("Error create config directory %s: %v\n", configPath.dir, err)
}
return
}
2 changes: 1 addition & 1 deletion config_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ const (

func initConfigDir() {
home := getUserHomeDir()
dsFile.dir = path.Join(home, ".cow")
configPath.dir = path.Join(home, ".cow")
}
2 changes: 1 addition & 1 deletion config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ const (
func initConfigDir() {
// On windows, put the configuration file in the same directory of cow executable
// This is not a reliable way to detect binary directory, but it works for double click and run
dsFile.dir = path.Dir(os.Args[0])
configPath.dir = path.Dir(os.Args[0])
}
3 changes: 2 additions & 1 deletion doc/init.d/cow
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ USER=usr
GROUP=grp
PID_DIR=/var/run
PID_FILE=$PID_DIR/cow.pid
LOG_FILE=/var/log/cow

RET_VAL=0

Expand Down Expand Up @@ -65,7 +66,7 @@ do_start() {
fi
echo "starting cow"
# sudo will set the group to the primary group of $USER
sudo -u $USER $BIN &
sudo -u $USER -H -- $BIN >$LOG_FILE 2>&1 &
PID=$!
echo $PID > $PID_FILE
sleep 0.3
Expand Down
5 changes: 3 additions & 2 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ func (h *Header) parseHeader(reader *bufio.Reader, raw *bytes.Buffer, url *URL)
return
}
if name, val, err = splitHeader(line); err != nil {
errl.Printf("%v raw header:\n%s\n", err, raw.Bytes())
errl.Printf("split header %v\nline: %s\nraw header:\n%s\n", err, line, raw.Bytes())
return
}
// Wait Go to solve/provide the string<->[]byte optimization
Expand All @@ -562,6 +562,7 @@ func (h *Header) parseHeader(reader *bufio.Reader, raw *bytes.Buffer, url *URL)
continue
}
if err = parseFunc(h, val); err != nil {
errl.Printf("parse header %v\nline: %s\nraw header:\n%s\n", err, line, raw.Bytes())
return
}
}
Expand Down Expand Up @@ -701,7 +702,7 @@ func parseResponse(sv *serverConn, r *Request, rp *Response) (err error) {
}

if err = rp.parseHeader(reader, rp.raw, r.URL); err != nil {
errl.Printf("parse response header: %v %s\n%s", err, rp, rp.Verbose())
errl.Printf("parse response header: %v %s\n%s", err, r, rp.Verbose())
return err
}

Expand Down
4 changes: 2 additions & 2 deletions install-cow.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

version=0.9
version=0.9.1

arch=`uname -m`
case $arch in
Expand Down Expand Up @@ -86,7 +86,7 @@ chmod +x $tmpbin ||
exit_on_fail "Can't chmod for $tmpbin"

# Download sample config file if no configuration directory present
doc_base="https://github.com/cyfdecyf/cow/raw/master/doc"
doc_base="https://raw.github.com/cyfdecyf/cow/$version/doc"
config_dir="$HOME/.cow"
is_update=true
if [ ! -e $config_dir ]; then
Expand Down
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ func sigHandler() {
syscall.SIGHUP)

for sig := range sigChan {
// May handle other signals in the future.
info.Printf("%v caught, exit\n", sig)
storeSiteStat()
storeSiteStat(siteStatExit)
break
}
/*
Expand Down
49 changes: 32 additions & 17 deletions sitestat.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func (ss *SiteStat) GetVisitCnt(url *URL) (vcnt *VisitCnt) {
return ss.create(url.Host)
}

func (ss *SiteStat) store(file string) (err error) {
func (ss *SiteStat) store(statPath string) (err error) {
if err = mkConfigDir(); err != nil {
return
}
Expand Down Expand Up @@ -289,10 +289,10 @@ func (ss *SiteStat) store(file string) (err error) {
}

// Store stat into temp file first and then rename.
// This avoids problem if SIGINT causes program to exit but stat writing
// is half done.
// Ensures atomic update to stat file to avoid file damage.

f, err := ioutil.TempFile("", "stat")
// Create tmp file inside config firectory to avoid cross FS rename.
f, err := ioutil.TempFile(configPath.dir, "stat")
if err != nil {
errl.Println("create tmp file to store stat", err)
return
Expand All @@ -305,9 +305,9 @@ func (ss *SiteStat) store(file string) (err error) {
f.Close()

// Windows don't allow rename to existing file.
os.Remove(file + ".bak")
os.Rename(file, file+".bak") // original stat may not exist
if err = os.Rename(f.Name(), file); err != nil {
os.Remove(statPath + ".bak")
os.Rename(statPath, statPath+".bak")
if err = os.Rename(f.Name(), statPath); err != nil {
errl.Println("can't rename newly created stat file", err)
return
}
Expand All @@ -326,10 +326,10 @@ func (ss *SiteStat) loadBuiltinList() {
}

func (ss *SiteStat) loadUserList() {
if directList, err := loadSiteList(dsFile.alwaysDirect); err == nil {
if directList, err := loadSiteList(configPath.alwaysDirect); err == nil {
ss.loadList(directList, userCnt, 0)
}
if blockedList, err := loadSiteList(dsFile.alwaysBlocked); err == nil {
if blockedList, err := loadSiteList(configPath.alwaysBlocked); err == nil {
ss.loadList(blockedList, 0, userCnt)
}
}
Expand Down Expand Up @@ -425,25 +425,40 @@ func (ss *SiteStat) GetDirectList() []string {
var siteStat = newSiteStat()

func initSiteStat() {
if err := siteStat.load(dsFile.stat); err != nil {
if err := siteStat.load(configPath.stat); err != nil {
os.Exit(1)
}
// dump site stat once every hour, so we don't always need to close cow to
// get updated stat
// Dump site stat while running, so we don't always need to close cow to
// get updated stat.
go func() {
for {
time.Sleep(time.Hour)
storeSiteStat()
time.Sleep(5 * time.Minute)
storeSiteStat(siteStatCont)
}
}()
}

const (
siteStatExit = iota
siteStatCont
)

// Lock ensures only one goroutine calling store.
// siteStatFini ensures no more calls after going to exit.
var storeLock sync.Mutex
var siteStatFini bool

func storeSiteStat() {
func storeSiteStat(cont byte) {
storeLock.Lock()
siteStat.store(dsFile.stat)
storeLock.Unlock()
defer storeLock.Unlock()

if siteStatFini {
return
}
siteStat.store(configPath.stat)
if cont == siteStatExit {
siteStatFini = true
}
}

func loadSiteList(fpath string) (lst []string, err error) {
Expand Down

0 comments on commit 5092cd8

Please sign in to comment.