// Gor is simple http traffic replication tool written in Go. Its main goal to replay traffic from production servers to staging and dev environments. // Now you can test your code on real user sessions in an automated and repeatable fashion. package main import ( "flag" "log" "net/http" "net/http/httputil" _ "net/http/pprof" "os" "os/signal" "runtime" _ "runtime/debug" "runtime/pprof" "syscall" "time" ) var ( cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") memprofile = flag.String("memprofile", "", "write memory profile to this file") ) func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rb, _ := httputil.DumpRequest(r, false) log.Println(string(rb)) next.ServeHTTP(w, r) }) } func main() { if len(os.Getenv("GOMAXPROCS")) == 0 { runtime.GOMAXPROCS(runtime.NumCPU() * 2) } args := os.Args[1:] var plugins *InOutPlugins if len(args) > 0 && args[0] == "file-server" { if len(args) != 2 { log.Fatal("You should specify port and IP (optional) for the file server. Example: `gor file-server :80`") } dir, _ := os.Getwd() Debug(0, "Started example file server for current directory on address ", args[1]) log.Fatal(http.ListenAndServe(args[1], loggingMiddleware(http.FileServer(http.Dir(dir))))) } else { flag.Parse() checkSettings() plugins = NewPlugins() } log.Printf("[PPID %d and PID %d] Version:%s\n", os.Getppid(), os.Getpid(), VERSION) if len(plugins.Inputs) == 0 || len(plugins.Outputs) == 0 { log.Fatal("Required at least 1 input and 1 output") } if *memprofile != "" { profileMEM(*memprofile) } if *cpuprofile != "" { profileCPU(*cpuprofile) } if Settings.Pprof != "" { go func() { log.Println(http.ListenAndServe(Settings.Pprof, nil)) }() } closeCh := make(chan int) emitter := NewEmitter() go emitter.Start(plugins, Settings.Middleware) if Settings.ExitAfter > 0 { log.Printf("Running gor for a duration of %s\n", Settings.ExitAfter) time.AfterFunc(Settings.ExitAfter, func() { log.Printf("gor run timeout %s\n", Settings.ExitAfter) close(closeCh) }) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) exit := 0 select { case <-c: exit = 1 case <-closeCh: exit = 0 } emitter.Close() os.Exit(exit) } func profileCPU(cpuprofile string) { if cpuprofile != "" { f, err := os.Create(cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) time.AfterFunc(30*time.Second, func() { pprof.StopCPUProfile() f.Close() }) } } func profileMEM(memprofile string) { if memprofile != "" { f, err := os.Create(memprofile) if err != nil { log.Fatal(err) } time.AfterFunc(30*time.Second, func() { pprof.WriteHeapProfile(f) f.Close() }) } }