First commit

This commit is contained in:
Leonid Bugaev
2013-05-30 15:38:18 +06:00
commit 034ab45abb
6 changed files with 404 additions and 0 deletions
View File
Executable
BIN
View File
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
package main
import (
"flag"
"fmt"
"gor/listener"
"gor/replay"
"os"
)
var mode string
func main() {
if len(os.Args) > 1 {
mode = os.Args[1]
}
// Remove mode attr
os.Args = append(os.Args[:1], os.Args[2:]...)
flag.Parse()
switch mode {
case "listen":
listener.Run()
case "replay":
replay.Run()
default:
fmt.Println("Usage: gor listen")
}
}
+161
View File
@@ -0,0 +1,161 @@
package listener
import (
"fmt"
"io"
"log"
"os/exec"
//"time"
"bufio"
"bytes"
"encoding/gob"
//"errors"
"flag"
"net"
"os"
"regexp"
"strconv"
"strings"
)
type ListenerSettings struct {
networkInterface string
port int
replayAddress string
}
var settings ListenerSettings = ListenerSettings{}
type HttpRequest struct {
Tag string
Method string
Url string
Headers map[string]string
}
func readOutput(pipe io.ReadCloser, c chan *HttpRequest, err chan int) {
re := regexp.MustCompile("(GET) (/.*) HTTP/1.1")
headers_re := regexp.MustCompile("([^ ]*): (.*)")
reader := bufio.NewScanner(pipe)
var request *HttpRequest
var requestStarted = false
for reader.Scan() {
line := reader.Text()
if strings.Index(line, "HTTP/1.1") != -1 {
match := re.FindAllString(line, -1)
if len(match) > 0 {
info := strings.Split(match[0], " ")
request = &HttpRequest{
Method: info[0],
Url: info[1],
Headers: make(map[string]string),
}
requestStarted = true
}
}
if requestStarted {
if line == "" {
c <- request
requestStarted = false
} else {
match := headers_re.FindAllString(line, -1)
if len(match) > 0 {
header := strings.Split(match[0], ": ")
request.Headers[header[0]] = header[1]
}
}
}
}
}
func sendOutput(c chan *HttpRequest, quite chan int) {
serverAddr, err := net.ResolveUDPAddr("udp4", settings.replayAddress)
conn, err := net.DialUDP("udp", nil, serverAddr)
defer conn.Close()
if err != nil {
log.Fatal("Connection error", err)
}
for {
select {
case request := <-c:
fmt.Println("Request:", request.Url)
msg := bytes.Buffer{}
enc := gob.NewEncoder(&msg)
err := enc.Encode(request)
conn.Write(msg.Bytes())
if err != nil {
log.Println("encode error:", err)
}
case <-quite:
conn.Close()
return
}
}
}
func Run() {
fmt.Println("Settings:", settings)
if !strings.Contains(settings.replayAddress, ":") {
settings.replayAddress = settings.replayAddress + ":28020"
}
cmd := exec.Command("tcpdump", "-vv", "-A", "-i", settings.networkInterface, "port "+strconv.Itoa(settings.port))
//cmd := exec.Command("ls", "-al")
stdout, _ := cmd.StdoutPipe()
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
c := make(chan *HttpRequest)
err := make(chan int)
go readOutput(stdout, c, err)
go sendOutput(c, err)
if err := cmd.Wait(); err != nil {
flag.Usage()
}
}
func init() {
if len(os.Args) > 1 && 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.")
}
+172
View File
@@ -0,0 +1,172 @@
package replay
import (
"bytes"
"encoding/gob"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"strconv"
)
const (
bufSize = 1024 * 10
)
type ReplaySettings struct {
port int
host string
limit int
address string
}
var settings ReplaySettings = ReplaySettings{}
type HttpRequest struct {
Tag string
Method string
Url string
Headers map[string]string
}
type Response struct {
req *HttpRequest
resp *http.Response
err error
}
func sendRequest(request *HttpRequest, responses chan *Response) {
var req *http.Request
client := &http.Client{}
req, err := http.NewRequest("GET", settings.address+request.Url, nil)
if err != nil {
responses <- &Response{err: err}
return
}
for key, value := range request.Headers {
req.Header.Add(key, value)
}
resp, err := client.Do(req)
responses <- &Response{request, resp, err}
}
func handleRequests(requests chan *HttpRequest) {
stat := &RequestStat{}
stat.reset()
responses := make(chan *Response)
for {
select {
case req := <-requests:
go sendRequest(req, responses)
case resp := <-responses:
stat.inc(resp)
if resp.err != nil {
log.Println("Request err:", resp.err)
} else {
log.Println("Request ok:", resp.req.Url)
}
}
}
}
func decodeRequest(enc []byte) (request *HttpRequest, err error) {
var buf bytes.Buffer
buf.Write(enc)
request = &HttpRequest{}
encoder := gob.NewDecoder(&buf)
err = encoder.Decode(request)
return
}
func Run() {
var buf [bufSize]byte
serverAddress := settings.host + ":" + strconv.Itoa(settings.port)
addr, err := net.ResolveUDPAddr("udp", serverAddress)
if err != nil {
log.Fatal("Can't start:", err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Fatal("Can't start:", err)
}
defer conn.Close()
if settings.address == "" {
}
fmt.Println("Starting replay server at:", serverAddress)
fmt.Println("Forwarding incoming requests to:", settings.address)
fmt.Println("Limiting concurrent request count to:", settings.limit)
requests := make(chan *HttpRequest)
go handleRequests(requests)
for {
rlen, _, err := conn.ReadFromUDP(buf[0:])
if err != nil {
continue
}
if rlen > 0 {
if rlen > bufSize {
log.Fatal("Too large udp packet", bufSize)
}
request, err := decodeRequest(buf[0:rlen])
if err != nil {
log.Println("Decode error:", err)
} else {
requests <- request
}
}
}
}
func init() {
if len(os.Args) > 1 && os.Args[1] != "replay" {
return
}
const (
defaultPort = 28020
defaultHost = "0.0.0.0"
defaultLimit = 10
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.IntVar(&settings.limit, "l", defaultLimit, "limit number for requests per second. It will start dropping packets.")
flag.StringVar(&settings.address, "h", defaultAddress, "http address to forward traffic ")
}
+39
View File
@@ -0,0 +1,39 @@
package replay
import (
"log"
"time"
)
type RequestStat struct {
timestamp int64
codes map[int]int
count int
errors int
}
func (s *RequestStat) inc(resp *Response) {
if s.timestamp != time.Now().Unix() {
s.reset()
}
if resp.err != nil {
s.errors++
return
}
s.count++
s.codes[resp.resp.StatusCode]++
}
func (s *RequestStat) reset() {
log.Println("reseting stats", s)
s.timestamp = time.Now().Unix()
s.codes = make(map[int]int)
s.count = 0
s.errors = 0
}