From f7e2d04cf4932eac3fa30bbc030bcd82dd750676 Mon Sep 17 00:00:00 2001 From: Nick Tan Date: Tue, 8 Mar 2016 06:09:45 -0800 Subject: [PATCH] initial implementation with fasthttp --- .../eriklupander/gotling/httpaction.go | 10 ++- .../eriklupander/gotling/httpreq.go | 71 +++++++++++++------ .../eriklupander/gotling/variableprocessor.go | 26 +++---- 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/github.com/eriklupander/gotling/httpaction.go b/src/github.com/eriklupander/gotling/httpaction.go index 69e4170..1a059f9 100644 --- a/src/github.com/eriklupander/gotling/httpaction.go +++ b/src/github.com/eriklupander/gotling/httpaction.go @@ -33,6 +33,7 @@ type HttpAction struct { Body string `yaml:"body"` Accept string `yaml:"accept"` Title string `yaml:"title"` + Client string `yaml:"client"` ResponseHandler HttpResponseHandler `yaml:"response"` } @@ -42,7 +43,7 @@ func (h HttpAction) Execute(resultsChannel chan HttpReqResult, sessionMap map[st type HttpResponseHandler struct { Jsonpath string `yaml:"jsonpath"` - Xmlpath string `yaml:"xmlpath"` + Xmlpath string `yaml:"xmlpath"` Variable string `yaml:"variable"` Index string `yaml:"index"` } @@ -108,12 +109,19 @@ func NewHttpAction(a map[interface{}]interface{}) HttpAction { accept = a["accept"].(string) } + // default http client as Go net/http + httpclient := "std" + if a["client"] != nil && len(a["client"].(string)) > 0 { + httpclient = a["client"].(string) + } + httpAction := HttpAction{ a["method"].(string), a["url"].(string), getBody(a), accept, a["title"].(string), + httpclient, responseHandler} return httpAction diff --git a/src/github.com/eriklupander/gotling/httpreq.go b/src/github.com/eriklupander/gotling/httpreq.go index 4b4e7a1..baf0dad 100644 --- a/src/github.com/eriklupander/gotling/httpreq.go +++ b/src/github.com/eriklupander/gotling/httpreq.go @@ -24,43 +24,77 @@ SOFTWARE. package main import ( + "bytes" "io/ioutil" "log" "math/rand" "net/http" "strings" "time" - "gopkg.in/xmlpath.v2" + "github.com/NodePrime/jsonpath" - "bytes" + "github.com/valyala/fasthttp" + "gopkg.in/xmlpath.v2" ) // Accepts a Httpaction and a one-way channel to write the results to. func DoHttpRequest(httpAction HttpAction, resultsChannel chan HttpReqResult, sessionMap map[string]string) { - req := buildHttpRequest(httpAction, sessionMap) - client := &http.Client{} - start := time.Now() - resp, err := client.Do(req) - if err != nil { - //log.Printf("HTTP request failed") - } else { + if httpAction.Client == "fasthttp" { + // TODO ADD TLS CERT CONF SUPPORT + c := &fasthttp.Client{} + + req := fasthttp.AcquireRequest() + req.Header.SetMethod(httpAction.Method) + req.Header.SetRequestURI(SubstParams(sessionMap, httpAction.Url)) + req.Header.Set("Accept", httpAction.Accept) + + resp := fasthttp.AcquireResponse() + + defer fasthttp.ReleaseResponse(resp) + defer fasthttp.ReleaseRequest(req) + + // TODO ADD TIMEOUT SUPPORT + start := time.Now() + err := c.Do(req, resp) elapsed := time.Since(start) - responseBody, err := ioutil.ReadAll(resp.Body) if err != nil { - //log.Fatal(err) - log.Printf("Reading HTTP response failed: %s\n", err) - httpReqResult := buildHttpResult(0, resp.StatusCode, elapsed.Nanoseconds(), httpAction.Title) + log.Printf("Send HTTP request failed: %s\n", err) + httpReqResult := buildHttpResult(0, resp.Header.StatusCode(), elapsed.Nanoseconds(), httpAction.Title) resultsChannel <- httpReqResult } else { - defer resp.Body.Close() - // if action specifies response action, parse using regexp/jsonpath + responseBody := resp.Body() processResult(httpAction, sessionMap, responseBody) - - httpReqResult := buildHttpResult(len(responseBody), resp.StatusCode, elapsed.Nanoseconds(), httpAction.Title) + httpReqResult := buildHttpResult(len(responseBody), resp.Header.StatusCode(), elapsed.Nanoseconds(), httpAction.Title) resultsChannel <- httpReqResult } + } else { + req := buildHttpRequest(httpAction, sessionMap) + client := &http.Client{} + start := time.Now() + resp, err := client.Do(req) + if err != nil { + //log.Printf("HTTP request failed") + } else { + responseBody, err := ioutil.ReadAll(resp.Body) + elapsed := time.Since(start) + if err != nil { + //log.Fatal(err) + log.Printf("Reading HTTP response failed: %s\n", err) + httpReqResult := buildHttpResult(0, resp.StatusCode, elapsed.Nanoseconds(), httpAction.Title) + + resultsChannel <- httpReqResult + } else { + defer resp.Body.Close() + // if action specifies response action, parse using regexp/jsonpath + processResult(httpAction, sessionMap, responseBody) + + httpReqResult := buildHttpResult(len(responseBody), resp.StatusCode, elapsed.Nanoseconds(), httpAction.Title) + + resultsChannel <- httpReqResult + } + } } } @@ -130,7 +164,6 @@ func processResult(httpAction HttpAction, sessionMap map[string]string, response passResultIntoSessionMap(resultsArray, httpAction, sessionMap) } - if httpAction.ResponseHandler.Xmlpath != "" { path := xmlpath.MustCompile(httpAction.ResponseHandler.Xmlpath) r := bytes.NewReader(responseBody) @@ -174,8 +207,6 @@ func trimChar(s string, r byte) string { return s } - - func passResultIntoSessionMap(resultsArray []string, httpAction HttpAction, sessionMap map[string]string) { resultCount := len(resultsArray) diff --git a/src/github.com/eriklupander/gotling/variableprocessor.go b/src/github.com/eriklupander/gotling/variableprocessor.go index d75a302..212db35 100644 --- a/src/github.com/eriklupander/gotling/variableprocessor.go +++ b/src/github.com/eriklupander/gotling/variableprocessor.go @@ -22,23 +22,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package main + import ( -"regexp" -"strings" - "net/url" + "net/url" + "regexp" + "strings" ) var re = regexp.MustCompile("\\$\\{([a-zA-Z0-9]{0,})\\}") func SubstParams(sessionMap map[string]string, textData string) string { - if strings.ContainsAny(textData, "${") { - res := re.FindAllStringSubmatch(textData, -1) - for _, v := range res { - textData = strings.Replace(textData, "${" + v[1] + "}", url.QueryEscape(sessionMap[v[1]]), 1) - } - return textData - } else { - return textData - } - return textData + if strings.ContainsAny(textData, "${") { + res := re.FindAllStringSubmatch(textData, -1) + for _, v := range res { + textData = strings.Replace(textData, "${"+v[1]+"}", url.QueryEscape(sessionMap[v[1]]), 1) + } + return textData + } else { + return textData + } }