diff --git a/README.md b/README.md index b4edc1e..0c0f8e8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A simple benchmark testing tool implemented in golang, the basic functions refer - simple code, easy to change ## Building -``` +```shell script git clone git://github.com/cnlh/benchmark.git cd benchmark go build @@ -14,12 +14,12 @@ go build ## Usage basic usage is quite simple: -``` +```shell script benchmark [flags] url ``` with the flags being -``` +```shell script -b string the body of request -c int @@ -40,7 +40,7 @@ with the flags being proxy of request ``` for example -``` +```shell script benchmark -c 1100 -n 1000000 http://127.0.0.1/ ``` diff --git a/benchmark.go b/benchmark.go index 706fc09..2ba5471 100644 --- a/benchmark.go +++ b/benchmark.go @@ -1,3 +1,7 @@ +// Copyright 2020 The benchmark. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package main import ( @@ -8,6 +12,7 @@ import ( "time" ) +// benchmark is used to manager connection and deal with the result type benchmark struct { connectionNum int reqNum int64 @@ -23,6 +28,7 @@ type benchmark struct { reqConnList []*ReqConn } +// Start benchmark with the param has setting func (pf *benchmark) Run() { fmt.Printf("Running %d test @ %s by %d connections\n", pf.reqNum, pf.target, pf.connectionNum) var err error @@ -54,6 +60,7 @@ func (pf *benchmark) Run() { return } +// Print the result of benchmark on console func (pf *benchmark) Print() { readAll := 0 writeAll := 0 @@ -65,10 +72,10 @@ func (pf *benchmark) Print() { allTimes = append(allTimes, v.reqTimes...) allError += v.ErrorTimes } - second := pf.endTime.Sub(pf.startTime).Seconds() - fmt.Printf("%d requests in %.2fs, %s read, %s write\n", pf.reqNum, second, formatFlow(float64(readAll)), formatFlow(float64(writeAll))) - fmt.Printf("Requests/sec: %.2f\n", float64(pf.reqNum)/second) - fmt.Printf("Transfer/sec: %s\n", formatFlow(float64(readAll+writeAll)/second)) + runSecond := pf.endTime.Sub(pf.startTime).Seconds() + fmt.Printf("%d requests in %.2fs, %s read, %s write\n", pf.reqNum, runSecond, formatFlow(float64(readAll)), formatFlow(float64(writeAll))) + fmt.Printf("Requests/sec: %.2f\n", float64(pf.reqNum)/runSecond) + fmt.Printf("Transfer/sec: %s\n", formatFlow(float64(readAll+writeAll)/runSecond)) fmt.Printf("Error : %d\n", allError) sort.Ints(allTimes) rates := []int{50, 65, 75, 80, 90, 95, 98, 99, 100} @@ -78,6 +85,7 @@ func (pf *benchmark) Print() { } } +// Format the flow data func formatFlow(size float64) string { var rt float64 var suffix string @@ -87,7 +95,6 @@ func formatFlow(size float64) string { MByte = KByte * 1024 GByte = MByte * 1024 ) - if size > GByte { rt = size / GByte suffix = "GB" @@ -101,7 +108,5 @@ func formatFlow(size float64) string { rt = size suffix = "bytes" } - - srt := fmt.Sprintf("%.2f%v", rt, suffix) - return srt + return fmt.Sprintf("%.2f%v", rt, suffix) } diff --git a/benchmark_test.go b/benchmark_test.go new file mode 100644 index 0000000..eb74521 --- /dev/null +++ b/benchmark_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "io" + "net/http" + "net/http/httputil" + "testing" + "time" +) + +func TestMain(m *testing.M) { + // create a http server + http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + io.WriteString(writer, "work well!") + }) + go http.ListenAndServe(":15342", nil) + time.Sleep(time.Second) + m.Run() +} + +func TestBenchmark_Run(t *testing.T) { + // create a request + r, err := http.NewRequest("GET", "http://127.0.0.0.1:15342", nil) + if err != nil { + t.Fatal(err) + } + writeBytes, err := httputil.DumpRequest(r, true) + if err != nil { + t.Fatal(err) + } + p := &benchmark{ + connectionNum: 100, + reqNum: 20000, + requestBytes: writeBytes, + target: "127.0.0.1:15342", + schema: r.URL.Scheme, + timeout: 30000, + reqConnList: make([]*ReqConn, 0), + } + p.Run() + p.Print() +} diff --git a/connection.go b/connection.go index 8d6d5f0..8fbf264 100644 --- a/connection.go +++ b/connection.go @@ -1,3 +1,7 @@ +// Copyright 2020 The benchmark. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package main import ( @@ -17,12 +21,13 @@ import ( ) var ( - rawBytes = []byte("\r\n\r\n") - rowBytes = []byte("\r\n") - lenBytes = []byte("Content-Length: ") - l = len(lenBytes) + bodyHeaderSepBytes = []byte{13, 10, 13, 10} + bodyHeaderSepBytesLen = 4 + headerSepBytes = []byte{13, 10} + contentLengthBytes = []byte{67, 111, 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58, 32} + contentLengthBytesLen = 16 ) - +// ReqConn is used to create a connection and record data type ReqConn struct { ErrorTimes int Count int64 @@ -39,6 +44,8 @@ type ReqConn struct { proxy string } +// Connect to the server, http and socks5 proxy support +// If the target is https, convert connection to tls client func (rc *ReqConn) dial() error { if rc.conn != nil { rc.conn.Close() @@ -79,6 +86,7 @@ func (rc *ReqConn) dial() error { return nil } +// Start a connection, send request to server and read response from server func (rc *ReqConn) Start() (err error) { var contentLen string var bodyHasRead int @@ -110,16 +118,16 @@ re: headerHasRead += n rc.readLen += n var bbArr [2][]byte - bodyPos := bytes.Index(rc.buf[:headerHasRead], rawBytes) + bodyPos := bytes.Index(rc.buf[:headerHasRead], bodyHeaderSepBytes) if bodyPos > -1 { bbArr[0] = rc.buf[:bodyPos] - bbArr[1] = rc.buf[bodyPos+len(rawBytes):] + bbArr[1] = rc.buf[bodyPos+bodyHeaderSepBytesLen:] } else { goto readHeader } - n := bytes.Index(bbArr[0], lenBytes) - start := n + l - end := bytes.Index(bbArr[0][start:], rowBytes) + n := bytes.Index(bbArr[0], contentLengthBytes) + start := n + contentLengthBytesLen + end := bytes.Index(bbArr[0][start:], headerSepBytes) if end == -1 { contentLen = Bytes2str(bbArr[0][start:]) } else { @@ -145,10 +153,12 @@ re: } } +// Convert bytes to strings func Bytes2str(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } +// Create a connection by http proxy server func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) { req, err := http.NewRequest("CONNECT", "http://"+remoteAddr, nil) if err != nil { @@ -156,7 +166,6 @@ func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) { } password, _ := url.User.Password() req.SetBasicAuth(url.User.Username(), password) - // we make a http proxy request proxyConn, err := net.Dial("tcp", url.Host) if err != nil { return nil, err diff --git a/main.go b/main.go index 15432c5..ac0211d 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,7 @@ +// Copyright 2020 The benchmark. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package main import ( @@ -5,6 +9,7 @@ import ( "fmt" "net/http" "net/http/httputil" + "net/url" "runtime" "strings" ) @@ -23,6 +28,10 @@ var ( func main() { flag.Parse() + if u, err := url.Parse(flag.Arg(0)); err != nil || u.Host == "" { + fmt.Printf("the request url %s is not correct \n", flag.Arg(0)) + return + } payload := strings.NewReader(*body) req, err := http.NewRequest(*method, flag.Arg(0), payload) if err != nil {