From 236ef48e136788ea8681496ea329037f317adf00 Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Mon, 3 Jun 2013 17:53:40 +0600 Subject: [PATCH] Settings refactoring. Verbose mode. Bumb to 1.1 version --- gor.go | 2 +- listener/listener.go | 64 ++++++++++++++------------------------- listener/settings.go | 54 +++++++++++++++++++++++++++++++++ replay/replay.go | 37 ++++++---------------- replay/request_factory.go | 8 ++--- replay/request_stats.go | 5 ++- replay/settings.go | 28 +++++++++++++++++ 7 files changed, 120 insertions(+), 78 deletions(-) create mode 100644 listener/settings.go diff --git a/gor.go b/gor.go index 9df00f5..36bc0d1 100644 --- a/gor.go +++ b/gor.go @@ -14,7 +14,7 @@ import ( ) const ( - VERSION = 0.1 + VERSION = "0.1.1" ) func main() { diff --git a/listener/listener.go b/listener/listener.go index 89a4448..9044214 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -16,19 +16,9 @@ import ( "os" "os/exec" "regexp" - "strconv" "strings" ) -type ListenerSettings struct { - networkInterface string - port int - - replayAddress string -} - -var settings ListenerSettings = ListenerSettings{} - type HttpRequest struct { Tag string // Not used yet Method string // Right now only 'GET' @@ -36,9 +26,14 @@ type HttpRequest struct { Headers map[string]string // Request Headers } +// Enable debug logging only if "--verbose" flag passed +func Debug(v ...interface{}) { + if Settings.verbose { log.Println(v...) } +} + // Parse `tcpdump` output to find HTTP GET requests // When HttpRequest found it get send to `requests` channel -func ParseRequest(pipe io.ReadCloser, requests chan *HttpRequest) { +func parseRequest(pipe io.ReadCloser, requests chan *HttpRequest) { request_re := regexp.MustCompile("(GET) (/.*) HTTP/1.1") headers_re := regexp.MustCompile("([^ ]*): (.*)") @@ -96,8 +91,8 @@ func ParseRequest(pipe io.ReadCloser, requests chan *HttpRequest) { // Sends request to replay server via UDP // Before sending it encode request object using standard gob encoder -func SendRequest(requests chan *HttpRequest) { - serverAddr, err := net.ResolveUDPAddr("udp4", settings.replayAddress) +func forwardRequest(requests chan *HttpRequest) { + serverAddr, err := net.ResolveUDPAddr("udp4", Settings.ReplayServer()) conn, err := net.DialUDP("udp", nil, serverAddr) defer conn.Close() @@ -109,7 +104,7 @@ func SendRequest(requests chan *HttpRequest) { for { select { case request := <-requests: - fmt.Println("Request:", request.Url) + Debug("Forwarding:", request.Url, "to", Settings.ReplayServer()) msg := bytes.Buffer{} @@ -125,6 +120,14 @@ func SendRequest(requests chan *HttpRequest) { } } + +func greeting() { + fmt.Println("Listening for HTTP traffic on", Settings.port, "port") + fmt.Println("Running: tcpdump "+strings.Join(Settings.TCPDumpConfig()," ")) + fmt.Println("Forwarding requests to replay server:", Settings.ReplayServer()) +} + + // Because its sub-program, Run acts as `main` func Run() { if os.Getuid() != 0 { @@ -132,13 +135,11 @@ func Run() { fmt.Println("This is required since listener sniff traffic on given port.") os.Exit(1) } - - if !strings.Contains(settings.replayAddress, ":") { - settings.replayAddress = settings.replayAddress + ":28020" - } - + // TODO: use RAW_SOCKETS instead of tcpdump - cmd := exec.Command("tcpdump", "-vv", "-A", "-i", settings.networkInterface, "port "+strconv.Itoa(settings.port)) + cmd := exec.Command("tcpdump", Settings.TCPDumpConfig()...) + + greeting() stdout, _ := cmd.StdoutPipe() cmd.Stderr = os.Stderr @@ -149,29 +150,10 @@ func Run() { requests := make(chan *HttpRequest) - go ParseRequest(stdout, requests) - go SendRequest(requests) + go parseRequest(stdout, requests) + go forwardRequest(requests) if err := cmd.Wait(); err != nil { flag.Usage() } } - -func init() { - if len(os.Args) < 2 || os.Args[1] != "listen" { - return - } - - const ( - defaultPort = 80 - defaultNetworkInterface = "any" - - defaultReplayAddress = "localhost:28020" - ) - - flag.IntVar(&settings.port, "p", defaultPort, "Specify the http server port whose traffic you want to capture") - - flag.StringVar(&settings.networkInterface, "i", defaultNetworkInterface, "By default it try to listen on all network interfaces.To get list of interfaces run `ifconfig`") - - flag.StringVar(&settings.replayAddress, "r", defaultReplayAddress, "Address of replay server.") -} diff --git a/listener/settings.go b/listener/settings.go new file mode 100644 index 0000000..4f6c2a4 --- /dev/null +++ b/listener/settings.go @@ -0,0 +1,54 @@ +package listener + +import ( + "strconv" + "strings" + "flag" + "os" +) + +const ( + defaultPort = 80 + defaultNetworkInterface = "any" + + defaultReplayAddress = "localhost:28020" +) + +type ListenerSettings struct { + networkInterface string + port int + + replayAddress string + + verbose bool +} + +var Settings ListenerSettings = ListenerSettings{} + +func (s *ListenerSettings) ReplayServer() string { + if !strings.Contains(s.replayAddress, ":") { + return s.replayAddress + ":28020" + } + + return s.replayAddress +} + +// tcpdump -vv -A -i all port 8080 +func (s *ListenerSettings) TCPDumpConfig() []string { + return []string{"-vv", "-A", "-i", Settings.networkInterface, "port "+strconv.Itoa(Settings.port)} +} + + +func init() { + if len(os.Args) < 2 || os.Args[1] != "listen" { + return + } + + flag.IntVar(&Settings.port, "p", defaultPort, "Specify the http server port whose traffic you want to capture") + + flag.StringVar(&Settings.networkInterface, "i", defaultNetworkInterface, "By default it try to listen on all network interfaces.To get list of interfaces run `ifconfig`") + + flag.StringVar(&Settings.replayAddress, "r", defaultReplayAddress, "Address of replay server.") + + flag.BoolVar(&Settings.verbose, "verbose", false, "Log requests") +} diff --git a/replay/replay.go b/replay/replay.go index f7f13d4..a7593b7 100644 --- a/replay/replay.go +++ b/replay/replay.go @@ -27,16 +27,16 @@ package replay import ( "bytes" "encoding/gob" - "flag" - "fmt" "log" "net" - "os" ) const bufSize = 1024 * 10 -var settings ReplaySettings = ReplaySettings{} +// Enable debug logging only if "--verbose" flag passed +func Debug(v ...interface{}) { + if Settings.verbose { log.Println(v...) } +} // Decode HttpRequest object using standard gob decoder func DecodeRequest(enc []byte) (request *HttpRequest, err error) { @@ -57,13 +57,13 @@ func DecodeRequest(enc []byte) (request *HttpRequest, err error) { func Run() { var buf [bufSize]byte - addr, err := net.ResolveUDPAddr("udp", settings.Address()) + addr, err := net.ResolveUDPAddr("udp", Settings.Address()) if err != nil { log.Fatal("Can't start:", err) } conn, err := net.ListenUDP("udp", addr) - fmt.Println("Starting replay server at:", settings.Address()) + log.Println("Starting replay server at:", Settings.Address()) if err != nil { log.Fatal("Can't start:", err) @@ -71,8 +71,8 @@ func Run() { defer conn.Close() - for _, host := range settings.ForwardedHosts() { - fmt.Println("Forwarding requests to:", host.Url, "limit:", host.Limit) + for _, host := range Settings.ForwardedHosts() { + log.Println("Forwarding requests to:", host.Url, "limit:", host.Limit) } requestFactory := NewRequestFactory() @@ -99,23 +99,4 @@ func Run() { } } -} - -func init() { - if len(os.Args) < 2 || os.Args[1] != "replay" { - return - } - - const ( - defaultPort = 28020 - defaultHost = "0.0.0.0" - - defaultAddress = "http://localhost:8080" - ) - - flag.IntVar(&settings.port, "p", defaultPort, "specify port number") - - flag.StringVar(&settings.host, "ip", defaultHost, "ip addresses to listen on") - - flag.StringVar(&settings.forwardAddress, "f", defaultAddress, "http address to forward traffic.\n\tYou can limit requests per second by adding `|num` after address.\n\tIf you have multiple addresses with different limits. For example: http://staging.example.com|100,http://dev.example.com|10") -} +} \ No newline at end of file diff --git a/replay/request_factory.go b/replay/request_factory.go index 0e6cb84..09be13f 100644 --- a/replay/request_factory.go +++ b/replay/request_factory.go @@ -1,7 +1,6 @@ package replay import ( - "fmt" "net/http" ) @@ -69,7 +68,7 @@ func (f *RequestFactory) sendRequest(host *ForwardHost, request *HttpRequest) { // Handle incoming requests, and they responses func (f *RequestFactory) handleRequests() { - hosts := settings.ForwardedHosts() + hosts := Settings.ForwardedHosts() for { select { @@ -82,10 +81,9 @@ func (f *RequestFactory) handleRequests() { // Increment Stat.Count host.Stat.IncReq() - fmt.Println("Sending request") + Debug("GET ",host.Url + req.Url) + go f.sendRequest(host, req) - } else { - fmt.Println("Throttling for host:", host.Url, host.Stat.Count, host.Limit) } } case resp := <-f.responses: diff --git a/replay/request_stats.go b/replay/request_stats.go index d122f50..d1bbf32 100644 --- a/replay/request_stats.go +++ b/replay/request_stats.go @@ -1,7 +1,6 @@ package replay import ( - "log" "time" ) @@ -44,8 +43,8 @@ func (s *RequestStat) IncResp(resp *HttpResponse) { // Updated stats timestamp to current time and reset to zero all stats values // TODO: Further on reset it should write stats to file func (s *RequestStat) reset() { - if s.timestamp != 0 { - log.Println("Host:", s.host.Url, "Requests:", s.Count, "Errors:", s.Errors, "Status codes:", s.Codes) + if s.timestamp != 0 { + Debug("Host:", s.host.Url, "Requests:", s.Count, "Errors:", s.Errors, "Status codes:", s.Codes) } s.timestamp = time.Now().Unix() diff --git a/replay/settings.go b/replay/settings.go index 11900e7..ce3ab71 100644 --- a/replay/settings.go +++ b/replay/settings.go @@ -3,6 +3,8 @@ package replay import ( "strconv" "strings" + "os" + "flag" ) type ForwardHost struct { @@ -17,6 +19,8 @@ type ReplaySettings struct { host string forwardAddress string + + verbose bool } // ForwardedHosts implements forwardAddress syntax support for multiple hosts (coma separated), and rate limiting by specifing "|maxRps" after host name. @@ -50,3 +54,27 @@ func (r *ReplaySettings) ForwardedHosts() (hosts []*ForwardHost) { func (r *ReplaySettings) Address() string { return r.host + ":" + strconv.Itoa(r.port) } + +var Settings ReplaySettings = ReplaySettings{} + + +func init() { + if len(os.Args) < 2 || os.Args[1] != "replay" { + return + } + + const ( + defaultPort = 28020 + defaultHost = "0.0.0.0" + + defaultAddress = "http://localhost:8080" + ) + + flag.IntVar(&Settings.port, "p", defaultPort, "specify port number") + + flag.StringVar(&Settings.host, "ip", defaultHost, "ip addresses to listen on") + + flag.StringVar(&Settings.forwardAddress, "f", defaultAddress, "http address to forward traffic.\n\tYou can limit requests per second by adding `|num` after address.\n\tIf you have multiple addresses with different limits. For example: http://staging.example.com|100,http://dev.example.com|10") + + flag.BoolVar(&Settings.verbose, "verbose", false, "Log requests") +}