mirror of
https://github.com/zhigang1992/cow.git
synced 2026-01-12 17:12:57 +08:00
110 lines
2.9 KiB
Go
110 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
// For once blocked site, use min dial/read timeout to make switching to
|
|
// parent proxy faster.
|
|
const minDialTimeout = 3 * time.Second
|
|
const minReadTimeout = 4 * time.Second
|
|
const defaultDialTimeout = 5 * time.Second
|
|
const defaultReadTimeout = 5 * time.Second
|
|
const maxTimeout = 15 * time.Second
|
|
|
|
var dialTimeout = defaultDialTimeout
|
|
var readTimeout = defaultReadTimeout
|
|
|
|
// estimateTimeout tries to fetch a url and adjust timeout value according to
|
|
// how much time is spent on connect and fetch. This avoids incorrectly
|
|
// considering non-blocked sites as blocked when network connection is bad.
|
|
func estimateTimeout(host string, payload []byte) {
|
|
//debug.Println("estimating timeout")
|
|
buf := connectBuf.Get()
|
|
defer connectBuf.Put(buf)
|
|
var est time.Duration
|
|
start := time.Now()
|
|
c, err := net.Dial("tcp", host+":80")
|
|
if err != nil {
|
|
errl.Printf("estimateTimeout: can't connect to %s: %v, network has problem?\n",
|
|
host, err)
|
|
goto onErr
|
|
}
|
|
defer c.Close()
|
|
|
|
est = time.Now().Sub(start) * 5
|
|
// debug.Println("estimated dialTimeout:", est)
|
|
if est > maxTimeout {
|
|
est = maxTimeout
|
|
}
|
|
if est > config.DialTimeout {
|
|
dialTimeout = est
|
|
debug.Println("new dial timeout:", dialTimeout)
|
|
} else if dialTimeout != config.DialTimeout {
|
|
dialTimeout = config.DialTimeout
|
|
debug.Println("new dial timeout:", dialTimeout)
|
|
}
|
|
|
|
start = time.Now()
|
|
// include time spent on sending request, reading all content to make it a
|
|
// little longer
|
|
|
|
if _, err = c.Write(payload); err != nil {
|
|
errl.Println("estimateTimeout: error sending request:", err)
|
|
goto onErr
|
|
}
|
|
for err == nil {
|
|
_, err = c.Read(buf)
|
|
}
|
|
if err != io.EOF {
|
|
errl.Printf("estimateTimeout: error getting %s: %v, network has problem?\n",
|
|
host, err)
|
|
goto onErr
|
|
}
|
|
est = time.Now().Sub(start) * 10
|
|
// debug.Println("estimated read timeout:", est)
|
|
if est > maxTimeout {
|
|
est = maxTimeout
|
|
}
|
|
if est > time.Duration(config.ReadTimeout) {
|
|
readTimeout = est
|
|
debug.Println("new read timeout:", readTimeout)
|
|
} else if readTimeout != config.ReadTimeout {
|
|
readTimeout = config.ReadTimeout
|
|
debug.Println("new read timeout:", readTimeout)
|
|
}
|
|
return
|
|
onErr:
|
|
dialTimeout += 2 * time.Second
|
|
readTimeout += 2 * time.Second
|
|
}
|
|
|
|
func runEstimateTimeout() {
|
|
const estimateReq = "GET / HTTP/1.1\r\n" +
|
|
"Host: %s\r\n" +
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:11.0) Gecko/20100101 Firefox/11.0\r\n" +
|
|
"Accept: */*\r\n" +
|
|
"Accept-Language: en-us,en;q=0.5\r\n" +
|
|
"Accept-Encoding: gzip, deflate\r\n" +
|
|
"Connection: close\r\n\r\n"
|
|
|
|
readTimeout = config.ReadTimeout
|
|
dialTimeout = config.DialTimeout
|
|
|
|
payload := []byte(fmt.Sprintf(estimateReq, config.EstimateTarget))
|
|
|
|
for {
|
|
estimateTimeout(config.EstimateTarget, payload)
|
|
time.Sleep(time.Minute)
|
|
}
|
|
}
|
|
|
|
// Guess network status based on doing HTTP request to estimateSite
|
|
func networkBad() bool {
|
|
return (readTimeout != config.ReadTimeout) ||
|
|
(dialTimeout != config.DialTimeout)
|
|
}
|