commit
94db94ce1b
@ -0,0 +1,84 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"runtime" |
||||
) |
||||
|
||||
const ( |
||||
TextBlack = iota + 30 |
||||
TextRed |
||||
TextGreen |
||||
TextYellow |
||||
TextBlue |
||||
TextMagenta |
||||
TextCyan |
||||
TextWhite |
||||
) |
||||
|
||||
func Black(str string) string { |
||||
return textColor(TextBlack, str) |
||||
} |
||||
|
||||
func Red(str string) string { |
||||
return textColor(TextRed, str) |
||||
} |
||||
|
||||
func Green(str string) string { |
||||
return textColor(TextGreen, str) |
||||
} |
||||
|
||||
func Yellow(str string) string { |
||||
return textColor(TextYellow, str) |
||||
} |
||||
|
||||
func Blue(str string) string { |
||||
return textColor(TextBlue, str) |
||||
} |
||||
|
||||
func Magenta(str string) string { |
||||
return textColor(TextMagenta, str) |
||||
} |
||||
|
||||
func Cyan(str string) string { |
||||
return textColor(TextCyan, str) |
||||
} |
||||
|
||||
func White(str string) string { |
||||
return textColor(TextWhite, str) |
||||
} |
||||
|
||||
func textColor(color int, str string) string { |
||||
if IsWindows() { |
||||
return str |
||||
} |
||||
|
||||
switch color { |
||||
case TextBlack: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextBlack, str) |
||||
case TextRed: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextRed, str) |
||||
case TextGreen: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextGreen, str) |
||||
case TextYellow: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextYellow, str) |
||||
case TextBlue: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextBlue, str) |
||||
case TextMagenta: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextMagenta, str) |
||||
case TextCyan: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextCyan, str) |
||||
case TextWhite: |
||||
return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextWhite, str) |
||||
default: |
||||
return str |
||||
} |
||||
} |
||||
|
||||
func IsWindows() bool { |
||||
if runtime.GOOS == "windows" { |
||||
return true |
||||
} else { |
||||
return false |
||||
} |
||||
} |
@ -0,0 +1,330 @@ |
||||
/* |
||||
Author:shepbao |
||||
Time:2016-06-01 |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"bytes" |
||||
"compress/flate" |
||||
"compress/gzip" |
||||
"encoding/json" |
||||
"errors" |
||||
"flag" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"math" |
||||
"net" |
||||
"net/http" |
||||
"net/http/httputil" |
||||
"net/url" |
||||
"os" |
||||
"regexp" |
||||
"strconv" |
||||
"time" |
||||
|
||||
"log" |
||||
) |
||||
|
||||
const ( |
||||
MaxOutstanding = 10 |
||||
) |
||||
|
||||
var Version string = "1.0" |
||||
var sem = make(chan int, MaxOutstanding) |
||||
|
||||
type AccessStatistics struct { |
||||
AllCount int64 |
||||
HostCount map[string]int64 |
||||
} |
||||
|
||||
var transport = &http.Transport{ |
||||
ResponseHeaderTimeout: 30 * time.Second, |
||||
} |
||||
|
||||
//config json
|
||||
type Config struct { |
||||
Port string `json :"port"` //"8080"
|
||||
Raddr string `json:"raddr,omitempty"` //"localhost:8888"
|
||||
} |
||||
|
||||
var port = flag.String("port", "8080", "Listen port") |
||||
var raddr = flag.String("raddr", "", "Remote addr") |
||||
var monitor = flag.Bool("m", false, "monitor mode") |
||||
|
||||
var cfg = flag.String("conf", "./config", "config file") |
||||
var cf *Config |
||||
|
||||
var logFile *os.File |
||||
var logger *log.Logger |
||||
|
||||
func main() { |
||||
var err error |
||||
logFile, err = os.Create("error.log") |
||||
if err != nil { |
||||
log.Fatalln("fail to create log file!") |
||||
} |
||||
|
||||
logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) |
||||
flag.Parse() |
||||
|
||||
cf, err = ParseConfig(*cfg) |
||||
if err != nil { |
||||
logger.Println("ParseConfig err:", err) |
||||
} else { |
||||
if len(cf.Port) == 0 { |
||||
log.Fatal("Miss Port") |
||||
} |
||||
*port = cf.Port |
||||
|
||||
if len(cf.Raddr) == 0 { |
||||
log.Fatal("Miss Raddr") |
||||
} |
||||
*raddr = cf.Raddr |
||||
} |
||||
|
||||
log.Println("Listen port: ", *port) |
||||
if len(*raddr) != 0 { |
||||
log.Println("Connect to Raddr: ", *raddr) |
||||
} |
||||
|
||||
ln, err := net.Listen("tcp", ":"+*port) |
||||
if err != nil { |
||||
logger.Println("listen err:", err) |
||||
} |
||||
defer ln.Close() |
||||
|
||||
for { |
||||
conn, err := ln.Accept() |
||||
if err != nil { |
||||
logger.Println("Accept err:", err) |
||||
continue |
||||
} |
||||
go handleConn(conn) |
||||
|
||||
} |
||||
} |
||||
|
||||
//处理连接
|
||||
func handleConn(conn net.Conn) { |
||||
reader := bufio.NewReader(conn) |
||||
req, err := http.ReadRequest(reader) |
||||
if err != nil { |
||||
logger.Println("readRequest err:", err) |
||||
conn.Close() |
||||
return |
||||
} |
||||
|
||||
host := req.Host |
||||
matched, _ := regexp.MatchString(":[0-9]+$", host) |
||||
if !matched { |
||||
host += ":80" |
||||
} |
||||
conn_proxy, err := Connect(host) |
||||
if err != nil { |
||||
return |
||||
} |
||||
defer conn_proxy.Close() |
||||
|
||||
if req.Method == "CONNECT" { |
||||
b := []byte("HTTP/1.1 200 Connection Established\r\n" + |
||||
"Proxy-Agent: golang_proxy/" + Version + "\r\n\r\n") |
||||
_, err := conn.Write(b) |
||||
if err != nil { |
||||
logger.Println("Write Connect err:", err) |
||||
return |
||||
} |
||||
} else { |
||||
req.Header.Del("Proxy-Connection") |
||||
req.Header.Set("Connection", "Keep-Alive") |
||||
if err = req.Write(conn_proxy); err != nil { |
||||
logger.Println("send to server err", err) |
||||
return |
||||
} |
||||
} |
||||
|
||||
if *monitor && req.Method != "CONNECT" { |
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn_proxy), req) |
||||
if err != nil { |
||||
logger.Println("read response err:", err) |
||||
return |
||||
} |
||||
|
||||
respDump, err := httputil.DumpResponse(resp, true) |
||||
if err != nil { |
||||
logger.Println("respDump err:", err) |
||||
} |
||||
|
||||
_, err = conn.Write(respDump) |
||||
if err != nil { |
||||
logger.Println("conn write err:", err) |
||||
} |
||||
// reqDump, _ := httputil.DumpRequest(req, false)
|
||||
// log.Println(string(reqDump), string(respDump))
|
||||
go httpDump(req, resp) |
||||
|
||||
} else { |
||||
err = Transport(conn, conn_proxy) |
||||
if err != nil { |
||||
logger.Println("Transport err:", err) |
||||
} |
||||
} |
||||
} |
||||
|
||||
//连接真实的主机
|
||||
func Connect(host string) (net.Conn, error) { |
||||
if len(*raddr) == 0 { |
||||
conn, err := net.DialTimeout("tcp", host, time.Second*30) |
||||
if err != nil { |
||||
// logger.Println("connect", host, "err", err)
|
||||
return nil, err |
||||
} |
||||
return conn, nil |
||||
} |
||||
|
||||
conn_proxy, err := net.DialTimeout("tcp", *raddr, time.Second*30) |
||||
if err != nil { |
||||
logger.Println("connect proxy err", err) |
||||
return nil, err |
||||
} |
||||
err = connectProxyServer(conn_proxy, host) |
||||
if err != nil { |
||||
logger.Println("connectServer err:", err) |
||||
return nil, err |
||||
} |
||||
return conn_proxy, nil |
||||
} |
||||
|
||||
//连接代理服务器
|
||||
func connectProxyServer(conn net.Conn, addr string) error { |
||||
|
||||
req := &http.Request{ |
||||
Method: "CONNECT", |
||||
URL: &url.URL{Host: addr}, |
||||
Host: addr, |
||||
ProtoMajor: 1, |
||||
ProtoMinor: 1, |
||||
Header: make(http.Header), |
||||
} |
||||
req.Header.Set("Proxy-Connection", "keep-alive") |
||||
|
||||
if err := req.Write(conn); err != nil { |
||||
return err |
||||
} |
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if resp.StatusCode != http.StatusOK { |
||||
return errors.New(resp.Status) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
//两个io口的连接
|
||||
func Transport(conn1, conn2 net.Conn) (err error) { |
||||
rChan := make(chan error, 1) |
||||
wChan := make(chan error, 1) |
||||
|
||||
go MyCopy(conn1, conn2, wChan) |
||||
go MyCopy(conn2, conn1, rChan) |
||||
|
||||
select { |
||||
case err = <-wChan: |
||||
case err = <-rChan: |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { |
||||
_, err := io.Copy(dst, src) |
||||
ch <- err |
||||
} |
||||
|
||||
//配置文件解析
|
||||
func ParseConfig(cfg string) (*Config, error) { |
||||
var conf Config |
||||
configContext, err := ioutil.ReadFile(cfg) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
err = json.Unmarshal(configContext, &conf) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &conf, nil |
||||
} |
||||
|
||||
//复制http头
|
||||
func copyHeader(src, dst http.Header) { |
||||
for k, vv := range src { |
||||
for _, v := range vv { |
||||
dst.Add(k, v) |
||||
} |
||||
} |
||||
} |
||||
|
||||
//打印http请求和响应
|
||||
func httpDump(req *http.Request, resp *http.Response) { |
||||
defer resp.Body.Close() |
||||
var respStatusStr string |
||||
respStatus := resp.StatusCode |
||||
respStatusHeader := int(math.Floor(float64(respStatus / 100))) |
||||
switch respStatusHeader { |
||||
case 2: |
||||
respStatusStr = Green("<--" + strconv.Itoa(respStatus)) |
||||
case 3: |
||||
respStatusStr = Yellow("<--" + strconv.Itoa(respStatus)) |
||||
case 4: |
||||
respStatusStr = Magenta("<--" + strconv.Itoa(respStatus)) |
||||
case 5: |
||||
respStatusStr = Red("<--" + strconv.Itoa(respStatus)) |
||||
} |
||||
fmt.Println(Green("Request:")) |
||||
fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) |
||||
for headerName, headerContext := range req.Header { |
||||
fmt.Printf("%s: %s\n", Blue(headerName), headerContext) |
||||
} |
||||
fmt.Println(Green("Response:")) |
||||
for headerName, headerContext := range resp.Header { |
||||
fmt.Printf("%s: %s\n", Blue(headerName), headerContext) |
||||
} |
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body) |
||||
if err != nil { |
||||
logger.Println("read resp body err:", err) |
||||
} else { |
||||
acceptEncode := resp.Header["Content-Encoding"] |
||||
var respBodyBin bytes.Buffer |
||||
w := bufio.NewWriter(&respBodyBin) |
||||
w.Write(respBody) |
||||
w.Flush() |
||||
for _, compress := range acceptEncode { |
||||
switch compress { |
||||
case "gzip": |
||||
r, err := gzip.NewReader(&respBodyBin) |
||||
if err != nil { |
||||
logger.Println("gzip reader err:", err) |
||||
} else { |
||||
defer r.Close() |
||||
respBody, _ = ioutil.ReadAll(r) |
||||
} |
||||
break |
||||
case "deflate": |
||||
r := flate.NewReader(&respBodyBin) |
||||
defer r.Close() |
||||
respBody, _ = ioutil.ReadAll(r) |
||||
break |
||||
} |
||||
} |
||||
fmt.Printf("%s\n", string(respBody)) |
||||
} |
||||
|
||||
fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) |
||||
} |
Loading…
Reference in new issue